mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-04-10 07:51:27 +03:00
Frontend improvement (#27)
* removed redundant dns lookups * frontend now correctly reflects the state of the backend * config.toml is loaded when starting gtk frontend
This commit is contained in:
committed by
Ferdinand Schober
parent
603646c799
commit
06725f4b14
@@ -3,13 +3,16 @@ mod imp;
|
||||
use gtk::glib::{self, Object};
|
||||
use adw::subclass::prelude::*;
|
||||
|
||||
use crate::client::ClientHandle;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct ClientObject(ObjectSubclass<imp::ClientObject>);
|
||||
}
|
||||
|
||||
impl ClientObject {
|
||||
pub fn new(hostname: String, port: u32, active: bool, position: String) -> Self {
|
||||
pub fn new(handle: ClientHandle, hostname: Option<String>, port: u32, position: String, active: bool) -> Self {
|
||||
Object::builder()
|
||||
.property("handle", handle)
|
||||
.property("hostname", hostname)
|
||||
.property("port", port)
|
||||
.property("active", active)
|
||||
@@ -24,7 +27,8 @@ impl ClientObject {
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct ClientData {
|
||||
pub hostname: String,
|
||||
pub handle: ClientHandle,
|
||||
pub hostname: Option<String>,
|
||||
pub port: u32,
|
||||
pub active: bool,
|
||||
pub position: String,
|
||||
|
||||
@@ -5,11 +5,14 @@ use gtk::glib;
|
||||
use gtk::prelude::*;
|
||||
use gtk::subclass::prelude::*;
|
||||
|
||||
use crate::client::ClientHandle;
|
||||
|
||||
use super::ClientData;
|
||||
|
||||
#[derive(Properties, Default)]
|
||||
#[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 = "port", get, set, type = u32, member = port, maximum = u16::MAX as u32)]
|
||||
#[property(name = "active", get, set, type = bool, member = active)]
|
||||
|
||||
@@ -31,8 +31,19 @@ impl ClientRow {
|
||||
|
||||
let hostname_binding = client_object
|
||||
.bind_property("hostname", &self.imp().hostname.get(), "text")
|
||||
.transform_to(|_, v: Option<String>| {
|
||||
if let Some(hostname) = v {
|
||||
Some(hostname)
|
||||
} else {
|
||||
Some("".to_string())
|
||||
}
|
||||
})
|
||||
.transform_from(|_, v: String| {
|
||||
if v == "" { Some("hostname".into()) } else { Some(v) }
|
||||
if v.as_str().trim() == "" {
|
||||
Some(None)
|
||||
} else {
|
||||
Some(Some(v))
|
||||
}
|
||||
})
|
||||
.bidirectional()
|
||||
.sync_create()
|
||||
@@ -40,18 +51,34 @@ impl ClientRow {
|
||||
|
||||
let title_binding = client_object
|
||||
.bind_property("hostname", self, "title")
|
||||
.transform_to(|_, v: Option<String>| {
|
||||
if let Some(hostname) = v {
|
||||
Some(hostname)
|
||||
} else {
|
||||
Some("<span font_style=\"italic\" font_weight=\"light\" foreground=\"darkgrey\">no hostname!</span>".to_string())
|
||||
}
|
||||
})
|
||||
.sync_create()
|
||||
.build();
|
||||
|
||||
let port_binding = client_object
|
||||
.bind_property("port", &self.imp().port.get(), "text")
|
||||
.transform_from(|_, v: String| {
|
||||
if v == "" {
|
||||
Some(4242)
|
||||
Some(DEFAULT_PORT as u32)
|
||||
} else {
|
||||
Some(v.parse::<u16>().unwrap_or(DEFAULT_PORT) as u32)
|
||||
}
|
||||
})
|
||||
.transform_to(|_, v: u32| {
|
||||
if v == 4242 {
|
||||
Some("".to_string())
|
||||
} else {
|
||||
Some(v.to_string())
|
||||
}
|
||||
})
|
||||
.bidirectional()
|
||||
.sync_create()
|
||||
.build();
|
||||
|
||||
let subtitle_binding = client_object
|
||||
|
||||
@@ -54,8 +54,8 @@ impl ObjectImpl for ClientRow {
|
||||
impl ClientRow {
|
||||
#[template_callback]
|
||||
fn handle_client_set_state(&self, state: bool, switch: &Switch) -> bool {
|
||||
let idx = self.obj().index();
|
||||
switch.activate_action("win.activate-client", Some(&idx.to_variant())).unwrap();
|
||||
let idx = self.obj().index() as u32;
|
||||
switch.activate_action("win.request-client-update", Some(&idx.to_variant())).unwrap();
|
||||
switch.set_state(state);
|
||||
|
||||
true // dont run default handler
|
||||
@@ -64,8 +64,10 @@ impl ClientRow {
|
||||
#[template_callback]
|
||||
fn handle_client_delete(&self, button: &Button) {
|
||||
log::debug!("delete button pressed");
|
||||
let idx = self.obj().index();
|
||||
button.activate_action("win.delete-client", Some(&idx.to_variant())).unwrap();
|
||||
let idx = self.obj().index() as u32;
|
||||
button
|
||||
.activate_action("win.request-client-delete", Some(&idx.to_variant()))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
mod imp;
|
||||
|
||||
use std::{path::{Path, PathBuf}, env, process, os::unix::net::UnixStream, io::Write};
|
||||
use std::io::Write;
|
||||
|
||||
use adw::prelude::*;
|
||||
use adw::subclass::prelude::*;
|
||||
use gtk::{glib, gio, NoSelection};
|
||||
use glib::{clone, Object};
|
||||
|
||||
use crate::{frontend::{gtk::client_object::ClientObject, FrontendEvent}, config::DEFAULT_PORT, client::Position};
|
||||
use crate::{frontend::{gtk::client_object::ClientObject, FrontendEvent}, client::{Position, ClientHandle}, config::DEFAULT_PORT};
|
||||
|
||||
use super::client_row::ClientRow;
|
||||
|
||||
@@ -67,16 +67,44 @@ impl Window {
|
||||
row
|
||||
}
|
||||
|
||||
fn new_client(&self) {
|
||||
let client = ClientObject::new(String::from(""), DEFAULT_PORT as u32, false, "left".into());
|
||||
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);
|
||||
self.clients().append(&client);
|
||||
self.set_placeholder_visible(false);
|
||||
}
|
||||
|
||||
pub fn update_client(&self, client: &ClientObject) {
|
||||
pub fn client_idx(&self, handle: ClientHandle) -> Option<usize> {
|
||||
self.clients()
|
||||
.iter::<ClientObject>()
|
||||
.position(|c| {
|
||||
if let Ok(c) = c {
|
||||
c.handle() == handle
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.map(|p| p as usize)
|
||||
}
|
||||
|
||||
pub fn delete_client(&self, handle: ClientHandle) {
|
||||
let Some(idx) = self.client_idx(handle) else {
|
||||
log::warn!("could not find client with handle {handle}");
|
||||
return;
|
||||
};
|
||||
|
||||
self.clients().remove(idx as u32);
|
||||
if self.clients().n_items() == 0 {
|
||||
self.set_placeholder_visible(true);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request_client_create(&self) {
|
||||
let event = FrontendEvent::AddClient(None, DEFAULT_PORT, Position::default());
|
||||
self.request(event);
|
||||
}
|
||||
|
||||
pub fn request_client_update(&self, client: &ClientObject) {
|
||||
let data = client.get_data();
|
||||
let socket_path = self.imp().socket_path.borrow();
|
||||
let socket_path = socket_path.as_ref().unwrap().as_path();
|
||||
let host_name = data.hostname;
|
||||
let position = match data.position.as_str() {
|
||||
"left" => Position::Left,
|
||||
"right" => Position::Right,
|
||||
@@ -87,18 +115,37 @@ impl Window {
|
||||
return
|
||||
}
|
||||
};
|
||||
let port = data.port;
|
||||
let event = if client.active() {
|
||||
FrontendEvent::DelClient(host_name, port as u16)
|
||||
} else {
|
||||
FrontendEvent::AddClient(host_name, port as u16, position)
|
||||
};
|
||||
let hostname = data.hostname;
|
||||
let port = data.port as u16;
|
||||
let event = FrontendEvent::UpdateClient(client.handle(), hostname, port, position);
|
||||
self.request(event);
|
||||
|
||||
let event = FrontendEvent::ActivateClient(client.handle(), !client.active());
|
||||
self.request(event);
|
||||
}
|
||||
|
||||
pub fn request_client_delete(&self, idx: u32) {
|
||||
if let Some(obj) = self.clients().item(idx) {
|
||||
let client_object: &ClientObject = obj
|
||||
.downcast_ref()
|
||||
.expect("Expected object of type `ClientObject`.");
|
||||
let handle = client_object.handle();
|
||||
let event = FrontendEvent::DelClient(handle);
|
||||
self.request(event);
|
||||
}
|
||||
}
|
||||
|
||||
fn request(&self, event: FrontendEvent) {
|
||||
let json = serde_json::to_string(&event).unwrap();
|
||||
let Ok(mut stream) = UnixStream::connect(socket_path) else {
|
||||
log::error!("Could not connect to lan-mouse-socket @ {socket_path:?}");
|
||||
return;
|
||||
log::debug!("requesting {json}");
|
||||
let mut stream = self.imp().stream.borrow_mut();
|
||||
let stream = stream.as_mut().unwrap();
|
||||
let bytes = json.as_bytes();
|
||||
let len = bytes.len().to_ne_bytes();
|
||||
if let Err(e) = stream.write(&len) {
|
||||
log::error!("error sending message: {e}");
|
||||
};
|
||||
if let Err(e) = stream.write(json.as_bytes()) {
|
||||
if let Err(e) = stream.write(bytes) {
|
||||
log::error!("error sending message: {e}");
|
||||
};
|
||||
}
|
||||
@@ -107,21 +154,7 @@ impl Window {
|
||||
self.imp()
|
||||
.add_client_button
|
||||
.connect_clicked(clone!(@weak self as window => move |_| {
|
||||
window.new_client();
|
||||
window.set_placeholder_visible(false);
|
||||
window.request_client_create();
|
||||
}));
|
||||
}
|
||||
|
||||
fn connect_stream(&self) {
|
||||
let xdg_runtime_dir = match env::var("XDG_RUNTIME_DIR") {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
log::error!("{e}");
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
let socket_path = Path::new(xdg_runtime_dir.as_str())
|
||||
.join("lan-mouse-socket.sock");
|
||||
self.imp().socket_path.borrow_mut().replace(PathBuf::from(socket_path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{cell::{Cell, RefCell}, path::PathBuf};
|
||||
use std::{cell::{Cell, RefCell}, os::unix::net::UnixStream};
|
||||
|
||||
use glib::subclass::InitializingObject;
|
||||
use adw::{prelude::*, ActionRow};
|
||||
@@ -16,7 +16,7 @@ pub struct Window {
|
||||
#[template_child]
|
||||
pub client_placeholder: TemplateChild<ActionRow>,
|
||||
pub clients: RefCell<Option<gio::ListStore>>,
|
||||
pub socket_path: RefCell<Option<PathBuf>>,
|
||||
pub stream: RefCell<Option<UnixStream>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
@@ -54,7 +54,6 @@ impl ObjectImpl for Window {
|
||||
obj.setup_icon();
|
||||
obj.setup_clients();
|
||||
obj.setup_callbacks();
|
||||
obj.connect_stream();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user