use slab instead of reinventing the wheel (#112)

This commit is contained in:
Ferdinand Schober
2024-04-26 00:10:04 +02:00
committed by GitHub
parent 279e582698
commit 3e96b42067
14 changed files with 83 additions and 115 deletions

1
Cargo.lock generated
View File

@@ -1263,6 +1263,7 @@ dependencies = [
"reis",
"serde",
"serde_json",
"slab",
"tempfile",
"tokio",
"toml",

View File

@@ -34,6 +34,7 @@ keycode = "0.4.0"
once_cell = "1.19.0"
num_enum = "0.7.2"
hostname = "0.4.0"
slab = "0.4.9"
[target.'cfg(unix)'.dependencies]
libc = "0.2.148"

View File

@@ -46,7 +46,7 @@ enum ProducerEvent {
pub struct LibeiInputCapture<'a> {
input_capture: Pin<Box<InputCapture<'a>>>,
libei_task: JoinHandle<Result<()>>,
event_rx: tokio::sync::mpsc::Receiver<(u32, Event)>,
event_rx: tokio::sync::mpsc::Receiver<(ClientHandle, Event)>,
notify_tx: tokio::sync::mpsc::Sender<ProducerEvent>,
}
@@ -183,7 +183,7 @@ async fn connect_to_eis(
async fn libei_event_handler(
mut ei_event_stream: EiConvertEventStream,
context: ei::Context,
event_tx: Sender<(u32, Event)>,
event_tx: Sender<(ClientHandle, Event)>,
current_client: Rc<Cell<Option<ClientHandle>>>,
) -> Result<()> {
loop {
@@ -396,7 +396,7 @@ async fn handle_ei_event(
ei_event: EiEvent,
current_client: Option<ClientHandle>,
context: &ei::Context,
event_tx: &Sender<(u32, Event)>,
event_tx: &Sender<(ClientHandle, Event)>,
) {
match ei_event {
EiEvent::SeatAdded(s) => {

View File

@@ -5,6 +5,7 @@ use std::{
};
use serde::{Deserialize, Serialize};
use slab::Slab;
#[derive(Debug, Eq, Hash, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub enum Position {
@@ -66,10 +67,6 @@ pub struct Client {
pub hostname: Option<String>,
/// fix ips, determined by the user
pub fix_ips: Vec<IpAddr>,
/// unique handle to refer to the client.
/// This way any emulation / capture backend does not
/// need to know anything about a client other than its handle.
pub handle: ClientHandle,
/// all ip addresses associated with a particular client
/// e.g. Laptops usually have at least an ethernet and a wifi port
/// which have different ip addresses
@@ -86,7 +83,7 @@ pub enum ClientEvent {
Destroy(ClientHandle),
}
pub type ClientHandle = u32;
pub type ClientHandle = u64;
#[derive(Debug, Clone)]
pub struct ClientState {
@@ -105,7 +102,7 @@ pub struct ClientState {
}
pub struct ClientManager {
clients: Vec<Option<ClientState>>, // HashMap likely not beneficial
clients: Slab<ClientState>,
}
impl Default for ClientManager {
@@ -116,7 +113,8 @@ impl Default for ClientManager {
impl ClientManager {
pub fn new() -> Self {
Self { clients: vec![] }
let clients = Slab::new();
Self { clients }
}
/// add a new client to this manager
@@ -128,38 +126,24 @@ impl ClientManager {
pos: Position,
active: bool,
) -> ClientHandle {
// get a new client_handle
let handle = self.free_id();
// store fix ip addresses
let fix_ips = ips.iter().cloned().collect();
// store the client
let client = Client {
hostname,
fix_ips,
handle,
ips,
port,
pos,
};
// client was never seen, nor pinged
let client_state = ClientState {
client,
client: Client {
hostname,
fix_ips,
ips,
port,
pos,
},
active,
active_addr: None,
alive: false,
pressed_keys: HashSet::new(),
};
if handle as usize >= self.clients.len() {
assert_eq!(handle as usize, self.clients.len());
self.clients.push(Some(client_state));
} else {
self.clients[handle as usize] = Some(client_state);
}
handle
self.clients.insert(client_state) as ClientHandle
}
/// find a client by its address
@@ -168,11 +152,11 @@ impl ClientManager {
// time this is likely faster than using a HashMap
self.clients
.iter()
.position(|c| {
if let Some(c) = c {
c.active && c.client.ips.contains(&addr.ip())
.find_map(|(k, c)| {
if c.active && c.client.ips.contains(&addr.ip()) {
Some(k)
} else {
false
None
}
})
.map(|p| p as ClientHandle)
@@ -181,11 +165,11 @@ impl ClientManager {
pub fn find_client(&self, pos: Position) -> Option<ClientHandle> {
self.clients
.iter()
.position(|c| {
if let Some(c) = c {
c.active && c.client.pos == pos
.find_map(|(k, c)| {
if c.active && c.client.pos == pos {
Some(k)
} else {
false
None
}
})
.map(|p| p as ClientHandle)
@@ -194,36 +178,26 @@ impl ClientManager {
/// remove a client from the list
pub fn remove_client(&mut self, client: ClientHandle) -> Option<ClientState> {
// remove id from occupied ids
self.clients.get_mut(client as usize)?.take()
}
/// get a free slot in the client list
fn free_id(&mut self) -> ClientHandle {
for i in 0..u32::MAX {
if self.clients.get(i as usize).is_none()
|| self.clients.get(i as usize).unwrap().is_none()
{
return i;
}
}
panic!("Out of client ids");
self.clients.try_remove(client as usize)
}
// returns an immutable reference to the client state corresponding to `client`
pub fn get(&self, client: ClientHandle) -> Option<&ClientState> {
self.clients.get(client as usize)?.as_ref()
self.clients.get(client as usize)
}
/// returns a mutable reference to the client state corresponding to `client`
pub fn get_mut(&mut self, client: ClientHandle) -> Option<&mut ClientState> {
self.clients.get_mut(client as usize)?.as_mut()
self.clients.get_mut(client as usize)
}
pub fn get_client_states(&self) -> impl Iterator<Item = &ClientState> {
self.clients.iter().filter_map(|x| x.as_ref())
pub fn get_client_states(&self) -> impl Iterator<Item = (ClientHandle, &ClientState)> {
self.clients.iter().map(|(k, v)| (k as ClientHandle, v))
}
pub fn get_client_states_mut(&mut self) -> impl Iterator<Item = &mut ClientState> {
self.clients.iter_mut().filter_map(|x| x.as_mut())
pub fn get_client_states_mut(
&mut self,
) -> impl Iterator<Item = (ClientHandle, &mut ClientState)> {
self.clients.iter_mut().map(|(k, v)| (k as ClientHandle, v))
}
}

View File

@@ -106,15 +106,15 @@ pub enum FrontendEvent {
/// the given client was activated
Activated(ClientHandle, bool),
/// a client was created
Created(Client),
Created(ClientHandle, Client),
/// a client was updated
Updated(Client),
Updated(ClientHandle, Client),
/// the client was deleted
Deleted(ClientHandle),
/// new port, reason of failure (if failed)
PortChanged(u16, Option<String>),
/// list of all clients, used for initial state synchronization
Enumerate(Vec<(Client, bool)>),
Enumerate(Vec<(ClientHandle, Client, bool)>),
/// an error occured
Error(String),
}

View File

@@ -90,15 +90,13 @@ pub fn run() -> Result<()> {
log::info!("client {handle} deactivated");
}
}
FrontendEvent::Created(client) => {
let handle = client.handle;
FrontendEvent::Created(handle, client) => {
let port = client.port;
let pos = client.pos;
let hostname = client.hostname.as_deref().unwrap_or("");
log::info!("new client ({handle}): {hostname}:{port} - {pos}");
}
FrontendEvent::Updated(client) => {
let handle = client.handle;
FrontendEvent::Updated(handle, client) => {
let port = client.port;
let pos = client.pos;
let hostname = client.hostname.as_deref().unwrap_or("");
@@ -111,10 +109,10 @@ pub fn run() -> Result<()> {
log::warn!("{e}");
}
FrontendEvent::Enumerate(clients) => {
for (client, active) in clients.into_iter() {
for (handle, client, active) in clients.into_iter() {
log::info!(
"client ({}) [{}]: active: {}, associated addresses: [{}]",
client.handle,
handle,
client.hostname.as_deref().unwrap_or(""),
if active { "yes" } else { "no" },
client

View File

@@ -122,11 +122,11 @@ fn build_ui(app: &Application) {
FrontendEvent::Activated(handle, active) => {
window.activate_client(handle, active);
}
FrontendEvent::Created(client) => {
window.new_client(client, false);
FrontendEvent::Created(handle, client) => {
window.new_client(handle, client, false);
},
FrontendEvent::Updated(client) => {
window.update_client(client);
FrontendEvent::Updated(handle, client) => {
window.update_client(handle, client);
}
FrontendEvent::Error(e) => {
window.show_toast(e.as_str());
@@ -135,12 +135,12 @@ fn build_ui(app: &Application) {
window.delete_client(client);
}
FrontendEvent::Enumerate(clients) => {
for (client, active) in clients {
if window.client_idx(client.handle).is_some() {
window.activate_client(client.handle, active);
window.update_client(client);
for (handle, client, active) in clients {
if window.client_idx(handle).is_some() {
window.activate_client(handle, active);
window.update_client(handle, client);
} else {
window.new_client(client, active);
window.new_client(handle, client, active);
}
}
},

View File

@@ -10,9 +10,9 @@ glib::wrapper! {
}
impl ClientObject {
pub fn new(client: Client, active: bool) -> Self {
pub fn new(handle: ClientHandle, client: Client, active: bool) -> Self {
Object::builder()
.property("handle", client.handle)
.property("handle", handle)
.property("hostname", client.hostname)
.property("port", client.port as u32)
.property("position", client.pos.to_string())

View File

@@ -87,8 +87,8 @@ impl Window {
row
}
pub fn new_client(&self, client: Client, active: bool) {
let client = ClientObject::new(client, active);
pub fn new_client(&self, handle: ClientHandle, client: Client, active: bool) {
let client = ClientObject::new(handle, client, active);
self.clients().append(&client);
self.set_placeholder_visible(false);
}
@@ -115,9 +115,9 @@ impl Window {
}
}
pub fn update_client(&self, client: Client) {
let Some(idx) = self.client_idx(client.handle) else {
log::warn!("could not find client with handle {}", client.handle);
pub fn update_client(&self, handle: ClientHandle, client: Client) {
let Some(idx) = self.client_idx(handle) else {
log::warn!("could not find client with handle {}", handle);
return;
};
let client_object = self.clients().item(idx as u32).unwrap();

View File

@@ -134,9 +134,9 @@ impl Server {
.client_manager
.borrow()
.get_client_states()
.filter_map(|s| {
.filter_map(|(h, s)| {
if s.active {
Some((s.client.handle, s.client.hostname.clone()))
Some((h, s.client.hostname.clone()))
} else {
None
}

View File

@@ -84,8 +84,8 @@ async fn handle_capture_event(
pressed_keys: &mut HashSet<scancode::Linux>,
release_bind: &[scancode::Linux],
) -> Result<()> {
let (c, mut e) = event;
log::trace!("({c}) {e:?}");
let (handle, mut e) = event;
log::trace!("({handle}) {e:?}");
if let Event::Keyboard(KeyboardEvent::Key { key, state, .. }) = e {
update_pressed_keys(pressed_keys, key, state);
@@ -107,7 +107,7 @@ async fn handle_capture_event(
// get client state for handle
let mut client_manager = server.client_manager.borrow_mut();
let client_state = match client_manager.get_mut(c) {
let client_state = match client_manager.get_mut(handle) {
Some(state) => state,
None => {
// should not happen
@@ -123,10 +123,8 @@ async fn handle_capture_event(
// we get a leave event
if let Event::Enter() = e {
server.state.replace(State::AwaitingLeave);
server
.active_client
.replace(Some(client_state.client.handle));
log::trace!("Active client => {}", client_state.client.handle);
server.active_client.replace(Some(handle));
log::trace!("Active client => {}", handle);
start_timer = true;
log::trace!("STATE ===> AwaitingLeave");
enter = true;

View File

@@ -65,7 +65,7 @@ pub fn new(
.client_manager
.borrow()
.get_client_states()
.map(|s| s.client.handle)
.map(|(h, _)| h)
.collect::<Vec<_>>();
for client in clients {
release_keys(&server, &mut emulate, client).await;

View File

@@ -137,7 +137,7 @@ async fn handle_frontend_event(
.client_manager
.borrow()
.get_client_states()
.map(|s| (s.client.clone(), s.active))
.map(|(h, s)| (h, s.client.clone(), s.active))
.collect();
notify_all(frontend, FrontendEvent::Enumerate(clients)).await;
}
@@ -199,7 +199,7 @@ pub async fn add_client(
.unwrap()
.client
.clone();
notify_all(frontend, FrontendEvent::Created(client)).await;
notify_all(frontend, FrontendEvent::Created(handle, client)).await;
}
pub async fn deactivate_client(
@@ -207,12 +207,12 @@ pub async fn deactivate_client(
frontend: &mut FrontendListener,
capture: &Sender<CaptureEvent>,
emulate: &Sender<EmulationEvent>,
client: ClientHandle,
handle: ClientHandle,
) {
let (client, _) = match server.client_manager.borrow_mut().get_mut(client) {
let (client, _) = match server.client_manager.borrow_mut().get_mut(handle) {
Some(state) => {
state.active = false;
(state.client.handle, state.client.pos)
(handle, state.client.pos)
}
None => return,
};
@@ -265,24 +265,24 @@ pub async fn remove_client(
frontend: &mut FrontendListener,
capture: &Sender<CaptureEvent>,
emulate: &Sender<EmulationEvent>,
client: ClientHandle,
handle: ClientHandle,
) {
let Some((client, active)) = server
let Some(active) = server
.client_manager
.borrow_mut()
.remove_client(client)
.map(|s| (s.client.handle, s.active))
.remove_client(handle)
.map(|s| s.active)
else {
return;
};
if active {
let destroy = ClientEvent::Destroy(client);
let destroy = ClientEvent::Destroy(handle);
let _ = capture.send(CaptureEvent::ClientEvent(destroy)).await;
let _ = emulate.send(EmulationEvent::ClientEvent(destroy)).await;
}
let event = FrontendEvent::Deleted(client);
let event = FrontendEvent::Deleted(handle);
notify_all(frontend, event).await;
}
@@ -296,7 +296,7 @@ async fn update_client(
) {
let (handle, hostname, port, pos) = client_update;
let mut changed = false;
let (hostname, handle, active) = {
let (hostname, active) = {
// retrieve state
let mut client_manager = server.client_manager.borrow_mut();
let Some(state) = client_manager.get_mut(handle) else {
@@ -325,11 +325,7 @@ async fn update_client(
}
log::debug!("client updated: {:?}", state);
(
state.client.hostname.clone(),
state.client.handle,
state.active,
)
(state.client.hostname.clone(), state.active)
};
// resolve dns if something changed
@@ -358,5 +354,5 @@ async fn update_client(
.unwrap()
.client
.clone();
notify_all(frontend, FrontendEvent::Updated(client)).await;
notify_all(frontend, FrontendEvent::Updated(handle, client)).await;
}

View File

@@ -34,8 +34,8 @@ pub fn new(
// if receiving we care about clients with pressed keys
client_manager
.get_client_states_mut()
.filter(|s| !s.pressed_keys.is_empty())
.map(|s| s.client.handle)
.filter(|(_, s)| !s.pressed_keys.is_empty())
.map(|(h, _)| h)
.collect()
} else {
// if sending we care about the active client
@@ -64,7 +64,7 @@ pub fn new(
};
// reset alive
for state in client_manager.get_client_states_mut() {
for (_, state) in client_manager.get_client_states_mut() {
state.alive = false;
}