diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 04cc963d..f4fc958d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -9,26 +9,45 @@ on: jobs: build: - + name: build ${{ matrix.variant }} runs-on: ${{matrix.os}} env: DISPLAY: ':99' strategy: fail-fast: false matrix: - os: [macos-latest, ubuntu-latest, windows-latest] include: - - os: ubuntu-latest + - variant: ubuntu-x11-latest + os: ubuntu-latest headless: Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & - - os: ubuntu-latest - dependencies: sudo apt-get install libxtst-dev libevdev-dev --assume-yes - - os: macos-latest + dependencies: sudo apt-get install libxtst-dev libevdev-dev libudev-dev --assume-yes + build: cargo build --verbose --features x11 + test: cargo test --verbose --features=serialize,x11 + - variant: ubuntu-wayland-latest + os: ubuntu-latest + headless: weston --no-config --socket=wl-test-env --backend=headless & + dependencies: | + sudo apt-get install libxtst-dev libevdev-dev libudev-dev libinput-dev libxkbcommon-dev weston --assume-yes + sudo chown root:input /dev/uinput + sudo chmod g+rw /dev/uinput + id -a + whoami + sudo usermod -aG input runner + echo 'KERNEL=="uinput", MODE="0660", GROUP="input", TAG+="uaccess"' | sudo tee /etc/udev/rules.d/99-uinput.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --attr-match=name="uinput" + + build: cargo build --verbose --features wayland + # Keyboard not implemented in wayland + test: WAYLAND_DISPLAY=wl-test-env cargo test --verbose --no-default-features --features=serialize,wayland -- --skip test_grab --skip test_listen_and_simulate + - variant: macos-latest + os: macos-latest + build: cargo build --verbose # TODO: We can't test this on github, we can't set accessibility yet. test: cargo test --verbose --all-features -- --skip test_listen_and_simulate --skip test_grab - - os: ubuntu-latest - # TODO unstable_grab feature is not supported on Linux. - test: cargo test --verbose --features=serialize - - os: windows-latest + - variant: windows-latest + os: windows-latest + build: cargo build --verbose test: cargo test --verbose --all-features steps: @@ -44,7 +63,7 @@ jobs: rustup component add rustfmt cargo fmt -- --check - name: Build - run: cargo build --verbose + run: ${{ matrix.build }} - name: Run tests run: ${{matrix.test}} - name: Linter diff --git a/Cargo.toml b/Cargo.toml index 6d22c668..7d483426 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,11 @@ serde = {version = "1.0", features = ["derive"], optional=true} lazy_static = "1.4" [features] +default = [] serialize = ["serde"] unstable_grab = ["evdev-rs", "epoll", "inotify"] +wayland = ["input", "input-linux", "xkbcommon"] +x11 = ["dep:x11"] [target.'cfg(target_os = "macos")'.dependencies] cocoa = "0.26" @@ -31,10 +34,13 @@ dispatch = "0.2" [target.'cfg(target_os = "linux")'.dependencies] libc = "0.2" -x11 = {version = "2.18", features = ["xlib", "xrecord", "xinput"]} +x11 = {version = "2.18", features = ["xlib", "xrecord", "xinput"], optional = true} evdev-rs = {version = "0.6", optional=true} epoll = {version = "4.1.0", optional=true} inotify = {version = "0.11", default-features=false, optional=true} +input = {version = "0.9", optional=true} +input-linux = { version = "0.7.1", optional = true } +xkbcommon = { version = "0.5.0", optional = true } [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["winuser", "errhandlingapi", "processthreadsapi"] } @@ -57,7 +63,7 @@ required-features = ["unstable_grab"] [[example]] name = "tokio_channel" -required-features = ["unstable_grab"] +required-features = [] [[test]] name = "grab" diff --git a/examples/simulate.rs b/examples/simulate.rs index 1f50f042..8212a927 100644 --- a/examples/simulate.rs +++ b/examples/simulate.rs @@ -1,5 +1,8 @@ use rdev::{simulate, Button, EventType, Key, SimulateError}; -use std::{thread, time}; +use std::{ + thread, + time::{self, Duration}, +}; fn send(event_type: &EventType) { let delay = time::Duration::from_millis(20); @@ -14,8 +17,14 @@ fn send(event_type: &EventType) { } fn main() { + send(&EventType::MouseMove { x: 0.0, y: 0.0 }); + thread::sleep(Duration::from_millis(1000)); send(&EventType::KeyPress(Key::KeyS)); send(&EventType::KeyRelease(Key::KeyS)); + send(&EventType::KeyPress(Key::KeyA)); + send(&EventType::KeyRelease(Key::KeyA)); + send(&EventType::KeyPress(Key::KeyB)); + send(&EventType::KeyRelease(Key::KeyB)); send(&EventType::MouseMove { x: 0.0, y: 0.0 }); send(&EventType::MouseMove { x: 400.0, y: 400.0 }); diff --git a/flake.lock b/flake.lock index a44ef7a5..6fb1525d 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1730531603, - "narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=", + "lastModified": 1747542820, + "narHash": "sha256-GaOZntlJ6gPPbbkTLjbd8BMWaDYafhuuYRNrxCGnPJw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d", + "rev": "292fa7d4f6519c074f0a50394dbbe69859bb6043", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 44346eee..6253e680 100644 --- a/flake.nix +++ b/flake.nix @@ -29,7 +29,6 @@ ++ ( if stdenv.isLinux then [ - xkbmon xorg.libX11 xorg.libXcursor xorg.libXrandr @@ -37,6 +36,9 @@ xorg.libXtst libevdev libnotify + libinput + libxkbcommon + udev ] else [ ] diff --git a/src/lib.rs b/src/lib.rs index 9cac0394..ed04c38f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -226,9 +226,9 @@ pub use crate::rdev::{ #[cfg(target_os = "macos")] mod macos; #[cfg(target_os = "macos")] -pub use crate::macos::{set_is_main_thread, Keyboard}; -#[cfg(target_os = "macos")] use crate::macos::{display_size as _display_size, listen as _listen, simulate as _simulate}; +#[cfg(target_os = "macos")] +pub use crate::macos::{set_is_main_thread, Keyboard}; #[cfg(target_os = "linux")] mod linux; @@ -305,7 +305,8 @@ where /// } /// ``` pub fn simulate(event_type: &EventType) -> Result<(), SimulateError> { - _simulate(event_type) + _simulate(event_type)?; + Ok(()) } /// Returns the size in pixels of the main screen. diff --git a/src/linux/mod.rs b/src/linux/mod.rs index bf073fa8..6b98fb6c 100644 --- a/src/linux/mod.rs +++ b/src/linux/mod.rs @@ -1,18 +1,14 @@ -extern crate libc; -extern crate x11; +#[cfg(feature = "x11")] +mod x11; -mod common; -mod display; -#[cfg(feature = "unstable_grab")] -mod grab; -mod keyboard; -mod keycodes; -mod listen; -mod simulate; +#[cfg(feature = "x11")] +pub use x11::*; -pub use crate::linux::display::display_size; -#[cfg(feature = "unstable_grab")] -pub use crate::linux::grab::grab; -pub use crate::linux::keyboard::Keyboard; -pub use crate::linux::listen::listen; -pub use crate::linux::simulate::simulate; +#[cfg(all(feature = "wayland", not(feature = "x11")))] +mod wayland; + +#[cfg(all(feature = "wayland", not(feature = "x11")))] +pub use wayland::*; + +#[cfg(not(any(feature = "wayland", feature = "x11")))] +compile_error!("Need to activate either wayland or x11 feature on linux"); diff --git a/src/linux/wayland/common.rs b/src/linux/wayland/common.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/linux/wayland/common.rs @@ -0,0 +1 @@ + diff --git a/src/linux/wayland/display.rs b/src/linux/wayland/display.rs new file mode 100644 index 00000000..1700fbc8 --- /dev/null +++ b/src/linux/wayland/display.rs @@ -0,0 +1,7 @@ +use crate::rdev::DisplayError; + +pub fn display_size() -> Result<(u64, u64), DisplayError> { + todo!("Not implemented"); + // let display = Display::new().ok_or(DisplayError::NoDisplay)?; + // display.get_size().ok_or(DisplayError::NoDisplay) +} diff --git a/src/linux/wayland/grab.rs b/src/linux/wayland/grab.rs new file mode 100644 index 00000000..0fb95d16 --- /dev/null +++ b/src/linux/wayland/grab.rs @@ -0,0 +1,529 @@ +use crate::linux::common::Display; +use crate::linux::keyboard::Keyboard; +use crate::rdev::{Button, Event, EventType, GrabError, Key, KeyboardState}; +use epoll::ControlOptions::{EPOLL_CTL_ADD, EPOLL_CTL_DEL}; +use evdev_rs::{ + enums::{EventCode, EV_KEY, EV_REL}, + Device, InputEvent, UInputDevice, +}; +use inotify::{Inotify, WatchMask}; +use std::ffi::{OsStr, OsString}; +use std::fs::{read_dir, File}; +use std::io; +use std::os::unix::{ + ffi::OsStrExt, + fs::FileTypeExt, + io::{AsRawFd, IntoRawFd, RawFd}, +}; +use std::path::Path; +use std::time::SystemTime; + +// TODO The x, y coordinates are currently wrong !! Is there mouse acceleration +// to take into account ?? + +macro_rules! convert_keys { + ($($ev_key:ident, $rdev_key:ident),*) => { + //TODO: make const when rust lang issue #49146 is fixed + #[allow(unreachable_patterns)] + fn evdev_key_to_rdev_key(key: &EV_KEY) -> Option { + match key { + $( + EV_KEY::$ev_key => Some(Key::$rdev_key), + )* + _ => None, + } + } + + // //TODO: make const when rust lang issue #49146 is fixed + // fn rdev_key_to_evdev_key(key: &Key) -> Option { + // match key { + // $( + // Key::$rdev_key => Some(EV_KEY::$ev_key), + // )* + // _ => None + // } + // } + }; +} + +macro_rules! convert_buttons { + ($($ev_key:ident, $rdev_key:ident),*) => { + //TODO: make const when rust lang issue #49146 is fixed + fn evdev_key_to_rdev_button(key: &EV_KEY) -> Option