Compare commits

...

15 Commits

Author SHA1 Message Date
Ferdinand Schober
e6d4585bb2 chore: Release lan-mouse version 0.8.0 2024-05-17 18:06:11 +02:00
Ferdinand Schober
1c082d5c0c fix win -> lin scancode translations for intl keys 2024-05-17 17:39:52 +02:00
Ferdinand Schober
cd98acbd08 fix right shift not working 2024-05-16 22:36:51 +02:00
Ferdinand Schober
11e1919588 fix most international keybindings 2024-05-16 22:29:41 +02:00
Ferdinand Schober
152bceaa86 fix dns resolving 2024-05-13 08:12:55 +02:00
Ferdinand Schober
60180d841c fix formatting 2024-05-12 15:50:03 +02:00
Ferdinand Schober
5802a0be0b fix discrete scrolling on wlroots 2024-05-12 15:49:39 +02:00
Ferdinand Schober
1737727d61 fix cast 2024-05-12 15:07:29 +02:00
Ferdinand Schober
ba46037a1f fix scrolling in windows 2024-05-12 14:55:56 +02:00
Ferdinand Schober
da768b6fb8 ignore Axis events corresponding to v120 events 2024-05-12 13:32:14 +02:00
Ferdinand Schober
799b45104a enter hook command (#130)
new configuration option `enter_hook` can now be used to spawn a command when a client is entered
2024-05-12 13:01:07 +02:00
Ferdinand Schober
e9738fc024 explicit state synchronisation (#129)
prevents unnecessary state updates and makes synchronization less error prone
2024-05-12 00:49:53 +02:00
Ferdinand Schober
9969f997d3 macos-latest on longer runs on intel macs 2024-05-07 11:46:01 +02:00
Ferdinand Schober
b8cc9e2197 hotfix: race condition when activating clients 2024-05-07 11:18:15 +02:00
Ferdinand Schober
ba6abafe75 windows: fix resolution with scaling enabled (#124) 2024-05-07 00:07:30 +02:00
20 changed files with 265 additions and 243 deletions

View File

@@ -7,7 +7,7 @@ jobs:
matrix: matrix:
os: os:
- ubuntu-latest - ubuntu-latest
- macos-latest - macos-13
- macos-14 - macos-14
name: "Build" name: "Build"
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@@ -31,7 +31,7 @@ jobs:
run: nix build --print-build-logs --show-trace .#packages.x86_64-linux.lan-mouse run: nix build --print-build-logs --show-trace .#packages.x86_64-linux.lan-mouse
- name: Build lan-mouse (x86_64-darwin) - name: Build lan-mouse (x86_64-darwin)
if: matrix.os == 'macos-latest' if: matrix.os == 'macos-13'
run: nix build --print-build-logs --show-trace .#packages.x86_64-darwin.lan-mouse run: nix build --print-build-logs --show-trace .#packages.x86_64-darwin.lan-mouse
- name: Build lan-mouse (aarch64-darwin) - name: Build lan-mouse (aarch64-darwin)

View File

@@ -78,7 +78,7 @@ jobs:
path: lan-mouse-windows.zip path: lan-mouse-windows.zip
macos-release-build: macos-release-build:
runs-on: macos-latest runs-on: macos-13
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: install dependencies - name: install dependencies

View File

@@ -92,7 +92,7 @@ jobs:
target/debug/*.dll target/debug/*.dll
build-macos: build-macos:
runs-on: macos-latest runs-on: macos-13
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: install dependencies - name: install dependencies

View File

@@ -74,7 +74,7 @@ jobs:
path: lan-mouse-windows.zip path: lan-mouse-windows.zip
macos-release-build: macos-release-build:
runs-on: macos-latest runs-on: macos-13
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: install dependencies - name: install dependencies

2
Cargo.lock generated
View File

@@ -1284,7 +1284,7 @@ dependencies = [
[[package]] [[package]]
name = "lan-mouse" name = "lan-mouse"
version = "0.7.3" version = "0.8.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"ashpd", "ashpd",

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "lan-mouse" name = "lan-mouse"
description = "Software KVM Switch / mouse & keyboard sharing software for Local Area Networks" description = "Software KVM Switch / mouse & keyboard sharing software for Local Area Networks"
version = "0.7.3" version = "0.8.0"
edition = "2021" edition = "2021"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
repository = "https://github.com/ferdinandschober/lan-mouse" repository = "https://github.com/ferdinandschober/lan-mouse"
@@ -22,7 +22,7 @@ anyhow = "1.0.71"
log = "0.4.20" log = "0.4.20"
env_logger = "0.11.3" env_logger = "0.11.3"
serde_json = "1.0.107" serde_json = "1.0.107"
tokio = {version = "1.32.0", features = ["io-util", "io-std", "macros", "net", "rt", "sync", "signal"] } tokio = {version = "1.32.0", features = ["io-util", "io-std", "macros", "net", "process", "rt", "sync", "signal"] }
async-trait = "0.1.73" async-trait = "0.1.73"
futures-core = "0.3.28" futures-core = "0.3.28"
futures = "0.3.28" futures = "0.3.28"

View File

@@ -111,6 +111,7 @@ struct State {
qh: QueueHandle<Self>, qh: QueueHandle<Self>,
pending_events: VecDeque<(ClientHandle, Event)>, pending_events: VecDeque<(ClientHandle, Event)>,
output_info: Vec<(WlOutput, OutputInfo)>, output_info: Vec<(WlOutput, OutputInfo)>,
scroll_discrete_pending: bool,
} }
struct Inner { struct Inner {
@@ -351,6 +352,7 @@ impl WaylandInputCapture {
read_guard: None, read_guard: None,
pending_events: VecDeque::new(), pending_events: VecDeque::new(),
output_info: vec![], output_info: vec![],
scroll_discrete_pending: false,
}; };
// dispatch registry to () again, in order to read all wl_outputs // dispatch registry to () again, in order to read all wl_outputs
@@ -752,17 +754,25 @@ impl Dispatch<WlPointer, ()> for State {
} }
wl_pointer::Event::Axis { time, axis, value } => { wl_pointer::Event::Axis { time, axis, value } => {
let (_, client) = app.focused.as_ref().unwrap(); let (_, client) = app.focused.as_ref().unwrap();
app.pending_events.push_back(( if app.scroll_discrete_pending {
*client, // each axisvalue120 event is coupled with
Event::Pointer(PointerEvent::Axis { // a corresponding axis event, which needs to
time, // be ignored to not duplicate the scrolling
axis: u32::from(axis) as u8, app.scroll_discrete_pending = false;
value, } else {
}), app.pending_events.push_back((
)); *client,
Event::Pointer(PointerEvent::Axis {
time,
axis: u32::from(axis) as u8,
value,
}),
));
}
} }
wl_pointer::Event::AxisValue120 { axis, value120 } => { wl_pointer::Event::AxisValue120 { axis, value120 } => {
let (_, client) = app.focused.as_ref().unwrap(); let (_, client) = app.focused.as_ref().unwrap();
app.scroll_discrete_pending = true;
app.pending_events.push_back(( app.pending_events.push_back((
*client, *client,
Event::Pointer(PointerEvent::AxisDiscrete120 { Event::Pointer(PointerEvent::AxisDiscrete120 {

View File

@@ -13,23 +13,23 @@ use std::sync::mpsc;
use std::task::ready; use std::task::ready;
use std::{io, pin::Pin, thread}; use std::{io, pin::Pin, thread};
use tokio::sync::mpsc::{channel, Receiver, Sender}; use tokio::sync::mpsc::{channel, Receiver, Sender};
use windows::core::w; use windows::core::{w, PCWSTR};
use windows::Win32::Foundation::{ use windows::Win32::Foundation::{FALSE, HINSTANCE, HWND, LPARAM, LRESULT, RECT, WPARAM};
BOOL, FALSE, HINSTANCE, HWND, LPARAM, LRESULT, RECT, TRUE, WPARAM,
};
use windows::Win32::Graphics::Gdi::{ use windows::Win32::Graphics::Gdi::{
EnumDisplayMonitors, GetMonitorInfoW, HDC, HMONITOR, MONITORINFO, EnumDisplayDevicesW, EnumDisplaySettingsW, DEVMODEW, DISPLAY_DEVICEW,
DISPLAY_DEVICE_ATTACHED_TO_DESKTOP, ENUM_CURRENT_SETTINGS,
}; };
use windows::Win32::System::LibraryLoader::GetModuleHandleW; use windows::Win32::System::LibraryLoader::GetModuleHandleW;
use windows::Win32::System::Threading::GetCurrentThreadId; use windows::Win32::System::Threading::GetCurrentThreadId;
use windows::Win32::UI::WindowsAndMessaging::{ use windows::Win32::UI::WindowsAndMessaging::{
CallNextHookEx, CreateWindowExW, DispatchMessageW, GetMessageW, PostThreadMessageW, CallNextHookEx, CreateWindowExW, DispatchMessageW, GetMessageW, PostThreadMessageW,
RegisterClassW, SetWindowsHookExW, TranslateMessage, HHOOK, HMENU, HOOKPROC, KBDLLHOOKSTRUCT, RegisterClassW, SetWindowsHookExW, TranslateMessage, EDD_GET_DEVICE_INTERFACE_NAME, HHOOK,
LLKHF_EXTENDED, MSG, MSLLHOOKSTRUCT, WH_KEYBOARD_LL, WH_MOUSE_LL, WINDOW_STYLE, HMENU, HOOKPROC, KBDLLHOOKSTRUCT, LLKHF_EXTENDED, MSG, MSLLHOOKSTRUCT, WH_KEYBOARD_LL,
WM_DISPLAYCHANGE, WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WH_MOUSE_LL, WINDOW_STYLE, WM_DISPLAYCHANGE, WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN,
WM_MBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SYSKEYDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_RBUTTONDOWN,
WM_SYSKEYUP, WM_USER, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WNDPROC, WM_RBUTTONUP, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_USER, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW,
WNDPROC,
}; };
use crate::client::Position; use crate::client::Position;
@@ -141,7 +141,7 @@ fn to_mouse_event(wparam: WPARAM, lparam: LPARAM) -> Option<PointerEvent> {
}, },
WPARAM(p) if p == WM_MOUSEWHEEL as usize => Some(PointerEvent::AxisDiscrete120 { WPARAM(p) if p == WM_MOUSEWHEEL as usize => Some(PointerEvent::AxisDiscrete120 {
axis: 0, axis: 0,
value: -(mouse_low_level.mouseData as i32), value: -(mouse_low_level.mouseData as i32 >> 16),
}), }),
WPARAM(p) if p == WM_XBUTTONDOWN as usize || p == WM_XBUTTONUP as usize => { WPARAM(p) if p == WM_XBUTTONDOWN as usize || p == WM_XBUTTONUP as usize => {
let hb = mouse_low_level.mouseData >> 16; let hb = mouse_low_level.mouseData >> 16;
@@ -347,37 +347,44 @@ unsafe extern "system" fn window_proc(
LRESULT(1) LRESULT(1)
} }
unsafe extern "system" fn monitor_enum_proc(
hmon: HMONITOR,
_hdc: HDC,
_lprect: *mut RECT,
lparam: LPARAM,
) -> BOOL {
let monitors = lparam.0 as *mut Vec<HMONITOR>;
(*monitors).push(hmon);
TRUE // continue enumeration
}
fn enumerate_displays() -> Vec<RECT> { fn enumerate_displays() -> Vec<RECT> {
unsafe { unsafe {
let mut display_rects = vec![]; let mut display_rects = vec![];
let mut monitors: Vec<HMONITOR> = Vec::new(); let mut devices = vec![];
let ret = EnumDisplayMonitors( for i in 0.. {
HDC::default(), let mut device: DISPLAY_DEVICEW = std::mem::zeroed();
None, device.cb = std::mem::size_of::<DISPLAY_DEVICEW>() as u32;
Some(monitor_enum_proc), let ret = EnumDisplayDevicesW(None, i, &mut device, EDD_GET_DEVICE_INTERFACE_NAME);
LPARAM(&mut monitors as *mut Vec<HMONITOR> as isize), if ret == FALSE {
); break;
if ret != TRUE {
panic!("could not enumerate displays");
}
for monitor in monitors {
let mut monitor_info: MONITORINFO = std::mem::zeroed();
monitor_info.cbSize = std::mem::size_of::<MONITORINFO>() as u32;
if GetMonitorInfoW(monitor, &mut monitor_info) == FALSE {
panic!();
} }
display_rects.push(monitor_info.rcMonitor); if device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP != 0 {
log::info!("{:?}", device.DeviceName);
devices.push(device.DeviceName);
}
}
for device in devices {
let mut dev_mode: DEVMODEW = std::mem::zeroed();
dev_mode.dmSize = std::mem::size_of::<DEVMODEW>() as u16;
let ret = EnumDisplaySettingsW(
PCWSTR::from_raw(&device as *const _),
ENUM_CURRENT_SETTINGS,
&mut dev_mode,
);
if ret == FALSE {
log::warn!("no display mode");
}
let pos = dev_mode.Anonymous1.Anonymous2.dmPosition;
let (x, y) = (pos.x, pos.y);
let (width, height) = (dev_mode.dmPelsWidth, dev_mode.dmPelsHeight);
display_rects.push(RECT {
left: x,
right: x + width as i32,
top: y,
bottom: y + height as i32,
});
} }
display_rects display_rects
} }

View File

@@ -102,6 +102,8 @@ pub struct ClientConfig {
pub port: u16, pub port: u16,
/// position of a client on screen /// position of a client on screen
pub pos: Position, pub pos: Position,
/// enter hook
pub cmd: Option<String>,
} }
impl Default for ClientConfig { impl Default for ClientConfig {
@@ -111,6 +113,7 @@ impl Default for ClientConfig {
hostname: Default::default(), hostname: Default::default(),
fix_ips: Default::default(), fix_ips: Default::default(),
pos: Default::default(), pos: Default::default(),
cmd: None,
} }
} }
} }

View File

@@ -31,6 +31,7 @@ pub struct TomlClient {
pub ips: Option<Vec<IpAddr>>, pub ips: Option<Vec<IpAddr>>,
pub port: Option<u16>, pub port: Option<u16>,
pub activate_on_startup: Option<bool>, pub activate_on_startup: Option<bool>,
pub enter_hook: Option<String>,
} }
impl ConfigToml { impl ConfigToml {
@@ -92,6 +93,7 @@ pub struct ConfigClient {
pub port: u16, pub port: u16,
pub pos: Position, pub pos: Position,
pub active: bool, pub active: bool,
pub enter_hook: Option<String>,
} }
const DEFAULT_RELEASE_KEYS: [scancode::Linux; 4] = const DEFAULT_RELEASE_KEYS: [scancode::Linux; 4] =
@@ -208,12 +210,14 @@ impl Config {
None => c.host_name.clone(), None => c.host_name.clone(),
}; };
let active = c.activate_on_startup.unwrap_or(false); let active = c.activate_on_startup.unwrap_or(false);
let enter_hook = c.enter_hook.clone();
ConfigClient { ConfigClient {
ips, ips,
hostname, hostname,
port, port,
pos: *pos, pos: *pos,
active, active,
enter_hook,
} }
}) })
.collect() .collect()

View File

@@ -182,7 +182,8 @@ impl VirtualInput {
} }
PointerEvent::AxisDiscrete120 { axis, value } => { PointerEvent::AxisDiscrete120 { axis, value } => {
let axis: Axis = (axis as u32).try_into()?; let axis: Axis = (axis as u32).try_into()?;
self.pointer.axis(0, axis, (value / 15) as f64); self.pointer
.axis_discrete(0, axis, value as f64 / 6., value / 120);
self.pointer.frame(); self.pointer.frame();
} }
PointerEvent::Frame {} => self.pointer.frame(), PointerEvent::Frame {} => self.pointer.frame(),

View File

@@ -107,16 +107,18 @@ pub enum FrontendRequest {
UpdatePosition(ClientHandle, Position), UpdatePosition(ClientHandle, Position),
/// update fix-ips /// update fix-ips
UpdateFixIps(ClientHandle, Vec<IpAddr>), UpdateFixIps(ClientHandle, Vec<IpAddr>),
/// request the state of the given client
GetState(ClientHandle),
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FrontendEvent { pub enum FrontendEvent {
/// a client was created /// a client was created
Created(ClientHandle, ClientConfig, ClientState), Created(ClientHandle, ClientConfig, ClientState),
/// a client was updated /// no such client
Updated(ClientHandle, ClientConfig), NoSuchClient(ClientHandle),
/// state changed /// state changed
StateChange(ClientHandle, ClientState), State(ClientHandle, ClientConfig, ClientState),
/// the client was deleted /// the client was deleted
Deleted(ClientHandle), Deleted(ClientHandle),
/// new port, reason of failure (if failed) /// new port, reason of failure (if failed)
@@ -235,9 +237,7 @@ impl FrontendListener {
let json = serde_json::to_string(&notify).unwrap(); let json = serde_json::to_string(&notify).unwrap();
let payload = json.as_bytes(); let payload = json.as_bytes();
let len = payload.len().to_be_bytes(); let len = payload.len().to_be_bytes();
log::debug!("json: {json}, len: {}", payload.len()); log::debug!("broadcasting event to streams: {json}");
log::debug!("broadcasting event to streams: {:?}", self.tx_streams);
let mut keep = vec![]; let mut keep = vec![];
// TODO do simultaneously // TODO do simultaneously
for tx in self.tx_streams.iter_mut() { for tx in self.tx_streams.iter_mut() {

View File

@@ -100,6 +100,18 @@ impl<'a> Cli<'a> {
} }
} }
async fn update_client(&mut self, handle: ClientHandle) -> Result<()> {
self.send_request(FrontendRequest::GetState(handle)).await?;
loop {
let event = self.await_event().await?;
self.handle_event(event.clone());
if let FrontendEvent::State(_, _, _) | FrontendEvent::NoSuchClient(_) = event {
break;
}
}
Ok(())
}
async fn execute(&mut self, cmd: Command) -> Result<()> { async fn execute(&mut self, cmd: Command) -> Result<()> {
match cmd { match cmd {
Command::None => {} Command::None => {}
@@ -125,14 +137,8 @@ impl<'a> Cli<'a> {
FrontendRequest::UpdatePosition(handle, pos), FrontendRequest::UpdatePosition(handle, pos),
] { ] {
self.send_request(request).await?; self.send_request(request).await?;
loop {
let event = self.await_event().await?;
self.handle_event(event.clone());
if let FrontendEvent::Updated(_, _) = event {
break;
}
}
} }
self.update_client(handle).await?;
} }
Command::Disconnect(id) => { Command::Disconnect(id) => {
self.send_request(FrontendRequest::Delete(id)).await?; self.send_request(FrontendRequest::Delete(id)).await?;
@@ -148,26 +154,12 @@ impl<'a> Cli<'a> {
Command::Activate(id) => { Command::Activate(id) => {
self.send_request(FrontendRequest::Activate(id, true)) self.send_request(FrontendRequest::Activate(id, true))
.await?; .await?;
loop { self.update_client(id).await?;
let event = self.await_event().await?;
self.handle_event(event.clone());
if let FrontendEvent::StateChange(_, _) = event {
self.handle_event(event);
break;
}
}
} }
Command::Deactivate(id) => { Command::Deactivate(id) => {
self.send_request(FrontendRequest::Activate(id, false)) self.send_request(FrontendRequest::Activate(id, false))
.await?; .await?;
loop { self.update_client(id).await?;
let event = self.await_event().await?;
self.handle_event(event.clone());
if let FrontendEvent::StateChange(_, _) = event {
self.handle_event(event);
break;
}
}
} }
Command::List => { Command::List => {
self.send_request(FrontendRequest::Enumerate()).await?; self.send_request(FrontendRequest::Enumerate()).await?;
@@ -182,25 +174,12 @@ impl<'a> Cli<'a> {
Command::SetHost(handle, host) => { Command::SetHost(handle, host) => {
let request = FrontendRequest::UpdateHostname(handle, Some(host.clone())); let request = FrontendRequest::UpdateHostname(handle, Some(host.clone()));
self.send_request(request).await?; self.send_request(request).await?;
loop { self.update_client(handle).await?;
let event = self.await_event().await?;
self.handle_event(event.clone());
if let FrontendEvent::Updated(_, _) = event {
self.handle_event(event);
break;
}
}
} }
Command::SetPort(handle, port) => { Command::SetPort(handle, port) => {
let request = FrontendRequest::UpdatePort(handle, port.unwrap_or(DEFAULT_PORT)); let request = FrontendRequest::UpdatePort(handle, port.unwrap_or(DEFAULT_PORT));
self.send_request(request).await?; self.send_request(request).await?;
loop { self.update_client(handle).await?;
let event = self.await_event().await?;
self.handle_event(event.clone());
if let FrontendEvent::Updated(_, _) = event {
break;
}
}
} }
Command::Help => { Command::Help => {
for cmd_type in [ for cmd_type in [
@@ -244,8 +223,11 @@ impl<'a> Cli<'a> {
eprintln!(); eprintln!();
self.clients.push((h, c, s)); self.clients.push((h, c, s));
} }
FrontendEvent::Updated(h, c) => { FrontendEvent::NoSuchClient(h) => {
if let Some((_, config, _)) = self.find_mut(h) { eprintln!("no such client: {h}");
}
FrontendEvent::State(h, c, s) => {
if let Some((_, config, state)) = self.find_mut(h) {
let old_host = config.hostname.clone().unwrap_or("\"\"".into()); let old_host = config.hostname.clone().unwrap_or("\"\"".into());
let new_host = c.hostname.clone().unwrap_or("\"\"".into()); let new_host = c.hostname.clone().unwrap_or("\"\"".into());
if old_host != new_host { if old_host != new_host {
@@ -261,10 +243,6 @@ impl<'a> Cli<'a> {
eprintln!("client {h} ips updated: {:?}", c.fix_ips) eprintln!("client {h} ips updated: {:?}", c.fix_ips)
} }
*config = c; *config = c;
}
}
FrontendEvent::StateChange(h, s) => {
if let Some((_, _, state)) = self.find_mut(h) {
if state.active ^ s.active { if state.active ^ s.active {
eprintln!( eprintln!(
"client {h} {}", "client {h} {}",

View File

@@ -126,12 +126,11 @@ fn build_ui(app: &Application) {
FrontendEvent::Deleted(client) => { FrontendEvent::Deleted(client) => {
window.delete_client(client); window.delete_client(client);
} }
FrontendEvent::Updated(handle, client) => { FrontendEvent::State(handle, config, state) => {
window.update_client_config(handle, client); window.update_client_config(handle, config);
}
FrontendEvent::StateChange(handle, state) => {
window.update_client_state(handle, state); window.update_client_state(handle, state);
} }
FrontendEvent::NoSuchClient(_) => { }
FrontendEvent::Error(e) => { FrontendEvent::Error(e) => {
window.show_toast(e.as_str()); window.show_toast(e.as_str());
}, },

View File

@@ -51,6 +51,10 @@ impl Window {
.expect("Could not get clients") .expect("Could not get clients")
} }
fn client_by_idx(&self, idx: u32) -> Option<ClientObject> {
self.clients().item(idx).map(|o| o.downcast().unwrap())
}
fn setup_clients(&self) { fn setup_clients(&self) {
let model = gio::ListStore::new::<ClientObject>(); let model = gio::ListStore::new::<ClientObject>();
self.imp().clients.replace(Some(model)); self.imp().clients.replace(Some(model));
@@ -62,27 +66,24 @@ impl Window {
let client_object = obj.downcast_ref().expect("Expected object of type `ClientObject`."); let client_object = obj.downcast_ref().expect("Expected object of type `ClientObject`.");
let row = window.create_client_row(client_object); let row = window.create_client_row(client_object);
row.connect_closure("request-update", false, closure_local!(@strong window => move |row: ClientRow, active: bool| { row.connect_closure("request-update", false, closure_local!(@strong window => move |row: ClientRow, active: bool| {
let index = row.index() as u32; if let Some(client) = window.client_by_idx(row.index() as u32) {
let Some(client) = window.clients().item(index) else { window.request_client_activate(&client, active);
return; window.request_client_update(&client);
}; window.request_client_state(&client);
let client = client.downcast_ref::<ClientObject>().unwrap(); }
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; if let Some(client) = window.client_by_idx(row.index() as u32) {
window.request_client_delete(index); window.request_client_delete(&client);
}
})); }));
row.connect_closure("request-dns", false, closure_local!(@strong window => move row.connect_closure("request-dns", false, closure_local!(@strong window => move
|row: ClientRow| { |row: ClientRow| {
let index = row.index() as u32; if let Some(client) = window.client_by_idx(row.index() as u32) {
let Some(client) = window.clients().item(index) else { window.request_client_update(&client);
return; window.request_dns(&client);
}; window.request_client_state(&client);
let client = client.downcast_ref::<ClientObject>().unwrap(); }
window.request_client_update(client);
window.request_dns(index);
})); }));
row.upcast() row.upcast()
}) })
@@ -177,7 +178,7 @@ impl Window {
if state.resolving != data.resolving { if state.resolving != data.resolving {
client_object.set_resolving(state.resolving); client_object.set_resolving(state.resolving);
log::debug!("resolving {}: {}", data.handle, state.active); log::debug!("resolving {}: {}", data.handle, state.resolving);
} }
self.update_dns_state(handle, !state.ips.is_empty()); self.update_dns_state(handle, !state.ips.is_empty());
@@ -204,20 +205,6 @@ impl Window {
} }
} }
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) {
let event = FrontendRequest::Create;
self.imp().set_port(DEFAULT_PORT);
self.request(event);
}
pub fn request_port_change(&self) { pub fn request_port_change(&self) {
let port = self.imp().port_entry.get().text().to_string(); let port = self.imp().port_entry.get().text().to_string();
if let Ok(port) = port.as_str().parse::<u16>() { if let Ok(port) = port.as_str().parse::<u16>() {
@@ -227,6 +214,23 @@ impl Window {
} }
} }
pub fn request_client_state(&self, client: &ClientObject) {
let handle = client.handle();
let event = FrontendRequest::GetState(handle);
self.request(event);
}
pub fn request_client_create(&self) {
let event = FrontendRequest::Create;
self.request(event);
}
pub fn request_dns(&self, client: &ClientObject) {
let data = client.get_data();
let event = FrontendRequest::ResolveDns(data.handle);
self.request(event);
}
pub fn request_client_update(&self, client: &ClientObject) { 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();
@@ -239,33 +243,25 @@ impl Window {
FrontendRequest::UpdatePosition(handle, position), FrontendRequest::UpdatePosition(handle, position),
FrontendRequest::UpdatePort(handle, port), FrontendRequest::UpdatePort(handle, port),
] { ] {
log::debug!("requesting: {event:?}");
self.request(event); self.request(event);
} }
} }
pub fn request_client_activate(&self, client: &ClientObject, active: bool) { pub fn request_client_activate(&self, client: &ClientObject, active: bool) {
let handle = client.handle(); let handle = client.handle();
let event = FrontendRequest::Activate(handle, active); let event = FrontendRequest::Activate(handle, active);
log::debug!("requesting: {event:?}");
self.request(event); self.request(event);
} }
pub fn request_client_delete(&self, idx: u32) { pub fn request_client_delete(&self, client: &ClientObject) {
if let Some(obj) = self.clients().item(idx) { let handle = client.handle();
let client_object: &ClientObject = obj let event = FrontendRequest::Delete(handle);
.downcast_ref() self.request(event);
.expect("Expected object of type `ClientObject`.");
let handle = client_object.handle();
let event = FrontendRequest::Delete(handle);
self.request(event);
}
} }
pub fn request(&self, event: FrontendRequest) { pub fn request(&self, event: FrontendRequest) {
let json = serde_json::to_string(&event).unwrap(); let json = serde_json::to_string(&event).unwrap();
log::debug!("requesting {json}"); log::debug!("requesting: {json}");
let mut stream = self.imp().stream.borrow_mut(); let mut stream = self.imp().stream.borrow_mut();
let stream = stream.as_mut().unwrap(); let stream = stream.as_mut().unwrap();
let bytes = json.as_bytes(); let bytes = json.as_bytes();

View File

@@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize};
/* /*
* https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input * https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input
* https://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf
* https://kbd-project.org/docs/scancodes/scancodes-1.html
*/ */
#[repr(u32)] #[repr(u32)]
#[derive(Debug, Clone, Copy, TryFromPrimitive)] #[derive(Debug, Clone, Copy, TryFromPrimitive)]
@@ -120,15 +122,15 @@ pub enum Windows {
KeyF21 = 0x006C, KeyF21 = 0x006C,
KeyF22 = 0x006D, KeyF22 = 0x006D,
KeyF23 = 0x006E, KeyF23 = 0x006E,
KeyF24 = 0x0076, KeyF24 = 0x0076, // KeyLANG5
KeypadComma = 0x007E, KeypadComma = 0x007E,
KeyInternational1 = 0x0073, KeyInternational1 = 0x0073,
KeyInternational2 = 0x0070, KeyInternational2 = 0x0070,
KeyInternational3 = 0x007D, KeyInternational3 = 0x007D, // typo in doc -> its Int'l 3 not Int'l 2
#[allow(dead_code)] #[allow(dead_code)]
KeyInternational4 = 0x0079, // FIXME unused KeyInternational4 = 0x0079,
#[allow(dead_code)] #[allow(dead_code)]
KeyInternational5 = 0x007B, // FIXME unused KeyInternational5 = 0x007B,
// KeyInternational6 = 0x005C, // KeyInternational6 = 0x005C,
KeyLANG1 = 0x0072, KeyLANG1 = 0x0072,
KeyLANG2 = 0x0071, KeyLANG2 = 0x0071,
@@ -141,6 +143,7 @@ pub enum Windows {
KeyLeftGUI = 0xE05B, KeyLeftGUI = 0xE05B,
KeyRightCtrl = 0xE01D, KeyRightCtrl = 0xE01D,
KeyRightShift = 0x0036, KeyRightShift = 0x0036,
KeyFakeRightShift = 0xE036,
KeyRightAlt = 0xE038, KeyRightAlt = 0xE038,
KeyRightGUI = 0xE05C, KeyRightGUI = 0xE05C,
KeyScanNextTrack = 0xE019, KeyScanNextTrack = 0xE019,
@@ -293,7 +296,7 @@ pub enum Linux {
KeyPause = 119, KeyPause = 119,
KeyScale = 120, /* AL Compiz Scale (Expose) */ KeyScale = 120, /* AL Compiz Scale (Expose) */
KeyKpcomma = 121, KeyKpcomma = 121,
KeyHangeul = 122, KeyHanguel = 122,
// KEY_HANGUEL = KeyHangeul, // KEY_HANGUEL = KeyHangeul,
KeyHanja = 123, KeyHanja = 123,
KeyYen = 124, KeyYen = 124,
@@ -518,16 +521,16 @@ impl TryFrom<Linux> for Windows {
Linux::KeyKp3 => Ok(Self::Keypad3PageDn), Linux::KeyKp3 => Ok(Self::Keypad3PageDn),
Linux::KeyKp0 => Ok(Self::Keypad0Insert), Linux::KeyKp0 => Ok(Self::Keypad0Insert),
Linux::KeyKpDot => Ok(Self::KeypadDot), Linux::KeyKpDot => Ok(Self::KeypadDot),
Linux::KeyZenkakuhankaku => Ok(Self::KeyLANG1), // TODO unsure Linux::KeyZenkakuhankaku => Ok(Self::KeyF24), // KeyLANG5
Linux::Key102nd => Ok(Self::KeyNonUSSlashBar), // TODO unsure Linux::Key102nd => Ok(Self::KeyNonUSSlashBar), // TODO unsure
Linux::KeyF11 => Ok(Self::KeyF11), Linux::KeyF11 => Ok(Self::KeyF11),
Linux::KeyF12 => Ok(Self::KeyF12), Linux::KeyF12 => Ok(Self::KeyF12),
Linux::KeyRo => Ok(Self::ErrorRollOver), // TODO unsure Linux::KeyRo => Ok(Self::KeyInternational1),
Linux::KeyKatakana => Ok(Self::KeyLANG1), // TODO unsure Linux::KeyKatakana => Ok(Self::KeyLANG3),
Linux::KeyHiragana => Ok(Self::KeyLANG2), // TODO unsure Linux::KeyHiragana => Ok(Self::KeyLANG4),
Linux::KeyHenkan => Ok(Self::KeyLANG3), // TODO unsure Linux::KeyHenkan => Ok(Self::KeyInternational4),
Linux::KeyKatakanahiragana => Ok(Self::KeyLANG4), // TODO unsure Linux::KeyKatakanahiragana => Ok(Self::KeyInternational2),
Linux::KeyMuhenkan => Ok(Self::KeyLANG4), // TODO unsure Linux::KeyMuhenkan => Ok(Self::KeyInternational5),
Linux::KeyKpJpComma => Ok(Self::KeypadComma), Linux::KeyKpJpComma => Ok(Self::KeypadComma),
Linux::KeyKpEnter => Ok(Self::KeypadEnter), Linux::KeyKpEnter => Ok(Self::KeypadEnter),
Linux::KeyRightCtrl => Ok(Self::KeyRightCtrl), Linux::KeyRightCtrl => Ok(Self::KeyRightCtrl),
@@ -555,9 +558,9 @@ impl TryFrom<Linux> for Windows {
Linux::KeyPause => Ok(Self::KeyPause), Linux::KeyPause => Ok(Self::KeyPause),
Linux::KeyScale => Err(()), // TODO Linux::KeyScale => Err(()), // TODO
Linux::KeyKpcomma => Ok(Self::KeypadComma), Linux::KeyKpcomma => Ok(Self::KeypadComma),
Linux::KeyHangeul => Ok(Self::KeyInternational1), // TODO unsure Linux::KeyHanguel => Ok(Self::KeyLANG1), // FIXME should be 00F2?
Linux::KeyHanja => Ok(Self::KeyInternational2), // TODO unsure Linux::KeyHanja => Ok(Self::KeyLANG2), // FIXME should be 00F1?
Linux::KeyYen => Ok(Self::KeyInternational3), // TODO unsure Linux::KeyYen => Ok(Self::KeyInternational3),
Linux::KeyLeftMeta => Ok(Self::KeyLeftGUI), Linux::KeyLeftMeta => Ok(Self::KeyLeftGUI),
Linux::KeyRightmeta => Ok(Self::KeyRightGUI), Linux::KeyRightmeta => Ok(Self::KeyRightGUI),
Linux::KeyCompose => Ok(Self::KeyApplication), Linux::KeyCompose => Ok(Self::KeyApplication),
@@ -807,21 +810,22 @@ impl TryFrom<Windows> for Linux {
Windows::KeyF23 => Ok(Self::KeyF23), Windows::KeyF23 => Ok(Self::KeyF23),
Windows::KeyF24 => Ok(Self::KeyF24), Windows::KeyF24 => Ok(Self::KeyF24),
Windows::KeypadComma => Ok(Self::KeyKpcomma), Windows::KeypadComma => Ok(Self::KeyKpcomma),
Windows::KeyInternational1 => Ok(Self::KeyHangeul), Windows::KeyInternational1 => Ok(Self::KeyRo),
Windows::KeyInternational2 => Ok(Self::KeyHanja), Windows::KeyInternational2 => Ok(Self::KeyKatakanahiragana),
Windows::KeyInternational3 => Ok(Self::KeyYen), Windows::KeyInternational3 => Ok(Self::KeyYen),
Windows::KeyInternational4 => Err(()), Windows::KeyInternational4 => Ok(Self::KeyHenkan),
Windows::KeyInternational5 => Err(()), Windows::KeyInternational5 => Ok(Self::KeyMuhenkan),
Windows::KeyLANG1 => Ok(Self::KeyKatakana), Windows::KeyLANG1 => Ok(Self::KeyHanguel),
Windows::KeyLANG2 => Ok(Self::KeyHiragana), Windows::KeyLANG2 => Ok(Self::KeyHanja),
Windows::KeyLANG3 => Ok(Self::KeyHenkan), Windows::KeyLANG3 => Ok(Self::KeyKatakana),
Windows::KeyLANG4 => Ok(Self::KeyKatakanahiragana), Windows::KeyLANG4 => Ok(Self::KeyHiragana),
Windows::KeyLeftCtrl => Ok(Self::KeyLeftCtrl), Windows::KeyLeftCtrl => Ok(Self::KeyLeftCtrl),
Windows::KeyLeftShift => Ok(Self::KeyLeftShift), Windows::KeyLeftShift => Ok(Self::KeyLeftShift),
Windows::KeyLeftAlt => Ok(Self::KeyLeftAlt), Windows::KeyLeftAlt => Ok(Self::KeyLeftAlt),
Windows::KeyLeftGUI => Ok(Self::KeyLeftMeta), Windows::KeyLeftGUI => Ok(Self::KeyLeftMeta),
Windows::KeyRightCtrl => Ok(Self::KeyRightCtrl), Windows::KeyRightCtrl => Ok(Self::KeyRightCtrl),
Windows::KeyRightShift => Ok(Self::KeyRightShift), Windows::KeyRightShift => Ok(Self::KeyRightShift),
Windows::KeyFakeRightShift => Ok(Self::KeyRightShift),
Windows::KeyRightAlt => Ok(Self::KeyRightalt), Windows::KeyRightAlt => Ok(Self::KeyRightalt),
Windows::KeyRightGUI => Ok(Self::KeyRightmeta), Windows::KeyRightGUI => Ok(Self::KeyRightmeta),
Windows::KeyScanNextTrack => Ok(Self::KeyNextsong), Windows::KeyScanNextTrack => Ok(Self::KeyNextsong),

View File

@@ -55,6 +55,7 @@ impl Server {
fix_ips: config_client.ips.into_iter().collect(), fix_ips: config_client.ips.into_iter().collect(),
port: config_client.port, port: config_client.port,
pos: config_client.pos, pos: config_client.pos,
cmd: config_client.enter_hook,
}; };
let state = ClientState { let state = ClientState {
active: config_client.active, active: config_client.active,

View File

@@ -2,7 +2,7 @@ use anyhow::{anyhow, Result};
use futures::StreamExt; use futures::StreamExt;
use std::{collections::HashSet, net::SocketAddr}; use std::{collections::HashSet, net::SocketAddr};
use tokio::{sync::mpsc::Sender, task::JoinHandle}; use tokio::{process::Command, sync::mpsc::Sender, task::JoinHandle};
use crate::{ use crate::{
capture::{self, InputCapture}, capture::{self, InputCapture},
@@ -140,6 +140,9 @@ async fn handle_capture_event(
if start_timer { if start_timer {
let _ = timer_tx.try_send(()); let _ = timer_tx.try_send(());
} }
if enter {
spawn_hook_command(server, handle);
}
if let Some(addr) = addr { if let Some(addr) = addr {
if enter { if enter {
let _ = sender_tx.send((Event::Enter(), addr)).await; let _ = sender_tx.send((Event::Enter(), addr)).await;
@@ -148,3 +151,34 @@ async fn handle_capture_event(
} }
Ok(()) Ok(())
} }
fn spawn_hook_command(server: &Server, handle: ClientHandle) {
let Some(cmd) = server
.client_manager
.borrow()
.get(handle)
.and_then(|(c, _)| c.cmd.clone())
else {
return;
};
tokio::task::spawn_local(async move {
log::info!("spawning command!");
let mut child = match Command::new("sh").arg("-c").arg(cmd.as_str()).spawn() {
Ok(c) => c,
Err(e) => {
log::warn!("could not execute cmd: {e}");
return;
}
};
match child.wait().await {
Ok(s) => {
if s.success() {
log::info!("{cmd} exited successfully");
} else {
log::warn!("{cmd} exited with {s}");
}
}
Err(e) => log::warn!("{cmd}: {e}"),
}
});
}

View File

@@ -107,20 +107,22 @@ async fn handle_frontend_event(
log::debug!("frontend: {event:?}"); log::debug!("frontend: {event:?}");
match event { match event {
FrontendRequest::Create => { FrontendRequest::Create => {
add_client(server, frontend).await; let handle = add_client(server, frontend).await;
resolve_dns(server, resolve_tx, handle).await;
} }
FrontendRequest::Activate(handle, active) => { FrontendRequest::Activate(handle, active) => {
if active { if active {
activate_client(server, frontend, capture, emulate, handle).await; activate_client(server, capture, emulate, handle).await;
} else { } else {
deactivate_client(server, frontend, capture, emulate, handle).await; deactivate_client(server, capture, emulate, handle).await;
} }
} }
FrontendRequest::ChangePort(port) => { FrontendRequest::ChangePort(port) => {
let _ = port_tx.send(port).await; let _ = port_tx.send(port).await;
} }
FrontendRequest::Delete(handle) => { FrontendRequest::Delete(handle) => {
remove_client(server, frontend, capture, emulate, handle).await; remove_client(server, capture, emulate, handle).await;
broadcast(frontend, FrontendEvent::Deleted(handle)).await;
} }
FrontendRequest::Enumerate() => { FrontendRequest::Enumerate() => {
let clients = server let clients = server
@@ -131,65 +133,74 @@ async fn handle_frontend_event(
.collect(); .collect();
broadcast(frontend, FrontendEvent::Enumerate(clients)).await; broadcast(frontend, FrontendEvent::Enumerate(clients)).await;
} }
FrontendRequest::GetState(handle) => {
broadcast_client(server, frontend, handle).await;
}
FrontendRequest::Terminate() => { FrontendRequest::Terminate() => {
log::info!("terminating gracefully..."); log::info!("terminating gracefully...");
return true; return true;
} }
FrontendRequest::UpdateFixIps(handle, fix_ips) => { FrontendRequest::UpdateFixIps(handle, fix_ips) => {
update_fix_ips(server, resolve_tx, handle, fix_ips).await; update_fix_ips(server, handle, fix_ips).await;
broadcast_client_update(server, frontend, handle).await; resolve_dns(server, resolve_tx, handle).await;
} }
FrontendRequest::UpdateHostname(handle, hostname) => { FrontendRequest::UpdateHostname(handle, hostname) => {
update_hostname(server, resolve_tx, handle, hostname).await; update_hostname(server, resolve_tx, handle, hostname).await;
broadcast_client_update(server, frontend, handle).await; resolve_dns(server, resolve_tx, handle).await;
} }
FrontendRequest::UpdatePort(handle, port) => { FrontendRequest::UpdatePort(handle, port) => {
update_port(server, handle, port).await; update_port(server, handle, port).await;
broadcast_client_update(server, frontend, handle).await;
} }
FrontendRequest::UpdatePosition(handle, pos) => { FrontendRequest::UpdatePosition(handle, pos) => {
update_pos(server, handle, capture, emulate, pos).await; update_pos(server, handle, capture, emulate, pos).await;
broadcast_client_update(server, frontend, handle).await;
} }
FrontendRequest::ResolveDns(handle) => { FrontendRequest::ResolveDns(handle) => {
let hostname = server resolve_dns(server, resolve_tx, handle).await;
.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
} }
async fn resolve_dns(server: &Server, resolve_tx: &Sender<DnsRequest>, handle: ClientHandle) {
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: hostname.clone(),
handle,
})
.await;
}
}
async fn broadcast(frontend: &mut FrontendListener, event: FrontendEvent) { async fn broadcast(frontend: &mut FrontendListener, event: FrontendEvent) {
if let Err(e) = frontend.broadcast_event(event).await { if let Err(e) = frontend.broadcast_event(event).await {
log::error!("error notifying frontend: {e}"); log::error!("error notifying frontend: {e}");
} }
} }
pub async fn add_client(server: &Server, frontend: &mut FrontendListener) { pub async fn add_client(server: &Server, frontend: &mut FrontendListener) -> ClientHandle {
let handle = server.client_manager.borrow_mut().add_client(); let handle = server.client_manager.borrow_mut().add_client();
log::info!("added client {handle}"); log::info!("added client {handle}");
let (c, s) = server.client_manager.borrow().get(handle).unwrap().clone(); let (c, s) = server.client_manager.borrow().get(handle).unwrap().clone();
broadcast(frontend, FrontendEvent::Created(handle, c, s)).await; broadcast(frontend, FrontendEvent::Created(handle, c, s)).await;
handle
} }
pub async fn deactivate_client( pub async fn deactivate_client(
server: &Server, server: &Server,
frontend: &mut FrontendListener,
capture: &Sender<CaptureEvent>, capture: &Sender<CaptureEvent>,
emulate: &Sender<EmulationEvent>, emulate: &Sender<EmulationEvent>,
handle: ClientHandle, handle: ClientHandle,
) { ) {
let state = match server.client_manager.borrow_mut().get_mut(handle) { match server.client_manager.borrow_mut().get_mut(handle) {
Some((_, s)) => { Some((_, s)) => {
s.active = false; s.active = false;
s.clone()
} }
None => return, None => return,
}; };
@@ -197,13 +208,10 @@ pub async fn deactivate_client(
let event = ClientEvent::Destroy(handle); let event = ClientEvent::Destroy(handle);
let _ = capture.send(CaptureEvent::ClientEvent(event)).await; let _ = capture.send(CaptureEvent::ClientEvent(event)).await;
let _ = emulate.send(EmulationEvent::ClientEvent(event)).await; let _ = emulate.send(EmulationEvent::ClientEvent(event)).await;
let event = FrontendEvent::StateChange(handle, state);
broadcast(frontend, event).await;
} }
pub async fn activate_client( pub async fn activate_client(
server: &Server, server: &Server,
frontend: &mut FrontendListener,
capture: &Sender<CaptureEvent>, capture: &Sender<CaptureEvent>,
emulate: &Sender<EmulationEvent>, emulate: &Sender<EmulationEvent>,
handle: ClientHandle, handle: ClientHandle,
@@ -217,14 +225,13 @@ pub async fn activate_client(
let other = server.client_manager.borrow_mut().find_client(pos); let other = server.client_manager.borrow_mut().find_client(pos);
if let Some(other) = other { if let Some(other) = other {
if other != handle { if other != handle {
deactivate_client(server, frontend, capture, emulate, other).await; deactivate_client(server, capture, emulate, other).await;
} }
} }
/* activate the client */ /* activate the client */
let state = if let Some((_, s)) = server.client_manager.borrow_mut().get_mut(handle) { if let Some((_, s)) = server.client_manager.borrow_mut().get_mut(handle) {
s.active = true; s.active = true;
s.clone()
} else { } else {
return; return;
}; };
@@ -233,13 +240,10 @@ pub async fn activate_client(
let event = ClientEvent::Create(handle, pos); let event = ClientEvent::Create(handle, pos);
let _ = capture.send(CaptureEvent::ClientEvent(event)).await; let _ = capture.send(CaptureEvent::ClientEvent(event)).await;
let _ = emulate.send(EmulationEvent::ClientEvent(event)).await; let _ = emulate.send(EmulationEvent::ClientEvent(event)).await;
let event = FrontendEvent::StateChange(handle, state);
broadcast(frontend, event).await;
} }
pub async fn remove_client( pub async fn remove_client(
server: &Server, server: &Server,
frontend: &mut FrontendListener,
capture: &Sender<CaptureEvent>, capture: &Sender<CaptureEvent>,
emulate: &Sender<EmulationEvent>, emulate: &Sender<EmulationEvent>,
handle: ClientHandle, handle: ClientHandle,
@@ -258,30 +262,15 @@ pub async fn remove_client(
let _ = capture.send(CaptureEvent::ClientEvent(destroy)).await; let _ = capture.send(CaptureEvent::ClientEvent(destroy)).await;
let _ = emulate.send(EmulationEvent::ClientEvent(destroy)).await; let _ = emulate.send(EmulationEvent::ClientEvent(destroy)).await;
} }
let event = FrontendEvent::Deleted(handle);
broadcast(frontend, event).await;
} }
async fn update_fix_ips( async fn update_fix_ips(server: &Server, handle: ClientHandle, fix_ips: Vec<IpAddr>) {
server: &Server, let mut client_manager = server.client_manager.borrow_mut();
resolve_tx: &Sender<DnsRequest>, let Some((c, _)) = client_manager.get_mut(handle) else {
handle: ClientHandle, return;
fix_ips: Vec<IpAddr>,
) {
let hostname = {
let mut client_manager = server.client_manager.borrow_mut();
let Some((c, _)) = client_manager.get_mut(handle) else {
return;
};
c.fix_ips = fix_ips;
c.hostname.clone()
}; };
if let Some(hostname) = hostname { c.fix_ips = fix_ips;
let _ = resolve_tx.send(DnsRequest { hostname, handle }).await;
}
} }
async fn update_hostname( async fn update_hostname(
@@ -356,11 +345,11 @@ async fn update_pos(
} }
} }
async fn broadcast_client_update( async fn broadcast_client(server: &Server, frontend: &mut FrontendListener, handle: ClientHandle) {
server: &Server, let client = server.client_manager.borrow().get(handle).cloned();
frontend: &mut FrontendListener, if let Some((config, state)) = client {
handle: ClientHandle, broadcast(frontend, FrontendEvent::State(handle, config, state)).await;
) { } else {
let (client, _) = server.client_manager.borrow().get(handle).unwrap().clone(); broadcast(frontend, FrontendEvent::NoSuchClient(handle)).await;
broadcast(frontend, FrontendEvent::Updated(handle, client)).await; }
} }

View File

@@ -35,7 +35,7 @@ pub fn new(
Ok(ips) => ips, Ok(ips) => ips,
Err(e) => { Err(e) => {
log::warn!("could not resolve host '{host}': {e}"); log::warn!("could not resolve host '{host}': {e}");
continue; vec![]
} }
}; };
@@ -59,14 +59,10 @@ async fn notify_state_change(
server: &mut Server, server: &mut Server,
handle: ClientHandle, handle: ClientHandle,
) { ) {
let state = server let state = server.client_manager.borrow_mut().get_mut(handle).cloned();
.client_manager if let Some((config, state)) = state {
.borrow_mut()
.get_mut(handle)
.map(|(_, s)| s.clone());
if let Some(state) = state {
let _ = frontend let _ = frontend
.send(FrontendEvent::StateChange(handle, state)) .send(FrontendEvent::State(handle, config, state))
.await; .await;
} }
} }