formatting

This commit is contained in:
Ferdinand Schober
2023-12-09 00:43:54 +01:00
parent 56e5f7a30d
commit 5a7e0cf89c
26 changed files with 881 additions and 497 deletions

View File

@@ -1,7 +1,7 @@
#[cfg(windows)] #[cfg(windows)]
pub mod windows; pub mod windows;
#[cfg(all(unix, feature="x11"))] #[cfg(all(unix, feature = "x11"))]
pub mod x11; pub mod x11;
#[cfg(all(unix, feature = "wayland"))] #[cfg(all(unix, feature = "wayland"))]

View File

@@ -1,13 +1,29 @@
use std::{os::{fd::{RawFd, FromRawFd}, unix::net::UnixStream}, collections::HashMap, time::{UNIX_EPOCH, SystemTime}, io}; use std::{
collections::HashMap,
io,
os::{
fd::{FromRawFd, RawFd},
unix::net::UnixStream,
},
time::{SystemTime, UNIX_EPOCH},
};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use futures::StreamExt; use ashpd::desktop::remote_desktop::{DeviceType, RemoteDesktop};
use ashpd::desktop::remote_desktop::{RemoteDesktop, DeviceType};
use async_trait::async_trait; use async_trait::async_trait;
use futures::StreamExt;
use reis::{ei::{self, handshake::ContextType, keyboard::KeyState, button::ButtonState}, tokio::EiEventStream, PendingRequestResult}; use reis::{
ei::{self, button::ButtonState, handshake::ContextType, keyboard::KeyState},
tokio::EiEventStream,
PendingRequestResult,
};
use crate::{consumer::EventConsumer, event::Event, client::{ClientHandle, ClientEvent}}; use crate::{
client::{ClientEvent, ClientHandle},
consumer::EventConsumer,
event::Event,
};
pub struct LibeiConsumer { pub struct LibeiConsumer {
handshake: bool, handshake: bool,
@@ -32,10 +48,17 @@ async fn get_ei_fd() -> Result<RawFd, ashpd::Error> {
let session = proxy.create_session().await?; let session = proxy.create_session().await?;
// I HATE EVERYTHING, THIS TOOK 8 HOURS OF DEBUGGING // I HATE EVERYTHING, THIS TOOK 8 HOURS OF DEBUGGING
proxy.select_devices(&session, proxy
DeviceType::Pointer | DeviceType::Keyboard |DeviceType::Touchscreen).await?; .select_devices(
&session,
DeviceType::Pointer | DeviceType::Keyboard | DeviceType::Touchscreen,
)
.await?;
proxy.start(&session, &ashpd::WindowIdentifier::default()).await?.response()?; proxy
.start(&session, &ashpd::WindowIdentifier::default())
.await?
.response()?;
proxy.connect_to_eis(&session).await proxy.connect_to_eis(&session).await
} }
@@ -59,7 +82,8 @@ impl LibeiConsumer {
let events = EiEventStream::new(context.clone())?; let events = EiEventStream::new(context.clone())?;
return Ok(Self { return Ok(Self {
handshake: false, handshake: false,
context, events, context,
events,
pointer: None, pointer: None,
button: None, button: None,
scroll: None, scroll: None,
@@ -72,32 +96,59 @@ impl LibeiConsumer {
capability_mask: 0, capability_mask: 0,
sequence: 0, sequence: 0,
serial: 0, serial: 0,
}) });
} }
} }
#[async_trait] #[async_trait]
impl EventConsumer for LibeiConsumer { impl EventConsumer for LibeiConsumer {
async fn consume(&mut self, event: Event, _client_handle: ClientHandle) { async fn consume(&mut self, event: Event, _client_handle: ClientHandle) {
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_micros() as u64; let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_micros() as u64;
match event { match event {
Event::Pointer(p) => match p { Event::Pointer(p) => match p {
crate::event::PointerEvent::Motion { time:_, relative_x, relative_y } => { crate::event::PointerEvent::Motion {
if !self.has_pointer { return } time: _,
relative_x,
relative_y,
} => {
if !self.has_pointer {
return;
}
if let Some((d, p)) = self.pointer.as_mut() { if let Some((d, p)) = self.pointer.as_mut() {
p.motion_relative(relative_x as f32, relative_y as f32); p.motion_relative(relative_x as f32, relative_y as f32);
d.frame(self.serial, now); d.frame(self.serial, now);
} }
}, }
crate::event::PointerEvent::Button { time: _, button, state } => { crate::event::PointerEvent::Button {
if !self.has_button { return } time: _,
button,
state,
} => {
if !self.has_button {
return;
}
if let Some((d, b)) = self.button.as_mut() { if let Some((d, b)) = self.button.as_mut() {
b.button(button, match state { 0 => ButtonState::Released, _ => ButtonState::Press }); b.button(
button,
match state {
0 => ButtonState::Released,
_ => ButtonState::Press,
},
);
d.frame(self.serial, now); d.frame(self.serial, now);
} }
}, }
crate::event::PointerEvent::Axis { time: _, axis, value } => { crate::event::PointerEvent::Axis {
if !self.has_scroll { return } time: _,
axis,
value,
} => {
if !self.has_scroll {
return;
}
if let Some((d, s)) = self.scroll.as_mut() { if let Some((d, s)) = self.scroll.as_mut() {
match axis { match axis {
0 => s.scroll(0., value as f32), 0 => s.scroll(0., value as f32),
@@ -105,18 +156,30 @@ impl EventConsumer for LibeiConsumer {
} }
d.frame(self.serial, now); d.frame(self.serial, now);
} }
}, }
crate::event::PointerEvent::Frame { } => {}, crate::event::PointerEvent::Frame {} => {}
}, },
Event::Keyboard(k) => match k { Event::Keyboard(k) => match k {
crate::event::KeyboardEvent::Key { time: _, key, state } => { crate::event::KeyboardEvent::Key {
if !self.has_keyboard { return } time: _,
key,
state,
} => {
if !self.has_keyboard {
return;
}
if let Some((d, k)) = &mut self.keyboard { if let Some((d, k)) = &mut self.keyboard {
k.key(key, match state { 0 => KeyState::Released, _ => KeyState::Press }); k.key(
key,
match state {
0 => KeyState::Released,
_ => KeyState::Press,
},
);
d.frame(self.serial, now); d.frame(self.serial, now);
} }
}, }
crate::event::KeyboardEvent::Modifiers { .. } => { }, crate::event::KeyboardEvent::Modifiers { .. } => {}
}, },
_ => {} _ => {}
} }
@@ -130,13 +193,17 @@ impl EventConsumer for LibeiConsumer {
}; };
let event = match event { let event = match event {
PendingRequestResult::Request(result) => result, PendingRequestResult::Request(result) => result,
PendingRequestResult::ProtocolError(e) => return Err(anyhow!("libei protocol violation: {e}")), PendingRequestResult::ProtocolError(e) => {
return Err(anyhow!("libei protocol violation: {e}"))
}
PendingRequestResult::InvalidObject(e) => return Err(anyhow!("invalid object {e}")), PendingRequestResult::InvalidObject(e) => return Err(anyhow!("invalid object {e}")),
}; };
match event { match event {
ei::Event::Handshake(handshake, request) => match request { ei::Event::Handshake(handshake, request) => match request {
ei::handshake::Event::HandshakeVersion { version } => { ei::handshake::Event::HandshakeVersion { version } => {
if self.handshake { return Ok(()) } if self.handshake {
return Ok(());
}
log::info!("libei version {}", version); log::info!("libei version {}", version);
// sender means we are sending events _to_ the eis server // sender means we are sending events _to_ the eis server
handshake.handshake_version(version); // FIXME handshake.handshake_version(version); // FIXME
@@ -163,8 +230,8 @@ impl EventConsumer for LibeiConsumer {
connection.sync(1); connection.sync(1);
self.serial = serial; self.serial = serial;
} }
_ => unreachable!() _ => unreachable!(),
} },
ei::Event::Connection(_connection, request) => match request { ei::Event::Connection(_connection, request) => match request {
ei::connection::Event::Seat { seat } => { ei::connection::Event::Seat { seat } => {
log::debug!("connected to seat: {seat:?}"); log::debug!("connected to seat: {seat:?}");
@@ -172,20 +239,45 @@ impl EventConsumer for LibeiConsumer {
ei::connection::Event::Ping { ping } => { ei::connection::Event::Ping { ping } => {
ping.done(0); ping.done(0);
} }
ei::connection::Event::Disconnected { last_serial: _, reason, explanation } => { ei::connection::Event::Disconnected {
last_serial: _,
reason,
explanation,
} => {
log::debug!("ei - disconnected: reason: {reason:?}: {explanation}") log::debug!("ei - disconnected: reason: {reason:?}: {explanation}")
} }
ei::connection::Event::InvalidObject { last_serial, invalid_id } => { ei::connection::Event::InvalidObject {
return Err(anyhow!("invalid object: id: {invalid_id}, serial: {last_serial}")); last_serial,
invalid_id,
} => {
return Err(anyhow!(
"invalid object: id: {invalid_id}, serial: {last_serial}"
));
} }
_ => unreachable!() _ => unreachable!(),
} },
ei::Event::Device(device, request) => match request { ei::Event::Device(device, request) => match request {
ei::device::Event::Destroyed { serial } => { log::debug!("device destroyed: {device:?} - serial: {serial}") }, ei::device::Event::Destroyed { serial } => {
ei::device::Event::Name { name } => {log::debug!("device name: {name}")}, log::debug!("device destroyed: {device:?} - serial: {serial}")
ei::device::Event::DeviceType { device_type } => log::debug!("device type: {device_type:?}"), }
ei::device::Event::Dimensions { width, height } => log::debug!("device dimensions: {width}x{height}"), ei::device::Event::Name { name } => {
ei::device::Event::Region { offset_x, offset_y, width, hight, scale } => log::debug!("device region: {width}x{hight} @ ({offset_x},{offset_y}), scale: {scale}"), log::debug!("device name: {name}")
}
ei::device::Event::DeviceType { device_type } => {
log::debug!("device type: {device_type:?}")
}
ei::device::Event::Dimensions { width, height } => {
log::debug!("device dimensions: {width}x{height}")
}
ei::device::Event::Region {
offset_x,
offset_y,
width,
hight,
scale,
} => log::debug!(
"device region: {width}x{hight} @ ({offset_x},{offset_y}), scale: {scale}"
),
ei::device::Event::Interface { object } => { ei::device::Event::Interface { object } => {
log::debug!("device interface: {object:?}"); log::debug!("device interface: {object:?}");
if object.interface().eq("ei_pointer") { if object.interface().eq("ei_pointer") {
@@ -204,31 +296,31 @@ impl EventConsumer for LibeiConsumer {
} }
ei::device::Event::Done => { ei::device::Event::Done => {
log::debug!("device: done {device:?}"); log::debug!("device: done {device:?}");
}, }
ei::device::Event::Resumed { serial } => { ei::device::Event::Resumed { serial } => {
self.serial = serial; self.serial = serial;
device.start_emulating(serial, self.sequence); device.start_emulating(serial, self.sequence);
self.sequence += 1; self.sequence += 1;
log::debug!("resumed: {device:?}"); log::debug!("resumed: {device:?}");
if let Some((d,_)) = &mut self.pointer { if let Some((d, _)) = &mut self.pointer {
if d == &device { if d == &device {
log::debug!("pointer resumed {serial}"); log::debug!("pointer resumed {serial}");
self.has_pointer = true; self.has_pointer = true;
} }
} }
if let Some((d,_)) = &mut self.button { if let Some((d, _)) = &mut self.button {
if d == &device { if d == &device {
log::debug!("button resumed {serial}"); log::debug!("button resumed {serial}");
self.has_button = true; self.has_button = true;
} }
} }
if let Some((d,_)) = &mut self.scroll { if let Some((d, _)) = &mut self.scroll {
if d == &device { if d == &device {
log::debug!("scroll resumed {serial}"); log::debug!("scroll resumed {serial}");
self.has_scroll = true; self.has_scroll = true;
} }
} }
if let Some((d,_)) = &mut self.keyboard { if let Some((d, _)) = &mut self.keyboard {
if d == &device { if d == &device {
log::debug!("keyboard resumed {serial}"); log::debug!("keyboard resumed {serial}");
self.has_keyboard = true; self.has_keyboard = true;
@@ -239,38 +331,44 @@ impl EventConsumer for LibeiConsumer {
self.has_pointer = false; self.has_pointer = false;
self.has_button = false; self.has_button = false;
self.serial = serial; self.serial = serial;
}, }
ei::device::Event::StartEmulating { serial, sequence } => log::debug!("start emulating {serial}, {sequence}"), ei::device::Event::StartEmulating { serial, sequence } => {
ei::device::Event::StopEmulating { serial } => log::debug!("stop emulating {serial}"), log::debug!("start emulating {serial}, {sequence}")
}
ei::device::Event::StopEmulating { serial } => {
log::debug!("stop emulating {serial}")
}
ei::device::Event::Frame { serial, timestamp } => { ei::device::Event::Frame { serial, timestamp } => {
log::debug!("frame: {serial}, {timestamp}"); log::debug!("frame: {serial}, {timestamp}");
} }
ei::device::Event::RegionMappingId { mapping_id } => log::debug!("RegionMappingId {mapping_id}"), ei::device::Event::RegionMappingId { mapping_id } => {
log::debug!("RegionMappingId {mapping_id}")
}
e => log::debug!("invalid event: {e:?}"), e => log::debug!("invalid event: {e:?}"),
} },
ei::Event::Seat(seat, request) => match request { ei::Event::Seat(seat, request) => match request {
ei::seat::Event::Destroyed { serial } => { ei::seat::Event::Destroyed { serial } => {
self.serial = serial; self.serial = serial;
log::debug!("seat destroyed: {seat:?}"); log::debug!("seat destroyed: {seat:?}");
}, }
ei::seat::Event::Name { name } => { ei::seat::Event::Name { name } => {
log::debug!("seat name: {name}"); log::debug!("seat name: {name}");
}, }
ei::seat::Event::Capability { mask, interface } => { ei::seat::Event::Capability { mask, interface } => {
log::debug!("seat capabilities: {mask}, interface: {interface:?}"); log::debug!("seat capabilities: {mask}, interface: {interface:?}");
self.capabilities.insert(interface, mask); self.capabilities.insert(interface, mask);
self.capability_mask |= mask; self.capability_mask |= mask;
}, }
ei::seat::Event::Done => { ei::seat::Event::Done => {
log::debug!("seat done"); log::debug!("seat done");
log::debug!("binding capabilities: {}", self.capability_mask); log::debug!("binding capabilities: {}", self.capability_mask);
seat.bind(self.capability_mask); seat.bind(self.capability_mask);
}, }
ei::seat::Event::Device { device } => { ei::seat::Event::Device { device } => {
log::debug!("seat: new device - {device:?}"); log::debug!("seat: new device - {device:?}");
}, }
_ => todo!(), _ => todo!(),
} },
e => log::debug!("unhandled event: {e:?}"), e => log::debug!("unhandled event: {e:?}"),
} }
self.context.flush()?; self.context.flush()?;
@@ -281,4 +379,3 @@ impl EventConsumer for LibeiConsumer {
async fn destroy(&mut self) {} async fn destroy(&mut self) {}
} }

View File

@@ -1,16 +1,15 @@
use crate::{
consumer::EventConsumer,
event::{KeyboardEvent, PointerEvent},
};
use async_trait::async_trait; use async_trait::async_trait;
use crate::{event::{KeyboardEvent, PointerEvent}, consumer::EventConsumer};
use winapi::{ use winapi::{
self, self,
um::winuser::{INPUT, INPUT_MOUSE, LPINPUT, MOUSEEVENTF_MOVE, MOUSEINPUT, um::winuser::{
MOUSEEVENTF_LEFTDOWN, INPUT, INPUT_KEYBOARD, INPUT_MOUSE, KEYBDINPUT, KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE,
MOUSEEVENTF_RIGHTDOWN, LPINPUT, MOUSEEVENTF_HWHEEL, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP,
MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_MOVE, MOUSEEVENTF_RIGHTDOWN,
MOUSEEVENTF_LEFTUP, MOUSEEVENTF_RIGHTUP, MOUSEEVENTF_WHEEL, MOUSEINPUT,
MOUSEEVENTF_RIGHTUP,
MOUSEEVENTF_MIDDLEUP,
MOUSEEVENTF_WHEEL,
MOUSEEVENTF_HWHEEL, INPUT_KEYBOARD, KEYBDINPUT, KEYEVENTF_SCANCODE, KEYEVENTF_KEYUP,
}, },
}; };
@@ -19,11 +18,12 @@ use crate::{
event::Event, event::Event,
}; };
pub struct WindowsConsumer {} pub struct WindowsConsumer {}
impl WindowsConsumer { impl WindowsConsumer {
pub fn new() -> Self { Self { } } pub fn new() -> Self {
Self {}
}
} }
#[async_trait] #[async_trait]
@@ -38,12 +38,24 @@ impl EventConsumer for WindowsConsumer {
} => { } => {
rel_mouse(relative_x as i32, relative_y as i32); rel_mouse(relative_x as i32, relative_y as i32);
} }
PointerEvent::Button { time:_, button, state } => { mouse_button(button, state)} PointerEvent::Button {
PointerEvent::Axis { time:_, axis, value } => { scroll(axis, value) } time: _,
button,
state,
} => mouse_button(button, state),
PointerEvent::Axis {
time: _,
axis,
value,
} => scroll(axis, value),
PointerEvent::Frame {} => {} PointerEvent::Frame {} => {}
}, },
Event::Keyboard(keyboard_event) => match keyboard_event { Event::Keyboard(keyboard_event) => match keyboard_event {
KeyboardEvent::Key { time:_, key, state } => { key_event(key, state) } KeyboardEvent::Key {
time: _,
key,
state,
} => key_event(key, state),
KeyboardEvent::Modifiers { .. } => {} KeyboardEvent::Modifiers { .. } => {}
}, },
_ => {} _ => {}
@@ -90,18 +102,19 @@ fn mouse_button(button: u32, state: u32) {
0x110 => MOUSEEVENTF_LEFTUP, 0x110 => MOUSEEVENTF_LEFTUP,
0x111 => MOUSEEVENTF_RIGHTUP, 0x111 => MOUSEEVENTF_RIGHTUP,
0x112 => MOUSEEVENTF_MIDDLEUP, 0x112 => MOUSEEVENTF_MIDDLEUP,
_ => return _ => return,
} },
1 => match button { 1 => match button {
0x110 => MOUSEEVENTF_LEFTDOWN, 0x110 => MOUSEEVENTF_LEFTDOWN,
0x111 => MOUSEEVENTF_RIGHTDOWN, 0x111 => MOUSEEVENTF_RIGHTDOWN,
0x112 => MOUSEEVENTF_MIDDLEDOWN, 0x112 => MOUSEEVENTF_MIDDLEDOWN,
_ => return _ => return,
} },
_ => return _ => return,
}; };
let mi = MOUSEINPUT { let mi = MOUSEINPUT {
dx: 0, dy: 0, // no movement dx: 0,
dy: 0, // no movement
mouseData: 0, mouseData: 0,
dwFlags: dw_flags, dwFlags: dw_flags,
time: 0, time: 0,
@@ -114,10 +127,11 @@ fn scroll(axis: u8, value: f64) {
let event_type = match axis { let event_type = match axis {
0 => MOUSEEVENTF_WHEEL, 0 => MOUSEEVENTF_WHEEL,
1 => MOUSEEVENTF_HWHEEL, 1 => MOUSEEVENTF_HWHEEL,
_ => return _ => return,
}; };
let mi = MOUSEINPUT { let mi = MOUSEINPUT {
dx: 0, dy: 0, dx: 0,
dy: 0,
mouseData: (-value * 15.0) as i32 as u32, mouseData: (-value * 15.0) as i32 as u32,
dwFlags: event_type, dwFlags: event_type,
time: 0, time: 0,
@@ -130,11 +144,12 @@ fn key_event(key: u32, state: u8) {
let ki = KEYBDINPUT { let ki = KEYBDINPUT {
wVk: 0, wVk: 0,
wScan: key as u16, wScan: key as u16,
dwFlags: KEYEVENTF_SCANCODE | match state { dwFlags: KEYEVENTF_SCANCODE
0 => KEYEVENTF_KEYUP, | match state {
1 => 0u32, 0 => KEYEVENTF_KEYUP,
_ => return 1 => 0u32,
}, _ => return,
},
time: 0, time: 0,
dwExtraInfo: 0, dwExtraInfo: 0,
}; };

View File

@@ -1,17 +1,17 @@
use async_trait::async_trait; use crate::client::{ClientEvent, ClientHandle};
use wayland_client::WEnum;
use wayland_client::backend::WaylandError;
use crate::client::{ClientHandle, ClientEvent};
use crate::consumer::EventConsumer; use crate::consumer::EventConsumer;
use async_trait::async_trait;
use std::collections::HashMap; use std::collections::HashMap;
use std::io; use std::io;
use std::os::fd::OwnedFd; use std::os::fd::OwnedFd;
use std::os::unix::prelude::AsRawFd; use std::os::unix::prelude::AsRawFd;
use wayland_client::backend::WaylandError;
use wayland_client::WEnum;
use anyhow::{Result, anyhow}; use anyhow::{anyhow, Result};
use wayland_client::globals::BindError; use wayland_client::globals::BindError;
use wayland_client::protocol::wl_pointer::{Axis, ButtonState};
use wayland_client::protocol::wl_keyboard::{self, WlKeyboard}; use wayland_client::protocol::wl_keyboard::{self, WlKeyboard};
use wayland_client::protocol::wl_pointer::{Axis, ButtonState};
use wayland_client::protocol::wl_seat::WlSeat; use wayland_client::protocol::wl_seat::WlSeat;
use wayland_protocols_wlr::virtual_pointer::v1::client::{ use wayland_protocols_wlr::virtual_pointer::v1::client::{
zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1 as VpManager, zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1 as VpManager,
@@ -104,7 +104,10 @@ impl WlrootsConsumer {
queue, queue,
}; };
while consumer.state.keymap.is_none() { while consumer.state.keymap.is_none() {
consumer.queue.blocking_dispatch(&mut consumer.state).unwrap(); consumer
.queue
.blocking_dispatch(&mut consumer.state)
.unwrap();
} }
// let fd = unsafe { &File::from_raw_fd(consumer.state.keymap.unwrap().1.as_raw_fd()) }; // let fd = unsafe { &File::from_raw_fd(consumer.state.keymap.unwrap().1.as_raw_fd()) };
// let mmap = unsafe { MmapOptions::new().map_copy(fd).unwrap() }; // let mmap = unsafe { MmapOptions::new().map_copy(fd).unwrap() };
@@ -153,8 +156,10 @@ impl EventConsumer for WlrootsConsumer {
* will overwhelm the output buffer and leave the * will overwhelm the output buffer and leave the
* wayland connection in a broken state * wayland connection in a broken state
*/ */
log::warn!("can't keep up, discarding event: ({client_handle}) - {event:?}"); log::warn!(
return "can't keep up, discarding event: ({client_handle}) - {event:?}"
);
return;
} }
} }
} }
@@ -166,13 +171,13 @@ impl EventConsumer for WlrootsConsumer {
} }
Err(WaylandError::Io(e)) => { Err(WaylandError::Io(e)) => {
log::error!("{e}") log::error!("{e}")
}, }
Err(WaylandError::Protocol(e)) => { Err(WaylandError::Protocol(e)) => {
panic!("wayland protocol violation: {e}") panic!("wayland protocol violation: {e}")
} }
Ok(()) => { Ok(()) => {
self.last_flush_failed = false; self.last_flush_failed = false;
}, }
} }
} }
} }
@@ -186,17 +191,16 @@ impl EventConsumer for WlrootsConsumer {
} }
} }
async fn destroy(&mut self) { } async fn destroy(&mut self) {}
} }
enum VirtualInput { enum VirtualInput {
Wlroots { pointer: Vp, keyboard: Vk }, Wlroots { pointer: Vp, keyboard: Vk },
Kde { fake_input: OrgKdeKwinFakeInput }, Kde { fake_input: OrgKdeKwinFakeInput },
} }
impl VirtualInput { impl VirtualInput {
fn consume_event(&self, event: Event) -> Result<(),()> { fn consume_event(&self, event: Event) -> Result<(), ()> {
match event { match event {
Event::Pointer(e) => { Event::Pointer(e) => {
match e { match e {
@@ -263,9 +267,9 @@ impl VirtualInput {
// insert a frame event after each mouse event // insert a frame event after each mouse event
pointer.frame(); pointer.frame();
} }
_ => {}, _ => {}
} }
}, }
Event::Keyboard(e) => match e { Event::Keyboard(e) => match e {
KeyboardEvent::Key { time, key, state } => match self { KeyboardEvent::Key { time, key, state } => match self {
VirtualInput::Wlroots { VirtualInput::Wlroots {
@@ -293,7 +297,7 @@ impl VirtualInput {
VirtualInput::Kde { fake_input: _ } => {} VirtualInput::Kde { fake_input: _ } => {}
}, },
}, },
_ => {}, _ => {}
} }
Ok(()) Ok(())
} }
@@ -330,7 +334,7 @@ impl Dispatch<WlKeyboard, ()> for State {
wl_keyboard::Event::Keymap { format, fd, size } => { wl_keyboard::Event::Keymap { format, fd, size } => {
state.keymap = Some((u32::from(format), fd, size)); state.keymap = Some((u32::from(format), fd, size));
} }
_ => {}, _ => {}
} }
} }
} }

