wl-uinput-proxy 0.0.4

Wayland proxy implementing zwp_virtual_keyboard_manager_v1 and zwlr_virtual_pointer_manager_v1 using uinput
//! Wayland proxy implementing `zwlr_virtual_pointer_manager_v1` and `zwp_virtual_keyboard_manager_v1` using uinput.

mod keyboard;
mod pointer;
mod seat;
mod uinput;

use nix::sys::{
    prctl,
    signal::{Signal, raise},
};
use std::{
    cell::RefCell,
    os::unix::process::{CommandExt, ExitStatusExt},
    process::Command,
    rc::Rc,
    sync::OnceLock,
    thread,
};

use wl_proxy::{
    baseline::Baseline,
    global_mapper::GlobalMapper,
    object::{ConcreteObject, Object, ObjectCoreApi, ObjectRcUtils},
    protocols::{
        ObjectInterface,
        virtual_keyboard_unstable_v1::zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1,
        wayland::{
            wl_callback::{WlCallback, WlCallbackHandler},
            wl_display::{WlDisplay, WlDisplayHandler},
            wl_registry::{WlRegistry, WlRegistryHandler},
        },
        wlr_virtual_pointer_unstable_v1::zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1,
    },
    simple::{SimpleCommandExt, SimpleProxy},
};

use crate::{
    keyboard::KeyboardManager,
    pointer::PointerManager,
    seat::{ServerRegistry, SharedKeymap},
};

#[macro_export]
macro_rules! wlog {
    ($($arg:tt)*) => {{
        eprintln!("wl-uinput-proxy: {}", format_args!($($arg)*));
    }};
}

#[macro_export]
macro_rules! wdebug {
    ($($arg:tt)*) => {{
        if $crate::is_debug() {
            eprintln!("wl-uinput-proxy: {}", format_args!($($arg)*));
        }
    }};
}

fn is_debug() -> bool {
    static DEBUG: OnceLock<bool> = OnceLock::new();
    *DEBUG.get_or_init(|| std::env::var("WUIP_DEBUG").is_ok())
}

fn main() {
    let mut args = std::env::args_os().skip(1);
    let Some(program) = args.next() else {
        println!("usage: wl-uinput-proxy <cmd> [args...]");
        std::process::exit(2);
    };
    let program_args: Vec<_> = args.collect();

    let proxy = match SimpleProxy::new(Baseline::ALL_OF_THEM) {
        Ok(p) => p,
        Err(e) => {
            wlog!("failed to create proxy: {e}");
            std::process::exit(1);
        }
    };

    let mut cmd = Command::new(&program);
    cmd.args(&program_args);
    cmd.with_wayland_display(proxy.display());

    unsafe {
        cmd.pre_exec(|| {
            // kill the child if we die
            prctl::set_pdeathsig(Signal::SIGTERM).map_err(std::io::Error::other)?;
            Ok(())
        });
    }

    let mut child = cmd.spawn().unwrap_or_else(|e| {
        wlog!("failed to spawn {:?}: {e}", program);
        std::process::exit(1);
    });

    thread::spawn(move || match child.wait() {
        Ok(status) => {
            if let Some(code) = status.code() {
                if code != 0 {
                    wlog!("child exited with status {code}");
                } else {
                    uinput::drain();
                }
                std::process::exit(code);
            }
            if let Some(signal) = status.signal() {
                wlog!("child killed by signal {signal}");
                if let Ok(signal) = Signal::try_from(signal) {
                    let _ = raise(signal);
                }
                std::process::exit(1);
            }
            wlog!("child exited for unknown reason");
            std::process::exit(1);
        }
        Err(e) => {
            wlog!("failed to wait for child: {e}");
            std::process::exit(1);
        }
    });

    let err = proxy.run(Display::default);
    wlog!("proxy terminated unexpectedly: {err}");
    std::process::exit(1);
}

#[derive(Default)]
struct Display {
    init: bool,
    keymap: SharedKeymap,
}

impl WlDisplayHandler for Display {
    fn handle_get_registry(&mut self, slf: &Rc<WlDisplay>, registry: &Rc<WlRegistry>) {
        if !self.init {
            self.init = true;
            let server_registry = slf.create_child::<WlRegistry>();
            server_registry.set_forward_to_client(false);
            server_registry.set_handler(ServerRegistry::new(self.keymap.clone()));
            slf.send_get_registry(&server_registry);
        }

        let mapper = Rc::new(RefCell::new(GlobalMapper::default()));
        registry.set_handler(Registry::new(mapper.clone(), self.keymap.clone()));
        slf.send_get_registry(registry);

        // our globals must only be added after the existing globals have been
        // forwarded since some clients depend on the order (e.g., wayvnc
        // silently fails if the wl_seat and outputs aren't first)
        let announce = slf.create_child::<WlCallback>();
        announce.set_forward_to_client(false);
        announce.set_handler(AddSynthetic {
            registry: registry.clone(),
            mapper,
        });
        slf.send_sync(&announce); // run the callback after the wl_registry is done
    }
}

struct AddSynthetic {
    registry: Rc<WlRegistry>,
    mapper: Rc<RefCell<GlobalMapper>>,
}

impl WlCallbackHandler for AddSynthetic {
    fn handle_done(&mut self, slf: &Rc<WlCallback>, _callback_data: u32) {
        let mut mapper = self.mapper.borrow_mut();
        mapper.add_synthetic_global(
            &self.registry,
            ObjectInterface::ZwlrVirtualPointerManagerV1,
            ZwlrVirtualPointerManagerV1::XML_VERSION,
        );
        mapper.add_synthetic_global(
            &self.registry,
            ObjectInterface::ZwpVirtualKeyboardManagerV1,
            ZwpVirtualKeyboardManagerV1::XML_VERSION,
        );
        slf.unset_handler();
    }
}

struct Registry {
    mapper: Rc<RefCell<GlobalMapper>>,
    keymap: SharedKeymap,
}

impl Registry {
    fn new(mapper: Rc<RefCell<GlobalMapper>>, keymap: SharedKeymap) -> Self {
        Self { mapper, keymap }
    }
}

impl WlRegistryHandler for Registry {
    fn handle_global(
        &mut self,
        slf: &Rc<WlRegistry>,
        name: u32,
        interface: ObjectInterface,
        version: u32,
    ) {
        let mut mapper = self.mapper.borrow_mut();
        match interface {
            ObjectInterface::ZwlrVirtualPointerManagerV1 => {
                mapper.ignore_global(name);
            }
            ObjectInterface::ZwpVirtualKeyboardManagerV1 => {
                mapper.ignore_global(name);
            }
            _ => {
                mapper.forward_global(slf, name, interface, version);
            }
        }
    }

    fn handle_global_remove(&mut self, slf: &Rc<WlRegistry>, name: u32) {
        self.mapper.borrow_mut().forward_global_remove(slf, name);
    }

    fn handle_bind(&mut self, slf: &Rc<WlRegistry>, name: u32, id: Rc<dyn Object>) {
        match id.interface() {
            ZwlrVirtualPointerManagerV1::INTERFACE => {
                id.set_forward_to_server(false);
                id.downcast::<ZwlrVirtualPointerManagerV1>()
                    .set_handler(PointerManager);
                return;
            }
            ZwpVirtualKeyboardManagerV1::INTERFACE => {
                id.set_forward_to_server(false);
                id.downcast::<ZwpVirtualKeyboardManagerV1>()
                    .set_handler(KeyboardManager::new(self.keymap.clone()));
                return;
            }
            _ => {}
        }
        self.mapper.borrow_mut().forward_bind(slf, name, &id);
    }
}