diff --git a/resources/client_row.ui b/resources/client_row.ui
index 2bdd726..317006d 100644
--- a/resources/client_row.ui
+++ b/resources/client_row.ui
@@ -13,12 +13,16 @@
+
+
+
diff --git a/src/client.rs b/src/client.rs
index 4371867..95b470c 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -139,6 +139,8 @@ pub struct ClientState {
pub ips: HashSet,
/// keys currently pressed by this client
pub pressed_keys: HashSet,
+ /// dns resolving in progress
+ pub resolving: bool,
}
pub struct ClientManager {
diff --git a/src/frontend.rs b/src/frontend.rs
index 5777cc7..ac4b15d 100644
--- a/src/frontend.rs
+++ b/src/frontend.rs
@@ -95,6 +95,8 @@ pub enum FrontendRequest {
Delete(ClientHandle),
/// request an enumeration of all clients
Enumerate(),
+ /// resolve dns
+ ResolveDns(ClientHandle),
/// service shutdown
Terminate(),
/// update hostname
diff --git a/src/frontend/gtk/client_object.rs b/src/frontend/gtk/client_object.rs
index 7b48f3c..a9da3b1 100644
--- a/src/frontend/gtk/client_object.rs
+++ b/src/frontend/gtk/client_object.rs
@@ -17,6 +17,15 @@ impl ClientObject {
.property("port", client.port as u32)
.property("position", client.pos.to_string())
.property("active", state.active)
+ .property(
+ "ips",
+ state
+ .ips
+ .iter()
+ .map(|ip| ip.to_string())
+ .collect::>(),
+ )
+ .property("resolving", state.resolving)
.build()
}
@@ -32,4 +41,6 @@ pub struct ClientData {
pub port: u32,
pub active: bool,
pub position: String,
+ pub resolving: bool,
+ pub ips: Vec,
}
diff --git a/src/frontend/gtk/client_object/imp.rs b/src/frontend/gtk/client_object/imp.rs
index 8ca456e..03e9e95 100644
--- a/src/frontend/gtk/client_object/imp.rs
+++ b/src/frontend/gtk/client_object/imp.rs
@@ -17,6 +17,8 @@ pub struct ClientObject {
#[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)]
+ #[property(name = "resolving", get, set, type = bool, member = resolving)]
+ #[property(name = "ips", get, set, type = Vec, member = ips)]
pub data: RefCell,
}
diff --git a/src/frontend/gtk/client_row.rs b/src/frontend/gtk/client_row.rs
index 082d185..58e11cf 100644
--- a/src/frontend/gtk/client_row.rs
+++ b/src/frontend/gtk/client_row.rs
@@ -109,6 +109,27 @@ impl ClientRow {
.sync_create()
.build();
+ let resolve_binding = client_object
+ .bind_property(
+ "resolving",
+ &self.imp().dns_loading_indicator.get(),
+ "spinning",
+ )
+ .sync_create()
+ .build();
+
+ let ip_binding = client_object
+ .bind_property("ips", &self.imp().dns_button.get(), "tooltip-text")
+ .transform_to(|_, ips: Vec| {
+ if ips.is_empty() {
+ Some("no ip addresses associated with this client".into())
+ } else {
+ Some(ips.join("\n"))
+ }
+ })
+ .sync_create()
+ .build();
+
bindings.push(active_binding);
bindings.push(switch_position_binding);
bindings.push(hostname_binding);
@@ -116,6 +137,8 @@ impl ClientRow {
bindings.push(port_binding);
bindings.push(subtitle_binding);
bindings.push(position_binding);
+ bindings.push(resolve_binding);
+ bindings.push(ip_binding);
}
pub fn unbind(&self) {
diff --git a/src/frontend/gtk/client_row/imp.rs b/src/frontend/gtk/client_row/imp.rs
index d6dead2..141173b 100644
--- a/src/frontend/gtk/client_row/imp.rs
+++ b/src/frontend/gtk/client_row/imp.rs
@@ -25,6 +25,8 @@ pub struct ClientRow {
pub delete_row: TemplateChild,
#[template_child]
pub delete_button: TemplateChild,
+ #[template_child]
+ pub dns_loading_indicator: TemplateChild,
pub bindings: RefCell>,
}
@@ -60,6 +62,7 @@ impl ObjectImpl for ClientRow {
static SIGNALS: OnceLock> = OnceLock::new();
SIGNALS.get_or_init(|| {
vec![
+ Signal::builder("request-dns").build(),
Signal::builder("request-update")
.param_types([bool::static_type()])
.build(),
@@ -79,8 +82,8 @@ impl ClientRow {
}
#[template_callback]
- fn handle_request_dns(&self) -> bool {
- false
+ fn handle_request_dns(&self, _: Button) {
+ self.obj().emit_by_name::<()>("request-dns", &[]);
}
#[template_callback]
diff --git a/src/frontend/gtk/window.rs b/src/frontend/gtk/window.rs
index 7d5b8a8..eeecb9f 100644
--- a/src/frontend/gtk/window.rs
+++ b/src/frontend/gtk/window.rs
@@ -14,7 +14,7 @@ use glib::{clone, Object};
use gtk::{
gio,
glib::{self, closure_local},
- NoSelection,
+ ListBox, NoSelection,
};
use crate::{
@@ -67,12 +67,23 @@ impl Window {
return;
};
let client = client.downcast_ref::().unwrap();
- window.request_client_update(client, active);
+ window.request_client_update(client);
+ window.request_client_activate(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.connect_closure("request-dns", false, closure_local!(@strong window => move
+ |row: ClientRow| {
+ 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);
+ window.request_dns(index);
+ }));
row.upcast()
})
);
@@ -100,9 +111,10 @@ impl Window {
}
pub fn new_client(&self, handle: ClientHandle, client: ClientConfig, state: ClientState) {
- let client = ClientObject::new(handle, client, state);
+ let client = ClientObject::new(handle, client, state.clone());
self.clients().append(&client);
self.set_placeholder_visible(false);
+ self.update_dns_state(handle, !state.ips.is_empty());
}
pub fn client_idx(&self, handle: ClientHandle) -> Option {
@@ -162,6 +174,42 @@ impl Window {
client_object.set_active(state.active);
log::debug!("set active to {}", state.active);
}
+
+ if state.resolving != data.resolving {
+ client_object.set_resolving(state.resolving);
+ log::debug!("resolving {}: {}", data.handle, state.active);
+ }
+
+ self.update_dns_state(handle, !state.ips.is_empty());
+ let ips = state
+ .ips
+ .into_iter()
+ .map(|ip| ip.to_string())
+ .collect::>();
+ 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"])
+ }
+ }
+
+ pub fn request_dns(&self, idx: u32) {
+ let client_object = self.clients().item(idx).unwrap();
+ let client_object: &ClientObject = client_object.downcast_ref().unwrap();
+ let data = client_object.get_data();
+ let event = FrontendRequest::ResolveDns(data.handle);
+ self.request(event);
}
pub fn request_client_create(&self) {
@@ -179,7 +227,7 @@ impl Window {
}
}
- pub fn request_client_update(&self, client: &ClientObject, active: bool) {
+ 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");
@@ -190,13 +238,20 @@ impl Window {
FrontendRequest::UpdateHostname(handle, hostname),
FrontendRequest::UpdatePosition(handle, position),
FrontendRequest::UpdatePort(handle, port),
- FrontendRequest::Activate(handle, active),
] {
log::debug!("requesting: {event:?}");
self.request(event);
}
}
+ pub fn request_client_activate(&self, client: &ClientObject, active: bool) {
+ let handle = client.handle();
+
+ let event = FrontendRequest::Activate(handle, active);
+ log::debug!("requesting: {event:?}");
+ self.request(event);
+ }
+
pub fn request_client_delete(&self, idx: u32) {
if let Some(obj) = self.clients().item(idx) {
let client_object: &ClientObject = obj
diff --git a/src/server.rs b/src/server.rs
index 8eb1450..b63db3f 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -92,7 +92,7 @@ impl Server {
// udp task
let (mut udp_task, sender_tx, receiver_rx, port_tx) =
- network_task::new(self.clone(), frontend_notify_tx).await?;
+ network_task::new(self.clone(), frontend_notify_tx.clone()).await?;
// input capture
let (mut capture_task, capture_channel) = capture_task::new(
@@ -113,7 +113,8 @@ impl Server {
// create dns resolver
let resolver = dns::DnsResolver::new().await?;
- let (mut resolver_task, resolve_tx) = resolver_task::new(resolver, self.clone());
+ let (mut resolver_task, resolve_tx) =
+ resolver_task::new(resolver, self.clone(), frontend_notify_tx);
// frontend listener
let (mut frontend_task, frontend_tx) = frontend_task::new(
diff --git a/src/server/frontend_task.rs b/src/server/frontend_task.rs
index 58a657c..abc75b1 100644
--- a/src/server/frontend_task.rs
+++ b/src/server/frontend_task.rs
@@ -151,6 +151,16 @@ async fn handle_frontend_event(
update_pos(server, handle, capture, emulate, pos).await;
broadcast_client_update(server, frontend, handle).await;
}
+ FrontendRequest::ResolveDns(handle) => {
+ let hostname = server
+ .client_manager
+ .borrow()
+ .get(handle)
+ .and_then(|(c, _)| c.hostname.clone());
+ if let Some(hostname) = hostname {
+ let _ = resolve_tx.send(DnsRequest { hostname, handle }).await;
+ }
+ }
};
false
}
diff --git a/src/server/resolver_task.rs b/src/server/resolver_task.rs
index 03c026b..ddfb17c 100644
--- a/src/server/resolver_task.rs
+++ b/src/server/resolver_task.rs
@@ -2,7 +2,7 @@ use std::collections::HashSet;
use tokio::{sync::mpsc::Sender, task::JoinHandle};
-use crate::{client::ClientHandle, dns::DnsResolver};
+use crate::{client::ClientHandle, dns::DnsResolver, frontend::FrontendEvent};
use super::Server;
@@ -12,7 +12,11 @@ pub struct DnsRequest {
pub handle: ClientHandle,
}
-pub fn new(resolver: DnsResolver, server: Server) -> (JoinHandle<()>, Sender) {
+pub fn new(
+ resolver: DnsResolver,
+ mut server: Server,
+ mut frontend: Sender,
+) -> (JoinHandle<()>, Sender) {
let (dns_tx, mut dns_rx) = tokio::sync::mpsc::channel::(32);
let resolver_task = tokio::task::spawn_local(async move {
loop {
@@ -20,6 +24,13 @@ pub fn new(resolver: DnsResolver, server: Server) -> (JoinHandle<()>, Sender (r.hostname, r.handle),
None => break,
};
+
+ /* update resolving status */
+ if let Some((_, s)) = server.client_manager.borrow_mut().get_mut(handle) {
+ s.resolving = true;
+ }
+ notify_state_change(&mut frontend, &mut server, handle).await;
+
let ips = match resolver.resolve(&host).await {
Ok(ips) => ips,
Err(e) => {
@@ -27,14 +38,35 @@ pub fn new(resolver: DnsResolver, server: Server) -> (JoinHandle<()>, Sender,
+ server: &mut Server,
+ handle: ClientHandle,
+) {
+ let state = server
+ .client_manager
+ .borrow_mut()
+ .get_mut(handle)
+ .map(|(_, s)| s.clone());
+ if let Some(state) = state {
+ let _ = frontend
+ .send(FrontendEvent::StateChange(handle, state))
+ .await;
+ }
+}