View File

@@ -1,11 +1,8 @@
use std::ptr;
use async_trait::async_trait; use async_trait::async_trait;
use std::ptr;
use x11::{xlib, xtest}; use x11::{xlib, xtest};
use crate::{ use crate::{client::ClientHandle, consumer::EventConsumer, event::Event};
client::ClientHandle,
event::Event, consumer::EventConsumer,
};
pub struct X11Consumer { pub struct X11Consumer {
display: *mut xlib::Display, display: *mut xlib::Display,
@@ -60,4 +57,3 @@ impl EventConsumer for X11Consumer {
async fn destroy(&mut self) {} async fn destroy(&mut self) {}
} }

View File

@@ -1,6 +1,12 @@
use async_trait::async_trait;
use anyhow::Result; use anyhow::Result;
use ashpd::{desktop::{remote_desktop::{RemoteDesktop, DeviceType, KeyState, Axis}, Session}, WindowIdentifier}; use ashpd::{
desktop::{
remote_desktop::{Axis, DeviceType, KeyState, RemoteDesktop},
Session,
},
WindowIdentifier,
};
use async_trait::async_trait;
use crate::consumer::EventConsumer; use crate::consumer::EventConsumer;
@@ -32,55 +38,86 @@ impl<'a> EventConsumer for DesktopPortalConsumer<'a> {
match event { match event {
crate::event::Event::Pointer(p) => { crate::event::Event::Pointer(p) => {
match p { match p {
crate::event::PointerEvent::Motion { time: _, relative_x, relative_y } => { crate::event::PointerEvent::Motion {
if let Err(e) = self.proxy.notify_pointer_motion(&self.session, relative_x, relative_y).await { time: _,
relative_x,
relative_y,
} => {
if let Err(e) = self
.proxy
.notify_pointer_motion(&self.session, relative_x, relative_y)
.await
{
log::warn!("{e}"); log::warn!("{e}");
} }
}, }
crate::event::PointerEvent::Button { time: _, button, state } => { crate::event::PointerEvent::Button {
time: _,
button,
state,
} => {
let state = match state { let state = match state {
0 => KeyState::Released, 0 => KeyState::Released,
_ => KeyState::Pressed, _ => KeyState::Pressed,
}; };
if let Err(e) = self.proxy.notify_pointer_button(&self.session, button as i32, state).await { if let Err(e) = self
.proxy
.notify_pointer_button(&self.session, button as i32, state)
.await
{
log::warn!("{e}"); log::warn!("{e}");
} }
}, }
crate::event::PointerEvent::Axis { time: _, axis, value } => { crate::event::PointerEvent::Axis {
time: _,
axis,
value,
} => {
let axis = match axis { let axis = match axis {
0 => Axis::Vertical, 0 => Axis::Vertical,
_ => Axis::Horizontal, _ => Axis::Horizontal,
}; };
// TODO smooth scrolling // TODO smooth scrolling
if let Err(e) = self.proxy.notify_pointer_axis_discrete(&self.session, axis, value as i32).await { if let Err(e) = self
.proxy
.notify_pointer_axis_discrete(&self.session, axis, value as i32)
.await
{
log::warn!("{e}"); log::warn!("{e}");
} }
}
}, crate::event::PointerEvent::Frame {} => {}
crate::event::PointerEvent::Frame { } => {},
} }
}, }
crate::event::Event::Keyboard(k) => { crate::event::Event::Keyboard(k) => {
match k { match k {
crate::event::KeyboardEvent::Key { time: _, key, state } => { crate::event::KeyboardEvent::Key {
time: _,
key,
state,
} => {
let state = match state { let state = match state {
0 => KeyState::Released, 0 => KeyState::Released,
_ => KeyState::Pressed, _ => KeyState::Pressed,
}; };
if let Err(e) = self.proxy.notify_keyboard_keycode(&self.session, key as i32, state).await { if let Err(e) = self
.proxy
.notify_keyboard_keycode(&self.session, key as i32, state)
.await
{
log::warn!("{e}"); log::warn!("{e}");
} }
}, }
crate::event::KeyboardEvent::Modifiers { .. } => { crate::event::KeyboardEvent::Modifiers { .. } => {
// ignore // ignore
}, }
} }
}, }
_ => {}, _ => {}
} }
} }
async fn notify(&mut self, _client: crate::client::ClientEvent) { } async fn notify(&mut self, _client: crate::client::ClientEvent) {}
async fn destroy(&mut self) { async fn destroy(&mut self) {
log::debug!("closing remote desktop session"); log::debug!("closing remote desktop session");

View File

@@ -3,28 +3,29 @@ use std::{io, task::Poll};
use futures_core::Stream; use futures_core::Stream;
use crate::{producer::EventProducer, event::Event, client::ClientHandle}; use crate::{client::ClientHandle, event::Event, producer::EventProducer};
pub struct LibeiProducer {} pub struct LibeiProducer {}
impl LibeiProducer { impl LibeiProducer {
pub fn new() -> Result<Self> { pub fn new() -> Result<Self> {
Ok(Self { }) Ok(Self {})
} }
} }
impl EventProducer for LibeiProducer { impl EventProducer for LibeiProducer {
fn notify(&mut self, _event: crate::client::ClientEvent) { fn notify(&mut self, _event: crate::client::ClientEvent) {}
}
fn release(&mut self) { fn release(&mut self) {}
}
} }
impl Stream for LibeiProducer { impl Stream for LibeiProducer {
type Item = io::Result<(ClientHandle, Event)>; type Item = io::Result<(ClientHandle, Event)>;
fn poll_next(self: std::pin::Pin<&mut Self>, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Option<Self::Item>> { fn poll_next(
self: std::pin::Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<Self::Item>> {
Poll::Pending Poll::Pending
} }
} }

View File

@@ -1,9 +1,19 @@
use crate::{client::{ClientHandle, Position, ClientEvent}, producer::EventProducer}; use crate::{
client::{ClientEvent, ClientHandle, Position},
producer::EventProducer,
};
use std::{os::fd::{OwnedFd, RawFd}, io::{ErrorKind, self}, env, pin::Pin, task::{Context, Poll, ready}, collections::VecDeque}; use anyhow::{anyhow, Result};
use futures_core::Stream; use futures_core::Stream;
use memmap::MmapOptions; use memmap::MmapOptions;
use anyhow::{anyhow, Result}; use std::{
collections::VecDeque,
env,
io::{self, ErrorKind},
os::fd::{OwnedFd, RawFd},
pin::Pin,
task::{ready, Context, Poll},
};
use tokio::io::unix::AsyncFd; use tokio::io::unix::AsyncFd;
use std::{ use std::{
@@ -13,20 +23,26 @@ use std::{
rc::Rc, rc::Rc,
}; };
use wayland_protocols::{wp::{ use wayland_protocols::{
keyboard_shortcuts_inhibit::zv1::client::{ wp::{
zwp_keyboard_shortcuts_inhibit_manager_v1::ZwpKeyboardShortcutsInhibitManagerV1, keyboard_shortcuts_inhibit::zv1::client::{
zwp_keyboard_shortcuts_inhibitor_v1::ZwpKeyboardShortcutsInhibitorV1, zwp_keyboard_shortcuts_inhibit_manager_v1::ZwpKeyboardShortcutsInhibitManagerV1,
zwp_keyboard_shortcuts_inhibitor_v1::ZwpKeyboardShortcutsInhibitorV1,
},
pointer_constraints::zv1::client::{
zwp_locked_pointer_v1::ZwpLockedPointerV1,
zwp_pointer_constraints_v1::{Lifetime, ZwpPointerConstraintsV1},
},
relative_pointer::zv1::client::{
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
zwp_relative_pointer_v1::{self, ZwpRelativePointerV1},
},
}, },
pointer_constraints::zv1::client::{ xdg::xdg_output::zv1::client::{
zwp_locked_pointer_v1::ZwpLockedPointerV1, zxdg_output_manager_v1::ZxdgOutputManagerV1,
zwp_pointer_constraints_v1::{Lifetime, ZwpPointerConstraintsV1}, zxdg_output_v1::{self, ZxdgOutputV1},
}, },
relative_pointer::zv1::client::{ };
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
zwp_relative_pointer_v1::{self, ZwpRelativePointerV1},
},
}, xdg::xdg_output::zv1::client::{zxdg_output_manager_v1::ZxdgOutputManagerV1, zxdg_output_v1::{self, ZxdgOutputV1}}};
use wayland_protocols_wlr::layer_shell::v1::client::{ use wayland_protocols_wlr::layer_shell::v1::client::{
zwlr_layer_shell_v1::{Layer, ZwlrLayerShellV1}, zwlr_layer_shell_v1::{Layer, ZwlrLayerShellV1},
@@ -34,14 +50,15 @@ use wayland_protocols_wlr::layer_shell::v1::client::{
}; };
use wayland_client::{ use wayland_client::{
backend::{WaylandError, ReadEventsGuard}, backend::{ReadEventsGuard, WaylandError},
delegate_noop, delegate_noop,
globals::{registry_queue_init, GlobalListContents}, globals::{registry_queue_init, GlobalListContents},
protocol::{ protocol::{
wl_buffer, wl_compositor, wl_keyboard, wl_pointer, wl_region, wl_registry, wl_seat, wl_shm, wl_buffer, wl_compositor, wl_keyboard,
wl_shm_pool, wl_surface, wl_output::{self, WlOutput}, wl_output::{self, WlOutput},
wl_pointer, wl_region, wl_registry, wl_seat, wl_shm, wl_shm_pool, wl_surface,
}, },
Connection, Dispatch, DispatchError, QueueHandle, WEnum, EventQueue, Connection, Dispatch, DispatchError, EventQueue, QueueHandle, WEnum,
}; };
use tempfile; use tempfile;
@@ -71,8 +88,8 @@ impl OutputInfo {
fn new() -> Self { fn new() -> Self {
Self { Self {
name: "".to_string(), name: "".to_string(),
position: (0,0), position: (0, 0),
size: (0,0), size: (0, 0),
} }
} }
} }
@@ -111,7 +128,13 @@ struct Window {
} }
impl Window { impl Window {
fn new(state: &State, qh: &QueueHandle<State>, output: &WlOutput, pos: Position, size: (i32, i32)) -> Window { fn new(
state: &State,
qh: &QueueHandle<State>,
output: &WlOutput,
pos: Position,
size: (i32, i32),
) -> Window {
let g = &state.g; let g = &state.g;
let (width, height) = match pos { let (width, height) = match pos {
@@ -152,7 +175,7 @@ impl Window {
layer_surface.set_anchor(anchor); layer_surface.set_anchor(anchor);
layer_surface.set_size(width, height); layer_surface.set_size(width, height);
layer_surface.set_exclusive_zone(-1); layer_surface.set_exclusive_zone(-1);
layer_surface.set_margin(0,0,0,0); layer_surface.set_margin(0, 0, 0, 0);
surface.set_input_region(None); surface.set_input_region(None);
surface.commit(); surface.commit();
Window { Window {
@@ -173,15 +196,20 @@ impl Drop for Window {
} }
fn get_edges(outputs: &[(WlOutput, OutputInfo)], pos: Position) -> Vec<(WlOutput, i32)> { fn get_edges(outputs: &[(WlOutput, OutputInfo)], pos: Position) -> Vec<(WlOutput, i32)> {
outputs.iter().map(|(o, i)| { outputs
(o.clone(), .iter()
match pos { .map(|(o, i)| {
Position::Left => i.position.0, (
Position::Right => i.position.0 + i.size.0, o.clone(),
Position::Top => i.position.1, match pos {
Position::Bottom => i.position.1 + i.size.1, Position::Left => i.position.0,
Position::Right => i.position.0 + i.size.0,
Position::Top => i.position.1,
Position::Bottom => i.position.1 + i.size.1,
},
)
}) })
}).collect() .collect()
} }
fn get_output_configuration(state: &State, pos: Position) -> Vec<(WlOutput, OutputInfo)> { fn get_output_configuration(state: &State, pos: Position) -> Vec<(WlOutput, OutputInfo)> {
@@ -191,12 +219,21 @@ fn get_output_configuration(state: &State, pos: Position) -> Vec<(WlOutput, Outp
// remove those edges that are at the same position // remove those edges that are at the same position
// as an opposite edge of a different output // as an opposite edge of a different output
let outputs: Vec<WlOutput> = edges.iter().filter(|(_,edge)| { let outputs: Vec<WlOutput> = edges
opposite_edges.iter().map(|(_,e)| *e).find(|e| e == edge).is_none()
}).map(|(o,_)| o.clone()).collect();
state.output_info
.iter() .iter()
.filter(|(o,_)| outputs.contains(o)) .filter(|(_, edge)| {
opposite_edges
.iter()
.map(|(_, e)| *e)
.find(|e| e == edge)
.is_none()
})
.map(|(o, _)| o.clone())
.collect();
state
.output_info
.iter()
.filter(|(o, _)| outputs.contains(o))
.map(|(o, i)| (o.clone(), i.clone())) .map(|(o, i)| (o.clone(), i.clone()))
.collect() .collect()
} }
@@ -265,10 +302,15 @@ impl WaylandEventProducer {
Err(_) => return Err(anyhow!("zwp_relative_pointer_manager_v1 not supported")), Err(_) => return Err(anyhow!("zwp_relative_pointer_manager_v1 not supported")),
}; };
let shortcut_inhibit_manager: ZwpKeyboardShortcutsInhibitManagerV1 = match g.bind(&qh, 1..=1, ()) { let shortcut_inhibit_manager: ZwpKeyboardShortcutsInhibitManagerV1 =
Ok(shortcut_inhibit_manager) => shortcut_inhibit_manager, match g.bind(&qh, 1..=1, ()) {
Err(_) => return Err(anyhow!("zwp_keyboard_shortcuts_inhibit_manager_v1 not supported")), Ok(shortcut_inhibit_manager) => shortcut_inhibit_manager,
}; Err(_) => {
return Err(anyhow!(
"zwp_keyboard_shortcuts_inhibit_manager_v1 not supported"
))
}
};
let outputs = vec![]; let outputs = vec![];
@@ -316,7 +358,10 @@ impl WaylandEventProducer {
// read outputs // read outputs
for output in state.g.outputs.iter() { for output in state.g.outputs.iter() {
state.g.xdg_output_manager.get_xdg_output(output, &state.qh, output.clone()); state
.g
.xdg_output_manager
.get_xdg_output(output, &state.qh, output.clone());
} }
// roundtrip to read xdg_output events // roundtrip to read xdg_output events
@@ -420,7 +465,6 @@ impl State {
} }
fn add_client(&mut self, client: ClientHandle, pos: Position) { fn add_client(&mut self, client: ClientHandle, pos: Position) {
let outputs = get_output_configuration(self, pos); let outputs = get_output_configuration(self, pos);
outputs.iter().for_each(|(o, i)| { outputs.iter().for_each(|(o, i)| {
@@ -428,7 +472,6 @@ impl State {
let window = Rc::new(window); let window = Rc::new(window);
self.client_for_window.push((window, client)); self.client_for_window.push((window, client));
}); });
} }
} }
@@ -485,17 +528,16 @@ impl Inner {
Err(e) => match e { Err(e) => match e {
WaylandError::Io(e) => { WaylandError::Io(e) => {
log::error!("error writing to wayland socket: {e}") log::error!("error writing to wayland socket: {e}")
}, }
WaylandError::Protocol(e) => { WaylandError::Protocol(e) => {
panic!("wayland protocol violation: {e}") panic!("wayland protocol violation: {e}")
}, }
}, },
} }
} }
} }
impl EventProducer for WaylandEventProducer { impl EventProducer for WaylandEventProducer {
fn notify(&mut self, client_event: ClientEvent) { fn notify(&mut self, client_event: ClientEvent) {
match client_event { match client_event {
ClientEvent::Create(handle, pos) => { ClientEvent::Create(handle, pos) => {
@@ -509,11 +551,12 @@ impl EventProducer for WaylandEventProducer {
.state .state
.client_for_window .client_for_window
.iter() .iter()
.position(|(_,c)| *c == handle) { .position(|(_, c)| *c == handle)
{
inner.state.client_for_window.remove(i); inner.state.client_for_window.remove(i);
inner.state.focused = None; inner.state.focused = None;
} else { } else {
break break;
} }
} }
} }
@@ -605,7 +648,6 @@ impl Dispatch<wl_pointer::WlPointer, ()> for State {
_: &Connection, _: &Connection,
qh: &QueueHandle<Self>, qh: &QueueHandle<Self>,
) { ) {
match event { match event {
wl_pointer::Event::Enter { wl_pointer::Event::Enter {
serial, serial,
@@ -618,7 +660,8 @@ impl Dispatch<wl_pointer::WlPointer, ()> for State {
if let Some((window, client)) = app if let Some((window, client)) = app
.client_for_window .client_for_window
.iter() .iter()
.find(|(w, _c)| w.surface == surface) { .find(|(w, _c)| w.surface == surface)
{
app.focused = Some((window.clone(), *client)); app.focused = Some((window.clone(), *client));
app.grab(&surface, pointer, serial.clone(), qh); app.grab(&surface, pointer, serial.clone(), qh);
} else { } else {
@@ -786,7 +829,8 @@ impl Dispatch<ZwlrLayerSurfaceV1, ()> for State {
if let Some((window, _client)) = app if let Some((window, _client)) = app
.client_for_window .client_for_window
.iter() .iter()
.find(|(w, _c)| &w.layer_surface == layer_surface) { .find(|(w, _c)| &w.layer_surface == layer_surface)
{
// client corresponding to the layer_surface // client corresponding to the layer_surface
let surface = &window.surface; let surface = &window.surface;
let buffer = &window.buffer; let buffer = &window.buffer;
@@ -821,17 +865,22 @@ impl Dispatch<wl_registry::WlRegistry, ()> for State {
qh: &QueueHandle<Self>, qh: &QueueHandle<Self>,
) { ) {
match event { match event {
wl_registry::Event::Global { name, interface, version: _ } => { wl_registry::Event::Global {
match interface.as_str() { name,
"wl_output" => { interface,
log::debug!("wl_output global"); version: _,
state.g.outputs.push(registry.bind::<wl_output::WlOutput, _, _>(name, 4, qh, ())) } => match interface.as_str() {
} "wl_output" => {
_ => {} log::debug!("wl_output global");
state
.g
.outputs
.push(registry.bind::<wl_output::WlOutput, _, _>(name, 4, qh, ()))
} }
_ => {}
}, },
wl_registry::Event::GlobalRemove { .. } => {}, wl_registry::Event::GlobalRemove { .. } => {}
_ => {}, _ => {}
} }
} }
} }
@@ -846,11 +895,8 @@ impl Dispatch<ZxdgOutputV1, WlOutput> for State {
_: &QueueHandle<Self>, _: &QueueHandle<Self>,
) { ) {
log::debug!("xdg-output - {event:?}"); log::debug!("xdg-output - {event:?}");
let output_info = match state let output_info = match state.output_info.iter_mut().find(|(o, _)| o == wl_output) {
.output_info Some((_, c)) => c,
.iter_mut()
.find(|(o, _)| o == wl_output) {
Some((_,c)) => c,
None => { None => {
let output_info = OutputInfo::new(); let output_info = OutputInfo::new();
state.output_info.push((wl_output.clone(), output_info)); state.output_info.push((wl_output.clone(), output_info));
@@ -861,16 +907,16 @@ impl Dispatch<ZxdgOutputV1, WlOutput> for State {
match event { match event {
zxdg_output_v1::Event::LogicalPosition { x, y } => { zxdg_output_v1::Event::LogicalPosition { x, y } => {
output_info.position = (x, y); output_info.position = (x, y);
}, }
zxdg_output_v1::Event::LogicalSize { width, height } => { zxdg_output_v1::Event::LogicalSize { width, height } => {
output_info.size = (width, height); output_info.size = (width, height);
}, }
zxdg_output_v1::Event::Done => {}, zxdg_output_v1::Event::Done => {}
zxdg_output_v1::Event::Name { name } => { zxdg_output_v1::Event::Name { name } => {
output_info.name = name; output_info.name = name;
}, }
zxdg_output_v1::Event::Description { .. } => {}, zxdg_output_v1::Event::Description { .. } => {}
_ => {}, _ => {}
} }
} }
} }

View File

@@ -1,20 +1,20 @@
use core::task::{Context, Poll};
use futures::Stream;
use std::io::Result; use std::io::Result;
use std::pin::Pin; use std::pin::Pin;
use futures::Stream;
use core::task::{Context, Poll};
use crate::{ use crate::{
client::{ClientHandle, ClientEvent}, client::{ClientEvent, ClientHandle},
event::Event, event::Event,
producer::EventProducer, producer::EventProducer,
}; };
pub struct WindowsProducer { } pub struct WindowsProducer {}
impl EventProducer for WindowsProducer { impl EventProducer for WindowsProducer {
fn notify(&mut self, _: ClientEvent) { } fn notify(&mut self, _: ClientEvent) {}
fn release(&mut self) { } fn release(&mut self) {}
} }
impl WindowsProducer { impl WindowsProducer {

View File

@@ -1,7 +1,6 @@
use std::io; use std::io;
use std::task::Poll; use std::task::Poll;
use futures_core::Stream; use futures_core::Stream;
use crate::event::Event; use crate::event::Event;
@@ -9,16 +8,16 @@ use crate::producer::EventProducer;
use crate::client::{ClientEvent, ClientHandle}; use crate::client::{ClientEvent, ClientHandle};
pub struct X11Producer { } pub struct X11Producer {}
impl X11Producer { impl X11Producer {
pub fn new() -> Self { pub fn new() -> Self {
Self { } Self {}
} }
} }
impl EventProducer for X11Producer { impl EventProducer for X11Producer {
fn notify(&mut self, _: ClientEvent) { } fn notify(&mut self, _: ClientEvent) {}
fn release(&mut self) {} fn release(&mut self) {}
} }
@@ -26,7 +25,10 @@ impl EventProducer for X11Producer {
impl Stream for X11Producer { impl Stream for X11Producer {
type Item = io::Result<(ClientHandle, Event)>; type Item = io::Result<(ClientHandle, Event)>;
fn poll_next(self: std::pin::Pin<&mut Self>, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Option<Self::Item>> { fn poll_next(
self: std::pin::Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<Self::Item>> {
Poll::Pending Poll::Pending
} }
} }

View File

@@ -1,6 +1,11 @@
use std::{net::{SocketAddr, IpAddr}, collections::HashSet, fmt::Display, time::Instant}; use std::{
collections::HashSet,
fmt::Display,
net::{IpAddr, SocketAddr},
time::Instant,
};
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Eq, Hash, PartialEq, Clone, Copy, Serialize, Deserialize)] #[derive(Debug, Eq, Hash, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub enum Position { pub enum Position {
@@ -29,12 +34,16 @@ impl Position {
impl Display for Position { impl Display for Position {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match self { write!(
Position::Left => "left", f,
Position::Right => "right", "{}",
Position::Top => "top", match self {
Position::Bottom => "bottom", Position::Left => "left",
}) Position::Right => "right",
Position::Top => "top",
Position::Bottom => "bottom",
}
)
} }
} }
@@ -82,9 +91,7 @@ pub struct ClientManager {
impl ClientManager { impl ClientManager {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self { clients: vec![] }
clients: vec![],
}
} }
/// add a new client to this manager /// add a new client to this manager
@@ -102,14 +109,17 @@ impl ClientManager {
let active_addr = None; let active_addr = None;
// map ip addresses to socket addresses // map ip addresses to socket addresses
let addrs = HashSet::from_iter( let addrs = HashSet::from_iter(addrs.into_iter().map(|ip| SocketAddr::new(ip, port)));
addrs
.into_iter()
.map(|ip| SocketAddr::new(ip, port))
);
// store the client // store the client
let client = Client { hostname, handle, active_addr, addrs, port, pos }; let client = Client {
hostname,
handle,
active_addr,
addrs,
port,
pos,
};
// client was never seen, nor pinged // client was never seen, nor pinged
let client_state = ClientState { let client_state = ClientState {
@@ -135,10 +145,12 @@ impl ClientManager {
// time this is likely faster than using a HashMap // time this is likely faster than using a HashMap
self.clients self.clients
.iter() .iter()
.position(|c| if let Some(c) = c { .position(|c| {
c.active && c.client.addrs.contains(&addr) if let Some(c) = c {
} else { c.active && c.client.addrs.contains(&addr)
false } else {
false
}
}) })
.map(|p| p as ClientHandle) .map(|p| p as ClientHandle)
} }
@@ -153,7 +165,8 @@ impl ClientManager {
fn free_id(&mut self) -> ClientHandle { fn free_id(&mut self) -> ClientHandle {
for i in 0..u32::MAX { for i in 0..u32::MAX {
if self.clients.get(i as usize).is_none() if self.clients.get(i as usize).is_none()
|| self.clients.get(i as usize).unwrap().is_none() { || self.clients.get(i as usize).unwrap().is_none()
{
return i; return i;
} }
} }
@@ -173,7 +186,7 @@ impl ClientManager {
pub fn enumerate(&self) -> Vec<(Client, bool)> { pub fn enumerate(&self) -> Vec<(Client, bool)> {
self.clients self.clients
.iter() .iter()
.filter_map(|s|s.as_ref()) .filter_map(|s| s.as_ref())
.map(|s| (s.client.clone(), s.active)) .map(|s| (s.client.clone(), s.active))
.collect() .collect()
} }

View File

@@ -1,11 +1,11 @@
use anyhow::Result; use anyhow::Result;
use clap::Parser;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashSet; use std::collections::HashSet;
use std::env;
use std::net::IpAddr; use std::net::IpAddr;
use std::{error::Error, fs}; use std::{error::Error, fs};
use std::env;
use toml; use toml;
use clap::Parser;
use crate::client::Position; use crate::client::Position;
@@ -74,15 +74,17 @@ impl Config {
pub fn new() -> Result<Self> { pub fn new() -> Result<Self> {
let args = CliArgs::parse(); let args = CliArgs::parse();
let config_file = "config.toml"; let config_file = "config.toml";
#[cfg(unix)] let config_path = { #[cfg(unix)]
let xdg_config_home = env::var("XDG_CONFIG_HOME") let config_path = {
.unwrap_or(format!("{}/.config", env::var("HOME")?)); let xdg_config_home =
env::var("XDG_CONFIG_HOME").unwrap_or(format!("{}/.config", env::var("HOME")?));
format!("{xdg_config_home}/lan-mouse/{config_file}") format!("{xdg_config_home}/lan-mouse/{config_file}")
}; };
#[cfg(not(unix))] let config_path = { #[cfg(not(unix))]
let app_data = env::var("LOCALAPPDATA") let config_path = {
.unwrap_or(format!("{}/.config", env::var("USERPROFILE")?)); let app_data =
env::var("LOCALAPPDATA").unwrap_or(format!("{}/.config", env::var("USERPROFILE")?));
format!("{app_data}\\lan-mouse\\{config_file}") format!("{app_data}\\lan-mouse\\{config_file}")
}; };
@@ -94,7 +96,7 @@ impl Config {
log::error!("{config_path}: {e}"); log::error!("{config_path}: {e}");
log::warn!("Continuing without config file ..."); log::warn!("Continuing without config file ...");
None None
}, }
Ok(c) => Some(c), Ok(c) => Some(c),
}; };
@@ -114,8 +116,8 @@ impl Config {
Some(s) => match s.as_str() { Some(s) => match s.as_str() {
"cli" => Frontend::Cli, "cli" => Frontend::Cli,
"gtk" => Frontend::Gtk, "gtk" => Frontend::Gtk,
_ => Frontend::Cli, _ => Frontend::Cli,
} },
}; };
let port = match args.port { let port = match args.port {
@@ -123,7 +125,7 @@ impl Config {
None => match &config_toml { None => match &config_toml {
Some(c) => c.port.unwrap_or(DEFAULT_PORT), Some(c) => c.port.unwrap_or(DEFAULT_PORT),
None => DEFAULT_PORT, None => DEFAULT_PORT,
} },
}; };
let mut clients: Vec<(Client, Position)> = vec![]; let mut clients: Vec<(Client, Position)> = vec![];
@@ -153,16 +155,19 @@ impl Config {
}) })
} }
pub fn get_clients(&self) -> Vec<(HashSet<IpAddr>, Option<String>, u16, Position)> { pub fn get_clients(&self) -> Vec<(HashSet<IpAddr>, Option<String>, u16, Position)> {
self.clients.iter().map(|(c,p)| { self.clients
let port = c.port.unwrap_or(DEFAULT_PORT); .iter()
let ips: HashSet<IpAddr> = if let Some(ips) = c.ips.as_ref() { .map(|(c, p)| {
HashSet::from_iter(ips.iter().cloned()) let port = c.port.unwrap_or(DEFAULT_PORT);
} else { let ips: HashSet<IpAddr> = if let Some(ips) = c.ips.as_ref() {
HashSet::new() HashSet::from_iter(ips.iter().cloned())
}; } else {
let host_name = c.host_name.clone(); HashSet::new()
(ips, host_name, port, *p) };
}).collect() let host_name = c.host_name.clone();
(ips, host_name, port, *p)
})
.collect()
} }
} }

View File

@@ -1,11 +1,15 @@
use std::future;
use async_trait::async_trait; use async_trait::async_trait;
use std::future;
#[cfg(unix)] #[cfg(unix)]
use std::env; use std::env;
use crate::{
backend::consumer,
client::{ClientEvent, ClientHandle},
event::Event,
};
use anyhow::Result; use anyhow::Result;
use crate::{backend::consumer, client::{ClientHandle, ClientEvent}, event::Event};
#[cfg(unix)] #[cfg(unix)]
#[derive(Debug)] #[derive(Debug)]
@@ -49,7 +53,9 @@ pub async fn create() -> Result<Box<dyn EventConsumer>> {
Backend::Libei Backend::Libei
} }
"KDE" => { "KDE" => {
log::info!("XDG_CURRENT_DESKTOP = KDE -> using xdg_desktop_portal backend"); log::info!(
"XDG_CURRENT_DESKTOP = KDE -> using xdg_desktop_portal backend"
);
Backend::RemoteDesktopPortal Backend::RemoteDesktopPortal
} }
"sway" => { "sway" => {
@@ -61,10 +67,12 @@ pub async fn create() -> Result<Box<dyn EventConsumer>> {
Backend::Wlroots Backend::Wlroots
} }
_ => { _ => {
log::warn!("unknown XDG_CURRENT_DESKTOP -> defaulting to wlroots backend"); log::warn!(
"unknown XDG_CURRENT_DESKTOP -> defaulting to wlroots backend"
);
Backend::Wlroots Backend::Wlroots
} }
} },
// default to wlroots backend for now // default to wlroots backend for now
_ => { _ => {
log::warn!("unknown XDG_CURRENT_DESKTOP -> defaulting to wlroots backend"); log::warn!("unknown XDG_CURRENT_DESKTOP -> defaulting to wlroots backend");
@@ -74,7 +82,9 @@ pub async fn create() -> Result<Box<dyn EventConsumer>> {
} }
_ => panic!("unknown XDG_SESSION_TYPE"), _ => panic!("unknown XDG_SESSION_TYPE"),
}, },
Err(_) => panic!("could not detect session type: XDG_SESSION_TYPE environment variable not set!"), Err(_) => {
panic!("could not detect session type: XDG_SESSION_TYPE environment variable not set!")
}
}; };
#[cfg(unix)] #[cfg(unix)]
@@ -84,24 +94,26 @@ pub async fn create() -> Result<Box<dyn EventConsumer>> {
panic!("feature libei not enabled"); panic!("feature libei not enabled");
#[cfg(feature = "libei")] #[cfg(feature = "libei")]
Ok(Box::new(consumer::libei::LibeiConsumer::new().await?)) Ok(Box::new(consumer::libei::LibeiConsumer::new().await?))
}, }
Backend::RemoteDesktopPortal => { Backend::RemoteDesktopPortal => {
#[cfg(not(feature = "xdg_desktop_portal"))] #[cfg(not(feature = "xdg_desktop_portal"))]
panic!("feature xdg_desktop_portal not enabled"); panic!("feature xdg_desktop_portal not enabled");
#[cfg(feature = "xdg_desktop_portal")] #[cfg(feature = "xdg_desktop_portal")]
Ok(Box::new(consumer::xdg_desktop_portal::DesktopPortalConsumer::new().await?)) Ok(Box::new(
}, consumer::xdg_desktop_portal::DesktopPortalConsumer::new().await?,
))
}
Backend::Wlroots => { Backend::Wlroots => {
#[cfg(not(feature = "wayland"))] #[cfg(not(feature = "wayland"))]
panic!("feature wayland not enabled"); panic!("feature wayland not enabled");
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
Ok(Box::new(consumer::wlroots::WlrootsConsumer::new()?)) Ok(Box::new(consumer::wlroots::WlrootsConsumer::new()?))
}, }
Backend::X11 => { Backend::X11 => {
#[cfg(not(feature = "x11"))] #[cfg(not(feature = "x11"))]
panic!("feature x11 not enabled"); panic!("feature x11 not enabled");
#[cfg(feature = "x11")] #[cfg(feature = "x11")]
Ok(Box::new(consumer::x11::X11Consumer::new())) Ok(Box::new(consumer::x11::X11Consumer::new()))
}, }
} }
} }

View File

@@ -1,4 +1,7 @@
use std::{error::Error, fmt::{self, Display}}; use std::{
error::Error,
fmt::{self, Display},
};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum PointerEvent { pub enum PointerEvent {
@@ -47,10 +50,22 @@ pub enum Event {
impl Display for PointerEvent { impl Display for PointerEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
PointerEvent::Motion { time: _ , relative_x, relative_y } => write!(f, "motion({relative_x},{relative_y})"), PointerEvent::Motion {
PointerEvent::Button { time: _ , button, state } => write!(f, "button({button}, {state})"), time: _,
PointerEvent::Axis { time: _, axis, value } => write!(f, "scroll({axis}, {value})"), relative_x,
PointerEvent::Frame { } => write!(f, "frame()"), relative_y,
} => write!(f, "motion({relative_x},{relative_y})"),
PointerEvent::Button {
time: _,
button,
state,
} => write!(f, "button({button}, {state})"),
PointerEvent::Axis {
time: _,
axis,
value,
} => write!(f, "scroll({axis}, {value})"),
PointerEvent::Frame {} => write!(f, "frame()"),
} }
} }
} }
@@ -58,8 +73,20 @@ impl Display for PointerEvent {
impl Display for KeyboardEvent { impl Display for KeyboardEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
KeyboardEvent::Key { time: _, key, state } => write!(f, "key({key}, {state})"), KeyboardEvent::Key {
KeyboardEvent::Modifiers { mods_depressed, mods_latched, mods_locked, group } => write!(f, "modifiers({mods_depressed},{mods_latched},{mods_locked},{group})"), time: _,
key,
state,
} => write!(f, "key({key}, {state})"),
KeyboardEvent::Modifiers {
mods_depressed,
mods_latched,
mods_locked,
group,
} => write!(
f,
"modifiers({mods_depressed},{mods_latched},{mods_locked},{group})"
),
} }
} }
} }

View File

@@ -1,25 +1,31 @@
use anyhow::{Result, anyhow}; use anyhow::{anyhow, Result};
use std::{str, io::ErrorKind, time::Duration, cmp::min}; use std::{cmp::min, io::ErrorKind, str, time::Duration};
#[cfg(unix)] #[cfg(unix)]
use std::{env, path::{Path, PathBuf}}; use std::{
env,
path::{Path, PathBuf},
};
use tokio::io::{AsyncReadExt, WriteHalf, AsyncWriteExt};
use tokio::io::ReadHalf; use tokio::io::ReadHalf;
use tokio::io::{AsyncReadExt, AsyncWriteExt, WriteHalf};
#[cfg(unix)]
use tokio::net::UnixStream;
#[cfg(unix)] #[cfg(unix)]
use tokio::net::UnixListener; use tokio::net::UnixListener;
#[cfg(unix)]
use tokio::net::UnixStream;
#[cfg(windows)]
use tokio::net::TcpStream;
#[cfg(windows)] #[cfg(windows)]
use tokio::net::TcpListener; use tokio::net::TcpListener;
#[cfg(windows)]
use tokio::net::TcpStream;
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
use crate::{client::{Position, ClientHandle, Client}, config::{Config, Frontend}}; use crate::{
client::{Client, ClientHandle, Position},
config::{Config, Frontend},
};
/// cli frontend /// cli frontend
pub mod cli; pub mod cli;
@@ -28,14 +34,17 @@ pub mod cli;
#[cfg(all(unix, feature = "gtk"))] #[cfg(all(unix, feature = "gtk"))]
pub mod gtk; pub mod gtk;
pub fn run_frontend(config: &Config) -> Result<()> { pub fn run_frontend(config: &Config) -> Result<()> {
match config.frontend { match config.frontend {
#[cfg(all(unix, feature = "gtk"))] #[cfg(all(unix, feature = "gtk"))]
Frontend::Gtk => { gtk::run(); } Frontend::Gtk => {
gtk::run();
}
#[cfg(any(not(feature = "gtk"), not(unix)))] #[cfg(any(not(feature = "gtk"), not(unix)))]
Frontend::Gtk => panic!("gtk frontend requested but feature not enabled!"), Frontend::Gtk => panic!("gtk frontend requested but feature not enabled!"),
Frontend::Cli => { cli::run()?; } Frontend::Cli => {
cli::run()?;
}
}; };
Ok(()) Ok(())
} }
@@ -54,7 +63,7 @@ pub fn wait_for_service() -> Result<std::os::unix::net::UnixStream> {
loop { loop {
use std::os::unix::net::UnixStream; use std::os::unix::net::UnixStream;
if let Ok(stream) = UnixStream::connect(&socket_path) { if let Ok(stream) = UnixStream::connect(&socket_path) {
break Ok(stream) break Ok(stream);
} }
// a signaling mechanism or inotify could be used to // a signaling mechanism or inotify could be used to
// improve this // improve this
@@ -68,7 +77,7 @@ pub fn wait_for_service() -> Result<std::net::TcpStream> {
loop { loop {
use std::net::TcpStream; use std::net::TcpStream;
if let Ok(stream) = TcpStream::connect("127.0.0.1:5252") { if let Ok(stream) = TcpStream::connect("127.0.0.1:5252") {
break Ok(stream) break Ok(stream);
} }
std::thread::sleep(*exponential_back_off(&mut duration)); std::thread::sleep(*exponential_back_off(&mut duration));
} }
@@ -146,7 +155,7 @@ impl FrontendListener {
Err(e) => { Err(e) => {
log::debug!("{socket_path:?}: {e} - removing left behind socket"); log::debug!("{socket_path:?}: {e} - removing left behind socket");
let _ = std::fs::remove_file(&socket_path); let _ = std::fs::remove_file(&socket_path);
}, }
} }
} }
let listener = match UnixListener::bind(&socket_path) { let listener = match UnixListener::bind(&socket_path) {
@@ -160,10 +169,10 @@ impl FrontendListener {
#[cfg(windows)] #[cfg(windows)]
let listener = match TcpListener::bind("127.0.0.1:5252").await { let listener = match TcpListener::bind("127.0.0.1:5252").await {
Ok(ls) => ls, Ok(ls) => ls,
// some other lan-mouse instance has bound the socket in the meantime // some other lan-mouse instance has bound the socket in the meantime
Err(e) if e.kind() == ErrorKind::AddrInUse => return None, Err(e) if e.kind() == ErrorKind::AddrInUse => return None,
Err(e) => return Some(Err(anyhow!("failed to bind lan-mouse-socket: {e}"))), Err(e) => return Some(Err(anyhow!("failed to bind lan-mouse-socket: {e}"))),
}; };
let adapter = Self { let adapter = Self {
@@ -194,7 +203,6 @@ impl FrontendListener {
Ok(rx) Ok(rx)
} }
pub(crate) async fn notify_all(&mut self, notify: FrontendNotify) -> Result<()> { pub(crate) async fn notify_all(&mut self, notify: FrontendNotify) -> Result<()> {
// encode event // encode event
let json = serde_json::to_string(&notify).unwrap(); let json = serde_json::to_string(&notify).unwrap();
@@ -207,7 +215,7 @@ impl FrontendListener {
// TODO do simultaneously // TODO do simultaneously
for tx in self.tx_streams.iter_mut() { for tx in self.tx_streams.iter_mut() {
// write len + payload // write len + payload
if let Err(_) = tx.write(&len).await { if let Err(_) = tx.write(&len).await {
keep.push(false); keep.push(false);
continue; continue;
} }
@@ -249,4 +257,3 @@ pub async fn read_event(stream: &mut ReadHalf<TcpStream>) -> Result<FrontendEven
stream.read_exact(&mut buf[..len as usize]).await?; stream.read_exact(&mut buf[..len as usize]).await?;
Ok(serde_json::from_slice(&buf[..len as usize])?) Ok(serde_json::from_slice(&buf[..len as usize])?)
} }

View File

@@ -1,12 +1,16 @@
use anyhow::{anyhow, Result, Context}; use anyhow::{anyhow, Context, Result};
use std::{thread, io::{Write, Read, ErrorKind}, str::SplitWhitespace};
#[cfg(windows)] #[cfg(windows)]
use std::net::SocketAddrV4; use std::net::SocketAddrV4;
use std::{
io::{ErrorKind, Read, Write},
str::SplitWhitespace,
thread,
};
#[cfg(unix)]
use std::os::unix::net::UnixStream;
#[cfg(windows)] #[cfg(windows)]
use std::net::TcpStream; use std::net::TcpStream;
#[cfg(unix)]
use std::os::unix::net::UnixStream;
use crate::{client::Position, config::DEFAULT_PORT}; use crate::{client::Position, config::DEFAULT_PORT};
@@ -31,40 +35,40 @@ pub fn run() -> Result<()> {
let reader = thread::Builder::new() let reader = thread::Builder::new()
.name("cli-frontend".to_string()) .name("cli-frontend".to_string())
.spawn(move || { .spawn(move || {
// all further prompts // all further prompts
prompt(); prompt();
loop { loop {
let mut buf = String::new(); let mut buf = String::new();
match std::io::stdin().read_line(&mut buf) { match std::io::stdin().read_line(&mut buf) {
Ok(0) => break, Ok(0) => break,
Ok(len) => { Ok(len) => {
if let Some(events) = parse_cmd(buf, len) { if let Some(events) = parse_cmd(buf, len) {
for event in events.iter() { for event in events.iter() {
let json = serde_json::to_string(&event).unwrap(); let json = serde_json::to_string(&event).unwrap();
let bytes = json.as_bytes(); let bytes = json.as_bytes();
let len = bytes.len().to_be_bytes(); let len = bytes.len().to_be_bytes();
if let Err(e) = tx.write(&len) { if let Err(e) = tx.write(&len) {
log::error!("error sending message: {e}"); log::error!("error sending message: {e}");
}; };
if let Err(e) = tx.write(bytes) { if let Err(e) = tx.write(bytes) {
log::error!("error sending message: {e}"); log::error!("error sending message: {e}");
}; };
if *event == FrontendEvent::Shutdown() { if *event == FrontendEvent::Shutdown() {
return; return;
}
} }
// prompt is printed after the server response is received
} else {
prompt();
} }
// prompt is printed after the server response is received }
} else { Err(e) => {
prompt(); log::error!("error reading from stdin: {e}");
break;
} }
} }
Err(e) => {
log::error!("error reading from stdin: {e}");
break
}
} }
} })?;
})?;
let writer = thread::Builder::new() let writer = thread::Builder::new()
.name("cli-frontend-notify".to_string()) .name("cli-frontend-notify".to_string())
@@ -93,35 +97,43 @@ pub fn run() -> Result<()> {
}; };
match notify { match notify {
FrontendNotify::NotifyClientCreate(client, host, port, pos) => { FrontendNotify::NotifyClientCreate(client, host, port, pos) => {
log::info!("new client ({client}): {}:{port} - {pos}", host.as_deref().unwrap_or("")); log::info!(
}, "new client ({client}): {}:{port} - {pos}",
host.as_deref().unwrap_or("")
);
}
FrontendNotify::NotifyClientUpdate(client, host, port, pos) => { FrontendNotify::NotifyClientUpdate(client, host, port, pos) => {
log::info!("client ({client}) updated: {}:{port} - {pos}", host.as_deref().unwrap_or("")); log::info!(
}, "client ({client}) updated: {}:{port} - {pos}",
host.as_deref().unwrap_or("")
);
}
FrontendNotify::NotifyClientDelete(client) => { FrontendNotify::NotifyClientDelete(client) => {
log::info!("client ({client}) deleted."); log::info!("client ({client}) deleted.");
}, }
FrontendNotify::NotifyError(e) => { FrontendNotify::NotifyError(e) => {
log::warn!("{e}"); log::warn!("{e}");
}, }
FrontendNotify::Enumerate(clients) => { FrontendNotify::Enumerate(clients) => {
for (client, active) in clients.into_iter() { for (client, active) in clients.into_iter() {
log::info!("client ({}) [{}]: active: {}, associated addresses: [{}]", log::info!(
"client ({}) [{}]: active: {}, associated addresses: [{}]",
client.handle, client.handle,
client.hostname.as_deref().unwrap_or(""), client.hostname.as_deref().unwrap_or(""),
if active { "yes" } else { "no" }, if active { "yes" } else { "no" },
client.addrs.into_iter().map(|a| a.to_string()) client
.collect::<Vec<String>>() .addrs
.join(", ") .into_iter()
.map(|a| a.to_string())
.collect::<Vec<String>>()
.join(", ")
); );
} }
},
FrontendNotify::NotifyPortChange(port, msg) => {
match msg {
Some(msg) => log::info!("could not change port: {msg}"),
None => log::info!("port changed: {port}"),
}
} }
FrontendNotify::NotifyPortChange(port, msg) => match msg {
Some(msg) => log::info!("could not change port: {msg}"),
None => log::info!("port changed: {port}"),
},
} }
prompt(); prompt();
} }
@@ -132,10 +144,10 @@ pub fn run() -> Result<()> {
let msg = match (e.downcast_ref::<&str>(), e.downcast_ref::<String>()) { let msg = match (e.downcast_ref::<&str>(), e.downcast_ref::<String>()) {
(Some(&s), _) => s, (Some(&s), _) => s,
(_, Some(s)) => s, (_, Some(s)) => s,
_ => "no panic info" _ => "no panic info",
}; };
log::error!("reader thread paniced: {msg}"); log::error!("reader thread paniced: {msg}");
}, }
} }
match writer.join() { match writer.join() {
Ok(_) => (), Ok(_) => (),
@@ -143,10 +155,10 @@ pub fn run() -> Result<()> {
let msg = match (e.downcast_ref::<&str>(), e.downcast_ref::<String>()) { let msg = match (e.downcast_ref::<&str>(), e.downcast_ref::<String>()) {
(Some(&s), _) => s, (Some(&s), _) => s,
(_, Some(s)) => s, (_, Some(s)) => s,
_ => "no panic info" _ => "no panic info",
}; };
log::error!("writer thread paniced: {msg}"); log::error!("writer thread paniced: {msg}");
}, }
} }
Ok(()) Ok(())
} }
@@ -158,7 +170,7 @@ fn prompt() {
fn parse_cmd(s: String, len: usize) -> Option<Vec<FrontendEvent>> { fn parse_cmd(s: String, len: usize) -> Option<Vec<FrontendEvent>> {
if len == 0 { if len == 0 {
return Some(vec![FrontendEvent::Shutdown()]) return Some(vec![FrontendEvent::Shutdown()]);
} }
let mut l = s.split_whitespace(); let mut l = s.split_whitespace();
let cmd = l.next()?; let cmd = l.next()?;
@@ -191,7 +203,7 @@ fn parse_cmd(s: String, len: usize) -> Option<Vec<FrontendEvent>> {
log::warn!("{e}"); log::warn!("{e}");
None None
} }
_ => None _ => None,
} }
} }
@@ -209,22 +221,34 @@ fn parse_connect(mut l: SplitWhitespace) -> Result<Vec<FrontendEvent>> {
} else { } else {
DEFAULT_PORT DEFAULT_PORT
}; };
Ok(vec![FrontendEvent::AddClient(Some(host), port, pos), FrontendEvent::Enumerate()]) Ok(vec![
FrontendEvent::AddClient(Some(host), port, pos),
FrontendEvent::Enumerate(),
])
} }
fn parse_disconnect(mut l: SplitWhitespace) -> Result<Vec<FrontendEvent>> { fn parse_disconnect(mut l: SplitWhitespace) -> Result<Vec<FrontendEvent>> {
let client = l.next().context("usage: disconnect <client_id>")?.parse()?; let client = l.next().context("usage: disconnect <client_id>")?.parse()?;
Ok(vec![FrontendEvent::DelClient(client), FrontendEvent::Enumerate()]) Ok(vec![
FrontendEvent::DelClient(client),
FrontendEvent::Enumerate(),
])
} }
fn parse_activate(mut l: SplitWhitespace) -> Result<Vec<FrontendEvent>> { fn parse_activate(mut l: SplitWhitespace) -> Result<Vec<FrontendEvent>> {
let client = l.next().context("usage: activate <client_id>")?.parse()?; let client = l.next().context("usage: activate <client_id>")?.parse()?;
Ok(vec![FrontendEvent::ActivateClient(client, true), FrontendEvent::Enumerate()]) Ok(vec![
FrontendEvent::ActivateClient(client, true),
FrontendEvent::Enumerate(),
])
} }
fn parse_deactivate(mut l: SplitWhitespace) -> Result<Vec<FrontendEvent>> { fn parse_deactivate(mut l: SplitWhitespace) -> Result<Vec<FrontendEvent>> {
let client = l.next().context("usage: deactivate <client_id>")?.parse()?; let client = l.next().context("usage: deactivate <client_id>")?.parse()?;
Ok(vec![FrontendEvent::ActivateClient(client, false), FrontendEvent::Enumerate()]) Ok(vec![
FrontendEvent::ActivateClient(client, false),
FrontendEvent::Enumerate(),
])
} }
fn parse_port(mut l: SplitWhitespace) -> Result<Vec<FrontendEvent>> { fn parse_port(mut l: SplitWhitespace) -> Result<Vec<FrontendEvent>> {

View File

@@ -1,13 +1,24 @@
mod window;
mod client_object; mod client_object;
mod client_row; mod client_row;
mod window;
use std::{io::{Read, ErrorKind}, env, process, str}; use std::{
env,
io::{ErrorKind, Read},
process, str,
};
use crate::{frontend::gtk::window::Window, config::DEFAULT_PORT}; use crate::{config::DEFAULT_PORT, frontend::gtk::window::Window};
use gtk::{prelude::*, IconTheme, gdk::Display, gio::{SimpleAction, SimpleActionGroup}, glib::{clone, MainContext, Priority}, CssProvider, subclass::prelude::ObjectSubclassIsExt};
use adw::Application; use adw::Application;
use gtk::{
gdk::Display,
gio::{SimpleAction, SimpleActionGroup},
glib::{clone, MainContext, Priority},
prelude::*,
subclass::prelude::ObjectSubclassIsExt,
CssProvider, IconTheme,
};
use gtk::{gio, glib, prelude::ApplicationExt}; use gtk::{gio, glib, prelude::ApplicationExt};
use self::client_object::ClientObject; use self::client_object::ClientObject;
@@ -22,8 +33,7 @@ pub fn run() -> glib::ExitCode {
} }
fn gtk_main() -> glib::ExitCode { fn gtk_main() -> glib::ExitCode {
gio::resources_register_include!("lan-mouse.gresource") gio::resources_register_include!("lan-mouse.gresource").expect("Failed to register resources.");
.expect("Failed to register resources.");
let app = Application::builder() let app = Application::builder()
.application_id("de.feschber.lan-mouse") .application_id("de.feschber.lan-mouse")
@@ -41,14 +51,15 @@ fn load_css() {
let provider = CssProvider::new(); let provider = CssProvider::new();
provider.load_from_resource("de/feschber/LanMouse/style.css"); provider.load_from_resource("de/feschber/LanMouse/style.css");
gtk::style_context_add_provider_for_display( gtk::style_context_add_provider_for_display(
&Display::default().expect("Could not connect to a display."), &Display::default().expect("Could not connect to a display."),
&provider, &provider,
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
); );
} }
fn load_icons() { fn load_icons() {
let icon_theme = IconTheme::for_display(&Display::default().expect("Could not connect to a display.")); let icon_theme =
IconTheme::for_display(&Display::default().expect("Could not connect to a display."));
icon_theme.add_resource_path("/de/feschber/LanMouse/icons"); icon_theme.add_resource_path("/de/feschber/LanMouse/icons");
} }
@@ -92,14 +103,13 @@ fn build_ui(app: &Application) {
}; };
// parse json // parse json
let json = str::from_utf8(&buf) let json = str::from_utf8(&buf).unwrap();
.unwrap();
match serde_json::from_str(json) { match serde_json::from_str(json) {
Ok(notify) => sender.send(notify).unwrap(), Ok(notify) => sender.send(notify).unwrap(),
Err(e) => log::error!("{e}"), Err(e) => log::error!("{e}"),
} }
} { } {
Ok(()) => {}, Ok(()) => {}
Err(e) => log::error!("{e}"), Err(e) => log::error!("{e}"),
} }
}); });
@@ -152,16 +162,12 @@ fn build_ui(app: &Application) {
} }
)); ));
let action_request_client_update = SimpleAction::new( let action_request_client_update =
"request-client-update", SimpleAction::new("request-client-update", Some(&u32::static_variant_type()));
Some(&u32::static_variant_type()),
);
// remove client // remove client
let action_client_delete = SimpleAction::new( let action_client_delete =
"request-client-delete", SimpleAction::new("request-client-delete", Some(&u32::static_variant_type()));
Some(&u32::static_variant_type()),
);
// update client state // update client state
action_request_client_update.connect_activate(clone!(@weak window => move |_action, param| { action_request_client_update.connect_activate(clone!(@weak window => move |_action, param| {

View File

@@ -1,7 +1,7 @@
mod imp; mod imp;
use gtk::glib::{self, Object};
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
use gtk::glib::{self, Object};
use crate::client::ClientHandle; use crate::client::ClientHandle;
@@ -10,7 +10,13 @@ glib::wrapper! {
} }
impl ClientObject { impl ClientObject {
pub fn new(handle: ClientHandle, hostname: Option<String>, port: u32, position: String, active: bool) -> Self { pub fn new(
handle: ClientHandle,
hostname: Option<String>,
port: u32,
position: String,
active: bool,
) -> Self {
Object::builder() Object::builder()
.property("handle", handle) .property("handle", handle)
.property("hostname", hostname) .property("hostname", hostname)

View File

@@ -16,8 +16,7 @@ glib::wrapper! {
impl ClientRow { impl ClientRow {
pub fn new(_client_object: &ClientObject) -> Self { pub fn new(_client_object: &ClientObject) -> Self {
Object::builder() Object::builder().build()
.build()
} }
pub fn bind(&self, client_object: &ClientObject) { pub fn bind(&self, client_object: &ClientObject) {
@@ -86,24 +85,19 @@ impl ClientRow {
.sync_create() .sync_create()
.build(); .build();
let position_binding = client_object let position_binding = client_object
.bind_property("position", &self.imp().position.get(), "selected") .bind_property("position", &self.imp().position.get(), "selected")
.transform_from(|_, v: u32| { .transform_from(|_, v: u32| match v {
match v { 1 => Some("right"),
1 => Some("right"), 2 => Some("top"),
2 => Some("top"), 3 => Some("bottom"),
3 => Some("bottom"), _ => Some("left"),
_ => Some("left"),
}
}) })
.transform_to(|_, v: String| { .transform_to(|_, v: String| match v.as_str() {
match v.as_str() { "right" => Some(1),
"right" => Some(1), "top" => Some(2u32),
"top" => Some(2u32), "bottom" => Some(3u32),
"bottom" => Some(3u32), _ => Some(0u32),
_ => Some(0u32),
}
}) })
.bidirectional() .bidirectional()
.sync_create() .sync_create()

View File

@@ -1,10 +1,10 @@
use std::cell::RefCell; use std::cell::RefCell;
use glib::{Binding, subclass::InitializingObject};
use adw::{prelude::*, ComboRow, ActionRow};
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
use adw::{prelude::*, ActionRow, ComboRow};
use glib::{subclass::InitializingObject, Binding};
use gtk::glib::clone; use gtk::glib::clone;
use gtk::{glib, CompositeTemplate, Switch, Button}; use gtk::{glib, Button, CompositeTemplate, Switch};
#[derive(CompositeTemplate, Default)] #[derive(CompositeTemplate, Default)]
#[template(resource = "/de/feschber/LanMouse/client_row.ui")] #[template(resource = "/de/feschber/LanMouse/client_row.ui")]
@@ -44,9 +44,10 @@ impl ObjectSubclass for ClientRow {
impl ObjectImpl for ClientRow { impl ObjectImpl for ClientRow {
fn constructed(&self) { fn constructed(&self) {
self.parent_constructed(); self.parent_constructed();
self.delete_button.connect_clicked(clone!(@weak self as row => move |button| { self.delete_button
row.handle_client_delete(button); .connect_clicked(clone!(@weak self as row => move |button| {
})); row.handle_client_delete(button);
}));
} }
} }
@@ -55,7 +56,9 @@ impl ClientRow {
#[template_callback] #[template_callback]
fn handle_client_set_state(&self, state: bool, switch: &Switch) -> bool { fn handle_client_set_state(&self, state: bool, switch: &Switch) -> bool {
let idx = self.obj().index() as u32; let idx = self.obj().index() as u32;
switch.activate_action("win.request-client-update", Some(&idx.to_variant())).unwrap(); switch
.activate_action("win.request-client-update", Some(&idx.to_variant()))
.unwrap();
switch.set_state(state); switch.set_state(state);
true // dont run default handler true // dont run default handler

View File

@@ -4,10 +4,14 @@ use std::io::Write;
use adw::prelude::*; use adw::prelude::*;
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
use gtk::{glib, gio, NoSelection};
use glib::{clone, Object}; use glib::{clone, Object};
use gtk::{gio, glib, NoSelection};
use crate::{frontend::{gtk::client_object::ClientObject, FrontendEvent}, client::{Position, ClientHandle}, config::DEFAULT_PORT}; use crate::{
client::{ClientHandle, Position},
config::DEFAULT_PORT,
frontend::{gtk::client_object::ClientObject, FrontendEvent},
};
use super::client_row::ClientRow; use super::client_row::ClientRow;
@@ -67,7 +71,14 @@ impl Window {
row row
} }
pub fn new_client(&self, handle: ClientHandle, hostname: Option<String>, port: u16, position: Position, active: bool) { 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); let client = ClientObject::new(handle, hostname, port as u32, position.to_string(), active);
self.clients().append(&client); self.clients().append(&client);
self.set_placeholder_visible(false); self.set_placeholder_visible(false);
@@ -122,7 +133,7 @@ impl Window {
"bottom" => Position::Bottom, "bottom" => Position::Bottom,
_ => { _ => {
log::error!("invalid position: {}", data.position); log::error!("invalid position: {}", data.position);
return return;
} }
}; };
let hostname = data.hostname; let hostname = data.hostname;
@@ -145,7 +156,7 @@ impl Window {
} }
} }
fn request(&self, event: FrontendEvent) { fn request(&self, event: FrontendEvent) {
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();

View File

@@ -1,9 +1,15 @@
use std::{cell::{Cell, RefCell}, os::unix::net::UnixStream}; use std::{
cell::{Cell, RefCell},
os::unix::net::UnixStream,
};
use glib::subclass::InitializingObject;
use adw::{ActionRow, ToastOverlay, prelude::{WidgetExt, EditableExt}};
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
use gtk::{glib, Button, CompositeTemplate, ListBox, gio, Entry}; use adw::{
prelude::{EditableExt, WidgetExt},
ActionRow, ToastOverlay,
};
use glib::subclass::InitializingObject;
use gtk::{gio, glib, Button, CompositeTemplate, Entry, ListBox};
use crate::config::DEFAULT_PORT; use crate::config::DEFAULT_PORT;
@@ -65,7 +71,8 @@ impl Window {
#[template_callback] #[template_callback]
fn handle_port_edit_cancel(&self) { fn handle_port_edit_cancel(&self) {
log::debug!("cancel port edit"); log::debug!("cancel port edit");
self.port_entry.set_text(self.port.get().to_string().as_str()); self.port_entry
.set_text(self.port.get().to_string().as_str());
self.port_edit_apply.set_visible(false); self.port_edit_apply.set_visible(false);
self.port_edit_cancel.set_visible(false); self.port_edit_cancel.set_visible(false);
} }
@@ -82,7 +89,6 @@ impl Window {
} }
} }
impl ObjectImpl for Window { impl ObjectImpl for Window {
fn constructed(&self) { fn constructed(&self) {
self.parent_constructed(); self.parent_constructed();

View File

@@ -2,9 +2,8 @@ use std::io::{self, Write};
use crate::client::Position; use crate::client::Position;
pub fn ask_confirmation(default: bool) -> Result<bool, io::Error> { pub fn ask_confirmation(default: bool) -> Result<bool, io::Error> {
eprint!("{}", if default {" [Y,n] "} else { " [y,N] "}); eprint!("{}", if default { " [Y,n] " } else { " [y,N] " });
io::stderr().flush()?; io::stderr().flush()?;
let answer = loop { let answer = loop {
let mut buffer = String::new(); let mut buffer = String::new();
@@ -18,7 +17,7 @@ pub fn ask_confirmation(default: bool) -> Result<bool, io::Error> {
_ => { _ => {
eprint!("Enter y for Yes or n for No: "); eprint!("Enter y for Yes or n for No: ");
io::stderr().flush()?; io::stderr().flush()?;
continue continue;
} }
} }
}; };
@@ -41,7 +40,7 @@ pub fn ask_position() -> Result<Position, io::Error> {
_ => { _ => {
eprint!("Invalid position: {answer} - enter top (t) | bottom (b) | left(l) | right(r): "); eprint!("Invalid position: {answer} - enter top (t) | bottom (b) | left(l) | right(r): ");
io::stderr().flush()?; io::stderr().flush()?;
continue continue;
} }
}; };
}; };

View File

@@ -1,17 +1,18 @@
use anyhow::Result; use anyhow::Result;
use std::process::{self, Command, Child}; use std::process::{self, Child, Command};
use env_logger::Env; use env_logger::Env;
use lan_mouse::{ use lan_mouse::{
consumer, producer, config::Config,
config::Config, server::Server, consumer,
frontend::{FrontendListener, self}, frontend::{self, FrontendListener},
producer,
server::Server,
}; };
use tokio::{task::LocalSet, join}; use tokio::{join, task::LocalSet};
pub fn main() { pub fn main() {
// init logging // init logging
let env = Env::default().filter_or("LAN_MOUSE_LOG_LEVEL", "info"); let env = Env::default().filter_or("LAN_MOUSE_LOG_LEVEL", "info");
env_logger::init_from_env(env); env_logger::init_from_env(env);
@@ -30,7 +31,6 @@ pub fn start_service() -> Result<Child> {
Ok(child) Ok(child)
} }
pub fn run() -> Result<()> { pub fn run() -> Result<()> {
// parse config file + cli args // parse config file + cli args
let config = Config::new()?; let config = Config::new()?;
@@ -46,7 +46,6 @@ pub fn run() -> Result<()> {
frontend::run_frontend(&config)?; frontend::run_frontend(&config)?;
} }
anyhow::Ok(()) anyhow::Ok(())
} }
@@ -66,16 +65,12 @@ fn run_service(config: &Config) -> Result<()> {
None => { None => {
// none means some other instance is already running // none means some other instance is already running
log::info!("service already running, exiting"); log::info!("service already running, exiting");
return anyhow::Ok(()) return anyhow::Ok(());
} }
,
}; };
// create event producer and consumer // create event producer and consumer
let (producer, consumer) = join!( let (producer, consumer) = join!(producer::create(), consumer::create(),);
producer::create(),
consumer::create(),
);
let (producer, consumer) = (producer?, consumer?); let (producer, consumer) = (producer?, consumer?);
// create server // create server

View File

@@ -3,8 +3,11 @@ use std::io;
use futures_core::Stream; use futures_core::Stream;
use crate::{client::{ClientHandle, ClientEvent}, event::Event};
use crate::backend::producer; use crate::backend::producer;
use crate::{
client::{ClientEvent, ClientHandle},
event::Event,
};
#[cfg(unix)] #[cfg(unix)]
use std::env; use std::env;
@@ -26,7 +29,7 @@ pub async fn create() -> Result<Box<dyn EventProducer>> {
"x11" => { "x11" => {
log::info!("XDG_SESSION_TYPE = x11 -> using X11 event producer"); log::info!("XDG_SESSION_TYPE = x11 -> using X11 event producer");
Backend::X11 Backend::X11
}, }
"wayland" => { "wayland" => {
log::info!("XDG_SESSION_TYPE = wayland -> using wayland event producer"); log::info!("XDG_SESSION_TYPE = wayland -> using wayland event producer");
match env::var("XDG_CURRENT_DESKTOP") { match env::var("XDG_CURRENT_DESKTOP") {
@@ -39,7 +42,7 @@ pub async fn create() -> Result<Box<dyn EventProducer>> {
log::info!("XDG_CURRENT_DESKTOP = {d} -> using layer_shell backend"); log::info!("XDG_CURRENT_DESKTOP = {d} -> using layer_shell backend");
Backend::LayerShell Backend::LayerShell
} }
} },
Err(_) => { Err(_) => {
log::warn!("XDG_CURRENT_DESKTOP not set! Assuming layer_shell support -> using layer_shell backend"); log::warn!("XDG_CURRENT_DESKTOP not set! Assuming layer_shell support -> using layer_shell backend");
Backend::LayerShell Backend::LayerShell
@@ -48,7 +51,9 @@ pub async fn create() -> Result<Box<dyn EventProducer>> {
} }
_ => panic!("unknown XDG_SESSION_TYPE"), _ => panic!("unknown XDG_SESSION_TYPE"),
}, },
Err(_) => panic!("could not detect session type: XDG_SESSION_TYPE environment variable not set!"), Err(_) => {
panic!("could not detect session type: XDG_SESSION_TYPE environment variable not set!")
}
}; };
#[cfg(unix)] #[cfg(unix)]
@@ -70,7 +75,7 @@ pub async fn create() -> Result<Box<dyn EventProducer>> {
panic!("feature libei not enabled"); panic!("feature libei not enabled");
#[cfg(feature = "libei")] #[cfg(feature = "libei")]
Ok(Box::new(producer::libei::LibeiProducer::new()?)) Ok(Box::new(producer::libei::LibeiProducer::new()?))
}, }
} }
} }

View File

@@ -1,7 +1,18 @@
use std::{error::Error, io::Result, collections::HashSet, time::{Duration, Instant}, net::IpAddr};
use log;
use tokio::{net::UdpSocket, io::ReadHalf, signal, sync::mpsc::{Sender, Receiver}};
use futures::stream::StreamExt; use futures::stream::StreamExt;
use log;
use std::{
collections::HashSet,
error::Error,
io::Result,
net::IpAddr,
time::{Duration, Instant},
};
use tokio::{
io::ReadHalf,
net::UdpSocket,
signal,
sync::mpsc::{Receiver, Sender},
};
#[cfg(unix)] #[cfg(unix)]
use tokio::net::UnixStream; use tokio::net::UnixStream;
@@ -9,10 +20,17 @@ use tokio::net::UnixStream;
#[cfg(windows)] #[cfg(windows)]
use tokio::net::TcpStream; use tokio::net::TcpStream;
use std::{net::SocketAddr, io::ErrorKind}; use std::{io::ErrorKind, net::SocketAddr};
use crate::{client::{ClientEvent, ClientManager, Position, ClientHandle}, consumer::EventConsumer, producer::EventProducer, frontend::{FrontendEvent, FrontendListener, FrontendNotify, self}, dns::{self, DnsResolver}, config::Config};
use crate::event::Event; use crate::event::Event;
use crate::{
client::{ClientEvent, ClientHandle, ClientManager, Position},
config::Config,
consumer::EventConsumer,
dns::{self, DnsResolver},
frontend::{self, FrontendEvent, FrontendListener, FrontendNotify},
producer::EventProducer,
};
/// keeps track of state to prevent a feedback loop /// keeps track of state to prevent a feedback loop
/// of continuously sending and receiving the same event. /// of continuously sending and receiving the same event.
@@ -41,7 +59,6 @@ impl Server {
consumer: Box<dyn EventConsumer>, consumer: Box<dyn EventConsumer>,
producer: Box<dyn EventProducer>, producer: Box<dyn EventProducer>,
) -> anyhow::Result<Self> { ) -> anyhow::Result<Self> {
// create dns resolver // create dns resolver
let resolver = dns::DnsResolver::new().await?; let resolver = dns::DnsResolver::new().await?;
@@ -65,7 +82,7 @@ impl Server {
}; };
// add clients from config // add clients from config
for (c,h,port,p) in config.get_clients().into_iter() { for (c, h, port, p) in config.get_clients().into_iter() {
server.add_client(h, c, port, p).await; server.add_client(h, c, port, p).await;
} }
@@ -73,7 +90,6 @@ impl Server {
} }
pub async fn run(&mut self) -> anyhow::Result<()> { pub async fn run(&mut self) -> anyhow::Result<()> {
loop { loop {
log::trace!("polling ..."); log::trace!("polling ...");
tokio::select! { tokio::select! {
@@ -134,7 +150,13 @@ impl Server {
Ok(()) Ok(())
} }
pub async fn add_client(&mut self, hostname: Option<String>, mut addr: HashSet<IpAddr>, port: u16, pos: Position) -> ClientHandle { pub async fn add_client(
&mut self,
hostname: Option<String>,
mut addr: HashSet<IpAddr>,
port: u16,
pos: Position,
) -> ClientHandle {
let ips = if let Some(hostname) = hostname.as_ref() { let ips = if let Some(hostname) = hostname.as_ref() {
match self.resolver.resolve(hostname.as_str()).await { match self.resolver.resolve(hostname.as_str()).await {
Ok(ips) => HashSet::from_iter(ips.iter().cloned()), Ok(ips) => HashSet::from_iter(ips.iter().cloned()),
@@ -147,8 +169,15 @@ impl Server {
HashSet::new() HashSet::new()
}; };
addr.extend(ips.iter()); addr.extend(ips.iter());
log::info!("adding client [{}]{} @ {:?}", pos, hostname.as_deref().unwrap_or(""), &ips); log::info!(
let client = self.client_manager.add_client(hostname.clone(), addr, port, pos); "adding client [{}]{} @ {:?}",
pos,
hostname.as_deref().unwrap_or(""),
&ips
);
let client = self
.client_manager
.add_client(hostname.clone(), addr, port, pos);
log::debug!("add_client {client}"); log::debug!("add_client {client}");
let notify = FrontendNotify::NotifyClientCreate(client, hostname, port, pos); let notify = FrontendNotify::NotifyClientCreate(client, hostname, port, pos);
if let Err(e) = self.frontend.notify_all(notify).await { if let Err(e) = self.frontend.notify_all(notify).await {
@@ -161,8 +190,11 @@ impl Server {
if let Some(state) = self.client_manager.get_mut(client) { if let Some(state) = self.client_manager.get_mut(client) {
state.active = active; state.active = active;
if state.active { if state.active {
self.producer.notify(ClientEvent::Create(client, state.client.pos)); self.producer
self.consumer.notify(ClientEvent::Create(client, state.client.pos)).await; .notify(ClientEvent::Create(client, state.client.pos));
self.consumer
.notify(ClientEvent::Create(client, state.client.pos))
.await;
} else { } else {
self.producer.notify(ClientEvent::Destroy(client)); self.producer.notify(ClientEvent::Destroy(client));
self.consumer.notify(ClientEvent::Destroy(client)).await; self.consumer.notify(ClientEvent::Destroy(client)).await;
@@ -173,7 +205,11 @@ impl Server {
pub async fn remove_client(&mut self, client: ClientHandle) -> Option<ClientHandle> { pub async fn remove_client(&mut self, client: ClientHandle) -> Option<ClientHandle> {
self.producer.notify(ClientEvent::Destroy(client)); self.producer.notify(ClientEvent::Destroy(client));
self.consumer.notify(ClientEvent::Destroy(client)).await; self.consumer.notify(ClientEvent::Destroy(client)).await;
if let Some(client) = self.client_manager.remove_client(client).map(|s| s.client.handle) { if let Some(client) = self
.client_manager
.remove_client(client)
.map(|s| s.client.handle)
{
let notify = FrontendNotify::NotifyClientDelete(client); let notify = FrontendNotify::NotifyClientDelete(client);
log::debug!("{notify:?}"); log::debug!("{notify:?}");
if let Err(e) = self.frontend.notify_all(notify).await { if let Err(e) = self.frontend.notify_all(notify).await {
@@ -194,7 +230,7 @@ impl Server {
) { ) {
// retrieve state // retrieve state
let Some(state) = self.client_manager.get_mut(client) else { let Some(state) = self.client_manager.get_mut(client) else {
return return;
}; };
// update pos // update pos
@@ -209,12 +245,20 @@ impl Server {
// update port // update port
if state.client.port != port { if state.client.port != port {
state.client.port = port; state.client.port = port;
state.client.addrs = state.client.addrs state.client.addrs = state
.client
.addrs
.iter() .iter()
.cloned() .cloned()
.map(|mut a| { a.set_port(port); a }) .map(|mut a| {
a.set_port(port);
a
})
.collect(); .collect();
state.client.active_addr.map(|a| { SocketAddr::new(a.ip(), port) }); state
.client
.active_addr
.map(|a| SocketAddr::new(a.ip(), port));
} }
// update hostname // update hostname
@@ -263,7 +307,7 @@ impl Server {
// set addr as new default for this client // set addr as new default for this client
state.client.active_addr = Some(addr); state.client.active_addr = Some(addr);
match (event, addr) { match (event, addr) {
(Event::Pong(), _) => {}, (Event::Pong(), _) => {}
(Event::Ping(), addr) => { (Event::Ping(), addr) => {
if let Err(e) = send_event(&self.socket, Event::Pong(), addr).await { if let Err(e) = send_event(&self.socket, Event::Pong(), addr).await {
log::error!("udp send: {}", e); log::error!("udp send: {}", e);
@@ -291,16 +335,17 @@ impl Server {
// let the server know we are still alive once every second // let the server know we are still alive once every second
let last_replied = state.last_replied; let last_replied = state.last_replied;
if last_replied.is_none() if last_replied.is_none()
|| last_replied.is_some() || last_replied.is_some()
&& last_replied.unwrap().elapsed() > Duration::from_secs(1) { && last_replied.unwrap().elapsed() > Duration::from_secs(1)
{
state.last_replied = Some(Instant::now()); state.last_replied = Some(Instant::now());
if let Err(e) = send_event(&self.socket, Event::Pong(), addr).await { if let Err(e) = send_event(&self.socket, Event::Pong(), addr).await {
log::error!("udp send: {}", e); log::error!("udp send: {}", e);
} }
} }
} }
} },
} }
} }
@@ -317,7 +362,7 @@ impl Server {
Some(state) => state, Some(state) => state,
None => { None => {
log::warn!("unknown client!"); log::warn!("unknown client!");
return return;
} }
}; };
// otherwise we should have an address to send to // otherwise we should have an address to send to
@@ -331,20 +376,21 @@ impl Server {
// if client last responded > 2 seconds ago // if client last responded > 2 seconds ago
// and we have not sent a ping since 500 milliseconds, // and we have not sent a ping since 500 milliseconds,
// send a ping // send a ping
if state.last_seen.is_some() if state.last_seen.is_some() && state.last_seen.unwrap().elapsed() < Duration::from_secs(2)
&& state.last_seen.unwrap().elapsed() < Duration::from_secs(2) { {
return return;
} }
// client last seen > 500ms ago // client last seen > 500ms ago
if state.last_ping.is_some() if state.last_ping.is_some()
&& state.last_ping.unwrap().elapsed() < Duration::from_millis(500) { && state.last_ping.unwrap().elapsed() < Duration::from_millis(500)
return {
return;
} }
// release mouse if client didnt respond to the first ping // release mouse if client didnt respond to the first ping
if state.last_ping.is_some() if state.last_ping.is_some() && state.last_ping.unwrap().elapsed() < Duration::from_secs(1)
&& state.last_ping.unwrap().elapsed() < Duration::from_secs(1) { {
should_release = true; should_release = true;
} }
@@ -414,12 +460,20 @@ impl Server {
async fn handle_frontend_event(&mut self, event: FrontendEvent) -> bool { async fn handle_frontend_event(&mut self, event: FrontendEvent) -> bool {
log::debug!("frontend: {event:?}"); log::debug!("frontend: {event:?}");
match event { match event {
FrontendEvent::AddClient(hostname, port, pos) => { self.add_client(hostname, HashSet::new(), port, pos).await; }, FrontendEvent::AddClient(hostname, port, pos) => {
FrontendEvent::ActivateClient(client, active) => self.activate_client(client, active).await, self.add_client(hostname, HashSet::new(), port, pos).await;
}
FrontendEvent::ActivateClient(client, active) => {
self.activate_client(client, active).await
}
FrontendEvent::ChangePort(port) => { FrontendEvent::ChangePort(port) => {
let current_port = self.socket.local_addr().unwrap().port(); let current_port = self.socket.local_addr().unwrap().port();
if current_port == port { if current_port == port {
if let Err(e) = self.frontend.notify_all(FrontendNotify::NotifyPortChange(port, None)).await { if let Err(e) = self
.frontend
.notify_all(FrontendNotify::NotifyPortChange(port, None))
.await
{
log::warn!("error notifying frontend: {e}"); log::warn!("error notifying frontend: {e}");
} }
return false; return false;
@@ -428,39 +482,60 @@ impl Server {
match UdpSocket::bind(listen_addr).await { match UdpSocket::bind(listen_addr).await {
Ok(socket) => { Ok(socket) => {
self.socket = socket; self.socket = socket;
if let Err(e) = self.frontend.notify_all(FrontendNotify::NotifyPortChange(port, None)).await { if let Err(e) = self
.frontend
.notify_all(FrontendNotify::NotifyPortChange(port, None))
.await
{
log::warn!("error notifying frontend: {e}"); log::warn!("error notifying frontend: {e}");
} }
}, }
Err(e) => { Err(e) => {
log::warn!("could not change port: {e}"); log::warn!("could not change port: {e}");
let port = self.socket.local_addr().unwrap().port(); 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 { 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}"); log::error!("error notifying frontend: {e}");
} }
} }
} }
}, }
FrontendEvent::DelClient(client) => { self.remove_client(client).await; }, FrontendEvent::DelClient(client) => {
self.remove_client(client).await;
}
FrontendEvent::Enumerate() => self.enumerate().await, FrontendEvent::Enumerate() => self.enumerate().await,
FrontendEvent::Shutdown() => { FrontendEvent::Shutdown() => {
log::info!("terminating gracefully..."); log::info!("terminating gracefully...");
return true; return true;
}, }
FrontendEvent::UpdateClient(client, hostname, port, pos) => self.update_client(client, hostname, port, pos).await, FrontendEvent::UpdateClient(client, hostname, port, pos) => {
self.update_client(client, hostname, port, pos).await
}
} }
false false
} }
async fn enumerate(&mut self) { async fn enumerate(&mut self) {
let clients = self.client_manager.enumerate(); let clients = self.client_manager.enumerate();
if let Err(e) = self.frontend.notify_all(FrontendNotify::Enumerate(clients)).await { if let Err(e) = self
.frontend
.notify_all(FrontendNotify::Enumerate(clients))
.await
{
log::error!("error notifying frontend: {e}"); log::error!("error notifying frontend: {e}");
} }
} }
} }
async fn receive_event(socket: &UdpSocket) -> std::result::Result<(Event, SocketAddr), Box<dyn Error>> { async fn receive_event(
socket: &UdpSocket,
) -> std::result::Result<(Event, SocketAddr), Box<dyn Error>> {
log::trace!("receive_event"); log::trace!("receive_event");
let mut buf = vec![0u8; 22]; let mut buf = vec![0u8; 22];
match socket.recv_from(&mut buf).await { match socket.recv_from(&mut buf).await {
@@ -469,7 +544,6 @@ async fn receive_event(socket: &UdpSocket) -> std::result::Result<(Event, Socket
} }
} }
async fn send_event(sock: &UdpSocket, e: Event, addr: SocketAddr) -> Result<usize> { async fn send_event(sock: &UdpSocket, e: Event, addr: SocketAddr) -> Result<usize> {
log::trace!("{:20} ------>->->-> {addr}", e.to_string()); log::trace!("{:20} ------>->->-> {addr}", e.to_string());
let data: Vec<u8> = (&e).into(); let data: Vec<u8> = (&e).into();
@@ -477,4 +551,3 @@ async fn send_event(sock: &UdpSocket, e: Event, addr: SocketAddr) -> Result<usiz
// It may be better to set the socket to non-blocking and only send when ready. // It may be better to set the socket to non-blocking and only send when ready.
sock.send_to(&data[..], addr).await sock.send_to(&data[..], addr).await
} }