mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-07 11:59:59 +03:00
Activate on startup (#70)
Frontends are now properly synced among each other and on startup the correct state is reflected. Closes #75 Closes #68
This commit is contained in:
committed by
GitHub
parent
2e52660714
commit
d90eb0cd0f
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -46,6 +46,20 @@ impl Display for Position {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Position {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
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<IpAddr>,
|
||||
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(),
|
||||
|
||||
@@ -15,17 +15,19 @@ pub const DEFAULT_PORT: u16 = 4242;
|
||||
pub struct ConfigToml {
|
||||
pub port: Option<u16>,
|
||||
pub frontend: Option<String>,
|
||||
pub left: Option<Client>,
|
||||
pub right: Option<Client>,
|
||||
pub top: Option<Client>,
|
||||
pub bottom: Option<Client>,
|
||||
pub left: Option<TomlClient>,
|
||||
pub right: Option<TomlClient>,
|
||||
pub top: Option<TomlClient>,
|
||||
pub bottom: Option<TomlClient>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||
pub struct Client {
|
||||
pub struct TomlClient {
|
||||
pub hostname: Option<String>,
|
||||
pub host_name: Option<String>,
|
||||
pub ips: Option<Vec<IpAddr>>,
|
||||
pub port: Option<u16>,
|
||||
pub activate_on_startup: Option<bool>,
|
||||
}
|
||||
|
||||
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<IpAddr>,
|
||||
pub hostname: Option<String>,
|
||||
pub port: u16,
|
||||
pub pos: Position,
|
||||
pub active: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new() -> Result<Self> {
|
||||
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<IpAddr>, Option<String>, u16, Position)> {
|
||||
pub fn get_clients(&self) -> Vec<ConfigClient> {
|
||||
self.clients
|
||||
.iter()
|
||||
.map(|(c, p)| {
|
||||
.map(|(c, pos)| {
|
||||
let port = c.port.unwrap_or(DEFAULT_PORT);
|
||||
let ips: HashSet<IpAddr> = 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()
|
||||
}
|
||||
|
||||
@@ -103,8 +103,9 @@ pub enum FrontendEvent {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum FrontendNotify {
|
||||
NotifyClientCreate(ClientHandle, Option<String>, u16, Position),
|
||||
NotifyClientUpdate(ClientHandle, Option<String>, u16, Position),
|
||||
NotifyClientActivate(ClientHandle, bool),
|
||||
NotifyClientCreate(Client),
|
||||
NotifyClientUpdate(Client),
|
||||
NotifyClientDelete(ClientHandle),
|
||||
/// new port, reason of failure (if failed)
|
||||
NotifyPortChange(u16, Option<String>),
|
||||
@@ -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
|
||||
|
||||
@@ -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::NotifyClientUpdate(client, host, port, pos) => {
|
||||
log::info!(
|
||||
"client ({client}) updated: {}:{port} - {pos}",
|
||||
host.as_deref().unwrap_or("")
|
||||
);
|
||||
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) => {
|
||||
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.");
|
||||
|
||||
@@ -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::<u32>()
|
||||
.unwrap();
|
||||
let Some(client) = window.clients().item(index) else {
|
||||
return;
|
||||
};
|
||||
let client = client.downcast_ref::<ClientObject>().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::<u32>()
|
||||
.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();
|
||||
}
|
||||
|
||||
@@ -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<imp::ClientObject>);
|
||||
}
|
||||
|
||||
impl ClientObject {
|
||||
pub fn new(
|
||||
handle: ClientHandle,
|
||||
hostname: Option<String>,
|
||||
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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String>| {
|
||||
@@ -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);
|
||||
|
||||
@@ -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<Vec<Signal>> = 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", &[]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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::<ClientObject>().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<String>,
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
191
src/server.rs
191
src/server.rs
@@ -67,14 +67,6 @@ pub enum ConsumerEvent {
|
||||
Terminate,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ClientUpdate {
|
||||
client: ClientHandle,
|
||||
hostname: Option<String>,
|
||||
port: u16,
|
||||
pos: Position,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Server {
|
||||
active_client: Rc<Cell<Option<ClientHandle>>>,
|
||||
@@ -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::<Vec<_>>();
|
||||
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<String>,
|
||||
addr: HashSet<IpAddr>,
|
||||
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<ProducerEvent>,
|
||||
consumer_notify_tx: &Sender<ConsumerEvent>,
|
||||
resolve_tx: &Sender<(String, ClientHandle)>,
|
||||
client_update: ClientUpdate,
|
||||
client_update: (ClientHandle, Option<String>, 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<FrontendEvent>,
|
||||
#[cfg(unix)] mut stream: ReadHalf<UnixStream>,
|
||||
#[cfg(windows)] mut stream: ReadHalf<TcpStream>,
|
||||
@@ -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<ProducerEvent>,
|
||||
consumer_notify_tx: &Sender<ConsumerEvent>,
|
||||
producer_tx: &Sender<ProducerEvent>,
|
||||
consumer_tx: &Sender<ConsumerEvent>,
|
||||
resolve_tx: &Sender<(String, ClientHandle)>,
|
||||
frontend: &mut FrontendListener,
|
||||
port_tx: &Sender<u16>,
|
||||
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)> {
|
||||
|
||||
Reference in New Issue
Block a user