diff --git a/README.md b/README.md index 83c55f8b..5292dee0 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,7 @@ port = 4242 # define a client on the right side with host name "iridium" [right] # hostname -host_name = "iridium" +hostname = "iridium" # optional list of (known) ip addresses ips = ["192.168.178.156"] @@ -212,7 +212,7 @@ ips = ["192.168.178.156"] [left] # The hostname is optional: When no hostname is specified, # at least one ip address needs to be specified. -host_name = "thorium" +hostname = "thorium" # ips for ethernet and wifi ips = ["192.168.178.189", "192.168.178.172"] # optional port diff --git a/config.toml b/config.toml index c9b75a4c..1ec47898 100644 --- a/config.toml +++ b/config.toml @@ -8,7 +8,7 @@ port = 4242 # define a client on the right side with host name "iridium" [right] # hostname -host_name = "iridium" +hostname = "iridium" # optional list of (known) ip addresses ips = ["192.168.178.156"] @@ -16,7 +16,7 @@ ips = ["192.168.178.156"] [left] # The hostname is optional: When no hostname is specified, # at least one ip address needs to be specified. -host_name = "thorium" +hostname = "thorium" # ips for ethernet and wifi ips = ["192.168.178.189", "192.168.178.172"] # optional port diff --git a/src/client.rs b/src/client.rs index ca490b70..02577268 100644 --- a/src/client.rs +++ b/src/client.rs @@ -46,6 +46,20 @@ impl Display for Position { } } +impl TryFrom<&str> for Position { + type Error = (); + + fn try_from(s: &str) -> Result { + match s { + "left" => Ok(Position::Left), + "right" => Ok(Position::Right), + "top" => Ok(Position::Top), + "bottom" => Ok(Position::Bottom), + _ => Err(()), + } + } +} + #[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] pub struct Client { /// hostname of this client @@ -112,6 +126,7 @@ impl ClientManager { ips: HashSet, port: u16, pos: Position, + active: bool, ) -> ClientHandle { // get a new client_handle let handle = self.free_id(); @@ -135,7 +150,7 @@ impl ClientManager { // client was never seen, nor pinged let client_state = ClientState { client, - active: false, + active, active_addr: None, alive: false, pressed_keys: HashSet::new(), diff --git a/src/config.rs b/src/config.rs index dda35615..2b35100e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -15,17 +15,19 @@ pub const DEFAULT_PORT: u16 = 4242; pub struct ConfigToml { pub port: Option, pub frontend: Option, - pub left: Option, - pub right: Option, - pub top: Option, - pub bottom: Option, + pub left: Option, + pub right: Option, + pub top: Option, + pub bottom: Option, } #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] -pub struct Client { +pub struct TomlClient { + pub hostname: Option, pub host_name: Option, pub ips: Option>, pub port: Option, + pub activate_on_startup: Option, } impl ConfigToml { @@ -66,10 +68,18 @@ pub enum Frontend { pub struct Config { pub frontend: Frontend, pub port: u16, - pub clients: Vec<(Client, Position)>, + pub clients: Vec<(TomlClient, Position)>, pub daemon: bool, } +pub struct ConfigClient { + pub ips: HashSet, + pub hostname: Option, + pub port: u16, + pub pos: Position, + pub active: bool, +} + impl Config { pub fn new() -> Result { let args = CliArgs::parse(); @@ -128,7 +138,7 @@ impl Config { }, }; - let mut clients: Vec<(Client, Position)> = vec![]; + let mut clients: Vec<(TomlClient, Position)> = vec![]; if let Some(config_toml) = config_toml { if let Some(c) = config_toml.right { @@ -155,18 +165,28 @@ impl Config { }) } - pub fn get_clients(&self) -> Vec<(HashSet, Option, u16, Position)> { + pub fn get_clients(&self) -> Vec { self.clients .iter() - .map(|(c, p)| { + .map(|(c, pos)| { let port = c.port.unwrap_or(DEFAULT_PORT); let ips: HashSet = if let Some(ips) = c.ips.as_ref() { HashSet::from_iter(ips.iter().cloned()) } else { HashSet::new() }; - let host_name = c.host_name.clone(); - (ips, host_name, port, *p) + let hostname = match &c.hostname { + Some(h) => Some(h.clone()), + None => c.host_name.clone(), + }; + let active = c.activate_on_startup.unwrap_or(false); + ConfigClient { + ips, + hostname, + port, + pos: *pos, + active, + } }) .collect() } diff --git a/src/frontend.rs b/src/frontend.rs index e69ed438..b9aeb0ac 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -103,8 +103,9 @@ pub enum FrontendEvent { #[derive(Debug, Clone, Serialize, Deserialize)] pub enum FrontendNotify { - NotifyClientCreate(ClientHandle, Option, u16, Position), - NotifyClientUpdate(ClientHandle, Option, u16, Position), + NotifyClientActivate(ClientHandle, bool), + NotifyClientCreate(Client), + NotifyClientUpdate(Client), NotifyClientDelete(ClientHandle), /// new port, reason of failure (if failed) NotifyPortChange(u16, Option), @@ -224,7 +225,6 @@ impl FrontendListener { log::debug!("json: {json}, len: {}", payload.len()); let mut keep = vec![]; - // TODO do simultaneously for tx in self.tx_streams.iter_mut() { // write len + payload diff --git a/src/frontend/cli.rs b/src/frontend/cli.rs index b1cf8093..b808c115 100644 --- a/src/frontend/cli.rs +++ b/src/frontend/cli.rs @@ -83,17 +83,26 @@ pub fn run() -> Result<()> { Err(e) => break log::error!("{e}"), }; match notify { - FrontendNotify::NotifyClientCreate(client, host, port, pos) => { - log::info!( - "new client ({client}): {}:{port} - {pos}", - host.as_deref().unwrap_or("") - ); + FrontendNotify::NotifyClientActivate(handle, active) => { + if active { + log::info!("client {handle} activated"); + } else { + log::info!("client {handle} deactivated"); + } + } + FrontendNotify::NotifyClientCreate(client) => { + let handle = client.handle; + let port = client.port; + let pos = client.pos; + let hostname = client.hostname.as_deref().unwrap_or(""); + log::info!("new client ({handle}): {hostname}:{port} - {pos}"); } - FrontendNotify::NotifyClientUpdate(client, host, port, pos) => { - log::info!( - "client ({client}) updated: {}:{port} - {pos}", - host.as_deref().unwrap_or("") - ); + FrontendNotify::NotifyClientUpdate(client) => { + let handle = client.handle; + let port = client.port; + let pos = client.pos; + let hostname = client.hostname.as_deref().unwrap_or(""); + log::info!("client ({handle}) updated: {hostname}:{port} - {pos}"); } FrontendNotify::NotifyClientDelete(client) => { log::info!("client ({client}) deleted."); diff --git a/src/frontend/gtk.rs b/src/frontend/gtk.rs index d4a88336..dff916a2 100644 --- a/src/frontend/gtk.rs +++ b/src/frontend/gtk.rs @@ -8,16 +8,12 @@ use std::{ process, str, }; -use crate::{config::DEFAULT_PORT, frontend::gtk::window::Window}; +use crate::frontend::gtk::window::Window; use adw::Application; use gtk::{ - gdk::Display, - gio::{SimpleAction, SimpleActionGroup}, - glib::clone, - prelude::*, - subclass::prelude::ObjectSubclassIsExt, - CssProvider, IconTheme, + gdk::Display, glib::clone, prelude::*, subclass::prelude::ObjectSubclassIsExt, CssProvider, + IconTheme, }; use gtk::{gio, glib, prelude::ApplicationExt}; @@ -68,8 +64,8 @@ fn load_css() { } fn load_icons() { - let icon_theme = - IconTheme::for_display(&Display::default().expect("Could not connect to a display.")); + let display = &Display::default().expect("Could not connect to a display."); + let icon_theme = IconTheme::for_display(display); icon_theme.add_resource_path("/de/feschber/LanMouse/icons"); } @@ -130,15 +126,17 @@ fn build_ui(app: &Application) { loop { let notify = receiver.recv().await.unwrap(); match notify { - FrontendNotify::NotifyClientCreate(client, hostname, port, position) => { - window.new_client(client, hostname, port, position, false); + FrontendNotify::NotifyClientActivate(handle, active) => { + window.activate_client(handle, active); + } + FrontendNotify::NotifyClientCreate(client) => { + window.new_client(client, false); }, - FrontendNotify::NotifyClientUpdate(client, hostname, port, position) => { - log::info!("client updated: {client}, {}:{port}, {position}", hostname.unwrap_or("".to_string())); + FrontendNotify::NotifyClientUpdate(client) => { + window.update_client(client); } FrontendNotify::NotifyError(e) => { - // TODO - log::error!("{e}"); + window.show_toast(e.as_str()); }, FrontendNotify::NotifyClientDelete(client) => { window.delete_client(client); @@ -146,19 +144,11 @@ fn build_ui(app: &Application) { FrontendNotify::Enumerate(clients) => { for (client, active) in clients { if window.client_idx(client.handle).is_some() { - continue + window.activate_client(client.handle, active); + window.update_client(client); + } else { + window.new_client(client, active); } - window.new_client( - client.handle, - client.hostname, - client.addrs - .iter() - .next() - .map(|s| s.port()) - .unwrap_or(DEFAULT_PORT), - client.pos, - active, - ); } }, FrontendNotify::NotifyPortChange(port, msg) => { @@ -172,37 +162,5 @@ fn build_ui(app: &Application) { } })); - let action_request_client_update = - SimpleAction::new("request-client-update", Some(&u32::static_variant_type())); - - // remove client - let action_client_delete = - SimpleAction::new("request-client-delete", Some(&u32::static_variant_type())); - - // update client state - action_request_client_update.connect_activate(clone!(@weak window => move |_action, param| { - log::debug!("request-client-update"); - let index = param.unwrap() - .get::() - .unwrap(); - let Some(client) = window.clients().item(index) else { - return; - }; - let client = client.downcast_ref::().unwrap(); - window.request_client_update(client); - })); - - action_client_delete.connect_activate(clone!(@weak window => move |_action, param| { - log::debug!("delete-client"); - let idx = param.unwrap() - .get::() - .unwrap(); - window.request_client_delete(idx); - })); - - let actions = SimpleActionGroup::new(); - window.insert_action_group("win", Some(&actions)); - actions.add_action(&action_request_client_update); - actions.add_action(&action_client_delete); window.present(); } diff --git a/src/frontend/gtk/client_object.rs b/src/frontend/gtk/client_object.rs index b8325a10..7067e374 100644 --- a/src/frontend/gtk/client_object.rs +++ b/src/frontend/gtk/client_object.rs @@ -3,26 +3,20 @@ mod imp; use adw::subclass::prelude::*; use gtk::glib::{self, Object}; -use crate::client::ClientHandle; +use crate::client::{Client, ClientHandle}; glib::wrapper! { pub struct ClientObject(ObjectSubclass); } impl ClientObject { - pub fn new( - handle: ClientHandle, - hostname: Option, - port: u32, - position: String, - active: bool, - ) -> Self { + pub fn new(client: Client, active: bool) -> Self { Object::builder() - .property("handle", handle) - .property("hostname", hostname) - .property("port", port) + .property("handle", client.handle) + .property("hostname", client.hostname) + .property("port", client.port as u32) + .property("position", client.pos.to_string()) .property("active", active) - .property("position", position) .build() } diff --git a/src/frontend/gtk/client_row.rs b/src/frontend/gtk/client_row.rs index 072ef0e8..082d1853 100644 --- a/src/frontend/gtk/client_row.rs +++ b/src/frontend/gtk/client_row.rs @@ -28,6 +28,12 @@ impl ClientRow { .sync_create() .build(); + let switch_position_binding = client_object + .bind_property("active", &self.imp().enable_switch.get(), "active") + .bidirectional() + .sync_create() + .build(); + let hostname_binding = client_object .bind_property("hostname", &self.imp().hostname.get(), "text") .transform_to(|_, v: Option| { @@ -104,6 +110,7 @@ impl ClientRow { .build(); bindings.push(active_binding); + bindings.push(switch_position_binding); bindings.push(hostname_binding); bindings.push(title_binding); bindings.push(port_binding); diff --git a/src/frontend/gtk/client_row/imp.rs b/src/frontend/gtk/client_row/imp.rs index befd167b..e2411737 100644 --- a/src/frontend/gtk/client_row/imp.rs +++ b/src/frontend/gtk/client_row/imp.rs @@ -4,6 +4,8 @@ use adw::subclass::prelude::*; use adw::{prelude::*, ActionRow, ComboRow}; use glib::{subclass::InitializingObject, Binding}; use gtk::glib::clone; +use gtk::glib::once_cell::sync::Lazy; +use gtk::glib::subclass::Signal; use gtk::{glib, Button, CompositeTemplate, Switch}; #[derive(CompositeTemplate, Default)] @@ -28,6 +30,8 @@ pub struct ClientRow { impl ObjectSubclass for ClientRow { // `NAME` needs to match `class` attribute of template const NAME: &'static str = "ClientRow"; + const ABSTRACT: bool = false; + type Type = super::ClientRow; type ParentType = adw::ExpanderRow; @@ -49,28 +53,33 @@ impl ObjectImpl for ClientRow { row.handle_client_delete(button); })); } + + fn signals() -> &'static [glib::subclass::Signal] { + static SIGNALS: Lazy> = Lazy::new(|| { + vec![ + Signal::builder("request-update") + .param_types([bool::static_type()]) + .build(), + Signal::builder("request-delete").build(), + ] + }); + SIGNALS.as_ref() + } } #[gtk::template_callbacks] impl ClientRow { #[template_callback] - fn handle_client_set_state(&self, state: bool, switch: &Switch) -> bool { - let idx = self.obj().index() as u32; - switch - .activate_action("win.request-client-update", Some(&idx.to_variant())) - .unwrap(); - switch.set_state(state); - + fn handle_client_set_state(&self, state: bool, _switch: &Switch) -> bool { + log::debug!("state change -> requesting update"); + self.obj().emit_by_name::<()>("request-update", &[&state]); true // dont run default handler } #[template_callback] - fn handle_client_delete(&self, button: &Button) { - log::debug!("delete button pressed"); - let idx = self.obj().index() as u32; - button - .activate_action("win.request-client-delete", Some(&idx.to_variant())) - .unwrap(); + fn handle_client_delete(&self, _button: &Button) { + log::debug!("delete button pressed -> requesting delete"); + self.obj().emit_by_name::<()>("request-delete", &[]); } } diff --git a/src/frontend/gtk/window.rs b/src/frontend/gtk/window.rs index c01c74da..72a89db7 100644 --- a/src/frontend/gtk/window.rs +++ b/src/frontend/gtk/window.rs @@ -5,10 +5,14 @@ use std::io::Write; use adw::prelude::*; use adw::subclass::prelude::*; use glib::{clone, Object}; -use gtk::{gio, glib, NoSelection}; +use gtk::{ + gio, + glib::{self, closure_local}, + NoSelection, +}; use crate::{ - client::{ClientHandle, Position}, + client::{Client, ClientHandle, Position}, config::DEFAULT_PORT, frontend::{gtk::client_object::ClientObject, FrontendEvent}, }; @@ -45,6 +49,18 @@ impl Window { clone!(@weak self as window => @default-panic, move |obj| { let client_object = obj.downcast_ref().expect("Expected object of type `ClientObject`."); let row = window.create_client_row(client_object); + row.connect_closure("request-update", false, closure_local!(@strong window => move |row: ClientRow, active: bool| { + let index = row.index() as u32; + let Some(client) = window.clients().item(index) else { + return; + }; + let client = client.downcast_ref::().unwrap(); + window.request_client_update(client, active); + })); + row.connect_closure("request-delete", false, closure_local!(@strong window => move |row: ClientRow| { + let index = row.index() as u32; + window.request_client_delete(index); + })); row.upcast() }) ); @@ -71,15 +87,8 @@ impl Window { row } - pub fn new_client( - &self, - handle: ClientHandle, - hostname: Option, - port: u16, - position: Position, - active: bool, - ) { - let client = ClientObject::new(handle, hostname, port as u32, position.to_string(), active); + pub fn new_client(&self, client: Client, active: bool) { + let client = ClientObject::new(client, active); self.clients().append(&client); self.set_placeholder_visible(false); } @@ -106,6 +115,42 @@ 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); + return; + }; + let client_object = self.clients().item(idx as u32).unwrap(); + let client_object: &ClientObject = client_object.downcast_ref().unwrap(); + let data = client_object.get_data(); + + /* only change if it actually has changed, otherwise + * the update signal is triggered */ + if data.hostname != client.hostname { + client_object.set_hostname(client.hostname.unwrap_or("".into())); + } + if data.port != client.port as u32 { + client_object.set_port(client.port as u32); + } + if data.position != client.pos.to_string() { + client_object.set_position(client.pos.to_string()); + } + } + + pub fn activate_client(&self, handle: ClientHandle, active: bool) { + 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(); + let client_object: &ClientObject = client_object.downcast_ref().unwrap(); + let data = client_object.get_data(); + if data.active != active { + client_object.set_active(active); + log::debug!("set active to {active}"); + } + } + pub fn request_client_create(&self) { let event = FrontendEvent::AddClient(None, DEFAULT_PORT, Position::default()); self.imp().set_port(DEFAULT_PORT); @@ -121,13 +166,10 @@ impl Window { } } - pub fn request_client_update(&self, client: &ClientObject) { + pub fn request_client_update(&self, client: &ClientObject, active: bool) { let data = client.get_data(); - let position = match data.position.as_str() { - "left" => Position::Left, - "right" => Position::Right, - "top" => Position::Top, - "bottom" => Position::Bottom, + let position = match Position::try_from(data.position.as_str()) { + Ok(pos) => pos, _ => { log::error!("invalid position: {}", data.position); return; @@ -135,10 +177,13 @@ impl Window { }; let hostname = data.hostname; let port = data.port as u16; + let event = FrontendEvent::UpdateClient(client.handle(), hostname, port, position); + log::debug!("requesting update: {event:?}"); self.request(event); - let event = FrontendEvent::ActivateClient(client.handle(), !client.active()); + let event = FrontendEvent::ActivateClient(client.handle(), active); + log::debug!("requesting activate: {event:?}"); self.request(event); } diff --git a/src/frontend/gtk/window/imp.rs b/src/frontend/gtk/window/imp.rs index dce24af2..de3ecf5a 100644 --- a/src/frontend/gtk/window/imp.rs +++ b/src/frontend/gtk/window/imp.rs @@ -6,10 +6,7 @@ use std::net::TcpStream; use std::os::unix::net::UnixStream; use adw::subclass::prelude::*; -use adw::{ - prelude::{EditableExt, WidgetExt}, - ActionRow, ToastOverlay, -}; +use adw::{prelude::*, ActionRow, ToastOverlay}; use glib::subclass::InitializingObject; use gtk::{gio, glib, Button, CompositeTemplate, Entry, ListBox}; @@ -42,6 +39,8 @@ pub struct Window { impl ObjectSubclass for Window { // `NAME` needs to match `class` attribute of template const NAME: &'static str = "LanMouseWindow"; + const ABSTRACT: bool = false; + type Type = super::Window; type ParentType = adw::ApplicationWindow; diff --git a/src/main.rs b/src/main.rs index 7fb431c2..95052c6e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,7 +38,6 @@ pub fn run() -> Result<()> { // run a frontend let mut service = start_service()?; frontend::run_frontend(&config)?; - log::info!("terminating service"); #[cfg(unix)] { // on unix we give the service a chance to terminate gracefully diff --git a/src/server.rs b/src/server.rs index 5a45acf5..1a9ba386 100644 --- a/src/server.rs +++ b/src/server.rs @@ -67,14 +67,6 @@ pub enum ConsumerEvent { Terminate, } -#[derive(Clone)] -struct ClientUpdate { - client: ClientHandle, - hostname: Option, - port: u16, - pos: Position, -} - #[derive(Clone)] pub struct Server { active_client: Rc>>, @@ -89,8 +81,14 @@ impl Server { let client_manager = Rc::new(RefCell::new(ClientManager::new())); let state = Rc::new(Cell::new(State::Receiving)); let port = Rc::new(Cell::new(config.port)); - for (ips, host, port, pos) in config.get_clients() { - client_manager.borrow_mut().add_client(host, ips, port, pos); + for config_client in config.get_clients() { + client_manager.borrow_mut().add_client( + config_client.hostname, + config_client.ips, + config_client.port, + config_client.pos, + config_client.active, + ); } Self { active_client, @@ -212,7 +210,7 @@ impl Server { continue; } }; - server.handle_frontend_stream(&mut frontend, &frontend_ch, stream).await; + server.handle_frontend_stream(&frontend_ch, stream).await; } event = frontend_rx.recv() => { let frontend_event = event.ok_or(anyhow!("frontend channel closed"))?; @@ -421,6 +419,23 @@ impl Server { // initial sync of clients frontend_tx.send(FrontendEvent::Enumerate()).await?; + let active = self + .client_manager + .borrow() + .get_client_states() + .filter_map(|s| { + if s.active { + Some(s.client.handle) + } else { + None + } + }) + .collect::>(); + for client in active { + frontend_tx + .send(FrontendEvent::ActivateClient(client, true)) + .await?; + } tokio::select! { _ = signal::ctrl_c() => { @@ -477,7 +492,6 @@ impl Server { pub async fn add_client( &self, resolver_tx: &Sender<(String, ClientHandle)>, - frontend: &mut FrontendListener, hostname: Option, addr: HashSet, port: u16, @@ -489,20 +503,18 @@ impl Server { hostname.as_deref().unwrap_or(""), &addr ); - let client = self - .client_manager - .borrow_mut() - .add_client(hostname.clone(), addr, port, pos); + let handle = + self.client_manager + .borrow_mut() + .add_client(hostname.clone(), addr, port, pos, false); - log::debug!("add_client {client}"); - if let Some(hostname) = hostname.clone() { - let _ = resolver_tx.send((hostname, client)).await; - }; - let notify = FrontendNotify::NotifyClientCreate(client, hostname, port, pos); - if let Err(e) = frontend.notify_all(notify).await { - log::error!("error notifying frontend: {e}"); - }; - client + log::debug!("add_client {handle}"); + + if let Some(hostname) = hostname { + let _ = resolver_tx.send((hostname, handle)).await; + } + + handle } pub async fn activate_client( @@ -572,41 +584,40 @@ impl Server { producer_notify_tx: &Sender, consumer_notify_tx: &Sender, resolve_tx: &Sender<(String, ClientHandle)>, - client_update: ClientUpdate, + client_update: (ClientHandle, Option, u16, Position), ) { + let (handle, hostname, port, pos) = client_update; let (hostname, handle, active) = { // retrieve state let mut client_manager = self.client_manager.borrow_mut(); - let Some(state) = client_manager.get_mut(client_update.client) else { + let Some(state) = client_manager.get_mut(handle) else { return; }; // update pos - state.client.pos = client_update.pos; + state.client.pos = pos; // update port - if state.client.port != client_update.port { - state.client.port = client_update.port; + if state.client.port != port { + state.client.port = port; state.client.addrs = state .client .addrs .iter() .cloned() .map(|mut a| { - a.set_port(client_update.port); + a.set_port(port); a }) .collect(); - state.active_addr = state - .active_addr - .map(|a| SocketAddr::new(a.ip(), client_update.port)); + state.active_addr = state.active_addr.map(|a| SocketAddr::new(a.ip(), port)); } // update hostname - if state.client.hostname != client_update.hostname { + if state.client.hostname != hostname { state.client.addrs = HashSet::new(); state.active_addr = None; - state.client.hostname = client_update.hostname; + state.client.hostname = hostname; } log::debug!("client updated: {:?}", state); @@ -625,26 +636,16 @@ impl Server { // update state in event consumer & producer if active { let _ = producer_notify_tx - .send(ProducerEvent::ClientEvent(ClientEvent::Destroy( - client_update.client, - ))) + .send(ProducerEvent::ClientEvent(ClientEvent::Destroy(handle))) .await; let _ = consumer_notify_tx - .send(ConsumerEvent::ClientEvent(ClientEvent::Destroy( - client_update.client, - ))) + .send(ConsumerEvent::ClientEvent(ClientEvent::Destroy(handle))) .await; let _ = producer_notify_tx - .send(ProducerEvent::ClientEvent(ClientEvent::Create( - client_update.client, - client_update.pos, - ))) + .send(ProducerEvent::ClientEvent(ClientEvent::Create(handle, pos))) .await; let _ = consumer_notify_tx - .send(ConsumerEvent::ClientEvent(ClientEvent::Create( - client_update.client, - client_update.pos, - ))) + .send(ConsumerEvent::ClientEvent(ClientEvent::Create(handle, pos))) .await; } } @@ -853,7 +854,6 @@ impl Server { async fn handle_frontend_stream( &self, - frontend: &mut FrontendListener, frontend_tx: &Sender, #[cfg(unix)] mut stream: ReadHalf, #[cfg(windows)] mut stream: ReadHalf, @@ -880,55 +880,85 @@ impl Server { } } }); - self.enumerate(frontend).await; + let _ = frontend_tx.send(FrontendEvent::Enumerate()).await; } async fn handle_frontend_event( &self, - producer_notify_tx: &Sender, - consumer_notify_tx: &Sender, + producer_tx: &Sender, + consumer_tx: &Sender, resolve_tx: &Sender<(String, ClientHandle)>, frontend: &mut FrontendListener, port_tx: &Sender, event: FrontendEvent, ) -> bool { log::debug!("frontend: {event:?}"); - match event { + let response = match event { FrontendEvent::AddClient(hostname, port, pos) => { - self.add_client(resolve_tx, frontend, hostname, HashSet::new(), port, pos) + let handle = self + .add_client(resolve_tx, hostname, HashSet::new(), port, pos) .await; + + let client = self + .client_manager + .borrow() + .get(handle) + .unwrap() + .client + .clone(); + Some(FrontendNotify::NotifyClientCreate(client)) } - FrontendEvent::ActivateClient(client, active) => { - self.activate_client(producer_notify_tx, consumer_notify_tx, client, active) - .await + FrontendEvent::ActivateClient(handle, active) => { + self.activate_client(producer_tx, consumer_tx, handle, active) + .await; + Some(FrontendNotify::NotifyClientActivate(handle, active)) } FrontendEvent::ChangePort(port) => { let _ = port_tx.send(port).await; + None } - FrontendEvent::DelClient(client) => { - self.remove_client(producer_notify_tx, consumer_notify_tx, frontend, client) + FrontendEvent::DelClient(handle) => { + self.remove_client(producer_tx, consumer_tx, frontend, handle) .await; + Some(FrontendNotify::NotifyClientDelete(handle)) + } + FrontendEvent::Enumerate() => { + let clients = self + .client_manager + .borrow() + .get_client_states() + .map(|s| (s.client.clone(), s.active)) + .collect(); + Some(FrontendNotify::Enumerate(clients)) } - FrontendEvent::Enumerate() => self.enumerate(frontend).await, FrontendEvent::Shutdown() => { log::info!("terminating gracefully..."); return true; } - FrontendEvent::UpdateClient(client, hostname, port, pos) => { - let client_update = ClientUpdate { - client, - hostname, - port, - pos, - }; + FrontendEvent::UpdateClient(handle, hostname, port, pos) => { self.update_client( - producer_notify_tx, - consumer_notify_tx, + producer_tx, + consumer_tx, resolve_tx, - client_update, + (handle, hostname, port, pos), ) - .await + .await; + + let client = self + .client_manager + .borrow() + .get(handle) + .unwrap() + .client + .clone(); + Some(FrontendNotify::NotifyClientUpdate(client)) } + }; + let Some(response) = response else { + return false; + }; + if let Err(e) = frontend.notify_all(response).await { + log::error!("error notifying frontend: {e}"); } false } @@ -964,21 +994,6 @@ impl Server { .consume(Event::Keyboard(modifiers_event), client) .await; } - - async fn enumerate(&self, frontend: &mut FrontendListener) { - let clients = self - .client_manager - .borrow() - .get_client_states() - .map(|s| (s.client.clone(), s.active)) - .collect(); - if let Err(e) = frontend - .notify_all(FrontendNotify::Enumerate(clients)) - .await - { - log::error!("error notifying frontend: {e}"); - } - } } async fn receive_event(socket: &UdpSocket) -> anyhow::Result<(Event, SocketAddr)> {