diff --git a/resources/style-dark.css b/resources/style-dark.css
index df5c5c3..e147077 100644
--- a/resources/style-dark.css
+++ b/resources/style-dark.css
@@ -1,3 +1,11 @@
#delete-button {
color: @red_1;
}
+
+#port-edit-cancel {
+ color: @red_1;
+}
+
+#port-edit-apply {
+ color: @green_1;
+}
diff --git a/resources/style.css b/resources/style.css
index 0d2d462..a21d63b 100644
--- a/resources/style.css
+++ b/resources/style.css
@@ -1,3 +1,11 @@
#delete-button {
color: @red_3;
}
+
+#port-edit-cancel {
+ color: @red_3;
+}
+
+#port-edit-apply {
+ color: @green_3;
+}
diff --git a/resources/window.ui b/resources/window.ui
index a353459..f04be68 100644
--- a/resources/window.ui
+++ b/resources/window.ui
@@ -8,114 +8,143 @@
window.close
-
+
600
Lan Mouse
True
-
-
-
-
-
-
-
-
-
+
+
-
+
diff --git a/src/event/server.rs b/src/event/server.rs
index 2ccf82b..a4997e4 100644
--- a/src/event/server.rs
+++ b/src/event/server.rs
@@ -453,13 +453,38 @@ impl Server {
match event {
FrontendEvent::AddClient(hostname, port, pos) => { self.add_client(hostname, HashSet::new(), port, pos).await; },
FrontendEvent::ActivateClient(client, active) => self.activate_client(client, active).await,
+ FrontendEvent::ChangePort(port) => {
+ let current_port = self.socket.local_addr().unwrap().port();
+ if current_port == port {
+ if let Err(e) = self.frontend.notify_all(FrontendNotify::NotifyPortChange(port, None)).await {
+ log::warn!("error notifying frontend: {e}");
+ }
+ return false;
+ }
+ let listen_addr = SocketAddr::new("0.0.0.0".parse().unwrap(), port);
+ match UdpSocket::bind(listen_addr).await {
+ Ok(socket) => {
+ self.socket = socket;
+ if let Err(e) = self.frontend.notify_all(FrontendNotify::NotifyPortChange(port, None)).await {
+ log::warn!("error notifying frontend: {e}");
+ }
+ },
+ Err(e) => {
+ log::warn!("could not change port: {e}");
+ let port = self.socket.local_addr().unwrap().port();
+ if let Err(e) = self.frontend.notify_all(FrontendNotify::NotifyPortChange(port, Some(format!("could not change port: {e}")))).await {
+ log::error!("error notifying frontend: {e}");
+ }
+ }
+ }
+ },
FrontendEvent::DelClient(client) => { self.remove_client(client).await; },
- FrontendEvent::UpdateClient(client, hostname, port, pos) => self.update_client(client, hostname, port, pos).await,
FrontendEvent::Enumerate() => self.enumerate().await,
FrontendEvent::Shutdown() => {
log::info!("terminating gracefully...");
return true;
},
+ FrontendEvent::UpdateClient(client, hostname, port, pos) => self.update_client(client, hostname, port, pos).await,
}
false
}
diff --git a/src/frontend.rs b/src/frontend.rs
index e08f27a..ac7baa5 100644
--- a/src/frontend.rs
+++ b/src/frontend.rs
@@ -33,14 +33,16 @@ pub enum FrontendEvent {
AddClient(Option, u16, Position),
/// activate/deactivate client
ActivateClient(ClientHandle, bool),
- /// update a client (hostname, port, position)
- UpdateClient(ClientHandle, Option, u16, Position),
+ /// change the listen port (recreate udp listener)
+ ChangePort(u16),
/// remove a client
DelClient(ClientHandle),
/// request an enumertaion of all clients
Enumerate(),
/// service shutdown
Shutdown(),
+ /// update a client (hostname, port, position)
+ UpdateClient(ClientHandle, Option, u16, Position),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -48,6 +50,8 @@ pub enum FrontendNotify {
NotifyClientCreate(ClientHandle, Option, u16, Position),
NotifyClientUpdate(ClientHandle, Option, u16, Position),
NotifyClientDelete(ClientHandle),
+ /// new port, reason of failure (if failed)
+ NotifyPortChange(u16, Option),
Enumerate(Vec<(Client, bool)>),
NotifyError(String),
}
diff --git a/src/frontend/cli.rs b/src/frontend/cli.rs
index b2f5ebe..9f522b5 100644
--- a/src/frontend/cli.rs
+++ b/src/frontend/cli.rs
@@ -115,6 +115,12 @@ pub fn start() -> Result<(JoinHandle<()>, JoinHandle<()>)> {
.join(", ")
);
}
+ },
+ FrontendNotify::NotifyPortChange(port, msg) => {
+ match msg {
+ Some(msg) => log::info!("could not change port: {msg}"),
+ None => log::info!("port changed: {port}"),
+ }
}
}
prompt();
@@ -142,6 +148,7 @@ fn parse_cmd(s: String, len: usize) -> Option> {
log::info!("activate activate a client");
log::info!("deactivate deactivate a client");
log::info!("exit exit lan-mouse");
+ log::info!("setport change port");
None
}
"exit" => return Some(vec![FrontendEvent::Shutdown()]),
@@ -150,13 +157,14 @@ fn parse_cmd(s: String, len: usize) -> Option> {
"disconnect" => Some(parse_disconnect(l)),
"activate" => Some(parse_activate(l)),
"deactivate" => Some(parse_deactivate(l)),
+ "setport" => Some(parse_port(l)),
_ => {
log::error!("unknown command: {s}");
None
}
};
match res {
- Some(Ok(e)) => Some(vec![e, FrontendEvent::Enumerate()]),
+ Some(Ok(e)) => Some(e),
Some(Err(e)) => {
log::warn!("{e}");
None
@@ -165,7 +173,7 @@ fn parse_cmd(s: String, len: usize) -> Option> {
}
}
-fn parse_connect(mut l: SplitWhitespace) -> Result {
+fn parse_connect(mut l: SplitWhitespace) -> Result> {
let usage = "usage: connect left|right|top|bottom [port]";
let host = l.next().context(usage)?.to_owned();
let pos = match l.next().context(usage)? {
@@ -179,19 +187,25 @@ fn parse_connect(mut l: SplitWhitespace) -> Result {
} else {
DEFAULT_PORT
};
- Ok(FrontendEvent::AddClient(Some(host), port, pos))
+ Ok(vec![FrontendEvent::AddClient(Some(host), port, pos), FrontendEvent::Enumerate()])
}
-fn parse_disconnect(mut l: SplitWhitespace) -> Result {
+fn parse_disconnect(mut l: SplitWhitespace) -> Result> {
let client = l.next().context("usage: disconnect ")?.parse()?;
- Ok(FrontendEvent::DelClient(client))
+ Ok(vec![FrontendEvent::DelClient(client), FrontendEvent::Enumerate()])
}
-fn parse_activate(mut l: SplitWhitespace) -> Result {
+fn parse_activate(mut l: SplitWhitespace) -> Result> {
let client = l.next().context("usage: activate ")?.parse()?;
- Ok(FrontendEvent::ActivateClient(client, true))
+ Ok(vec![FrontendEvent::ActivateClient(client, true), FrontendEvent::Enumerate()])
}
-fn parse_deactivate(mut l: SplitWhitespace) -> Result {
+
+fn parse_deactivate(mut l: SplitWhitespace) -> Result> {
let client = l.next().context("usage: deactivate ")?.parse()?;
- Ok(FrontendEvent::ActivateClient(client, false))
+ Ok(vec![FrontendEvent::ActivateClient(client, false), FrontendEvent::Enumerate()])
+}
+
+fn parse_port(mut l: SplitWhitespace) -> Result> {
+ let port = l.next().context("usage: setport ")?.parse()?;
+ Ok(vec![FrontendEvent::ChangePort(port)])
}
diff --git a/src/frontend/gtk.rs b/src/frontend/gtk.rs
index 8b498a4..20fddf5 100644
--- a/src/frontend/gtk.rs
+++ b/src/frontend/gtk.rs
@@ -145,6 +145,13 @@ fn build_ui(app: &Application) {
);
}
},
+ FrontendNotify::NotifyPortChange(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);
+ }
}
glib::ControlFlow::Continue
}
diff --git a/src/frontend/gtk/window.rs b/src/frontend/gtk/window.rs
index 4d8b7ee..0bf2d48 100644
--- a/src/frontend/gtk/window.rs
+++ b/src/frontend/gtk/window.rs
@@ -100,9 +100,19 @@ impl Window {
pub fn request_client_create(&self) {
let event = FrontendEvent::AddClient(None, DEFAULT_PORT, Position::default());
+ self.imp().set_port(DEFAULT_PORT);
self.request(event);
}
+ pub fn request_port_change(&self) {
+ let port = self.imp().port_entry.get().text().to_string();
+ if let Ok(port) = u16::from_str_radix(port.as_str(), 10) {
+ self.request(FrontendEvent::ChangePort(port));
+ } else {
+ self.request(FrontendEvent::ChangePort(DEFAULT_PORT));
+ }
+ }
+
pub fn request_client_update(&self, client: &ClientObject) {
let data = client.get_data();
let position = match data.position.as_str() {
@@ -150,11 +160,9 @@ impl Window {
};
}
- fn setup_callbacks(&self) {
- self.imp()
- .add_client_button
- .connect_clicked(clone!(@weak self as window => move |_| {
- window.request_client_create();
- }));
+ pub fn show_toast(&self, msg: &str) {
+ let toast = adw::Toast::new(msg);
+ let toast_overlay = &self.imp().toast_overlay;
+ toast_overlay.add_toast(toast);
}
}
diff --git a/src/frontend/gtk/window/imp.rs b/src/frontend/gtk/window/imp.rs
index 8c65eca..6b90f9f 100644
--- a/src/frontend/gtk/window/imp.rs
+++ b/src/frontend/gtk/window/imp.rs
@@ -1,22 +1,32 @@
use std::{cell::{Cell, RefCell}, os::unix::net::UnixStream};
use glib::subclass::InitializingObject;
-use adw::{prelude::*, ActionRow};
+use adw::{ActionRow, ToastOverlay, prelude::{WidgetExt, EditableExt}};
use adw::subclass::prelude::*;
-use gtk::{glib, Button, CompositeTemplate, ListBox, gio};
+use gtk::{glib, Button, CompositeTemplate, ListBox, gio, Entry};
+
+use crate::config::DEFAULT_PORT;
#[derive(CompositeTemplate, Default)]
#[template(resource = "/de/feschber/LanMouse/window.ui")]
pub struct Window {
- pub number: Cell,
+ #[template_child]
+ pub port_edit_apply: TemplateChild