Add raw evdev input emulation
This pull request adds another input emulation backend that uses evdev to create a virtual mouse/keyboard, bypassing Wayland restrictions. It is meant to be a fallback for the other Linux backends.
This backend successfully fixes input emulation issues on COSMIC and potentially other platforms that lack a proper remote desktop portal.
Limitations
Currently this feature requires lan-mouse to be run as root, e.g. with sudo -E lan-mouse. This is a temporary limitation that I hope to alleviate by creating a separate daemon and authentication mechanism.
To Do
- [x] Discrete scrolling needs to be tested. I just put a formula I thought might work. If I'm correct this is a Mac feature, which I don't have.
- [x] ~A daemon that allows unprivileged execution of lan-mouse, as described above.~ We are going to use an udev rule.
- [ ] Natural scrolling doesn't work because presumably the WM is responsible for the conversion. We'd need to emulate it. (optional?)
Potentially somewhat related: https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2664
@samvv I'm not sure running the daemon as root is going to work. The input capture backends need to be run as the current user to access xdg-desktop portal or the wayland socket.
But you can change the permissions on /dev/uinput to allow access to your user. (Steam also does this afaik for controller input)
Btw: For the cli backend override to work, you have to add the backend to the config struct as well:
diff --git a/src/config.rs b/src/config.rs
index 41f442d..361bb5e 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -172,6 +172,9 @@ impl From<CaptureBackend> for input_capture::Backend {
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, ValueEnum)]
pub enum EmulationBackend {
+ #[cfg(all(target_os = "linux", feature = "evdev_emulation"))]
+ #[serde(rename = "evdev")]
+ Evdev,
#[cfg(all(unix, feature = "wlroots_emulation", not(target_os = "macos")))]
#[serde(rename = "wlroots")]
Wlroots,
@@ -197,6 +200,8 @@ pub enum EmulationBackend {
impl From<EmulationBackend> for input_emulation::Backend {
fn from(backend: EmulationBackend) -> Self {
match backend {
+ #[cfg(all(target_os = "linux", feature = "evdev_emulation"))]
+ EmulationBackend::Evdev => Self::Evdev,
#[cfg(all(unix, feature = "wlroots_emulation", not(target_os = "macos")))]
EmulationBackend::Wlroots => Self::Wlroots,
#[cfg(all(unix, feature = "libei_emulation", not(target_os = "macos")))]
@@ -217,6 +222,8 @@ impl From<EmulationBackend> for input_emulation::Backend {
impl Display for EmulationBackend {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
+ #[cfg(all(target_os = "linux", feature = "evdev_emulation"))]
+ EmulationBackend::Evdev => write!(f, "evdev"),
#[cfg(all(unix, feature = "wlroots_emulation", not(target_os = "macos")))]
EmulationBackend::Wlroots => write!(f, "wlroots"),
#[cfg(all(unix, feature = "libei_emulation", not(target_os = "macos")))]
I think I might want to remove this duplication at some point
Thanks for the information. That steam 'hack' might actually work. At least on Arch it could be as simple as usermod -aG input username.
The daemon I was talking about isn't a lan-mouse daemon, it's this one I just wrote: https://github.com/samvv/input-daemon. It uses polkit to authenticate whatever process connects to it. If I'm correct it should in theory be more secure than the steam hack, where every user process has access (no questions asked), at the price of way more complexity.
What solution would you prefer?
I'll update this pull request with the CLI logic.
I just tested and can confirm that adding the group input to the user works!
I just tested and can confirm that adding the group
inputto the user works!
Adding the user to input is not necessary and a much bigger security risk than giving the user access to /dev/uinput via ACLs
I'm a bit hesitant to chown the /dev/uinput file. Do you mean that adding the input group is less secure because it allows access to all devices?
If so, what about:
sudo groupadd --system lan-mouse
sudo usermod -aG lan-mouse <username>
echo 'KERNEL=="uinput", GROUP="lan-mouse"' | sudo tee /lib/udev/rules.d/05-lan-mouse.rules
I've just tested this and it works.
@sithlord48 May be of interest.
I'm a bit hesitant to chown the
/dev/uinputfile. Do you mean that adding theinputgroup is less secure because it allows access to all devices?
Yes, adding the user to input allows arbitrary processes to also read input, which is a much bigger risk.
If so, what about:
sudo groupadd --system lan-mouse sudo usermod -aG lan-mouse <username> echo 'KERNEL=="uinput", GROUP="lan-mouse"' | sudo tee /lib/udev/rules.d/05-lan-mouse.rulesI've just tested this and it works.
Sounds like a good solution, if it works!
@feschber I've fixed the mouse wheel, at the very least on my setup. We might need to make WHEEL_SENSITIVITY a configuration variable for other setups, but that can happen later.
Is there anything else that needs to be done before this can be merged?
As far as I'm concerned, it's looking good! I might even consider moving this backend up in priority for input emulation on systems that have access to uinput. And we might look into a way of automatically setting permissions somehow. But thats something for the future :).
If you fix that formatting error, I would be ready to merge!