diff --git a/Cargo.lock b/Cargo.lock index 92e659a..6a660a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1263,6 +1263,7 @@ dependencies = [ "reis", "serde", "serde_json", + "slab", "tempfile", "tokio", "toml", diff --git a/Cargo.toml b/Cargo.toml index d6bf019..7bb6e74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/capture/libei.rs b/src/capture/libei.rs index 44e49c0..1b27587 100644 --- a/src/capture/libei.rs +++ b/src/capture/libei.rs @@ -46,7 +46,7 @@ enum ProducerEvent { pub struct LibeiInputCapture<'a> { input_capture: Pin>>, libei_task: JoinHandle>, - event_rx: tokio::sync::mpsc::Receiver<(u32, Event)>, + event_rx: tokio::sync::mpsc::Receiver<(ClientHandle, Event)>, notify_tx: tokio::sync::mpsc::Sender, } @@ -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>>, ) -> Result<()> { loop { @@ -396,7 +396,7 @@ async fn handle_ei_event( ei_event: EiEvent, current_client: Option, context: &ei::Context, - event_tx: &Sender<(u32, Event)>, + event_tx: &Sender<(ClientHandle, Event)>, ) { match ei_event { EiEvent::SeatAdded(s) => { diff --git a/src/client.rs b/src/client.rs index c834c89..2d3d279 100644 --- a/src/client.rs +++ b/src/client.rs @@ -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, /// fix ips, determined by the user pub fix_ips: Vec, - /// 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>, // HashMap likely not beneficial + clients: Slab, } 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 { 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 { // 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 { - self.clients.iter().filter_map(|x| x.as_ref()) + pub fn get_client_states(&self) -> impl Iterator { + self.clients.iter().map(|(k, v)| (k as ClientHandle, v)) } - pub fn get_client_states_mut(&mut self) -> impl Iterator { - self.clients.iter_mut().filter_map(|x| x.as_mut()) + pub fn get_client_states_mut( + &mut self, + ) -> impl Iterator { + self.clients.iter_mut().map(|(k, v)| (k as ClientHandle, v)) } } diff --git a/src/frontend.rs b/src/frontend.rs index fe0b62a..53d9aaa 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -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), /// list of all clients, used for initial state synchronization - Enumerate(Vec<(Client, bool)>), + Enumerate(Vec<(ClientHandle, Client, bool)>), /// an error occured Error(String), } diff --git a/src/frontend/cli.rs b/src/frontend/cli.rs index b3b537b..536860e 100644 --- a/src/frontend/cli.rs +++ b/src/frontend/cli.rs @@ -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 diff --git a/src/frontend/gtk.rs b/src/frontend/gtk.rs index 0ccada3..590f4e9 100644 --- a/src/frontend/gtk.rs +++ b/src/frontend/gtk.rs @@ -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); } } }, diff --git a/src/frontend/gtk/client_object.rs b/src/frontend/gtk/client_object.rs index 7067e37..aba7af6 100644 --- a/src/frontend/gtk/client_object.rs +++ b/src/frontend/gtk/client_object.rs @@ -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()) diff --git a/src/frontend/gtk/window.rs b/src/frontend/gtk/window.rs index f0aa0f6..887ed40 100644 --- a/src/frontend/gtk/window.rs +++ b/src/frontend/gtk/window.rs @@ -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(); diff --git a/src/server.rs b/src/server.rs index 4b2f28c..bf926f5 100644 --- a/src/server.rs +++ b/src/server.rs @@ -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 } diff --git a/src/server/capture_task.rs b/src/server/capture_task.rs index 886455b..ab32fc5 100644 --- a/src/server/capture_task.rs +++ b/src/server/capture_task.rs @@ -84,8 +84,8 @@ async fn handle_capture_event( pressed_keys: &mut HashSet, 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; diff --git a/src/server/emulation_task.rs b/src/server/emulation_task.rs index 6ac7c38..fbf1b81 100644 --- a/src/server/emulation_task.rs +++ b/src/server/emulation_task.rs @@ -65,7 +65,7 @@ pub fn new( .client_manager .borrow() .get_client_states() - .map(|s| s.client.handle) + .map(|(h, _)| h) .collect::>(); for client in clients { release_keys(&server, &mut emulate, client).await; diff --git a/src/server/frontend_task.rs b/src/server/frontend_task.rs index 3179a69..2acf4ea 100644 --- a/src/server/frontend_task.rs +++ b/src/server/frontend_task.rs @@ -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, emulate: &Sender, - 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, emulate: &Sender, - 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; } diff --git a/src/server/ping_task.rs b/src/server/ping_task.rs index 7f5918d..e66ccba 100644 --- a/src/server/ping_task.rs +++ b/src/server/ping_task.rs @@ -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; }