diff --git a/lan-mouse-cli/src/lib.rs b/lan-mouse-cli/src/lib.rs index f74a406..2c1db0d 100644 --- a/lan-mouse-cli/src/lib.rs +++ b/lan-mouse-cli/src/lib.rs @@ -30,7 +30,6 @@ pub fn run() -> Result<(), IpcError> { struct Cli { clients: Vec<(ClientHandle, ClientConfig, ClientState)>, - changed: Option, rx: AsyncFrontendEventReader, tx: AsyncFrontendRequestWriter, } @@ -39,7 +38,6 @@ impl Cli { fn new(rx: AsyncFrontendEventReader, tx: AsyncFrontendRequestWriter) -> Cli { Self { clients: vec![], - changed: None, rx, tx, } @@ -87,23 +85,9 @@ impl Cli { } } } - if let Some(handle) = self.changed.take() { - self.update_client(handle).await?; - } } } - async fn update_client(&mut self, handle: ClientHandle) -> Result<(), IpcError> { - self.tx.request(FrontendRequest::GetState(handle)).await?; - while let Some(Ok(event)) = self.rx.next().await { - self.handle_event(event.clone()); - if let FrontendEvent::State(_, _, _) | FrontendEvent::NoSuchClient(_) = event { - break; - } - } - Ok(()) - } - async fn execute(&mut self, cmd: Command) -> Result<(), IpcError> { match cmd { Command::None => {} @@ -131,7 +115,6 @@ impl Cli { ] { self.tx.request(request).await?; } - self.update_client(handle).await?; } Command::Disconnect(id) => { self.tx.request(FrontendRequest::Delete(id)).await?; @@ -147,13 +130,11 @@ impl Cli { } Command::Activate(id) => { self.tx.request(FrontendRequest::Activate(id, true)).await?; - self.update_client(id).await?; } Command::Deactivate(id) => { self.tx .request(FrontendRequest::Activate(id, false)) .await?; - self.update_client(id).await?; } Command::List => { self.tx.request(FrontendRequest::Enumerate()).await?; @@ -168,12 +149,10 @@ impl Cli { Command::SetHost(handle, host) => { let request = FrontendRequest::UpdateHostname(handle, Some(host.clone())); self.tx.request(request).await?; - self.update_client(handle).await?; } Command::SetPort(handle, port) => { let request = FrontendRequest::UpdatePort(handle, port.unwrap_or(DEFAULT_PORT)); self.tx.request(request).await?; - self.update_client(handle).await?; } Command::Help => { for cmd_type in [ @@ -209,7 +188,6 @@ impl Cli { fn handle_event(&mut self, event: FrontendEvent) { match event { - FrontendEvent::Changed(h) => self.changed = Some(h), FrontendEvent::Created(h, c, s) => { eprint!("client added ({h}): "); print_config(&c); diff --git a/lan-mouse-gtk/resources/client_row.ui b/lan-mouse-gtk/resources/client_row.ui index 7ef7219..87c8e0f 100644 --- a/lan-mouse-gtk/resources/client_row.ui +++ b/lan-mouse-gtk/resources/client_row.ui @@ -5,7 +5,6 @@ - center end enable diff --git a/lan-mouse-gtk/src/client_object/imp.rs b/lan-mouse-gtk/src/client_object/imp.rs index fbcd5db..016ae8c 100644 --- a/lan-mouse-gtk/src/client_object/imp.rs +++ b/lan-mouse-gtk/src/client_object/imp.rs @@ -13,7 +13,7 @@ use super::ClientData; #[properties(wrapper_type = super::ClientObject)] pub struct ClientObject { #[property(name = "handle", get, set, type = ClientHandle, member = handle)] - #[property(name = "hostname", get, set, type = String, member = hostname)] + #[property(name = "hostname", get, set, type = Option, member = hostname)] #[property(name = "port", get, set, type = u32, member = port, maximum = u16::MAX as u32)] #[property(name = "active", get, set, type = bool, member = active)] #[property(name = "position", get, set, type = String, member = position)] diff --git a/lan-mouse-gtk/src/client_row.rs b/lan-mouse-gtk/src/client_row.rs index bcbad7e..ca9c5f4 100644 --- a/lan-mouse-gtk/src/client_row.rs +++ b/lan-mouse-gtk/src/client_row.rs @@ -4,7 +4,7 @@ use adw::prelude::*; use adw::subclass::prelude::*; use gtk::glib::{self, Object}; -use lan_mouse_ipc::DEFAULT_PORT; +use lan_mouse_ipc::{Position, DEFAULT_PORT}; use super::ClientObject; @@ -15,25 +15,32 @@ glib::wrapper! { } impl ClientRow { - pub fn new(_client_object: &ClientObject) -> Self { - Object::builder().build() + pub fn new(client_object: &ClientObject) -> Self { + let client_row: Self = Object::builder().build(); + client_row + .imp() + .client_object + .borrow_mut() + .replace(client_object.clone()); + client_row } pub fn bind(&self, client_object: &ClientObject) { let mut bindings = self.imp().bindings.borrow_mut(); + // bind client active to switch state let active_binding = client_object .bind_property("active", &self.imp().enable_switch.get(), "state") - .bidirectional() .sync_create() .build(); + // bind client active to switch position let switch_position_binding = client_object .bind_property("active", &self.imp().enable_switch.get(), "active") - .bidirectional() .sync_create() .build(); + // bind hostname to hostname edit field let hostname_binding = client_object .bind_property("hostname", &self.imp().hostname.get(), "text") .transform_to(|_, v: Option| { @@ -43,72 +50,48 @@ impl ClientRow { Some("".to_string()) } }) - .transform_from(|_, v: String| { - if v.as_str().trim() == "" { - Some(None) - } else { - Some(Some(v)) - } - }) - .bidirectional() .sync_create() .build(); + // bind hostname to title let title_binding = client_object .bind_property("hostname", self, "title") - .transform_to(|_, v: Option| { - if let Some(hostname) = v { - Some(hostname) - } else { - Some("no hostname!".to_string()) - } - }) + .transform_to(|_, v: Option| v.or(Some("no hostname!".to_string()))) .sync_create() .build(); + // bind port to port edit field let port_binding = client_object .bind_property("port", &self.imp().port.get(), "text") - .transform_from(|_, v: String| { - if v.is_empty() { - Some(DEFAULT_PORT as u32) - } else { - Some(v.parse::().unwrap_or(DEFAULT_PORT) as u32) - } - }) .transform_to(|_, v: u32| { - if v == 4242 { + if v == DEFAULT_PORT as u32 { Some("".to_string()) } else { Some(v.to_string()) } }) - .bidirectional() .sync_create() .build(); + // bind port to subtitle let subtitle_binding = client_object .bind_property("port", self, "subtitle") .sync_create() .build(); + // bind position to selected position let position_binding = client_object .bind_property("position", &self.imp().position.get(), "selected") - .transform_from(|_, v: u32| match v { - 1 => Some("right"), - 2 => Some("top"), - 3 => Some("bottom"), - _ => Some("left"), - }) .transform_to(|_, v: String| match v.as_str() { - "right" => Some(1), + "right" => Some(1u32), "top" => Some(2u32), "bottom" => Some(3u32), _ => Some(0u32), }) - .bidirectional() .sync_create() .build(); + // bind resolving status to spinner visibility let resolve_binding = client_object .bind_property( "resolving", @@ -118,6 +101,7 @@ impl ClientRow { .sync_create() .build(); + // bind ips to tooltip-text let ip_binding = client_object .bind_property("ips", &self.imp().dns_button.get(), "tooltip-text") .transform_to(|_, ips: Vec| { @@ -146,4 +130,24 @@ impl ClientRow { binding.unbind(); } } + + pub fn set_active(&self, active: bool) { + self.imp().set_active(active); + } + + pub fn set_hostname(&self, hostname: Option) { + self.imp().set_hostname(hostname); + } + + pub fn set_port(&self, port: u16) { + self.imp().set_port(port); + } + + pub fn set_position(&self, pos: Position) { + self.imp().set_pos(pos); + } + + pub fn set_dns_state(&self, resolved: bool) { + self.imp().set_dns_state(resolved); + } } diff --git a/lan-mouse-gtk/src/client_row/imp.rs b/lan-mouse-gtk/src/client_row/imp.rs index 1e8b711..8c5b20a 100644 --- a/lan-mouse-gtk/src/client_row/imp.rs +++ b/lan-mouse-gtk/src/client_row/imp.rs @@ -3,11 +3,14 @@ use std::cell::RefCell; use adw::subclass::prelude::*; use adw::{prelude::*, ActionRow, ComboRow}; use glib::{subclass::InitializingObject, Binding}; -use gtk::glib::clone; use gtk::glib::subclass::Signal; -use gtk::{glib, Button, CompositeTemplate, Switch}; +use gtk::glib::{clone, SignalHandlerId}; +use gtk::{glib, Button, CompositeTemplate, Entry, Switch}; +use lan_mouse_ipc::Position; use std::sync::OnceLock; +use crate::client_object::ClientObject; + #[derive(CompositeTemplate, Default)] #[template(resource = "/de/feschber/LanMouse/client_row.ui")] pub struct ClientRow { @@ -28,6 +31,11 @@ pub struct ClientRow { #[template_child] pub dns_loading_indicator: TemplateChild, pub bindings: RefCell>, + hostname_change_handler: RefCell>, + port_change_handler: RefCell>, + position_change_handler: RefCell>, + set_state_handler: RefCell>, + pub client_object: RefCell>, } #[glib::object_subclass] @@ -59,17 +67,61 @@ impl ObjectImpl for ClientRow { row.handle_client_delete(button); } )); + let handler = self.hostname.connect_changed(clone!( + #[weak(rename_to = row)] + self, + move |entry| { + row.handle_hostname_changed(entry); + } + )); + self.hostname_change_handler.replace(Some(handler)); + let handler = self.port.connect_changed(clone!( + #[weak(rename_to = row)] + self, + move |entry| { + row.handle_port_changed(entry); + } + )); + self.port_change_handler.replace(Some(handler)); + let handler = self.position.connect_selected_notify(clone!( + #[weak(rename_to = row)] + self, + move |position| { + row.handle_position_changed(position); + } + )); + self.position_change_handler.replace(Some(handler)); + let handler = self.enable_switch.connect_state_set(clone!( + #[weak(rename_to = row)] + self, + #[upgrade_or] + glib::Propagation::Proceed, + move |switch, state| { + row.handle_activate_switch(state, switch); + glib::Propagation::Proceed + } + )); + self.set_state_handler.replace(Some(handler)); } fn signals() -> &'static [glib::subclass::Signal] { static SIGNALS: OnceLock> = OnceLock::new(); SIGNALS.get_or_init(|| { vec![ - Signal::builder("request-dns").build(), - Signal::builder("request-update") + Signal::builder("request-activate") .param_types([bool::static_type()]) .build(), Signal::builder("request-delete").build(), + Signal::builder("request-dns").build(), + Signal::builder("request-hostname-change") + .param_types([String::static_type()]) + .build(), + Signal::builder("request-port-change") + .param_types([u32::static_type()]) + .build(), + Signal::builder("request-position-change") + .param_types([u32::static_type()]) + .build(), ] }) } @@ -78,22 +130,97 @@ impl ObjectImpl for ClientRow { #[gtk::template_callbacks] impl ClientRow { #[template_callback] - 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]); + fn handle_activate_switch(&self, state: bool, _switch: &Switch) -> bool { + self.obj().emit_by_name::<()>("request-activate", &[&state]); true // dont run default handler } #[template_callback] - fn handle_request_dns(&self, _: Button) { + fn handle_request_dns(&self, _: &Button) { self.obj().emit_by_name::<()>("request-dns", &[]); } #[template_callback] fn handle_client_delete(&self, _button: &Button) { - log::debug!("delete button pressed -> requesting delete"); self.obj().emit_by_name::<()>("request-delete", &[]); } + + fn handle_port_changed(&self, port_entry: &Entry) { + if let Ok(port) = port_entry.text().parse::() { + self.obj() + .emit_by_name::<()>("request-port-change", &[&(port as u32)]); + } + } + + fn handle_hostname_changed(&self, hostname_entry: &Entry) { + self.obj() + .emit_by_name::<()>("request-hostname-change", &[&hostname_entry.text()]); + } + + fn handle_position_changed(&self, position: &ComboRow) { + self.obj() + .emit_by_name("request-position-change", &[&position.selected()]) + } + + pub(super) fn set_hostname(&self, hostname: Option) { + let position = self.hostname.position(); + let handler = self.hostname_change_handler.borrow(); + let handler = handler.as_ref().expect("signal handler"); + self.hostname.block_signal(handler); + self.client_object + .borrow_mut() + .as_mut() + .expect("client object") + .set_property("hostname", hostname); + self.hostname.unblock_signal(handler); + self.hostname.set_position(position); + } + + pub(super) fn set_port(&self, port: u16) { + let position = self.port.position(); + let handler = self.port_change_handler.borrow(); + let handler = handler.as_ref().expect("signal handler"); + self.port.block_signal(handler); + self.client_object + .borrow_mut() + .as_mut() + .expect("client object") + .set_port(port as u32); + self.port.unblock_signal(handler); + self.port.set_position(position); + } + + pub(super) fn set_pos(&self, pos: Position) { + let handler = self.position_change_handler.borrow(); + let handler = handler.as_ref().expect("signal handler"); + self.position.block_signal(handler); + self.client_object + .borrow_mut() + .as_mut() + .expect("client object") + .set_position(pos.to_string()); + self.position.unblock_signal(handler); + } + + pub(super) fn set_active(&self, active: bool) { + let handler = self.set_state_handler.borrow(); + let handler = handler.as_ref().expect("signal handler"); + self.enable_switch.block_signal(handler); + self.client_object + .borrow_mut() + .as_mut() + .expect("client object") + .set_active(active); + self.enable_switch.unblock_signal(handler); + } + + pub(super) fn set_dns_state(&self, resolved: bool) { + if resolved { + self.dns_button.set_css_classes(&["success"]) + } else { + self.dns_button.set_css_classes(&["warning"]) + } + } } impl WidgetImpl for ClientRow {} diff --git a/lan-mouse-gtk/src/lib.rs b/lan-mouse-gtk/src/lib.rs index 2469c57..f5e72b7 100644 --- a/lan-mouse-gtk/src/lib.rs +++ b/lan-mouse-gtk/src/lib.rs @@ -9,12 +9,10 @@ use std::{env, process, str}; use window::Window; -use lan_mouse_ipc::{FrontendEvent, FrontendRequest}; +use lan_mouse_ipc::FrontendEvent; use adw::Application; -use gtk::{ - gdk::Display, glib::clone, prelude::*, subclass::prelude::ObjectSubclassIsExt, IconTheme, -}; +use gtk::{gdk::Display, glib::clone, prelude::*, IconTheme}; use gtk::{gio, glib, prelude::ApplicationExt}; use self::client_object::ClientObject; @@ -127,54 +125,28 @@ fn build_ui(app: &Application) { loop { let notify = receiver.recv().await.unwrap_or_else(|_| process::exit(1)); match notify { - FrontendEvent::Changed(handle) => { - window.request(FrontendRequest::GetState(handle)); - } FrontendEvent::Created(handle, client, state) => { - window.new_client(handle, client, state); - } - FrontendEvent::Deleted(client) => { - window.delete_client(client); + window.new_client(handle, client, state) } + FrontendEvent::Deleted(client) => window.delete_client(client), FrontendEvent::State(handle, config, state) => { window.update_client_config(handle, config); window.update_client_state(handle, state); } FrontendEvent::NoSuchClient(_) => {} - FrontendEvent::Error(e) => { - window.show_toast(e.as_str()); + FrontendEvent::Error(e) => window.show_toast(e.as_str()), + FrontendEvent::Enumerate(clients) => window.update_client_list(clients), + FrontendEvent::PortChanged(port, msg) => window.update_port(port, msg), + FrontendEvent::CaptureStatus(s) => window.set_capture(s.into()), + FrontendEvent::EmulationStatus(s) => window.set_emulation(s.into()), + FrontendEvent::AuthorizedUpdated(keys) => window.set_authorized_keys(keys), + FrontendEvent::PublicKeyFingerprint(fp) => window.set_pk_fp(&fp), + FrontendEvent::IncomingConnected(_fingerprint, addr, pos) => { + window.show_toast(format!("device connected: {addr} ({pos})").as_str()); } - FrontendEvent::Enumerate(clients) => { - for (handle, client, state) in clients { - if window.client_idx(handle).is_some() { - window.update_client_config(handle, client); - window.update_client_state(handle, state); - } else { - window.new_client(handle, client, state); - } - } + FrontendEvent::IncomingDisconnected(addr) => { + window.show_toast(format!("{addr} disconnected").as_str()); } - FrontendEvent::PortChanged(port, msg) => { - match msg { - None => window.show_toast(format!("port changed: {port}").as_str()), - Some(msg) => window.show_toast(msg.as_str()), - } - window.imp().set_port(port); - } - FrontendEvent::CaptureStatus(s) => { - window.set_capture(s.into()); - } - FrontendEvent::EmulationStatus(s) => { - window.set_emulation(s.into()); - } - FrontendEvent::AuthorizedUpdated(keys) => { - window.set_authorized_keys(keys); - } - FrontendEvent::PublicKeyFingerprint(fp) => { - window.set_pk_fp(&fp); - } - FrontendEvent::IncomingConnected(..) => {} - FrontendEvent::IncomingDisconnected(..) => {} } } } diff --git a/lan-mouse-gtk/src/window.rs b/lan-mouse-gtk/src/window.rs index 8be703a..3eef58b 100644 --- a/lan-mouse-gtk/src/window.rs +++ b/lan-mouse-gtk/src/window.rs @@ -8,7 +8,7 @@ use glib::{clone, Object}; use gtk::{ gio, glib::{self, closure_local}, - ListBox, NoSelection, + NoSelection, }; use lan_mouse_ipc::{ @@ -28,7 +28,7 @@ glib::wrapper! { } impl Window { - pub(crate) fn new(app: &adw::Application, conn: FrontendRequestWriter) -> Self { + pub(super) fn new(app: &adw::Application, conn: FrontendRequestWriter) -> Self { let window: Self = Object::builder().property("application", app).build(); window .imp() @@ -38,7 +38,7 @@ impl Window { window } - pub fn clients(&self) -> gio::ListStore { + fn clients(&self) -> gio::ListStore { self.imp() .clients .borrow() @@ -46,7 +46,7 @@ impl Window { .expect("Could not get clients") } - pub fn authorized(&self) -> gio::ListStore { + fn authorized(&self) -> gio::ListStore { self.imp() .authorized .borrow() @@ -62,6 +62,14 @@ impl Window { self.authorized().item(idx).map(|o| o.downcast().unwrap()) } + fn row_by_idx(&self, idx: i32) -> Option { + self.imp() + .client_list + .get() + .row_at_index(idx) + .map(|o| o.downcast().expect("expected ClientRow")) + } + fn setup_authorized(&self) { let store = gio::ListStore::new::(); self.imp().authorized.replace(Some(store)); @@ -112,16 +120,57 @@ impl Window { .expect("Expected object of type `ClientObject`."); let row = window.create_client_row(client_object); row.connect_closure( - "request-update", + "request-hostname-change", + false, + closure_local!( + #[strong] + window, + move |row: ClientRow, hostname: String| { + log::info!("request-hostname-change"); + if let Some(client) = window.client_by_idx(row.index() as u32) { + let hostname = Some(hostname).filter(|s| !s.is_empty()); + /* changed in response to FrontendEvent + * -> do not request additional update */ + window.request(FrontendRequest::UpdateHostname( + client.handle(), + hostname, + )); + } + } + ), + ); + row.connect_closure( + "request-port-change", + false, + closure_local!( + #[strong] + window, + move |row: ClientRow, port: u32| { + if let Some(client) = window.client_by_idx(row.index() as u32) { + window.request(FrontendRequest::UpdatePort( + client.handle(), + port as u16, + )); + } + } + ), + ); + row.connect_closure( + "request-activate", false, closure_local!( #[strong] window, move |row: ClientRow, active: bool| { if let Some(client) = window.client_by_idx(row.index() as u32) { - window.request_client_activate(&client, active); - window.request_client_update(&client); - window.request_client_state(&client); + log::info!( + "request: {} client", + if active { "activating" } else { "deactivating" } + ); + window.request(FrontendRequest::Activate( + client.handle(), + active, + )); } } ), @@ -134,7 +183,7 @@ impl Window { window, move |row: ClientRow| { if let Some(client) = window.client_by_idx(row.index() as u32) { - window.request_client_delete(&client); + window.request(FrontendRequest::Delete(client.handle())); } } ), @@ -147,9 +196,31 @@ impl Window { window, move |row: ClientRow| { if let Some(client) = window.client_by_idx(row.index() as u32) { - window.request_client_update(&client); - window.request_dns(&client); - window.request_client_state(&client); + window.request(FrontendRequest::ResolveDns( + client.get_data().handle, + )); + } + } + ), + ); + row.connect_closure( + "request-position-change", + false, + closure_local!( + #[strong] + window, + move |row: ClientRow, pos_idx: u32| { + if let Some(client) = window.client_by_idx(row.index() as u32) { + let position = match pos_idx { + 0 => Position::Left, + 1 => Position::Right, + 2 => Position::Top, + _ => Position::Bottom, + }; + window.request(FrontendRequest::UpdatePosition( + client.handle(), + position, + )); } } ), @@ -160,10 +231,14 @@ impl Window { ); } + fn setup_icon(&self) { + self.set_icon_name(Some("de.feschber.LanMouse")); + } + /// workaround for a bug in libadwaita that shows an ugly line beneath /// the last element if a placeholder is set. /// https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/6308 - pub fn update_placeholder_visibility(&self) { + fn update_placeholder_visibility(&self) { let visible = self.clients().n_items() == 0; let placeholder = self.imp().client_placeholder.get(); self.imp().client_list.set_placeholder(match visible { @@ -172,7 +247,7 @@ impl Window { }); } - pub fn update_auth_placeholder_visibility(&self) { + fn update_auth_placeholder_visibility(&self) { let visible = self.authorized().n_items() == 0; let placeholder = self.imp().authorized_placeholder.get(); self.imp().authorized_list.set_placeholder(match visible { @@ -181,10 +256,6 @@ impl Window { }); } - fn setup_icon(&self) { - self.set_icon_name(Some("de.feschber.LanMouse")); - } - fn create_client_row(&self, client_object: &ClientObject) -> ClientRow { let row = ClientRow::new(client_object); row.bind(client_object); @@ -197,24 +268,46 @@ impl Window { row } - pub fn new_client(&self, handle: ClientHandle, client: ClientConfig, state: ClientState) { + pub(super) fn new_client( + &self, + handle: ClientHandle, + client: ClientConfig, + state: ClientState, + ) { let client = ClientObject::new(handle, client, state.clone()); self.clients().append(&client); self.update_placeholder_visibility(); self.update_dns_state(handle, !state.ips.is_empty()); } - pub fn client_idx(&self, handle: ClientHandle) -> Option { - self.clients().iter::().position(|c| { - if let Ok(c) = c { - c.handle() == handle + pub(super) fn update_client_list( + &self, + clients: Vec<(ClientHandle, ClientConfig, ClientState)>, + ) { + for (handle, client, state) in clients { + if self.client_idx(handle).is_some() { + self.update_client_config(handle, client); + self.update_client_state(handle, state); } else { - false + self.new_client(handle, client, state); } - }) + } } - pub fn delete_client(&self, handle: ClientHandle) { + pub(super) fn update_port(&self, port: u16, msg: Option) { + if let Some(msg) = msg { + self.show_toast(msg.as_str()); + } + self.imp().set_port(port); + } + + fn client_idx(&self, handle: ClientHandle) -> Option { + self.clients() + .iter::() + .position(|c| c.ok().map(|c| c.handle() == handle).unwrap_or_default()) + } + + pub(super) fn delete_client(&self, handle: ClientHandle) { let Some(idx) = self.client_idx(handle) else { log::warn!("could not find client with handle {handle}"); return; @@ -226,46 +319,31 @@ impl Window { } } - pub fn update_client_config(&self, handle: ClientHandle, client: ClientConfig) { - let Some(idx) = self.client_idx(handle) else { - log::warn!("could not find client with handle {}", handle); + pub(super) fn update_client_config(&self, handle: ClientHandle, client: ClientConfig) { + let Some(row) = self.row_for_handle(handle) else { + log::warn!("could not find row for 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(); - - /* 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()); - } + row.set_hostname(client.hostname); + row.set_port(client.port); + row.set_position(client.pos); } - pub fn update_client_state(&self, handle: ClientHandle, state: ClientState) { - let Some(idx) = self.client_idx(handle) else { - log::warn!("could not find client with handle {}", handle); + pub(super) fn update_client_state(&self, handle: ClientHandle, state: ClientState) { + let Some(row) = self.row_for_handle(handle) else { + log::warn!("could not find row for handle {}", handle); + return; + }; + let Some(client_object) = self.client_object_for_handle(handle) else { + log::warn!("could not find row for 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 state.active != data.active { - client_object.set_active(state.active); - log::debug!("set active to {}", state.active); - } + /* activation state */ + row.set_active(state.active); - if state.resolving != data.resolving { - client_object.set_resolving(state.resolving); - log::debug!("resolving {}: {}", data.handle, state.resolving); - } + /* dns state */ + client_object.set_resolving(state.resolving); self.update_dns_state(handle, !state.ips.is_empty()); let ips = state @@ -276,22 +354,23 @@ impl Window { client_object.set_ips(ips); } - pub fn update_dns_state(&self, handle: ClientHandle, resolved: bool) { - let Some(idx) = self.client_idx(handle) else { - log::warn!("could not find client with handle {}", handle); - return; - }; - let list_box: ListBox = self.imp().client_list.get(); - let row = list_box.row_at_index(idx as i32).unwrap(); - let client_row: ClientRow = row.downcast().expect("expected ClientRow Object"); - if resolved { - client_row.imp().dns_button.set_css_classes(&["success"]) - } else { - client_row.imp().dns_button.set_css_classes(&["warning"]) + fn client_object_for_handle(&self, handle: ClientHandle) -> Option { + self.client_idx(handle) + .and_then(|i| self.client_by_idx(i as u32)) + } + + fn row_for_handle(&self, handle: ClientHandle) -> Option { + self.client_idx(handle) + .and_then(|i| self.row_by_idx(i as i32)) + } + + fn update_dns_state(&self, handle: ClientHandle, resolved: bool) { + if let Some(client_row) = self.row_for_handle(handle) { + client_row.set_dns_state(resolved); } } - pub fn request_port_change(&self) { + fn request_port_change(&self) { let port = self .imp() .port_entry @@ -303,55 +382,19 @@ impl Window { self.request(FrontendRequest::ChangePort(port)); } - pub fn request_capture(&self) { + fn request_capture(&self) { self.request(FrontendRequest::EnableCapture); } - pub fn request_emulation(&self) { + fn request_emulation(&self) { self.request(FrontendRequest::EnableEmulation); } - pub fn request_client_state(&self, client: &ClientObject) { - self.request_client_state_for(client.handle()); - } - - pub fn request_client_state_for(&self, handle: ClientHandle) { - self.request(FrontendRequest::GetState(handle)); - } - - pub fn request_client_create(&self) { + fn request_client_create(&self) { self.request(FrontendRequest::Create); } - pub fn request_dns(&self, client: &ClientObject) { - self.request(FrontendRequest::ResolveDns(client.get_data().handle)); - } - - pub fn request_client_update(&self, client: &ClientObject) { - let handle = client.handle(); - let data = client.get_data(); - let position = Position::try_from(data.position.as_str()).expect("invalid position"); - let hostname = data.hostname; - let port = data.port as u16; - - for event in [ - FrontendRequest::UpdateHostname(handle, hostname), - FrontendRequest::UpdatePosition(handle, position), - FrontendRequest::UpdatePort(handle, port), - ] { - self.request(event); - } - } - - pub fn request_client_activate(&self, client: &ClientObject, active: bool) { - self.request(FrontendRequest::Activate(client.handle(), active)); - } - - pub fn request_client_delete(&self, client: &ClientObject) { - self.request(FrontendRequest::Delete(client.handle())); - } - - pub fn open_fingerprint_dialog(&self) { + fn open_fingerprint_dialog(&self) { let window = FingerprintWindow::new(); window.set_transient_for(Some(self)); window.connect_closure( @@ -369,15 +412,15 @@ impl Window { window.present(); } - pub fn request_fingerprint_add(&self, desc: String, fp: String) { + fn request_fingerprint_add(&self, desc: String, fp: String) { self.request(FrontendRequest::AuthorizeKey(desc, fp)); } - pub fn request_fingerprint_remove(&self, fp: String) { + fn request_fingerprint_remove(&self, fp: String) { self.request(FrontendRequest::RemoveAuthorizedKey(fp)); } - pub fn request(&self, request: FrontendRequest) { + fn request(&self, request: FrontendRequest) { let mut requester = self.imp().frontend_request_writer.borrow_mut(); let requester = requester.as_mut().unwrap(); if let Err(e) = requester.request(request) { @@ -385,18 +428,18 @@ impl Window { }; } - pub fn show_toast(&self, msg: &str) { + pub(super) fn show_toast(&self, msg: &str) { let toast = adw::Toast::new(msg); let toast_overlay = &self.imp().toast_overlay; toast_overlay.add_toast(toast); } - pub fn set_capture(&self, active: bool) { + pub(super) fn set_capture(&self, active: bool) { self.imp().capture_active.replace(active); self.update_capture_emulation_status(); } - pub fn set_emulation(&self, active: bool) { + pub(super) fn set_emulation(&self, active: bool) { self.imp().emulation_active.replace(active); self.update_capture_emulation_status(); } @@ -411,7 +454,7 @@ impl Window { .set_visible(!capture || !emulation); } - pub(crate) fn set_authorized_keys(&self, fingerprints: HashMap) { + pub(super) fn set_authorized_keys(&self, fingerprints: HashMap) { let authorized = self.authorized(); // clear list authorized.remove_all(); @@ -423,7 +466,7 @@ impl Window { self.update_auth_placeholder_visibility(); } - pub(crate) fn set_pk_fp(&self, fingerprint: &str) { + pub(super) fn set_pk_fp(&self, fingerprint: &str) { self.imp().fingerprint_row.set_subtitle(fingerprint); } } diff --git a/lan-mouse-ipc/src/lib.rs b/lan-mouse-ipc/src/lib.rs index 7c2a51b..0d0b87c 100644 --- a/lan-mouse-ipc/src/lib.rs +++ b/lan-mouse-ipc/src/lib.rs @@ -177,8 +177,6 @@ pub struct ClientState { #[derive(Debug, Clone, Serialize, Deserialize)] pub enum FrontendEvent { - /// client state has changed, new state must be requested via [`FrontendRequest::GetState`] - Changed(ClientHandle), /// a client was created Created(ClientHandle, ClientConfig, ClientState), /// no such client @@ -229,8 +227,6 @@ pub enum FrontendRequest { UpdatePosition(ClientHandle, Position), /// update fix-ips UpdateFixIps(ClientHandle, Vec), - /// request the state of the given client - GetState(ClientHandle), /// request reenabling input capture EnableCapture, /// request reenabling input emulation diff --git a/src/config.rs b/src/config.rs index 9cfccd3..95305c7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -38,7 +38,6 @@ pub struct ConfigToml { #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] pub struct TomlClient { - pub capture_backend: Option, pub hostname: Option, pub host_name: Option, pub ips: Option>, diff --git a/src/service.rs b/src/service.rs index 2717649..5ae65d8 100644 --- a/src/service.rs +++ b/src/service.rs @@ -186,7 +186,6 @@ impl Service { FrontendRequest::EnableCapture => self.capture.reenable(), FrontendRequest::EnableEmulation => self.emulation.reenable(), FrontendRequest::Enumerate() => self.enumerate(), - FrontendRequest::GetState(handle) => self.broadcast_client(handle), FrontendRequest::UpdateFixIps(handle, fix_ips) => self.update_fix_ips(handle, fix_ips), FrontendRequest::UpdateHostname(handle, host) => self.update_hostname(handle, host), FrontendRequest::UpdatePort(handle, port) => self.update_port(handle, port), @@ -283,7 +282,7 @@ impl Service { handle } }; - self.notify_frontend(FrontendEvent::Changed(handle)); + self.broadcast_client(handle); } fn resolve(&self, handle: ClientHandle) { @@ -399,7 +398,7 @@ impl Service { log::debug!("deactivating client {handle}"); if self.client_manager.deactivate_client(handle) { self.capture.destroy(handle); - self.notify_frontend(FrontendEvent::Changed(handle)); + self.broadcast_client(handle); log::info!("deactivated client {handle}"); } } @@ -425,7 +424,7 @@ impl Service { if self.client_manager.activate_client(handle) { /* notify capture and frontends */ self.capture.create(handle, pos, CaptureType::Default); - self.notify_frontend(FrontendEvent::Changed(handle)); + self.broadcast_client(handle); log::info!("activated client {handle} ({pos})"); } } @@ -452,19 +451,20 @@ impl Service { fn update_fix_ips(&mut self, handle: ClientHandle, fix_ips: Vec) { self.client_manager.set_fix_ips(handle, fix_ips); - self.notify_frontend(FrontendEvent::Changed(handle)); + self.broadcast_client(handle); } fn update_hostname(&mut self, handle: ClientHandle, hostname: Option) { + log::info!("hostname changed: {hostname:?}"); if self.client_manager.set_hostname(handle, hostname.clone()) { self.resolve(handle); } - self.notify_frontend(FrontendEvent::Changed(handle)); + self.broadcast_client(handle); } fn update_port(&mut self, handle: ClientHandle, port: u16) { self.client_manager.set_port(handle, port); - self.notify_frontend(FrontendEvent::Changed(handle)); + self.broadcast_client(handle); } fn update_pos(&mut self, handle: ClientHandle, pos: Position) { @@ -473,7 +473,7 @@ impl Service { self.deactivate_client(handle); self.activate_client(handle); } - self.notify_frontend(FrontendEvent::Changed(handle)); + self.broadcast_client(handle); } fn broadcast_client(&mut self, handle: ClientHandle) {