mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-04-04 13:41:28 +03:00
enable conditional compilation for all backends
To reduce binary size one can now enable only specific backends, e.g. wayland or x11 via cargo features Additionally adds stubs for libei and xdg-desktop-portal backends
This commit is contained in:
9
src/backend/consumer/libei.rs
Normal file
9
src/backend/consumer/libei.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use std::sync::mpsc::Receiver;
|
||||
|
||||
use crate::{event::Event, client::{ClientHandle, Client}};
|
||||
|
||||
|
||||
|
||||
pub(crate) fn run(_consume_rx: Receiver<(Event, ClientHandle)>, _clients: Vec<Client>) {
|
||||
todo!()
|
||||
}
|
||||
60
src/backend/consumer/windows.rs
Normal file
60
src/backend/consumer/windows.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use std::sync::mpsc::Receiver;
|
||||
|
||||
use crate::event::{KeyboardEvent, PointerEvent};
|
||||
use winapi::{
|
||||
self,
|
||||
um::winuser::{INPUT, INPUT_MOUSE, LPINPUT, MOUSEEVENTF_MOVE, MOUSEINPUT},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
client::{Client, ClientHandle},
|
||||
event::Event,
|
||||
};
|
||||
|
||||
fn rel_mouse(dx: i32, dy: i32) {
|
||||
let mi = MOUSEINPUT {
|
||||
dx,
|
||||
dy,
|
||||
mouseData: 0,
|
||||
dwFlags: MOUSEEVENTF_MOVE,
|
||||
time: 0,
|
||||
dwExtraInfo: 0,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut input = INPUT {
|
||||
type_: INPUT_MOUSE,
|
||||
u: std::mem::transmute(mi),
|
||||
};
|
||||
|
||||
winapi::um::winuser::SendInput(
|
||||
1 as u32,
|
||||
&mut input as LPINPUT,
|
||||
std::mem::size_of::<INPUT>() as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(event_rx: Receiver<(Event, ClientHandle)>, _clients: Vec<Client>) {
|
||||
loop {
|
||||
match event_rx.recv().expect("event receiver unavailable").0 {
|
||||
Event::Pointer(pointer_event) => match pointer_event {
|
||||
PointerEvent::Motion {
|
||||
time: _,
|
||||
relative_x,
|
||||
relative_y,
|
||||
} => {
|
||||
rel_mouse(relative_x as i32, relative_y as i32);
|
||||
}
|
||||
PointerEvent::Button { .. } => {}
|
||||
PointerEvent::Axis { .. } => {}
|
||||
PointerEvent::Frame {} => {}
|
||||
},
|
||||
Event::Keyboard(keyboard_event) => match keyboard_event {
|
||||
KeyboardEvent::Key { .. } => {}
|
||||
KeyboardEvent::Modifiers { .. } => {}
|
||||
},
|
||||
Event::Release() => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
297
src/backend/consumer/wlroots.rs
Normal file
297
src/backend/consumer/wlroots.rs
Normal file
@@ -0,0 +1,297 @@
|
||||
use crate::client::{Client, ClientHandle};
|
||||
use crate::request::{self, Request};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::mpsc::Receiver;
|
||||
use std::time::Duration;
|
||||
use std::{io, thread};
|
||||
use std::{
|
||||
io::{BufWriter, Write},
|
||||
os::unix::prelude::AsRawFd,
|
||||
};
|
||||
|
||||
use wayland_client::globals::BindError;
|
||||
use wayland_client::protocol::wl_pointer::{Axis, ButtonState};
|
||||
use wayland_protocols_wlr::virtual_pointer::v1::client::{
|
||||
zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1 as VpManager,
|
||||
zwlr_virtual_pointer_v1::ZwlrVirtualPointerV1 as Vp,
|
||||
};
|
||||
|
||||
use wayland_protocols_misc::zwp_virtual_keyboard_v1::client::{
|
||||
zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1 as VkManager,
|
||||
zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1 as Vk,
|
||||
};
|
||||
|
||||
use wayland_protocols_plasma::fake_input::client::org_kde_kwin_fake_input::OrgKdeKwinFakeInput;
|
||||
|
||||
use wayland_client::{
|
||||
delegate_noop,
|
||||
globals::{registry_queue_init, GlobalListContents},
|
||||
protocol::{wl_registry, wl_seat},
|
||||
Connection, Dispatch, EventQueue, QueueHandle,
|
||||
};
|
||||
|
||||
use tempfile;
|
||||
|
||||
use crate::event::{Event, KeyboardEvent, PointerEvent};
|
||||
|
||||
enum VirtualInputManager {
|
||||
Wlroots { vpm: VpManager, vkm: VkManager },
|
||||
Kde { fake_input: OrgKdeKwinFakeInput },
|
||||
}
|
||||
|
||||
// App State, implements Dispatch event handlers
|
||||
struct App {
|
||||
input_for_client: HashMap<ClientHandle, VirtualInput>,
|
||||
seat: wl_seat::WlSeat,
|
||||
event_rx: Receiver<(Event, ClientHandle)>,
|
||||
virtual_input_manager: VirtualInputManager,
|
||||
queue: EventQueue<Self>,
|
||||
qh: QueueHandle<Self>,
|
||||
}
|
||||
|
||||
pub fn run(event_rx: Receiver<(Event, ClientHandle)>, clients: Vec<Client>) {
|
||||
let mut app = App::new(event_rx, clients);
|
||||
app.run();
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(event_rx: Receiver<(Event, ClientHandle)>, clients: Vec<Client>) -> Self {
|
||||
let conn = Connection::connect_to_env().unwrap();
|
||||
let (globals, queue) = registry_queue_init::<App>(&conn).unwrap();
|
||||
let qh = queue.handle();
|
||||
|
||||
let vpm: Result<VpManager, BindError> = globals.bind(&qh, 1..=1, ());
|
||||
let vkm: Result<VkManager, BindError> = globals.bind(&qh, 1..=1, ());
|
||||
let fake_input: Result<OrgKdeKwinFakeInput, BindError> = globals.bind(&qh, 4..=4, ());
|
||||
|
||||
let virtual_input_manager = match (vpm, vkm, fake_input) {
|
||||
(Ok(vpm), Ok(vkm), _) => VirtualInputManager::Wlroots { vpm, vkm },
|
||||
(_, _, Ok(fake_input)) => {
|
||||
fake_input.authenticate(
|
||||
"lan-mouse".into(),
|
||||
"Allow remote clients to control this devices".into(),
|
||||
);
|
||||
VirtualInputManager::Kde { fake_input }
|
||||
}
|
||||
(Err(e1), Err(e2), Err(e3)) => {
|
||||
eprintln!("zwlr_virtual_pointer_v1: {e1}");
|
||||
eprintln!("zwp_virtual_keyboard_v1: {e2}");
|
||||
eprintln!("org_kde_kwin_fake_input: {e3}");
|
||||
panic!("neither wlroots nor kde input emulation protocol supported!")
|
||||
}
|
||||
_ => {
|
||||
panic!()
|
||||
}
|
||||
};
|
||||
|
||||
let input_for_client: HashMap<ClientHandle, VirtualInput> = HashMap::new();
|
||||
let seat: wl_seat::WlSeat = globals.bind(&qh, 7..=8, ()).unwrap();
|
||||
let mut app = App {
|
||||
input_for_client,
|
||||
seat,
|
||||
event_rx,
|
||||
virtual_input_manager,
|
||||
queue,
|
||||
qh,
|
||||
};
|
||||
for client in clients {
|
||||
app.add_client(client);
|
||||
}
|
||||
app
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
loop {
|
||||
let (event, client) = self.event_rx.recv().expect("event receiver unavailable");
|
||||
if let Some(virtual_input) = self.input_for_client.get(&client) {
|
||||
virtual_input.consume_event(event).unwrap();
|
||||
if let Err(e) = self.queue.flush() {
|
||||
eprintln!("{}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_client(&mut self, client: Client) {
|
||||
// create virtual input devices
|
||||
match &self.virtual_input_manager {
|
||||
VirtualInputManager::Wlroots { vpm, vkm } => {
|
||||
let pointer: Vp = vpm.create_virtual_pointer(None, &self.qh, ());
|
||||
let keyboard: Vk = vkm.create_virtual_keyboard(&self.seat, &self.qh, ());
|
||||
|
||||
// receive keymap from device
|
||||
eprint!("\rconnecting to {} ", client.addr);
|
||||
let mut attempts = 0;
|
||||
let data = loop {
|
||||
let result = request::request_data(client.addr, Request::KeyMap);
|
||||
eprint!("\rconnecting to {} ", client.addr);
|
||||
for _ in 0..attempts {
|
||||
eprint!(".");
|
||||
}
|
||||
match result {
|
||||
Ok(data) => break data,
|
||||
Err(e) => {
|
||||
eprint!(" - {}", e);
|
||||
}
|
||||
}
|
||||
io::stderr().flush().unwrap();
|
||||
thread::sleep(Duration::from_millis(500));
|
||||
attempts += 1;
|
||||
};
|
||||
|
||||
eprint!("\rconnecting to {} ", client.addr);
|
||||
for _ in 0..attempts {
|
||||
eprint!(".");
|
||||
}
|
||||
eprintln!(" done! ");
|
||||
|
||||
// TODO use shm_open
|
||||
let f = tempfile::tempfile().unwrap();
|
||||
let mut buf = BufWriter::new(&f);
|
||||
buf.write_all(&data[..]).unwrap();
|
||||
buf.flush().unwrap();
|
||||
keyboard.keymap(1, f.as_raw_fd(), data.len() as u32);
|
||||
|
||||
let vinput = VirtualInput::Wlroots { pointer, keyboard };
|
||||
|
||||
self.input_for_client.insert(client.handle, vinput);
|
||||
}
|
||||
VirtualInputManager::Kde { fake_input } => {
|
||||
let fake_input = fake_input.clone();
|
||||
let vinput = VirtualInput::Kde { fake_input };
|
||||
self.input_for_client.insert(client.handle, vinput);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum VirtualInput {
|
||||
Wlroots { pointer: Vp, keyboard: Vk },
|
||||
Kde { fake_input: OrgKdeKwinFakeInput },
|
||||
}
|
||||
|
||||
impl VirtualInput {
|
||||
/// main loop handling udp packets
|
||||
fn consume_event(&self, event: Event) -> std::io::Result<()> {
|
||||
match event {
|
||||
Event::Pointer(e) => match e {
|
||||
PointerEvent::Motion {
|
||||
time,
|
||||
relative_x,
|
||||
relative_y,
|
||||
} => match self {
|
||||
VirtualInput::Wlroots {
|
||||
pointer,
|
||||
keyboard: _,
|
||||
} => {
|
||||
pointer.motion(time, relative_x, relative_y);
|
||||
pointer.frame();
|
||||
}
|
||||
VirtualInput::Kde { fake_input } => {
|
||||
fake_input.pointer_motion(relative_y, relative_y);
|
||||
}
|
||||
},
|
||||
PointerEvent::Button {
|
||||
time,
|
||||
button,
|
||||
state,
|
||||
} => {
|
||||
let state: ButtonState = state.try_into().unwrap();
|
||||
match self {
|
||||
VirtualInput::Wlroots {
|
||||
pointer,
|
||||
keyboard: _,
|
||||
} => {
|
||||
pointer.button(time, button, state);
|
||||
pointer.frame();
|
||||
}
|
||||
VirtualInput::Kde { fake_input } => {
|
||||
fake_input.button(button, state as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
PointerEvent::Axis { time, axis, value } => {
|
||||
let axis: Axis = (axis as u32).try_into().unwrap();
|
||||
match self {
|
||||
VirtualInput::Wlroots {
|
||||
pointer,
|
||||
keyboard: _,
|
||||
} => {
|
||||
pointer.axis(time, axis, value);
|
||||
pointer.frame();
|
||||
}
|
||||
VirtualInput::Kde { fake_input } => {
|
||||
fake_input.axis(axis as u32, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
PointerEvent::Frame {} => match self {
|
||||
VirtualInput::Wlroots {
|
||||
pointer,
|
||||
keyboard: _,
|
||||
} => {
|
||||
pointer.frame();
|
||||
}
|
||||
VirtualInput::Kde { fake_input: _ } => {}
|
||||
},
|
||||
},
|
||||
Event::Keyboard(e) => match e {
|
||||
KeyboardEvent::Key { time, key, state } => match self {
|
||||
VirtualInput::Wlroots {
|
||||
pointer: _,
|
||||
keyboard,
|
||||
} => {
|
||||
keyboard.key(time, key, state as u32);
|
||||
}
|
||||
VirtualInput::Kde { fake_input } => {
|
||||
fake_input.keyboard_key(key, state as u32);
|
||||
}
|
||||
},
|
||||
KeyboardEvent::Modifiers {
|
||||
mods_depressed,
|
||||
mods_latched,
|
||||
mods_locked,
|
||||
group,
|
||||
} => match self {
|
||||
VirtualInput::Wlroots {
|
||||
pointer: _,
|
||||
keyboard,
|
||||
} => {
|
||||
keyboard.modifiers(mods_depressed, mods_latched, mods_locked, group);
|
||||
}
|
||||
VirtualInput::Kde { fake_input: _ } => {}
|
||||
},
|
||||
},
|
||||
Event::Release() => match self {
|
||||
VirtualInput::Wlroots {
|
||||
pointer: _,
|
||||
keyboard,
|
||||
} => {
|
||||
keyboard.modifiers(77, 0, 0, 0);
|
||||
keyboard.modifiers(0, 0, 0, 0);
|
||||
}
|
||||
VirtualInput::Kde { fake_input: _ } => {}
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
delegate_noop!(App: Vp);
|
||||
delegate_noop!(App: Vk);
|
||||
delegate_noop!(App: VpManager);
|
||||
delegate_noop!(App: VkManager);
|
||||
delegate_noop!(App: wl_seat::WlSeat);
|
||||
delegate_noop!(App: OrgKdeKwinFakeInput);
|
||||
|
||||
impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for App {
|
||||
fn event(
|
||||
_: &mut App,
|
||||
_: &wl_registry::WlRegistry,
|
||||
_: wl_registry::Event,
|
||||
_: &GlobalListContents,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<App>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
49
src/backend/consumer/x11.rs
Normal file
49
src/backend/consumer/x11.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use std::{ptr, sync::mpsc::Receiver};
|
||||
use x11::{xlib, xtest};
|
||||
|
||||
use crate::{
|
||||
client::{Client, ClientHandle},
|
||||
event::Event,
|
||||
};
|
||||
|
||||
fn open_display() -> Option<*mut xlib::Display> {
|
||||
unsafe {
|
||||
match xlib::XOpenDisplay(ptr::null()) {
|
||||
d if d == ptr::null::<xlib::Display>() as *mut xlib::Display => None,
|
||||
display => Some(display),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn relative_motion(display: *mut xlib::Display, dx: i32, dy: i32) {
|
||||
unsafe {
|
||||
xtest::XTestFakeRelativeMotionEvent(display, dx, dy, 0, 0);
|
||||
xlib::XFlush(display);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(event_rx: Receiver<(Event, ClientHandle)>, _clients: Vec<Client>) {
|
||||
let display = match open_display() {
|
||||
None => panic!("could not open display!"),
|
||||
Some(display) => display,
|
||||
};
|
||||
|
||||
loop {
|
||||
match event_rx.recv().expect("event receiver unavailable").0 {
|
||||
Event::Pointer(pointer_event) => match pointer_event {
|
||||
crate::event::PointerEvent::Motion {
|
||||
time: _,
|
||||
relative_x,
|
||||
relative_y,
|
||||
} => {
|
||||
relative_motion(display, relative_x as i32, relative_y as i32);
|
||||
}
|
||||
crate::event::PointerEvent::Button { .. } => {}
|
||||
crate::event::PointerEvent::Axis { .. } => {}
|
||||
crate::event::PointerEvent::Frame {} => {}
|
||||
},
|
||||
Event::Keyboard(_) => {}
|
||||
Event::Release() => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/backend/consumer/xdg_desktop_portal.rs
Normal file
9
src/backend/consumer/xdg_desktop_portal.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use std::sync::mpsc::Receiver;
|
||||
|
||||
use crate::{event::Event, client::{ClientHandle, Client}};
|
||||
|
||||
|
||||
|
||||
pub(crate) fn run(_consume_rx: Receiver<(Event, ClientHandle)>, _clients: Vec<Client>) {
|
||||
todo!()
|
||||
}
|
||||
Reference in New Issue
Block a user