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)]
pub mod windows;
#[cfg(all(unix, feature="x11"))]
#[cfg(all(unix, feature = "x11"))]
pub mod x11;
#[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 futures::StreamExt;
use ashpd::desktop::remote_desktop::{RemoteDesktop, DeviceType};
use ashpd::desktop::remote_desktop::{DeviceType, RemoteDesktop};
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 {
handshake: bool,
@@ -32,10 +48,17 @@ async fn get_ei_fd() -> Result<RawFd, ashpd::Error> {
let session = proxy.create_session().await?;
// I HATE EVERYTHING, THIS TOOK 8 HOURS OF DEBUGGING
proxy.select_devices(&session,
DeviceType::Pointer | DeviceType::Keyboard |DeviceType::Touchscreen).await?;
proxy
.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
}
@@ -59,7 +82,8 @@ impl LibeiConsumer {
let events = EiEventStream::new(context.clone())?;
return Ok(Self {
handshake: false,
context, events,
context,
events,
pointer: None,
button: None,
scroll: None,
@@ -72,32 +96,59 @@ impl LibeiConsumer {
capability_mask: 0,
sequence: 0,
serial: 0,
})
});
}
}
#[async_trait]
impl EventConsumer for LibeiConsumer {
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 {
Event::Pointer(p) => match p {
crate::event::PointerEvent::Motion { time:_, relative_x, relative_y } => {
if !self.has_pointer { return }
crate::event::PointerEvent::Motion {
time: _,
relative_x,
relative_y,
} => {
if !self.has_pointer {
return;
}
if let Some((d, p)) = self.pointer.as_mut() {
p.motion_relative(relative_x as f32, relative_y as f32);
d.frame(self.serial, now);
}
},
crate::event::PointerEvent::Button { time: _, button, state } => {
if !self.has_button { return }
}
crate::event::PointerEvent::Button {
time: _,
button,
state,
} => {
if !self.has_button {
return;
}
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);
}
},
crate::event::PointerEvent::Axis { time: _, axis, value } => {
if !self.has_scroll { return }
}
crate::event::PointerEvent::Axis {
time: _,
axis,
value,
} => {
if !self.has_scroll {
return;
}
if let Some((d, s)) = self.scroll.as_mut() {
match axis {
0 => s.scroll(0., value as f32),
@@ -105,18 +156,30 @@ impl EventConsumer for LibeiConsumer {
}
d.frame(self.serial, now);
}
},
crate::event::PointerEvent::Frame { } => {},
}
crate::event::PointerEvent::Frame {} => {}
},
Event::Keyboard(k) => match k {
crate::event::KeyboardEvent::Key { time: _, key, state } => {
if !self.has_keyboard { return }
crate::event::KeyboardEvent::Key {
time: _,
key,
state,
} => {
if !self.has_keyboard {
return;
}
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);
}
},
crate::event::KeyboardEvent::Modifiers { .. } => { },
}
crate::event::KeyboardEvent::Modifiers { .. } => {}
},
_ => {}
}
@@ -130,13 +193,17 @@ impl EventConsumer for LibeiConsumer {
};
let event = match event {
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}")),
};
match event {
ei::Event::Handshake(handshake, request) => match request {
ei::handshake::Event::HandshakeVersion { version } => {
if self.handshake { return Ok(()) }
if self.handshake {
return Ok(());
}
log::info!("libei version {}", version);
// sender means we are sending events _to_ the eis server
handshake.handshake_version(version); // FIXME
@@ -163,8 +230,8 @@ impl EventConsumer for LibeiConsumer {
connection.sync(1);
self.serial = serial;
}
_ => unreachable!()
}
_ => unreachable!(),
},
ei::Event::Connection(_connection, request) => match request {
ei::connection::Event::Seat { seat } => {
log::debug!("connected to seat: {seat:?}");
@@ -172,20 +239,45 @@ impl EventConsumer for LibeiConsumer {
ei::connection::Event::Ping { ping } => {
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}")
}
ei::connection::Event::InvalidObject { last_serial, invalid_id } => {
return Err(anyhow!("invalid object: id: {invalid_id}, serial: {last_serial}"));
ei::connection::Event::InvalidObject {
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::device::Event::Destroyed { serial } => { log::debug!("device destroyed: {device:?} - serial: {serial}") },
ei::device::Event::Name { name } => {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::Destroyed { serial } => {
log::debug!("device destroyed: {device:?} - serial: {serial}")
}
ei::device::Event::Name { name } => {
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 } => {
log::debug!("device interface: {object:?}");
if object.interface().eq("ei_pointer") {
@@ -204,31 +296,31 @@ impl EventConsumer for LibeiConsumer {
}
ei::device::Event::Done => {
log::debug!("device: done {device:?}");
},
}
ei::device::Event::Resumed { serial } => {
self.serial = serial;
device.start_emulating(serial, self.sequence);
self.sequence += 1;
log::debug!("resumed: {device:?}");
if let Some((d,_)) = &mut self.pointer {
if let Some((d, _)) = &mut self.pointer {
if d == &device {
log::debug!("pointer resumed {serial}");
self.has_pointer = true;
}
}
if let Some((d,_)) = &mut self.button {
if let Some((d, _)) = &mut self.button {
if d == &device {
log::debug!("button resumed {serial}");
self.has_button = true;
}
}
if let Some((d,_)) = &mut self.scroll {
if let Some((d, _)) = &mut self.scroll {
if d == &device {
log::debug!("scroll resumed {serial}");
self.has_scroll = true;
}
}
if let Some((d,_)) = &mut self.keyboard {
if let Some((d, _)) = &mut self.keyboard {
if d == &device {
log::debug!("keyboard resumed {serial}");
self.has_keyboard = true;
@@ -239,38 +331,44 @@ impl EventConsumer for LibeiConsumer {
self.has_pointer = false;
self.has_button = false;
self.serial = serial;
},
ei::device::Event::StartEmulating { serial, sequence } => log::debug!("start emulating {serial}, {sequence}"),
ei::device::Event::StopEmulating { serial } => log::debug!("stop emulating {serial}"),
}
ei::device::Event::StartEmulating { serial, sequence } => {
log::debug!("start emulating {serial}, {sequence}")
}
ei::device::Event::StopEmulating { serial } => {
log::debug!("stop emulating {serial}")
}
ei::device::Event::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:?}"),
}
},
ei::Event::Seat(seat, request) => match request {
ei::seat::Event::Destroyed { serial } => {
self.serial = serial;
log::debug!("seat destroyed: {seat:?}");
},
}
ei::seat::Event::Name { name } => {
log::debug!("seat name: {name}");
},
}
ei::seat::Event::Capability { mask, interface } => {
log::debug!("seat capabilities: {mask}, interface: {interface:?}");
self.capabilities.insert(interface, mask);
self.capability_mask |= mask;
},
}
ei::seat::Event::Done => {
log::debug!("seat done");
log::debug!("binding capabilities: {}", self.capability_mask);
seat.bind(self.capability_mask);
},
}
ei::seat::Event::Device { device } => {
log::debug!("seat: new device - {device:?}");
},
}
_ => todo!(),
}
},
e => log::debug!("unhandled event: {e:?}"),
}
self.context.flush()?;
@@ -281,4 +379,3 @@ impl EventConsumer for LibeiConsumer {
async fn destroy(&mut self) {}
}

View File

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

View File

@@ -1,17 +1,17 @@
use async_trait::async_trait;
use wayland_client::WEnum;
use wayland_client::backend::WaylandError;
use crate::client::{ClientHandle, ClientEvent};
use crate::client::{ClientEvent, ClientHandle};
use crate::consumer::EventConsumer;
use async_trait::async_trait;
use std::collections::HashMap;
use std::io;
use std::os::fd::OwnedFd;
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::protocol::wl_pointer::{Axis, ButtonState};
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_protocols_wlr::virtual_pointer::v1::client::{
zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1 as VpManager,
@@ -104,7 +104,10 @@ impl WlrootsConsumer {
queue,
};
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 mmap = unsafe { MmapOptions::new().map_copy(fd).unwrap() };
@@ -153,8 +156,10 @@ impl EventConsumer for WlrootsConsumer {
* will overwhelm the output buffer and leave the
* wayland connection in a broken state
*/
log::warn!("can't keep up, discarding event: ({client_handle}) - {event:?}");
return
log::warn!(
"can't keep up, discarding event: ({client_handle}) - {event:?}"
);
return;
}
}
}
@@ -166,13 +171,13 @@ impl EventConsumer for WlrootsConsumer {
}
Err(WaylandError::Io(e)) => {
log::error!("{e}")
},
}
Err(WaylandError::Protocol(e)) => {
panic!("wayland protocol violation: {e}")
}
Ok(()) => {
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 {
Wlroots { pointer: Vp, keyboard: Vk },
Kde { fake_input: OrgKdeKwinFakeInput },
}
impl VirtualInput {
fn consume_event(&self, event: Event) -> Result<(),()> {
fn consume_event(&self, event: Event) -> Result<(), ()> {
match event {
Event::Pointer(e) => {
match e {
@@ -263,9 +267,9 @@ impl VirtualInput {
// insert a frame event after each mouse event
pointer.frame();
}
_ => {},
_ => {}
}
},
}
Event::Keyboard(e) => match e {
KeyboardEvent::Key { time, key, state } => match self {
VirtualInput::Wlroots {
@@ -293,7 +297,7 @@ impl VirtualInput {
VirtualInput::Kde { fake_input: _ } => {}
},
},
_ => {},
_ => {}
}
Ok(())
}
@@ -330,7 +334,7 @@ impl Dispatch<WlKeyboard, ()> for State {
wl_keyboard::Event::Keymap { 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 std::ptr;
use x11::{xlib, xtest};
use crate::{
client::ClientHandle,
event::Event, consumer::EventConsumer,
};
use crate::{client::ClientHandle, consumer::EventConsumer, event::Event};
pub struct X11Consumer {
display: *mut xlib::Display,
@@ -60,4 +57,3 @@ impl EventConsumer for X11Consumer {
async fn destroy(&mut self) {}
}

View File

@@ -1,6 +1,12 @@
use async_trait::async_trait;
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;
@@ -32,55 +38,86 @@ impl<'a> EventConsumer for DesktopPortalConsumer<'a> {
match event {
crate::event::Event::Pointer(p) => {
match p {
crate::event::PointerEvent::Motion { time: _, relative_x, relative_y } => {
if let Err(e) = self.proxy.notify_pointer_motion(&self.session, relative_x, relative_y).await {
crate::event::PointerEvent::Motion {
time: _,
relative_x,
relative_y,
} => {
if let Err(e) = self
.proxy
.notify_pointer_motion(&self.session, relative_x, relative_y)
.await
{
log::warn!("{e}");
}
},
crate::event::PointerEvent::Button { time: _, button, state } => {
}
crate::event::PointerEvent::Button {
time: _,
button,
state,
} => {
let state = match state {
0 => KeyState::Released,
_ => 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}");
}
},
crate::event::PointerEvent::Axis { time: _, axis, value } => {
}
crate::event::PointerEvent::Axis {
time: _,
axis,
value,
} => {
let axis = match axis {
0 => Axis::Vertical,
_ => Axis::Horizontal,
};
// 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}");
}
},
crate::event::PointerEvent::Frame { } => {},
}
crate::event::PointerEvent::Frame {} => {}
}
},
}
crate::event::Event::Keyboard(k) => {
match k {
crate::event::KeyboardEvent::Key { time: _, key, state } => {
crate::event::KeyboardEvent::Key {
time: _,
key,
state,
} => {
let state = match state {
0 => KeyState::Released,
_ => 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}");
}
},
}
crate::event::KeyboardEvent::Modifiers { .. } => {
// ignore
},
}
}
},
_ => {},
}
_ => {}
}
}
async fn notify(&mut self, _client: crate::client::ClientEvent) { }
async fn notify(&mut self, _client: crate::client::ClientEvent) {}
async fn destroy(&mut self) {
log::debug!("closing remote desktop session");

View File

@@ -3,28 +3,29 @@ use std::{io, task::Poll};
use futures_core::Stream;
use crate::{producer::EventProducer, event::Event, client::ClientHandle};
use crate::{client::ClientHandle, event::Event, producer::EventProducer};
pub struct LibeiProducer {}
impl LibeiProducer {
pub fn new() -> Result<Self> {
Ok(Self { })
Ok(Self {})
}
}
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 {
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
}
}

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

View File

@@ -1,7 +1,6 @@
use std::io;
use std::task::Poll;
use futures_core::Stream;
use crate::event::Event;
@@ -9,16 +8,16 @@ use crate::producer::EventProducer;
use crate::client::{ClientEvent, ClientHandle};
pub struct X11Producer { }
pub struct X11Producer {}
impl X11Producer {
pub fn new() -> Self {
Self { }
Self {}
}
}
impl EventProducer for X11Producer {
fn notify(&mut self, _: ClientEvent) { }
fn notify(&mut self, _: ClientEvent) {}
fn release(&mut self) {}
}
@@ -26,7 +25,10 @@ impl EventProducer for X11Producer {
impl Stream for X11Producer {
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
}
}

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)]
pub enum Position {
@@ -29,12 +34,16 @@ impl Position {
impl Display for Position {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match self {
Position::Left => "left",
Position::Right => "right",
Position::Top => "top",
Position::Bottom => "bottom",
})
write!(
f,
"{}",
match self {
Position::Left => "left",
Position::Right => "right",
Position::Top => "top",
Position::Bottom => "bottom",
}
)
}
}
@@ -82,9 +91,7 @@ pub struct ClientManager {
impl ClientManager {
pub fn new() -> Self {
Self {
clients: vec![],
}
Self { clients: vec![] }
}
/// add a new client to this manager
@@ -102,14 +109,17 @@ impl ClientManager {
let active_addr = None;
// map ip addresses to socket addresses
let addrs = HashSet::from_iter(
addrs
.into_iter()
.map(|ip| SocketAddr::new(ip, port))
);
let addrs = HashSet::from_iter(addrs.into_iter().map(|ip| SocketAddr::new(ip, port)));
// 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
let client_state = ClientState {
@@ -135,10 +145,12 @@ impl ClientManager {
// time this is likely faster than using a HashMap
self.clients
.iter()
.position(|c| if let Some(c) = c {
c.active && c.client.addrs.contains(&addr)
} else {
false
.position(|c| {
if let Some(c) = c {
c.active && c.client.addrs.contains(&addr)
} else {
false
}
})
.map(|p| p as ClientHandle)
}
@@ -153,7 +165,8 @@ impl ClientManager {
fn free_id(&mut self) -> ClientHandle {
for i in 0..u32::MAX {
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;
}
}
@@ -173,7 +186,7 @@ impl ClientManager {
pub fn enumerate(&self) -> Vec<(Client, bool)> {
self.clients
.iter()
.filter_map(|s|s.as_ref())
.filter_map(|s| s.as_ref())
.map(|s| (s.client.clone(), s.active))
.collect()
}

View File

@@ -1,11 +1,11 @@
use anyhow::Result;
use clap::Parser;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::env;
use std::net::IpAddr;
use std::{error::Error, fs};
use std::env;
use toml;
use clap::Parser;
use crate::client::Position;
@@ -74,15 +74,17 @@ impl Config {
pub fn new() -> Result<Self> {
let args = CliArgs::parse();
let config_file = "config.toml";
#[cfg(unix)] let config_path = {
let xdg_config_home = env::var("XDG_CONFIG_HOME")
.unwrap_or(format!("{}/.config", env::var("HOME")?));
#[cfg(unix)]
let config_path = {
let xdg_config_home =
env::var("XDG_CONFIG_HOME").unwrap_or(format!("{}/.config", env::var("HOME")?));
format!("{xdg_config_home}/lan-mouse/{config_file}")
};
#[cfg(not(unix))] let config_path = {
let app_data = env::var("LOCALAPPDATA")
.unwrap_or(format!("{}/.config", env::var("USERPROFILE")?));
#[cfg(not(unix))]
let config_path = {
let app_data =
env::var("LOCALAPPDATA").unwrap_or(format!("{}/.config", env::var("USERPROFILE")?));
format!("{app_data}\\lan-mouse\\{config_file}")
};
@@ -94,7 +96,7 @@ impl Config {
log::error!("{config_path}: {e}");
log::warn!("Continuing without config file ...");
None
},
}
Ok(c) => Some(c),
};
@@ -114,8 +116,8 @@ impl Config {
Some(s) => match s.as_str() {
"cli" => Frontend::Cli,
"gtk" => Frontend::Gtk,
_ => Frontend::Cli,
}
_ => Frontend::Cli,
},
};
let port = match args.port {
@@ -123,7 +125,7 @@ impl Config {
None => match &config_toml {
Some(c) => c.port.unwrap_or(DEFAULT_PORT),
None => DEFAULT_PORT,
}
},
};
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)> {
self.clients.iter().map(|(c,p)| {
let port = c.port.unwrap_or(DEFAULT_PORT);
let ips: HashSet<IpAddr> = if let Some(ips) = c.ips.as_ref() {
HashSet::from_iter(ips.iter().cloned())
} else {
HashSet::new()
};
let host_name = c.host_name.clone();
(ips, host_name, port, *p)
}).collect()
pub fn get_clients(&self) -> Vec<(HashSet<IpAddr>, Option<String>, u16, Position)> {
self.clients
.iter()
.map(|(c, p)| {
let port = c.port.unwrap_or(DEFAULT_PORT);
let ips: HashSet<IpAddr> = if let Some(ips) = c.ips.as_ref() {
HashSet::from_iter(ips.iter().cloned())
} else {
HashSet::new()
};
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 std::future;
#[cfg(unix)]
use std::env;
use crate::{
backend::consumer,
client::{ClientEvent, ClientHandle},
event::Event,
};
use anyhow::Result;
use crate::{backend::consumer, client::{ClientHandle, ClientEvent}, event::Event};
#[cfg(unix)]
#[derive(Debug)]
@@ -49,7 +53,9 @@ pub async fn create() -> Result<Box<dyn EventConsumer>> {
Backend::Libei
}
"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
}
"sway" => {
@@ -61,10 +67,12 @@ pub async fn create() -> Result<Box<dyn EventConsumer>> {
Backend::Wlroots
}
_ => {
log::warn!("unknown XDG_CURRENT_DESKTOP -> defaulting to wlroots backend");
log::warn!(
"unknown XDG_CURRENT_DESKTOP -> defaulting to wlroots backend"
);
Backend::Wlroots
}
}
},
// default to wlroots backend for now
_ => {
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"),
},
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)]
@@ -84,24 +94,26 @@ pub async fn create() -> Result<Box<dyn EventConsumer>> {
panic!("feature libei not enabled");
#[cfg(feature = "libei")]
Ok(Box::new(consumer::libei::LibeiConsumer::new().await?))
},
}
Backend::RemoteDesktopPortal => {
#[cfg(not(feature = "xdg_desktop_portal"))]
panic!("feature xdg_desktop_portal not enabled");
#[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 => {
#[cfg(not(feature = "wayland"))]
panic!("feature wayland not enabled");
#[cfg(feature = "wayland")]
Ok(Box::new(consumer::wlroots::WlrootsConsumer::new()?))
},
}
Backend::X11 => {
#[cfg(not(feature = "x11"))]
panic!("feature x11 not enabled");
#[cfg(feature = "x11")]
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)]
pub enum PointerEvent {
@@ -47,10 +50,22 @@ pub enum Event {
impl Display for PointerEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PointerEvent::Motion { time: _ , relative_x, 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()"),
PointerEvent::Motion {
time: _,
relative_x,
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 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
KeyboardEvent::Key { 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})"),
KeyboardEvent::Key {
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 std::{str, io::ErrorKind, time::Duration, cmp::min};
use anyhow::{anyhow, Result};
use std::{cmp::min, io::ErrorKind, str, time::Duration};
#[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::{AsyncReadExt, AsyncWriteExt, WriteHalf};
#[cfg(unix)]
use tokio::net::UnixStream;
#[cfg(unix)]
use tokio::net::UnixListener;
#[cfg(unix)]
use tokio::net::UnixStream;
#[cfg(windows)]
use tokio::net::TcpStream;
#[cfg(windows)]
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
pub mod cli;
@@ -28,14 +34,17 @@ pub mod cli;
#[cfg(all(unix, feature = "gtk"))]
pub mod gtk;
pub fn run_frontend(config: &Config) -> Result<()> {
match config.frontend {
#[cfg(all(unix, feature = "gtk"))]
Frontend::Gtk => { gtk::run(); }
Frontend::Gtk => {
gtk::run();
}
#[cfg(any(not(feature = "gtk"), not(unix)))]
Frontend::Gtk => panic!("gtk frontend requested but feature not enabled!"),
Frontend::Cli => { cli::run()?; }
Frontend::Cli => {
cli::run()?;
}
};
Ok(())
}
@@ -54,7 +63,7 @@ pub fn wait_for_service() -> Result<std::os::unix::net::UnixStream> {
loop {
use std::os::unix::net::UnixStream;
if let Ok(stream) = UnixStream::connect(&socket_path) {
break Ok(stream)
break Ok(stream);
}
// a signaling mechanism or inotify could be used to
// improve this
@@ -68,7 +77,7 @@ pub fn wait_for_service() -> Result<std::net::TcpStream> {
loop {
use std::net::TcpStream;
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));
}
@@ -146,7 +155,7 @@ impl FrontendListener {
Err(e) => {
log::debug!("{socket_path:?}: {e} - removing left behind socket");
let _ = std::fs::remove_file(&socket_path);
},
}
}
}
let listener = match UnixListener::bind(&socket_path) {
@@ -160,10 +169,10 @@ impl FrontendListener {
#[cfg(windows)]
let listener = match TcpListener::bind("127.0.0.1:5252").await {
Ok(ls) => ls,
// some other lan-mouse instance has bound the socket in the meantime
Err(e) if e.kind() == ErrorKind::AddrInUse => return None,
Err(e) => return Some(Err(anyhow!("failed to bind lan-mouse-socket: {e}"))),
Ok(ls) => ls,
// some other lan-mouse instance has bound the socket in the meantime
Err(e) if e.kind() == ErrorKind::AddrInUse => return None,
Err(e) => return Some(Err(anyhow!("failed to bind lan-mouse-socket: {e}"))),
};
let adapter = Self {
@@ -194,7 +203,6 @@ impl FrontendListener {
Ok(rx)
}
pub(crate) async fn notify_all(&mut self, notify: FrontendNotify) -> Result<()> {
// encode event
let json = serde_json::to_string(&notify).unwrap();
@@ -207,7 +215,7 @@ impl FrontendListener {
// TODO do simultaneously
for tx in self.tx_streams.iter_mut() {
// write len + payload
if let Err(_) = tx.write(&len).await {
if let Err(_) = tx.write(&len).await {
keep.push(false);
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?;
Ok(serde_json::from_slice(&buf[..len as usize])?)
}

View File

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

View File

@@ -1,13 +1,24 @@
mod window;
mod client_object;
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 gtk::{
gdk::Display,
gio::{SimpleAction, SimpleActionGroup},
glib::{clone, MainContext, Priority},
prelude::*,
subclass::prelude::ObjectSubclassIsExt,
CssProvider, IconTheme,
};
use gtk::{gio, glib, prelude::ApplicationExt};
use self::client_object::ClientObject;
@@ -22,8 +33,7 @@ pub fn run() -> glib::ExitCode {
}
fn gtk_main() -> glib::ExitCode {
gio::resources_register_include!("lan-mouse.gresource")
.expect("Failed to register resources.");
gio::resources_register_include!("lan-mouse.gresource").expect("Failed to register resources.");
let app = Application::builder()
.application_id("de.feschber.lan-mouse")
@@ -41,14 +51,15 @@ fn load_css() {
let provider = CssProvider::new();
provider.load_from_resource("de/feschber/LanMouse/style.css");
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,
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
}
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");
}
@@ -69,7 +80,7 @@ fn build_ui(app: &Application) {
}
};
log::debug!("connected to lan-mouse-socket");
let (sender, receiver) = MainContext::channel::<FrontendNotify>(Priority::default());
gio::spawn_blocking(move || {
@@ -92,14 +103,13 @@ fn build_ui(app: &Application) {
};
// parse json
let json = str::from_utf8(&buf)
.unwrap();
let json = str::from_utf8(&buf).unwrap();
match serde_json::from_str(json) {
Ok(notify) => sender.send(notify).unwrap(),
Err(e) => log::error!("{e}"),
}
} {
Ok(()) => {},
Ok(()) => {}
Err(e) => log::error!("{e}"),
}
});
@@ -152,16 +162,12 @@ fn build_ui(app: &Application) {
}
));
let action_request_client_update = SimpleAction::new(
"request-client-update",
Some(&u32::static_variant_type()),
);
let action_request_client_update =
SimpleAction::new("request-client-update", Some(&u32::static_variant_type()));
// remove client
let action_client_delete = SimpleAction::new(
"request-client-delete",
Some(&u32::static_variant_type()),
);
let action_client_delete =
SimpleAction::new("request-client-delete", Some(&u32::static_variant_type()));
// update client state
action_request_client_update.connect_activate(clone!(@weak window => move |_action, param| {

View File

@@ -1,7 +1,7 @@
mod imp;
use gtk::glib::{self, Object};
use adw::subclass::prelude::*;
use gtk::glib::{self, Object};
use crate::client::ClientHandle;
@@ -10,7 +10,13 @@ glib::wrapper! {
}
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()
.property("handle", handle)
.property("hostname", hostname)

View File

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

View File

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

View File

@@ -4,10 +4,14 @@ use std::io::Write;
use adw::prelude::*;
use adw::subclass::prelude::*;
use gtk::{glib, gio, NoSelection};
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;
@@ -67,7 +71,14 @@ impl Window {
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);
self.clients().append(&client);
self.set_placeholder_visible(false);
@@ -122,7 +133,7 @@ impl Window {
"bottom" => Position::Bottom,
_ => {
log::error!("invalid position: {}", data.position);
return
return;
}
};
let hostname = data.hostname;
@@ -133,7 +144,7 @@ impl Window {
let event = FrontendEvent::ActivateClient(client.handle(), !client.active());
self.request(event);
}
pub fn request_client_delete(&self, idx: u32) {
if let Some(obj) = self.clients().item(idx) {
let client_object: &ClientObject = obj
@@ -145,7 +156,7 @@ impl Window {
}
}
fn request(&self, event: FrontendEvent) {
fn request(&self, event: FrontendEvent) {
let json = serde_json::to_string(&event).unwrap();
log::debug!("requesting {json}");
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 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;
@@ -65,7 +71,8 @@ impl Window {
#[template_callback]
fn handle_port_edit_cancel(&self) {
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_cancel.set_visible(false);
}
@@ -82,7 +89,6 @@ impl Window {
}
}
impl ObjectImpl for Window {
fn constructed(&self) {
self.parent_constructed();

View File

@@ -2,9 +2,8 @@ use std::io::{self, Write};
use crate::client::Position;
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()?;
let answer = loop {
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: ");
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): ");
io::stderr().flush()?;
continue
continue;
}
};
};

View File

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

View File

@@ -3,8 +3,11 @@ use std::io;
use futures_core::Stream;
use crate::{client::{ClientHandle, ClientEvent}, event::Event};
use crate::backend::producer;
use crate::{
client::{ClientEvent, ClientHandle},
event::Event,
};
#[cfg(unix)]
use std::env;
@@ -26,7 +29,7 @@ pub async fn create() -> Result<Box<dyn EventProducer>> {
"x11" => {
log::info!("XDG_SESSION_TYPE = x11 -> using X11 event producer");
Backend::X11
},
}
"wayland" => {
log::info!("XDG_SESSION_TYPE = wayland -> using wayland event producer");
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");
Backend::LayerShell
}
}
},
Err(_) => {
log::warn!("XDG_CURRENT_DESKTOP not set! Assuming layer_shell support -> using layer_shell backend");
Backend::LayerShell
@@ -48,7 +51,9 @@ pub async fn create() -> Result<Box<dyn EventProducer>> {
}
_ => 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)]
@@ -70,7 +75,7 @@ pub async fn create() -> Result<Box<dyn EventProducer>> {
panic!("feature libei not enabled");
#[cfg(feature = "libei")]
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 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)]
use tokio::net::UnixStream;
@@ -9,10 +20,17 @@ use tokio::net::UnixStream;
#[cfg(windows)]
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::{
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
/// of continuously sending and receiving the same event.
@@ -41,7 +59,6 @@ impl Server {
consumer: Box<dyn EventConsumer>,
producer: Box<dyn EventProducer>,
) -> anyhow::Result<Self> {
// create dns resolver
let resolver = dns::DnsResolver::new().await?;
@@ -65,7 +82,7 @@ impl Server {
};
// 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;
}
@@ -73,7 +90,6 @@ impl Server {
}
pub async fn run(&mut self) -> anyhow::Result<()> {
loop {
log::trace!("polling ...");
tokio::select! {
@@ -127,14 +143,20 @@ impl Server {
}
}
}
// destroy consumer
self.consumer.destroy().await;
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() {
match self.resolver.resolve(hostname.as_str()).await {
Ok(ips) => HashSet::from_iter(ips.iter().cloned()),
@@ -147,8 +169,15 @@ impl Server {
HashSet::new()
};
addr.extend(ips.iter());
log::info!("adding client [{}]{} @ {:?}", pos, hostname.as_deref().unwrap_or(""), &ips);
let client = self.client_manager.add_client(hostname.clone(), addr, port, pos);
log::info!(
"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}");
let notify = FrontendNotify::NotifyClientCreate(client, hostname, port, pos);
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) {
state.active = active;
if state.active {
self.producer.notify(ClientEvent::Create(client, state.client.pos));
self.consumer.notify(ClientEvent::Create(client, state.client.pos)).await;
self.producer
.notify(ClientEvent::Create(client, state.client.pos));
self.consumer
.notify(ClientEvent::Create(client, state.client.pos))
.await;
} else {
self.producer.notify(ClientEvent::Destroy(client));
self.consumer.notify(ClientEvent::Destroy(client)).await;
@@ -173,7 +205,11 @@ impl Server {
pub async fn remove_client(&mut self, client: ClientHandle) -> Option<ClientHandle> {
self.producer.notify(ClientEvent::Destroy(client));
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);
log::debug!("{notify:?}");
if let Err(e) = self.frontend.notify_all(notify).await {
@@ -194,7 +230,7 @@ impl Server {
) {
// retrieve state
let Some(state) = self.client_manager.get_mut(client) else {
return
return;
};
// update pos
@@ -209,12 +245,20 @@ impl Server {
// update port
if state.client.port != port {
state.client.port = port;
state.client.addrs = state.client.addrs
state.client.addrs = state
.client
.addrs
.iter()
.cloned()
.map(|mut a| { a.set_port(port); a })
.map(|mut a| {
a.set_port(port);
a
})
.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
@@ -258,12 +302,12 @@ impl Server {
}
};
// reset ttl for client and
// reset ttl for client and
state.last_seen = Some(Instant::now());
// set addr as new default for this client
state.client.active_addr = Some(addr);
match (event, addr) {
(Event::Pong(), _) => {},
(Event::Pong(), _) => {}
(Event::Ping(), addr) => {
if let Err(e) = send_event(&self.socket, Event::Pong(), addr).await {
log::error!("udp send: {}", e);
@@ -291,16 +335,17 @@ impl Server {
// let the server know we are still alive once every second
let last_replied = state.last_replied;
if last_replied.is_none()
|| last_replied.is_some()
&& last_replied.unwrap().elapsed() > Duration::from_secs(1) {
if last_replied.is_none()
|| last_replied.is_some()
&& last_replied.unwrap().elapsed() > Duration::from_secs(1)
{
state.last_replied = Some(Instant::now());
if let Err(e) = send_event(&self.socket, Event::Pong(), addr).await {
log::error!("udp send: {}", e);
}
}
}
}
},
}
}
@@ -317,7 +362,7 @@ impl Server {
Some(state) => state,
None => {
log::warn!("unknown client!");
return
return;
}
};
// otherwise we should have an address to send to
@@ -331,20 +376,21 @@ impl Server {
// if client last responded > 2 seconds ago
// and we have not sent a ping since 500 milliseconds,
// send a ping
if state.last_seen.is_some()
&& state.last_seen.unwrap().elapsed() < Duration::from_secs(2) {
return
if state.last_seen.is_some() && state.last_seen.unwrap().elapsed() < Duration::from_secs(2)
{
return;
}
// client last seen > 500ms ago
if state.last_ping.is_some()
&& state.last_ping.unwrap().elapsed() < Duration::from_millis(500) {
return
&& state.last_ping.unwrap().elapsed() < Duration::from_millis(500)
{
return;
}
// release mouse if client didnt respond to the first ping
if state.last_ping.is_some()
&& state.last_ping.unwrap().elapsed() < Duration::from_secs(1) {
if state.last_ping.is_some() && state.last_ping.unwrap().elapsed() < Duration::from_secs(1)
{
should_release = true;
}
@@ -414,12 +460,20 @@ impl Server {
async fn handle_frontend_event(&mut self, event: FrontendEvent) -> bool {
log::debug!("frontend: {event:?}");
match event {
FrontendEvent::AddClient(hostname, port, pos) => { self.add_client(hostname, HashSet::new(), port, pos).await; },
FrontendEvent::ActivateClient(client, active) => self.activate_client(client, active).await,
FrontendEvent::AddClient(hostname, port, pos) => {
self.add_client(hostname, HashSet::new(), port, pos).await;
}
FrontendEvent::ActivateClient(client, active) => {
self.activate_client(client, active).await
}
FrontendEvent::ChangePort(port) => {
let current_port = self.socket.local_addr().unwrap().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}");
}
return false;
@@ -428,39 +482,60 @@ impl Server {
match UdpSocket::bind(listen_addr).await {
Ok(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}");
}
},
}
Err(e) => {
log::warn!("could not change port: {e}");
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}");
}
}
}
},
FrontendEvent::DelClient(client) => { self.remove_client(client).await; },
}
FrontendEvent::DelClient(client) => {
self.remove_client(client).await;
}
FrontendEvent::Enumerate() => self.enumerate().await,
FrontendEvent::Shutdown() => {
log::info!("terminating gracefully...");
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
}
async fn enumerate(&mut self) {
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}");
}
}
}
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");
let mut buf = vec![0u8; 22];
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> {
log::trace!("{:20} ------>->->-> {addr}", e.to_string());
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.
sock.send_to(&data[..], addr).await
}