mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-04-12 17:31:28 +03:00
implement dns indicator (#119)
This commit is contained in:
committed by
GitHub
parent
5318f5a02d
commit
c76d9ef7af
@@ -13,12 +13,16 @@
|
|||||||
</child>
|
</child>
|
||||||
<child type="suffix">
|
<child type="suffix">
|
||||||
<object class="GtkButton" id="dns_button">
|
<object class="GtkButton" id="dns_button">
|
||||||
<signal name="activate" handler="handle_request_dns" swapped="true"/>
|
<signal name="clicked" handler="handle_request_dns" swapped="true"/>
|
||||||
<!--<property name="icon-name">network-wired-disconnected-symbolic</property>-->
|
<!--<property name="icon-name">network-wired-disconnected-symbolic</property>-->
|
||||||
<property name="icon-name">network-wired-symbolic</property>
|
<property name="icon-name">network-wired-symbolic</property>
|
||||||
<property name="valign">center</property>
|
<property name="valign">center</property>
|
||||||
<property name="halign">end</property>
|
<property name="halign">end</property>
|
||||||
<property name="tooltip-text" translatable="yes">resolve dns</property>
|
<property name="tooltip-text" translatable="yes">resolve host</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child type="suffix">
|
||||||
|
<object class="GtkSpinner" id="dns_loading_indicator">
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<!-- host -->
|
<!-- host -->
|
||||||
|
|||||||
@@ -139,6 +139,8 @@ pub struct ClientState {
|
|||||||
pub ips: HashSet<IpAddr>,
|
pub ips: HashSet<IpAddr>,
|
||||||
/// keys currently pressed by this client
|
/// keys currently pressed by this client
|
||||||
pub pressed_keys: HashSet<u32>,
|
pub pressed_keys: HashSet<u32>,
|
||||||
|
/// dns resolving in progress
|
||||||
|
pub resolving: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClientManager {
|
pub struct ClientManager {
|
||||||
|
|||||||
@@ -95,6 +95,8 @@ pub enum FrontendRequest {
|
|||||||
Delete(ClientHandle),
|
Delete(ClientHandle),
|
||||||
/// request an enumeration of all clients
|
/// request an enumeration of all clients
|
||||||
Enumerate(),
|
Enumerate(),
|
||||||
|
/// resolve dns
|
||||||
|
ResolveDns(ClientHandle),
|
||||||
/// service shutdown
|
/// service shutdown
|
||||||
Terminate(),
|
Terminate(),
|
||||||
/// update hostname
|
/// update hostname
|
||||||
|
|||||||
@@ -17,6 +17,15 @@ impl ClientObject {
|
|||||||
.property("port", client.port as u32)
|
.property("port", client.port as u32)
|
||||||
.property("position", client.pos.to_string())
|
.property("position", client.pos.to_string())
|
||||||
.property("active", state.active)
|
.property("active", state.active)
|
||||||
|
.property(
|
||||||
|
"ips",
|
||||||
|
state
|
||||||
|
.ips
|
||||||
|
.iter()
|
||||||
|
.map(|ip| ip.to_string())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
.property("resolving", state.resolving)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,4 +41,6 @@ pub struct ClientData {
|
|||||||
pub port: u32,
|
pub port: u32,
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
pub position: String,
|
pub position: String,
|
||||||
|
pub resolving: bool,
|
||||||
|
pub ips: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ pub struct ClientObject {
|
|||||||
#[property(name = "port", get, set, type = u32, member = port, maximum = u16::MAX as u32)]
|
#[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 = "active", get, set, type = bool, member = active)]
|
||||||
#[property(name = "position", get, set, type = String, member = position)]
|
#[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<String>, member = ips)]
|
||||||
pub data: RefCell<ClientData>,
|
pub data: RefCell<ClientData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,27 @@ impl ClientRow {
|
|||||||
.sync_create()
|
.sync_create()
|
||||||
.build();
|
.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<String>| {
|
||||||
|
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(active_binding);
|
||||||
bindings.push(switch_position_binding);
|
bindings.push(switch_position_binding);
|
||||||
bindings.push(hostname_binding);
|
bindings.push(hostname_binding);
|
||||||
@@ -116,6 +137,8 @@ impl ClientRow {
|
|||||||
bindings.push(port_binding);
|
bindings.push(port_binding);
|
||||||
bindings.push(subtitle_binding);
|
bindings.push(subtitle_binding);
|
||||||
bindings.push(position_binding);
|
bindings.push(position_binding);
|
||||||
|
bindings.push(resolve_binding);
|
||||||
|
bindings.push(ip_binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unbind(&self) {
|
pub fn unbind(&self) {
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ pub struct ClientRow {
|
|||||||
pub delete_row: TemplateChild<ActionRow>,
|
pub delete_row: TemplateChild<ActionRow>,
|
||||||
#[template_child]
|
#[template_child]
|
||||||
pub delete_button: TemplateChild<gtk::Button>,
|
pub delete_button: TemplateChild<gtk::Button>,
|
||||||
|
#[template_child]
|
||||||
|
pub dns_loading_indicator: TemplateChild<gtk::Spinner>,
|
||||||
pub bindings: RefCell<Vec<Binding>>,
|
pub bindings: RefCell<Vec<Binding>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +62,7 @@ impl ObjectImpl for ClientRow {
|
|||||||
static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
|
static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
|
||||||
SIGNALS.get_or_init(|| {
|
SIGNALS.get_or_init(|| {
|
||||||
vec![
|
vec![
|
||||||
|
Signal::builder("request-dns").build(),
|
||||||
Signal::builder("request-update")
|
Signal::builder("request-update")
|
||||||
.param_types([bool::static_type()])
|
.param_types([bool::static_type()])
|
||||||
.build(),
|
.build(),
|
||||||
@@ -79,8 +82,8 @@ impl ClientRow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[template_callback]
|
#[template_callback]
|
||||||
fn handle_request_dns(&self) -> bool {
|
fn handle_request_dns(&self, _: Button) {
|
||||||
false
|
self.obj().emit_by_name::<()>("request-dns", &[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[template_callback]
|
#[template_callback]
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use glib::{clone, Object};
|
|||||||
use gtk::{
|
use gtk::{
|
||||||
gio,
|
gio,
|
||||||
glib::{self, closure_local},
|
glib::{self, closure_local},
|
||||||
NoSelection,
|
ListBox, NoSelection,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -67,12 +67,23 @@ impl Window {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let client = client.downcast_ref::<ClientObject>().unwrap();
|
let client = client.downcast_ref::<ClientObject>().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| {
|
row.connect_closure("request-delete", false, closure_local!(@strong window => move |row: ClientRow| {
|
||||||
let index = row.index() as u32;
|
let index = row.index() as u32;
|
||||||
window.request_client_delete(index);
|
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::<ClientObject>().unwrap();
|
||||||
|
window.request_client_update(client);
|
||||||
|
window.request_dns(index);
|
||||||
|
}));
|
||||||
row.upcast()
|
row.upcast()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -100,9 +111,10 @@ impl Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_client(&self, handle: ClientHandle, client: ClientConfig, state: ClientState) {
|
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.clients().append(&client);
|
||||||
self.set_placeholder_visible(false);
|
self.set_placeholder_visible(false);
|
||||||
|
self.update_dns_state(handle, !state.ips.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn client_idx(&self, handle: ClientHandle) -> Option<usize> {
|
pub fn client_idx(&self, handle: ClientHandle) -> Option<usize> {
|
||||||
@@ -162,6 +174,42 @@ impl Window {
|
|||||||
client_object.set_active(state.active);
|
client_object.set_active(state.active);
|
||||||
log::debug!("set active to {}", 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::<Vec<_>>();
|
||||||
|
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) {
|
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 handle = client.handle();
|
||||||
let data = client.get_data();
|
let data = client.get_data();
|
||||||
let position = Position::try_from(data.position.as_str()).expect("invalid position");
|
let position = Position::try_from(data.position.as_str()).expect("invalid position");
|
||||||
@@ -190,13 +238,20 @@ impl Window {
|
|||||||
FrontendRequest::UpdateHostname(handle, hostname),
|
FrontendRequest::UpdateHostname(handle, hostname),
|
||||||
FrontendRequest::UpdatePosition(handle, position),
|
FrontendRequest::UpdatePosition(handle, position),
|
||||||
FrontendRequest::UpdatePort(handle, port),
|
FrontendRequest::UpdatePort(handle, port),
|
||||||
FrontendRequest::Activate(handle, active),
|
|
||||||
] {
|
] {
|
||||||
log::debug!("requesting: {event:?}");
|
log::debug!("requesting: {event:?}");
|
||||||
self.request(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) {
|
pub fn request_client_delete(&self, idx: u32) {
|
||||||
if let Some(obj) = self.clients().item(idx) {
|
if let Some(obj) = self.clients().item(idx) {
|
||||||
let client_object: &ClientObject = obj
|
let client_object: &ClientObject = obj
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ impl Server {
|
|||||||
|
|
||||||
// udp task
|
// udp task
|
||||||
let (mut udp_task, sender_tx, receiver_rx, port_tx) =
|
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
|
// input capture
|
||||||
let (mut capture_task, capture_channel) = capture_task::new(
|
let (mut capture_task, capture_channel) = capture_task::new(
|
||||||
@@ -113,7 +113,8 @@ impl Server {
|
|||||||
|
|
||||||
// create dns resolver
|
// create dns resolver
|
||||||
let resolver = dns::DnsResolver::new().await?;
|
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
|
// frontend listener
|
||||||
let (mut frontend_task, frontend_tx) = frontend_task::new(
|
let (mut frontend_task, frontend_tx) = frontend_task::new(
|
||||||
|
|||||||
@@ -151,6 +151,16 @@ async fn handle_frontend_event(
|
|||||||
update_pos(server, handle, capture, emulate, pos).await;
|
update_pos(server, handle, capture, emulate, pos).await;
|
||||||
broadcast_client_update(server, frontend, handle).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
|
false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::collections::HashSet;
|
|||||||
|
|
||||||
use tokio::{sync::mpsc::Sender, task::JoinHandle};
|
use tokio::{sync::mpsc::Sender, task::JoinHandle};
|
||||||
|
|
||||||
use crate::{client::ClientHandle, dns::DnsResolver};
|
use crate::{client::ClientHandle, dns::DnsResolver, frontend::FrontendEvent};
|
||||||
|
|
||||||
use super::Server;
|
use super::Server;
|
||||||
|
|
||||||
@@ -12,7 +12,11 @@ pub struct DnsRequest {
|
|||||||
pub handle: ClientHandle,
|
pub handle: ClientHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(resolver: DnsResolver, server: Server) -> (JoinHandle<()>, Sender<DnsRequest>) {
|
pub fn new(
|
||||||
|
resolver: DnsResolver,
|
||||||
|
mut server: Server,
|
||||||
|
mut frontend: Sender<FrontendEvent>,
|
||||||
|
) -> (JoinHandle<()>, Sender<DnsRequest>) {
|
||||||
let (dns_tx, mut dns_rx) = tokio::sync::mpsc::channel::<DnsRequest>(32);
|
let (dns_tx, mut dns_rx) = tokio::sync::mpsc::channel::<DnsRequest>(32);
|
||||||
let resolver_task = tokio::task::spawn_local(async move {
|
let resolver_task = tokio::task::spawn_local(async move {
|
||||||
loop {
|
loop {
|
||||||
@@ -20,6 +24,13 @@ pub fn new(resolver: DnsResolver, server: Server) -> (JoinHandle<()>, Sender<Dns
|
|||||||
Some(r) => (r.hostname, r.handle),
|
Some(r) => (r.hostname, r.handle),
|
||||||
None => break,
|
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 {
|
let ips = match resolver.resolve(&host).await {
|
||||||
Ok(ips) => ips,
|
Ok(ips) => ips,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -27,14 +38,35 @@ pub fn new(resolver: DnsResolver, server: Server) -> (JoinHandle<()>, Sender<Dns
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* update ips and resolving state */
|
||||||
if let Some((c, s)) = server.client_manager.borrow_mut().get_mut(handle) {
|
if let Some((c, s)) = server.client_manager.borrow_mut().get_mut(handle) {
|
||||||
let mut addrs = HashSet::from_iter(c.fix_ips.iter().cloned());
|
let mut addrs = HashSet::from_iter(c.fix_ips.iter().cloned());
|
||||||
for ip in ips {
|
for ip in ips {
|
||||||
addrs.insert(ip);
|
addrs.insert(ip);
|
||||||
}
|
}
|
||||||
s.ips = addrs;
|
s.ips = addrs;
|
||||||
|
s.resolving = false;
|
||||||
}
|
}
|
||||||
|
notify_state_change(&mut frontend, &mut server, handle).await;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
(resolver_task, dns_tx)
|
(resolver_task, dns_tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn notify_state_change(
|
||||||
|
frontend: &mut Sender<FrontendEvent>,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user