evdev-0.13.1/.cargo_vcs_info.json0000644000000001360000000000100122270ustar { "git": { "sha1": "5d282a0fec505d6c2c5f02d92e00f7cb2c3e5052" }, "path_in_vcs": "" }evdev-0.13.1/.github/workflows/rust.yml000064400000000000000000000045151046102023000161410ustar 00000000000000name: Rust on: push: branches: [ "main" ] pull_request: branches: [ "main" ] env: CARGO_TERM_COLOR: always RUSTFLAGS: "-D warnings" jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Format run: cargo fmt --check - name: Clippy run: cargo clippy && cargo clippy --all-features - name: Build run: cargo build --verbose --all-features - name: Run tests run: cargo test --verbose - name: Doc run: cargo doc --all-features cross-linux: runs-on: ubuntu-latest strategy: matrix: target: - arch: 'aarch64' rust: 'aarch64-unknown-linux-gnu' os: 'aarch64-linux-gnu' - arch: 'arm' rust: 'armv7-unknown-linux-gnueabihf' os: 'arm-linux-gnueabihf' - arch: 'i386' rust: 'i686-unknown-linux-gnu' os: 'i686-linux-gnu' steps: - uses: actions/checkout@v3 - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: toolchain: stable override: true components: rustfmt, clippy target: ${{ matrix.target.rust }} - name: Install QEMU and toolchain run: | sudo apt update sudo apt -y install qemu-user qemu-user-static gcc-${{ matrix.target.os }} binutils-${{ matrix.target.os }} binutils-${{ matrix.target.os }}-dbg - name: Append .cargo config run: | echo "[target.${{ matrix.target.rust }}]" >> ~/.cargo/config echo "runner = \"qemu-${{ matrix.target.arch }} -L /usr/${{ matrix.target.os }}/\"" >> ~/.cargo/config echo "linker = \"${{ matrix.target.os }}-gcc\"" >> ~/.cargo/config - name: Build run: cargo build --target ${{ matrix.target.rust }} - name: Run tests run: cargo test --target ${{ matrix.target.rust }} - name: Doc run: cargo doc --target ${{ matrix.target.rust }} freebsd: runs-on: ubuntu-latest name: build (FreeBSD) steps: - uses: actions/checkout@v3 - name: Build uses: vmactions/freebsd-vm@v1 with: envs: 'RUSTFLAGS' usesh: true prepare: | pkg install -y curl run: | curl https://sh.rustup.rs -sSf | sh -s -- --profile minimal -y . "$HOME/.cargo/env" cargo build --all-features cargo test evdev-0.13.1/.gitignore000064400000000000000000000000321046102023000130020ustar 00000000000000target Cargo.lock .vscode evdev-0.13.1/.mailmap000064400000000000000000000004641046102023000124440ustar 00000000000000# # This list is used by git-shortlog to aggregate contributions. It is # necessary when either the author's full name is not always written # the same way, and/or the same author contributes from different # email addresses. # Noa <33094578+coolreader18@users.noreply.github.com> evdev-0.13.1/.travis.yml000064400000000000000000000025621046102023000131350ustar 00000000000000# https://doc.rust-lang.org/cargo/guide/continuous-integration.html # https://docs.travis-ci.com/user/deployment/cargo/ language: rust rust: - stable - beta - nightly jobs: include: - os: linux dist: xenial - os: freebsd allow_failures: - rust: nightly - os: freebsd before_script: - | rustup component add rustfmt clippy && pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH script: - | cargo fmt -- --check && cargo clippy && cargo build --verbose --release && cargo test --verbose && cargo doc after_success: - travis-cargo --only stable doc-upload deploy: provider: cargo on: branch: release env: global: - secure: rFkOMXHERQhTse4FnnAYDJXIJSGYvJ/YkkFGBjz25YEFUYmuW+ueCvErrpQPCySD+Kf5MeolN3M6qkvP6DUid+31BlBejuEQeDZtBu52IURZst3gSLkSkj7HwImlqNMx31JX47wEYDychaMcmyP9kNjiTfI8vCK+AJALl3H6foC+VTDL6CHAokWwOjAmDF79ysahRUXYknzKjDFkU2JF2k6Z0h1rerv531w+Jf1e3O78OVgY3nWiZAcZWfhYAxyZgwZ7zZeoksRqW9nTesJca28uQ0y/L/sn4EsfEuoU+xiDLg1PUH/JUkxxxAVEIWvCKSl8nHybbs9Z9VOJp/ZpdS2TqMCEY6151cbSS9cX/kLpPOHXa73LFJcp0klzrsdlCitJnddBU+Y1x8t7PpJUntiOiQ2TnJh7kh1MrDFgiFCd8PEyuY3jFisXR64sMH8lkYASyqNfNTlOcBvqgYC3WQYEy81hesS4e49mKwx3wBaJT2TdfT0LE8EumgFkLQ/vZkdH0xaqan43Oqh+WPVlcZ78Aq/QBd7xF4L319oWrsKE0V/m0ZRIoTvv8ogF3Wr03wSyhUtfOV7qjnBSUOa9GUKfioGFVGJbqsvAnavltz5Y4SE7dcmTwtyFFS9fQ66hEVYJlR3AGvFnbXOflU+GU8IZOfNVFXyEcnmyJ4p5drU= evdev-0.13.1/CHANGELOG.md000064400000000000000000000151311046102023000126310ustar 00000000000000# Changelog ## evdev next [6aed780...HEAD](https://github.com/emberian/evdev/compare/7cbae16...HEAD) ### Added ### Changed ### Fixed ## evdev 0.13.1 (2025-03-31) [7cbae16...6aed780](https://github.com/emberian/evdev/compare/7cbae16...6aed780) ### Added - `Device::is_grabbed() -> bool` ### Changed - `Device` now implements `Debug`. - `Device` now calls `ungrab()` on Drop. ### Fixed ## evdev 0.13.0 (2025-01-22) [02aef0c...5a5ec3c](https://github.com/emberian/evdev/compare/02aef0c...5a5ec3c) ### Added - Create a `...Event` struct for each `EventType` to hold the `InputEvent` - Guarantee that each `...Event` struct can only hold a `InputEvent` of the corresponding `EventType` - Demonstrate what the `FFEvent` does in the `force_feedback` example. - `Device`, `RawDevice`, and `VirtualDevice` now implement `AsFd`. - `VirtualDevice::builder()` as an alias for `VirtualDeviceBuilder::new()` - `VirtualDeviceBuilder::new()` is now deprecated. ### Changed - Removed the `evdev::Error` type - fallible functions now just return `io::Error`. - Consistent naming and structure of all new-types for event-codes - Some of them where previously named `...Type` now they are all named `...Code` - Rename `InputEventKind` to `EventSummary` - Created missing `EventSummary` variants. I know some of them are kind of unused but it is less confusing if they are all there and look the same. - Each variant of the `EventSummary` enum now has the structure `Variant(...Event, ...Type, value)` - Renamed `Key` struct (the one with all the Key constants) to `KeyCode` to keep the naming consistent! - Rename `InputEvent::kind` to `InputEvent::destructure` this now returns a `EventSummary` - `InputEvent::new` no longer takes the `EventType` but `u16` as first argument. If the `EventType` is known we can directly construct the correct variant. - Ensure the unsafe code still does what we expect. - Update the Examples. - The minimum supported rust version (MSRV) is now `1.63`, due to `AsFd` support. - In order for the `EventStream` types to implement Stream, the `stream-trait` feature must now be specified. - `FFEffect` and `AutoRepeat` are now defined in the root of the crate instead of in `raw_stream`. - `VirtualDeviceBuilder::with_phys` now accepts a `&CStr` instead of a `&str`. ### Fixed - Update `VirtualDevice::fetch_events` to yield `InputEvent`s instead of `UInputEvent`s. That was a bug which was not accounted for be the type system. Yielding `UInputEvent`s there will now panic. ## evdev 0.12.2 (2024-05-08) [8fc58e1...af3c9b3](https://github.com/emberian/evdev/compare/8fc58e1...af3c9b3) ### Added - `&AttributeSetRef` and `&mut AttributeSetRef` now implement `Default`. - `&AttributeSetRef` now implements `IntoIterator`. - `AttributeSet` now implements `FromIterator<&T>`. ### Changed ### Fixed - `enumerate_dev_nodes[_blocking]` now always returns a path to a file in `/dev/input` ## evdev 0.12.1 (2022-12-09) [86dfe33...8fc58e1](https://github.com/emberian/evdev/compare/86dfe33...8fc58e1) ### Added - Add `Device::max_ff_effects()` to return the maximum number of force feedback effects that can be played simultaneously. - Add support for `EV_MSC` (miscellaneous events) to `VirtualDeviceBuilder`. - Add support for device properties to `VirtualDeviceBuilder`. ### Changed - Examples now show the device path of the virtual device. ### Fixed - Avoid infinite loop in `DevNodes::next_entry()`. - Fix issue on 32-bit platforms where `tv_sec` (`time_t`) is 32-bit. - Fix documentation links. - Document all the features (on docs.rs). ## evdev 0.12.0 (2022-08-17) [c0bd8dd...86dfe33](https://github.com/emberian/evdev/compare/c0bd8dd...86dfe33) ### Added - Document `FFEffect`. - Publicly export `FFEffect` from root. - Add `FFEffect::id()` as an accessor for the effect ID. - Add missing `EventStream::device_mut()` in `sync_stream.rs`. ### Changed ### Fixed - Make sure that the `DevNodesBlocking` iterator is not blocking indefinitely when all entries in the directory have been exhausted. - Fix incorrect cast in `eviocrmff` to support 32-bit platforms [#82](https://github.com/emberian/evdev/pull/82). - FreeBSD support [#88](https://github.com/emberian/evdev/pull/88). ## evdev 0.11.6 (2022-08-03) [372d000...c0bd8dd](https://github.com/emberian/evdev/compare/372d000...c0bd8dd) ### Added - Add a `CHANGELOG.md` with a changelog for each new release. - Force feedback support [#74](https://github.com/emberian/evdev/pull/74). - Implement serde support for `evdev_enum!` types and `InputEventKind` [#76](https://github.com/emberian/evdev/pull/76). - Implement `VirtualDevice::get_sys_path()` as well as an iterator over the device node paths for virtual devices [#72](https://github.com/emberian/evdev/pull/72). - Implement an `Error` type [#75](https://github.com/emberian/evdev/pull/75). - Add `EventStream::device_mut()` to get a mutable reference to `RawDevice` [#73](https://github.com/emberian/evdev/pull/73). - Add support for absolute axes for virtual devices [#71](https://github.com/emberian/evdev/pull/71). ### Changed ### Fixed - Documentation and code tidying [#67](https://github.com/emberian/evdev/pull/67). ## evdev 0.11.5 (2022-03-05) [099b6e9...372d000](https://github.com/emberian/evdev/compare/099b6e9...372d000) ### Added - Introduce `RawDevice::sys_path` and `Device::sys_path` [#62](https://github.com/emberian/evdev/pull/62). - Implement `FromIterator` for `AttributeSet`. ### Changed ### Fixed ## evdev 0.11.4 (2022-01-12) [1d020f1...099b6e9](https://github.com/emberian/evdev/compare/1d020f1...099b6e9) ### Added ### Changed - Update bitvec to 1.0. ### Fixed ## evdev 0.11.3 (2021-12-07) [898bb5c...1d020f1](https://github.com/emberian/evdev/compare/898bb5c...1d020f1) ### Added - Introduce `RawDevice::send_event` and `Device::send_event` to toggle LEDs, play sounds and play force feedback effects [#60](https://github.com/emberian/evdev/pull/60). ### Changed ### Fixed - Fix a bug in `compensate_events` where it returned the same event when invoking `next()` multiple times [#61](https://github.com/emberian/evdev/pull/61). ## evdev 0.11.2 (2021-12-03) [763ef01...898bb5c](https://github.com/emberian/evdev/compare/763ef01...898bb5c) ### Added ### Changed - Update bitvec to 1.0.0-rc1. ### Fixed ## evdev 0.11.1 (2021-10-08) [1898f49...763ef01](https://github.com/emberian/evdev/compare/1898f49...763ef01) ### Added - Implement `Device::grab` and `Device::ungrab` - Implement `VirtualDeviceBuilder::with_switches`. - Support autorepeats and getting keymap entries. ### Changed - Update nix to 0.23. ### Fixed ## evdev 0.11.0 (2021-04-01) [79b6c2b...1898f49](https://github.com/emberian/evdev/compare/79b6c2b...1898f49) evdev-0.13.1/Cargo.lock0000644000000230670000000000100102120ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] name = "adler2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "backtrace" version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", "windows-targets", ] [[package]] name = "bitflags" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", "tap", "wyz", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "evdev" version = "0.13.1" dependencies = [ "bitvec", "cfg-if", "futures-core", "itertools", "libc", "nix", "serde", "tokio", ] [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "libc" version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] [[package]] name = "mio" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "wasi", "windows-sys", ] [[package]] name = "nix" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags", "cfg-if", "cfg_aliases", "libc", ] [[package]] name = "object" version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "proc-macro2" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "radium" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "serde" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "socket2" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys", ] [[package]] name = "syn" version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tap" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tokio" version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "libc", "mio", "pin-project-lite", "socket2", "tokio-macros", "windows-sys", ] [[package]] name = "tokio-macros" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "wyz" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] evdev-0.13.1/Cargo.toml0000644000000050210000000000100102230ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.64" name = "evdev" version = "0.13.1" authors = ["Corey Richardson "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "evdev interface for Linux" documentation = "https://docs.rs/evdev" readme = "README.md" license = "Apache-2.0 OR MIT" repository = "https://github.com/cmr/evdev" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [features] serde = ["dep:serde"] stream-trait = [ "tokio", "futures-core", ] tokio = ["dep:tokio"] [lib] name = "evdev" path = "src/lib.rs" [[example]] name = "_pick_device" path = "examples/_pick_device.rs" [[example]] name = "blink_keyboard_leds" path = "examples/blink_keyboard_leds.rs" [[example]] name = "evtest" path = "examples/evtest.rs" [[example]] name = "evtest_nonblocking" path = "examples/evtest_nonblocking.rs" [[example]] name = "evtest_tokio" path = "examples/evtest_tokio.rs" required-features = ["tokio"] [[example]] name = "force_feedback" path = "examples/force_feedback.rs" [[example]] name = "virtual_ff" path = "examples/virtual_ff.rs" [[example]] name = "virtual_joystick" path = "examples/virtual_joystick.rs" [[example]] name = "virtual_keyboard" path = "examples/virtual_keyboard.rs" [[example]] name = "virtual_mouse" path = "examples/virtual_mouse.rs" [[test]] name = "virtual_device" path = "tests/virtual_device.rs" [dependencies.bitvec] version = "1.0.0" [dependencies.cfg-if] version = "1.0" [dependencies.futures-core] version = "0.3" optional = true [dependencies.libc] version = "0.2.121" features = ["extra_traits"] [dependencies.nix] version = "0.29" features = [ "ioctl", "fs", "event", ] [dependencies.serde] version = "1.0" features = ["derive"] optional = true [dependencies.tokio] version = "1.17" features = [ "fs", "time", "net", ] optional = true [dev-dependencies.itertools] version = "0.10" [dev-dependencies.tokio] version = "1.17" features = [ "macros", "rt-multi-thread", "time", ] evdev-0.13.1/Cargo.toml.orig000064400000000000000000000017771046102023000137220ustar 00000000000000[package] name = "evdev" version = "0.13.1" authors = ["Corey Richardson "] description = "evdev interface for Linux" license = "Apache-2.0 OR MIT" repository = "https://github.com/cmr/evdev" documentation = "https://docs.rs/evdev" edition = "2021" rust-version = "1.64" [features] serde = ["dep:serde"] tokio = ["dep:tokio"] stream-trait = ["tokio", "futures-core"] [dependencies] libc = { version = "0.2.121", features = ["extra_traits"]} bitvec = "1.0.0" cfg-if = "1.0" nix = { version = "0.29", features = ["ioctl", "fs", "event"] } serde = { version = "1.0", features = ["derive"], optional = true } tokio = { version = "1.17", features = ["fs","time", "net"], optional = true } futures-core = { version = "0.3", optional = true } [dev-dependencies] tokio = { version = "1.17", features = ["macros", "rt-multi-thread", "time"] } itertools = "0.10" [[example]] name = "evtest_tokio" required-features = ["tokio"] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] evdev-0.13.1/LICENSE-APACHE000064400000000000000000000227731046102023000127560ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS evdev-0.13.1/LICENSE-MIT000064400000000000000000000017771046102023000124670ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. evdev-0.13.1/README.md000064400000000000000000000052261046102023000123030ustar 00000000000000`evdev` ======= [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/emberian/evdev/rust.yml?branch=main)](https://github.com/emberian/evdev/actions/workflows/rust.yml) [![Crates.io](https://img.shields.io/crates/v/evdev.svg?style=flat-square)](https://crates.io/crates/evdev) [Documentation](https://docs.rs/evdev) Nice(r) access to `evdev` devices. What is `evdev`? =================== `evdev` is the Linux kernel's generic input interface, also implemented by other kernels such as FreeBSD. [libevdev](https://www.freedesktop.org/wiki/Software/libevdev/) is a userspace library written in c for interacting with this system in a high level way rather than using `ioctl` system calls directly. This crate is a re-implementation of `libevdev` in rust. There is some trickery involved, so please read the crate documentation. There is also an alternative crate: [evdev-rs](https://crates.io/crates/evdev-rs) which wraps `libevdev` instead. Overview ======== This crate provides functionality for reading streams of events from input devices. Like `libevdev`, this crate also provides functionality for interacting with [uinput](https://www.kernel.org/doc/html/latest/input/uinput.html). Uinput is a kernel module which allows virtual input devices to be created from userspace. Synchronization =============== This library exposes raw evdev events, but uses the Rust `Iterator` trait to do so. When processing events via `fetch_events`, the library will handle `SYN_DROPPED` events by injecting fake state updates in an attempt to ensure callers see state transition messages consistent with actual device state. When processing via `*_no_sync` this correction is not done, and `SYN_DROPPED` messages will appear if the kernel ring buffer is overrun before messages are read. I try to match [libevdev](https://www.freedesktop.org/software/libevdev/doc/latest/) closely, where possible. Limitations =========== There is no abstraction for gamepad-like devices that allows mapping button numbers to logical buttons, nor is one planned. Such a thing should take place in a higher-level crate, likely supporting multiple platforms. Example ======= Plenty of nice examples of how to use this crate can be found in the [examples](examples) directory of this repository. If you feel like an example of how to use a certain part of the evdev crate is missing, then feel free to open a pull request. A good introduction is the [evtest.rs](examples/evtest.rs) example (which roughly corresponds to the userspace [evtest](https://cgit.freedesktop.org/evtest/) tool. Releases ======== Detailed release notes are available in this repository at [CHANGELOG.md](CHANGELOG.md). evdev-0.13.1/examples/_pick_device.rs000064400000000000000000000020021046102023000156010ustar 00000000000000#![allow(dead_code)] //! An arg parser/prompt shared between the `evtest*` examples. Also demonstrates opening/finding //! connected devices. pub fn pick_device() -> evdev::Device { use std::io::prelude::*; let mut args = std::env::args_os(); args.next(); if let Some(dev_file) = args.next() { evdev::Device::open(dev_file).unwrap() } else { let mut devices = evdev::enumerate().map(|t| t.1).collect::>(); // readdir returns them in reverse order from their eventN names for some reason devices.reverse(); for (i, d) in devices.iter().enumerate() { println!("{}: {}", i, d.name().unwrap_or("Unnamed device")); } print!("Select the device [0-{}]: ", devices.len()); let _ = std::io::stdout().flush(); let mut chosen = String::new(); std::io::stdin().read_line(&mut chosen).unwrap(); let n = chosen.trim().parse::().unwrap(); devices.into_iter().nth(n).unwrap() } } fn main() {} evdev-0.13.1/examples/blink_keyboard_leds.rs000064400000000000000000000011161046102023000171700ustar 00000000000000use evdev::{LedCode, LedEvent}; mod _pick_device; fn main() { let mut d = _pick_device::pick_device(); println!("{d}"); println!("Blinking the Keyboard LEDS..."); for i in 0..5 { let on = i % 2 != 0; d.send_events(&[ *LedEvent::new(LedCode::LED_CAPSL, if on { i32::MAX } else { 0 }), *LedEvent::new(LedCode::LED_NUML, if on { i32::MAX } else { 0 }), *LedEvent::new(LedCode::LED_SCROLLL, if on { i32::MAX } else { 0 }), ]) .unwrap(); std::thread::sleep(std::time::Duration::from_secs(1)); } } evdev-0.13.1/examples/evtest.rs000064400000000000000000000004661046102023000145230ustar 00000000000000//! Similar to the evtest tool. // cli/"tui" shared between the evtest examples mod _pick_device; fn main() { let mut d = _pick_device::pick_device(); println!("{d}"); println!("Events:"); loop { for ev in d.fetch_events().unwrap() { println!("{ev:?}"); } } } evdev-0.13.1/examples/evtest_nonblocking.rs000064400000000000000000000032011046102023000170740ustar 00000000000000//! This example demonstrates how to use the evdev crate with a nonblocking file descriptor. //! //! Note that for this implementation the caller is responsible for ensuring the underlying //! Device file descriptor is set to O_NONBLOCK. The caller must also create the epoll descriptor, //! bind it, check for EAGAIN returns from fetch_events_*, call epoll_wait as appropriate, and //! clean up the epoll file descriptor when finished. #[cfg(not(target_os = "linux"))] fn main() {} // cli/"tui" shared between the evtest examples #[cfg(target_os = "linux")] mod _pick_device; #[cfg(target_os = "linux")] fn main() -> std::io::Result<()> { use nix::sys::epoll; let mut dev = _pick_device::pick_device(); println!("{dev}"); dev.set_nonblocking(true)?; // Create epoll handle and attach raw_fd let epoll = epoll::Epoll::new(epoll::EpollCreateFlags::EPOLL_CLOEXEC)?; let event = epoll::EpollEvent::new(epoll::EpollFlags::EPOLLIN, 0); epoll.add(&dev, event)?; // We don't care about these, but the kernel wants to fill them. let mut events = [epoll::EpollEvent::empty(); 2]; println!("Events:"); loop { match dev.fetch_events() { Ok(iterator) => { for ev in iterator { println!("{ev:?}"); } } Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => { // Wait forever for bytes available on dev epoll.wait(&mut events, epoll::EpollTimeout::NONE)?; } Err(e) => { eprintln!("{e}"); break; } } } Ok(()) } evdev-0.13.1/examples/evtest_tokio.rs000064400000000000000000000006561046102023000157310ustar 00000000000000//! Demonstrating how to monitor events with evdev + tokio // cli/"tui" shared between the evtest examples mod _pick_device; #[tokio::main] async fn main() -> Result<(), Box> { let d = _pick_device::pick_device(); println!("{}", d); println!("Events:"); let mut events = d.into_event_stream()?; loop { let ev = events.next_event().await?; println!("{:?}", ev); } } evdev-0.13.1/examples/force_feedback.rs000064400000000000000000000030571046102023000161120ustar 00000000000000use evdev::{ EventSummary, FFEffectCode, FFEffectData, FFEffectKind, FFReplay, FFStatusCode, FFTrigger, }; mod _pick_device; fn main() -> std::io::Result<()> { let mut d = _pick_device::pick_device(); println!("{d}"); println!("It's time to rumble!"); let effect_data = FFEffectData { direction: 0, trigger: FFTrigger::default(), replay: FFReplay { length: 1000, delay: 0, }, kind: FFEffectKind::Rumble { strong_magnitude: 0x0000, weak_magnitude: 0xffff, }, }; let mut effect = d.upload_ff_effect(effect_data)?; std::thread::spawn(move || { loop { // monitor the response from the device const STOPPED: i32 = FFStatusCode::FF_STATUS_STOPPED.0 as i32; const STARTED: i32 = FFStatusCode::FF_STATUS_PLAYING.0 as i32; for ev in d.fetch_events().unwrap() { match ev.destructure() { EventSummary::ForceFeedback(_, FFEffectCode(id), STARTED) => { println!("Device Started effect id {}", id); } EventSummary::ForceFeedback(_, FFEffectCode(id), STOPPED) => { println!("Device Stopped effect id {}", id); } _ => (), } } } }); effect.play(1)?; std::thread::sleep(std::time::Duration::from_secs(1)); effect.stop()?; std::thread::sleep(std::time::Duration::from_secs(1)); Ok(()) } evdev-0.13.1/examples/virtual_ff.rs000064400000000000000000000045631046102023000153540ustar 00000000000000// Create a virtual force feedback device, just while this is running. use evdev::{ uinput::VirtualDevice, AttributeSet, EventSummary, FFEffectCode, FFStatusCode, InputEvent, UInputCode, }; use std::collections::BTreeSet; fn main() -> std::io::Result<()> { let mut device = VirtualDevice::builder()? .name("Fake Force Feedback") .with_ff(&AttributeSet::from_iter([FFEffectCode::FF_RUMBLE]))? .with_ff_effects_max(16) .build()?; for path in device.enumerate_dev_nodes_blocking()? { let path = path?; println!("Available as {}", path.display()); } let mut ids: BTreeSet = (0..16).collect(); println!("Waiting for Ctrl-C..."); loop { let events: Vec = device.fetch_events()?.collect(); const STOPPED: i32 = FFStatusCode::FF_STATUS_STOPPED.0 as i32; const PLAYING: i32 = FFStatusCode::FF_STATUS_PLAYING.0 as i32; for event in events { match event.destructure() { EventSummary::UInput(event, UInputCode::UI_FF_UPLOAD, ..) => { let mut event = device.process_ff_upload(event)?; let id = ids.iter().next().copied(); match id { Some(id) => { ids.remove(&id); event.set_effect_id(id as i16); event.set_retval(0); } None => { event.set_retval(-1); } } println!("upload effect {:?}", event.effect()); } EventSummary::UInput(event, UInputCode::UI_FF_ERASE, ..) => { let event = device.process_ff_erase(event)?; ids.insert(event.effect_id() as u16); println!("erase effect ID = {}", event.effect_id()); } EventSummary::ForceFeedback(.., effect_id, STOPPED) => { println!("stopped effect ID = {}", effect_id.0); } EventSummary::ForceFeedback(.., effect_id, PLAYING) => { println!("playing effect ID = {}", effect_id.0); } _ => { println!("event = {:?}", event); } }; } } } evdev-0.13.1/examples/virtual_joystick.rs000064400000000000000000000023501046102023000166100ustar 00000000000000// Create a virtual joystick, just while this is running. // Generally this requires root. use evdev::{uinput::VirtualDevice, AbsInfo, AbsoluteAxisCode, AbsoluteAxisEvent, UinputAbsSetup}; use std::thread::sleep; use std::time::Duration; fn main() -> std::io::Result<()> { let abs_setup = AbsInfo::new(256, 0, 512, 20, 20, 1); let abs_x = UinputAbsSetup::new(AbsoluteAxisCode::ABS_X, abs_setup); let mut device = VirtualDevice::builder()? .name("Fake Joystick") .with_absolute_axis(&abs_x)? .build() .unwrap(); for path in device.enumerate_dev_nodes_blocking()? { let path = path?; println!("Available as {}", path.display()); } // Hopefully you don't have ABS_X bound to anything important. let code = AbsoluteAxisCode::ABS_X.0; println!("Waiting for Ctrl-C..."); loop { let down_event = *AbsoluteAxisEvent::new(AbsoluteAxisCode(code), 0); device.emit(&[down_event]).unwrap(); println!("Minned out."); sleep(Duration::from_secs(2)); let up_event = *AbsoluteAxisEvent::new(AbsoluteAxisCode(code), 512); device.emit(&[up_event]).unwrap(); println!("Maxed out."); sleep(Duration::from_secs(2)); } } evdev-0.13.1/examples/virtual_keyboard.rs000064400000000000000000000026211046102023000165520ustar 00000000000000// Create a virtual keyboard, just while this is running. // Generally this requires root. use evdev::KeyEvent; use evdev::{uinput::VirtualDevice, AttributeSet, EventType, InputEvent, KeyCode}; use std::thread::sleep; use std::time::Duration; fn main() -> std::io::Result<()> { let mut keys = AttributeSet::::new(); keys.insert(KeyCode::BTN_DPAD_UP); let mut device = VirtualDevice::builder()? .name("Fake Keyboard") .with_keys(&keys)? .build() .unwrap(); for path in device.enumerate_dev_nodes_blocking()? { let path = path?; println!("Available as {}", path.display()); } // Note this will ACTUALLY PRESS the button on your computer. // Hopefully you don't have BTN_DPAD_UP bound to anything important. let code = KeyCode::BTN_DPAD_UP.code(); println!("Waiting for Ctrl-C..."); loop { // this guarantees a key event let down_event = *KeyEvent::new(KeyCode(code), 1); device.emit(&[down_event]).unwrap(); println!("Pressed."); sleep(Duration::from_secs(2)); // alternativeley we can create a InputEvent, which will be any variant of InputEvent // depending on the type_ value let up_event = InputEvent::new(EventType::KEY.0, code, 0); device.emit(&[up_event]).unwrap(); println!("Released."); sleep(Duration::from_secs(2)); } } evdev-0.13.1/examples/virtual_mouse.rs000064400000000000000000000063511046102023000161060ustar 00000000000000// Create a virtual mouse, just while this is running. // Generally this requires root. use evdev::{uinput::VirtualDevice, AttributeSet, EventType, InputEvent, RelativeAxisCode}; use std::thread::sleep; use std::time::Duration; use MoveDirection::*; fn main() -> std::io::Result<()> { let mut device = VirtualDevice::builder()? .name("fake-mouse") .with_relative_axes(&AttributeSet::from_iter([ RelativeAxisCode::REL_X, RelativeAxisCode::REL_Y, RelativeAxisCode::REL_WHEEL, RelativeAxisCode::REL_HWHEEL, ]))? .build() .unwrap(); for path in device.enumerate_dev_nodes_blocking()? { let path = path?; println!("Available as {}", path.display()); } println!("Waiting for Ctrl-C..."); loop { let ev = new_move_mouse_event(Up, 50); device.emit(&[ev]).unwrap(); println!("Moved mouse up"); sleep(Duration::from_millis(100)); let ev = new_move_mouse_event(Down, 50); device.emit(&[ev]).unwrap(); println!("Moved mouse down"); sleep(Duration::from_millis(100)); let ev = new_move_mouse_event(Left, 50); device.emit(&[ev]).unwrap(); println!("Moved mouse left"); sleep(Duration::from_millis(100)); let ev = new_move_mouse_event(Right, 50); device.emit(&[ev]).unwrap(); println!("Moved mouse right"); sleep(Duration::from_millis(100)); let ev = new_scroll_mouse_event(Up, 1); device.emit(&[ev]).unwrap(); println!("Scrolled mouse up"); sleep(Duration::from_millis(100)); let ev = new_scroll_mouse_event(Down, 1); device.emit(&[ev]).unwrap(); println!("Scrolled mouse down"); sleep(Duration::from_millis(100)); let ev = new_scroll_mouse_event(Left, 1); device.emit(&[ev]).unwrap(); println!("Scrolled mouse left"); sleep(Duration::from_millis(100)); let ev = new_scroll_mouse_event(Right, 1); device.emit(&[ev]).unwrap(); println!("Scrolled mouse right"); sleep(Duration::from_millis(100)); } } enum MoveDirection { Up, Down, Left, Right, } fn new_move_mouse_event(direction: MoveDirection, distance: u16) -> InputEvent { let (axis, distance) = match direction { MoveDirection::Up => (RelativeAxisCode::REL_Y, -i32::from(distance)), MoveDirection::Down => (RelativeAxisCode::REL_Y, i32::from(distance)), MoveDirection::Left => (RelativeAxisCode::REL_X, -i32::from(distance)), MoveDirection::Right => (RelativeAxisCode::REL_X, i32::from(distance)), }; InputEvent::new_now(EventType::RELATIVE.0, axis.0, distance) } fn new_scroll_mouse_event(direction: MoveDirection, distance: u16) -> InputEvent { let (axis, distance) = match direction { MoveDirection::Up => (RelativeAxisCode::REL_WHEEL.0, i32::from(distance)), MoveDirection::Down => (RelativeAxisCode::REL_WHEEL.0, -i32::from(distance)), MoveDirection::Left => (RelativeAxisCode::REL_HWHEEL.0, -i32::from(distance)), MoveDirection::Right => (RelativeAxisCode::REL_HWHEEL.0, i32::from(distance)), }; InputEvent::new_now(EventType::RELATIVE.0, axis, distance) } evdev-0.13.1/src/attribute_set.rs000064400000000000000000000237531046102023000150440ustar 00000000000000use bitvec::prelude::*; use std::fmt; use std::ops::{Deref, DerefMut}; /// A collection of bits representing either device capability or state. /// /// This can be used to iterate across all keys supported by a keyboard, or all buttons supported /// by a joystick. You can also query directly whether a specific bit is set (corresponding to /// whether a key or button is depressed). #[repr(transparent)] pub struct AttributeSetRef { _indexer: std::marker::PhantomData, bitslice: BitSlice, } impl AttributeSetRef { #[inline] fn new(bitslice: &BitSlice) -> &Self { // SAFETY: for AttributeSet is repr(transparent) over BitSlice unsafe { &*(bitslice as *const BitSlice as *const Self) } } #[inline] fn new_mut(bitslice: &mut BitSlice) -> &mut Self { // SAFETY: for AttributeSet is repr(transparent) over BitSlice unsafe { &mut *(bitslice as *mut BitSlice as *mut Self) } } /// Returns `true` if this AttributeSet contains the passed T. #[inline] pub fn contains(&self, attr: T) -> bool { self.bitslice.get(attr.to_index()).map_or(false, |b| *b) } /// Provides an iterator over all "set" bits in the collection. #[inline] pub fn iter(&self) -> AttributeSetRefIter<'_, T> { self.into_iter() } #[inline] pub(crate) fn slice(&self, start: T) -> &Self { Self::new(&self.bitslice[start.to_index()..]) } pub fn insert(&mut self, attr: T) { self.set(attr, true) } pub fn remove(&mut self, attr: T) { self.set(attr, false) } // TODO: figure out a good name for this if we make it public #[inline] pub(crate) fn set(&mut self, attr: T, on: bool) { self.bitslice.set(attr.to_index(), on) } } impl fmt::Debug for AttributeSetRef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_set().entries(self.iter()).finish() } } impl Default for &AttributeSetRef { fn default() -> Self { AttributeSetRef::new(BitSlice::empty()) } } impl Default for &mut AttributeSetRef { fn default() -> Self { AttributeSetRef::new_mut(BitSlice::empty_mut()) } } impl<'a, T: EvdevEnum> IntoIterator for &'a AttributeSetRef { type Item = T; type IntoIter = AttributeSetRefIter<'a, T>; fn into_iter(self) -> Self::IntoIter { AttributeSetRefIter { _indexer: std::marker::PhantomData, inner: self.bitslice.iter_ones(), } } } pub struct AttributeSetRefIter<'a, T> { _indexer: std::marker::PhantomData<&'a T>, inner: bitvec::slice::IterOnes<'a, u8, Lsb0>, } impl Iterator for AttributeSetRefIter<'_, T> { type Item = T; fn next(&mut self) -> Option { self.inner.next().map(T::from_index) } } impl ExactSizeIterator for AttributeSetRefIter<'_, T> { fn len(&self) -> usize { self.inner.len() } } impl DoubleEndedIterator for AttributeSetRefIter<'_, T> { fn next_back(&mut self) -> Option { self.inner.next_back().map(T::from_index) } } pub struct AttributeSet { container: T::Array, } impl AttributeSet { pub fn new() -> Self { Self { container: T::zeroed_array(), } } fn as_bitslice(&self) -> &BitSlice { T::array_as_slice(&self.container) } fn as_mut_bitslice(&mut self) -> &mut BitSlice { T::array_as_slice_mut(&mut self.container) } #[inline] pub(crate) fn as_mut_raw_slice(&mut self) -> &mut [u8] { T::array_as_buf(&mut self.container) } } impl Default for AttributeSet { fn default() -> Self { Self::new() } } impl FromIterator for AttributeSet { fn from_iter>(iter: I) -> Self { let mut set = AttributeSet::default(); iter.into_iter().for_each(|el| set.insert(el)); set } } impl<'a, T: ArrayedEvdevEnum> FromIterator<&'a T> for AttributeSet { fn from_iter>(iter: I) -> Self { Self::from_iter(iter.into_iter().copied()) } } impl Deref for AttributeSet { type Target = AttributeSetRef; fn deref(&self) -> &AttributeSetRef { AttributeSetRef::new(self.as_bitslice()) } } impl DerefMut for AttributeSet { fn deref_mut(&mut self) -> &mut AttributeSetRef { AttributeSetRef::new_mut(self.as_mut_bitslice()) } } impl<'a, T: ArrayedEvdevEnum> IntoIterator for &'a AttributeSet { type Item = T; type IntoIter = AttributeSetRefIter<'a, T>; fn into_iter(self) -> Self::IntoIter { (**self).into_iter() } } impl Clone for AttributeSet where T::Array: Clone, { fn clone(&self) -> Self { Self { container: self.container.clone(), } } fn clone_from(&mut self, other: &Self) { self.container.clone_from(&other.container) } } impl fmt::Debug for AttributeSet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) } } pub trait EvdevEnum: Copy + 'static { fn from_index(i: usize) -> Self; fn to_index(self) -> usize; } pub trait ArrayedEvdevEnum: EvdevEnum { type Array; fn array_as_slice(arr: &Self::Array) -> &BitSlice; fn array_as_slice_mut(arr: &mut Self::Array) -> &mut BitSlice; fn array_as_buf(arr: &mut Self::Array) -> &mut [u8]; fn zeroed_array() -> Self::Array; } macro_rules! evdev_enum { ($t:ty, Array, $($(#[$attr:meta])* $c:ident = $val:expr,)*) => { evdev_enum!( $t, Array: bitvec::BitArr!(for <$t>::COUNT, in u8), bitvec::array::BitArray::as_raw_mut_slice, bitvec::array::BitArray::ZERO, $($(#[$attr])* $c = $val,)* ); }; ($t:ty, box Array, $($(#[$attr:meta])* $c:ident = $val:expr,)*) => { evdev_enum!( $t, Array: Box::COUNT, in u8)>, bitvec::array::BitArray::as_raw_mut_slice, Box::new(bitvec::array::BitArray::ZERO), $($(#[$attr])* $c = $val,)* ); }; ( $t:ty, Array: $Array:ty, $arr_as_buf:expr, $zero:expr, $($(#[$attr:meta])* $c:ident = $val:expr,)* ) => { impl $crate::attribute_set::ArrayedEvdevEnum for $t { type Array = $Array; fn array_as_slice(arr: &Self::Array) -> &bitvec::slice::BitSlice { arr } fn array_as_slice_mut(arr: &mut Self::Array) -> &mut bitvec::slice::BitSlice { arr } fn array_as_buf(arr: &mut Self::Array) -> &mut [u8] { $arr_as_buf(arr) } fn zeroed_array() -> Self::Array { $zero } } evdev_enum!($t, $($(#[$attr])* $c = $val,)*); }; ($t:ty, $($(#[$attr:meta])* $c:ident = $val:expr,)*) => { impl $t { $($(#[$attr])* pub const $c: Self = Self($val);)* const NAME_MAP: &'static [(&'static str, $t)] = &[ $((stringify!($c), Self::$c),)* ]; } impl std::str::FromStr for $t { type Err = crate::EnumParseError; fn from_str(s: &str) -> Result { match Self::NAME_MAP.iter().find(|e| e.0 == s) { Some(e) => Ok(e.1), None => Err(crate::EnumParseError(())), } } } impl std::fmt::Debug for $t { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { #[allow(unreachable_patterns)] match *self { $(Self::$c => f.pad(stringify!($c)),)* _ => write!(f, "unknown key: {}", self.0), } } } impl $crate::attribute_set::EvdevEnum for $t { #[inline] fn from_index(i: usize) -> Self { Self(i as _) } #[inline] fn to_index(self) -> usize { self.0 as _ } } #[cfg(feature = "serde")] #[allow(unreachable_patterns)] impl serde::Serialize for $t { fn serialize(&self, serializer: S) -> Result where S: serde::ser::Serializer, { let value = match *self { $(Self::$c => stringify!($c),)* _ => unreachable!(), }; serializer.serialize_str(value) } } #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for $t { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, { struct Visitor; impl<'de> serde::de::Visitor<'de> for Visitor { type Value = $t; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { write!(formatter, "a string with any of the constants in {}", stringify!($t)) } fn visit_str(self, s: &str) -> Result where E: serde::de::Error, { match <$t>::NAME_MAP.iter().find(|(key, _)| s.eq_ignore_ascii_case(key)) { Some((_, v)) => Ok(*v), None => Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)), } } } deserializer.deserialize_str(Visitor) } } } } evdev-0.13.1/src/compat/non_linux.rs000064400000000000000000000100331046102023000154450ustar 00000000000000//! FreeBSD and other non-Linux targets don't have these available in libc, because they're in //! the "linux-like" impl directory. They are copied here for convenience and compatibility. //! //! BSD-likes are only minimally supported by evdev. Use at your own risk. #![allow(non_camel_case_types)] pub const FF_MAX: u16 = 0x7f; pub const FF_CNT: usize = FF_MAX as usize + 1; pub const INPUT_PROP_MAX: u16 = 0x1f; pub const INPUT_PROP_CNT: usize = INPUT_PROP_MAX as usize + 1; pub const EV_MAX: u16 = 0x1f; pub const EV_CNT: usize = EV_MAX as usize + 1; pub const KEY_MAX: u16 = 0x2ff; pub const KEY_CNT: usize = KEY_MAX as usize + 1; pub const REL_MAX: u16 = 0x0f; pub const REL_CNT: usize = REL_MAX as usize + 1; pub const ABS_MAX: u16 = 0x3f; pub const ABS_CNT: usize = ABS_MAX as usize + 1; pub const SW_MAX: u16 = 0x10; pub const SW_CNT: usize = SW_MAX as usize + 1; pub const MSC_MAX: u16 = 0x07; pub const MSC_CNT: usize = MSC_MAX as usize + 1; pub const LED_MAX: u16 = 0x0f; pub const LED_CNT: usize = LED_MAX as usize + 1; pub const SND_MAX: u16 = 0x07; pub const SND_CNT: usize = SND_MAX as usize + 1; pub const UINPUT_MAX_NAME_SIZE: usize = 80; #[repr(C)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct input_event { pub time: libc::timeval, pub type_: u16, pub code: u16, pub value: i32, } #[repr(C)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct input_id { pub bustype: u16, pub vendor: u16, pub product: u16, pub version: u16, } #[repr(C)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct input_absinfo { pub value: i32, pub minimum: i32, pub maximum: i32, pub fuzz: i32, pub flat: i32, pub resolution: i32, } #[repr(C)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct input_keymap_entry { pub flags: u8, pub len: u8, pub index: u16, pub keycode: u32, pub scancode: [u8; 32], } #[repr(C)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct ff_replay { pub length: u16, pub delay: u16, } #[repr(C)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct ff_trigger { pub button: u16, pub interval: u16, } #[repr(C)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct ff_envelope { pub attack_length: u16, pub attack_level: u16, pub fade_length: u16, pub fade_level: u16, } #[repr(C)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct ff_constant_effect { pub level: i16, pub envelope: ff_envelope, } #[repr(C)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct ff_ramp_effect { pub start_level: i16, pub end_level: i16, pub envelope: ff_envelope, } #[repr(C)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct ff_condition_effect { pub right_saturation: u16, pub left_saturation: u16, pub right_coeff: i16, pub left_coeff: i16, pub deadband: u16, pub center: i16, } #[repr(C)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct ff_periodic_effect { pub waveform: u16, pub period: u16, pub magnitude: i16, pub offset: i16, pub phase: u16, pub envelope: ff_envelope, pub custom_len: u32, pub custom_data: *mut i16, } #[repr(C)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct ff_rumble_effect { pub strong_magnitude: u16, pub weak_magnitude: u16, } #[repr(C)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct ff_effect { pub type_: u16, pub id: i16, pub direction: u16, pub trigger: ff_trigger, pub replay: ff_replay, // FIXME this is actually a union #[cfg(target_pointer_width = "64")] pub u: [u64; 4], #[cfg(target_pointer_width = "32")] pub u: [u32; 7], } #[repr(C)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct uinput_abs_setup { pub code: u16, pub absinfo: input_absinfo, } #[repr(C)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct uinput_setup { pub id: input_id, pub name: [libc::c_char; UINPUT_MAX_NAME_SIZE], pub ff_effects_max: u32, } evdev-0.13.1/src/compat.rs000064400000000000000000000025271046102023000134450ustar 00000000000000//! Compatibility layer for non-Linux builds. //! //! // input_absinfo, input_id, input_keymap_entry, uinput_abs_setup, uinput_setup input_event // ff_envelope ff_condition_effect ff_trigger ff_replay // EV_CNT INPUT_PROP_CNT REL_CNT ABS_CNT SW_CNT LED_CNT MSC_CNT FF_CNT SND_CNT use cfg_if::cfg_if; cfg_if! { if #[cfg(any( target_os = "linux", target_os = "l4re", target_os = "android", target_os = "emscripten" ))] { pub(crate) use libc::{ ff_condition_effect, ff_constant_effect, ff_envelope, ff_periodic_effect, ff_ramp_effect, ff_replay, ff_rumble_effect, ff_trigger, input_absinfo, input_event, input_id, input_keymap_entry, uinput_abs_setup, uinput_setup, ABS_CNT, EV_CNT, FF_CNT, INPUT_PROP_CNT, KEY_CNT, LED_CNT, MSC_CNT, REL_CNT, SND_CNT, SW_CNT, UINPUT_MAX_NAME_SIZE, }; } else { mod non_linux; pub(crate) use non_linux::{ ff_condition_effect, ff_constant_effect, ff_envelope, ff_periodic_effect, ff_ramp_effect, ff_replay, ff_rumble_effect, ff_trigger, input_absinfo, input_event, input_id, input_keymap_entry, uinput_abs_setup, uinput_setup, ABS_CNT, EV_CNT, FF_CNT, INPUT_PROP_CNT, KEY_CNT, LED_CNT, MSC_CNT, REL_CNT, SND_CNT, SW_CNT, UINPUT_MAX_NAME_SIZE, }; } } evdev-0.13.1/src/constants.rs000064400000000000000000000264511046102023000142000ustar 00000000000000use crate::compat::{ ABS_CNT, EV_CNT, FF_CNT, INPUT_PROP_CNT, LED_CNT, MSC_CNT, REL_CNT, SND_CNT, SW_CNT, }; /// Event types supported by the device. /// /// Values correspond to [/usr/include/linux/input-event-codes.h](https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h) /// /// This is implemented as a newtype around the u16 "type" field of `input_event`. #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct EventType(pub u16); evdev_enum!( EventType, Array, /// A bookkeeping event. Usually not important to applications. SYNCHRONIZATION = 0x00, /// A key changed state. A key, or button, is usually a momentary switch (in the circuit sense). It has two /// states: down, or up. There are events for when keys are pressed (become down) and /// released (become up). There are also "key repeats", where multiple events are sent /// while a key is down. KEY = 0x01, /// Movement on a relative axis. There is no absolute coordinate frame, just the fact that /// there was a change of a certain amount of units. Used for things like mouse movement or /// scroll wheels. RELATIVE = 0x02, /// Movement on an absolute axis. Used for things such as touch events and joysticks. ABSOLUTE = 0x03, /// Miscellaneous events that don't fall into other categories. For example, Key presses may /// send `MSC_SCAN` events before each KEY event MISC = 0x04, /// Change in a switch value. Switches are boolean conditions and usually correspond to a /// toggle switch of some kind in hardware. SWITCH = 0x05, /// An LED was toggled. LED = 0x11, /// A sound was made. SOUND = 0x12, /// There are no events of this type, to my knowledge, but represents metadata about key /// repeat configuration. REPEAT = 0x14, /// Looking at the source of [`fftest`](https://github.com/flosse/linuxconsole/blob/master/utils/fftest.c) /// This seems to be sent to the device with a previusly obtained effect id as a code in order to toggle the effect. FORCEFEEDBACK = 0x15, /// I think this is unused? POWER = 0x16, /// A force feedback effect's state changed. FORCEFEEDBACKSTATUS = 0x17, /// An event originating from uinput. UINPUT = 0x0101, ); impl EventType { pub(crate) const COUNT: usize = EV_CNT; } /// A "synchronization" message type published by the kernel into the events stream. #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct SynchronizationCode(pub u16); evdev_enum!( SynchronizationCode, /// Used to mark the end of a single atomic "reading" from the device. SYN_REPORT = 0, /// Appears to be unused. SYN_CONFIG = 1, /// "Used to synchronize and separate touch events" SYN_MT_REPORT = 2, /// Ring buffer filled, events were dropped. SYN_DROPPED = 3, ); /// Device properties. #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct PropType(pub u16); evdev_enum!( PropType, Array, /// This input device needs a pointer ("cursor") for the user to know its state. POINTER = 0x00, /// "direct input devices", according to the header. DIRECT = 0x01, /// "has button(s) under pad", according to the header. BUTTONPAD = 0x02, /// Touch rectangle only (I think this means that if there are multiple touches, then the /// bounding rectangle of all the touches is returned, not each touch). SEMI_MT = 0x03, /// "softbuttons at top of pad", according to the header. TOPBUTTONPAD = 0x04, /// Is a pointing stick ("nub" etc, ) POINTING_STICK = 0x05, /// Has an accelerometer. Probably reports relative events in that case? ACCELEROMETER = 0x06, ); impl PropType { pub(crate) const COUNT: usize = INPUT_PROP_CNT; } /// A type of relative axis measurement, typically produced by mice. #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct RelativeAxisCode(pub u16); evdev_enum!( RelativeAxisCode, Array, REL_X = 0x00, REL_Y = 0x01, REL_Z = 0x02, REL_RX = 0x03, REL_RY = 0x04, REL_RZ = 0x05, REL_HWHEEL = 0x06, REL_DIAL = 0x07, REL_WHEEL = 0x08, REL_MISC = 0x09, REL_RESERVED = 0x0a, REL_WHEEL_HI_RES = 0x0b, REL_HWHEEL_HI_RES = 0x0c, ); impl RelativeAxisCode { pub(crate) const COUNT: usize = REL_CNT; } /// A type of absolute axis measurement, typically used for touch events and joysticks. #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct AbsoluteAxisCode(pub u16); evdev_enum!( AbsoluteAxisCode, Array, ABS_X = 0x00, ABS_Y = 0x01, ABS_Z = 0x02, ABS_RX = 0x03, ABS_RY = 0x04, ABS_RZ = 0x05, ABS_THROTTLE = 0x06, ABS_RUDDER = 0x07, ABS_WHEEL = 0x08, ABS_GAS = 0x09, ABS_BRAKE = 0x0a, ABS_HAT0X = 0x10, ABS_HAT0Y = 0x11, ABS_HAT1X = 0x12, ABS_HAT1Y = 0x13, ABS_HAT2X = 0x14, ABS_HAT2Y = 0x15, ABS_HAT3X = 0x16, ABS_HAT3Y = 0x17, ABS_PRESSURE = 0x18, ABS_DISTANCE = 0x19, ABS_TILT_X = 0x1a, ABS_TILT_Y = 0x1b, ABS_TOOL_WIDTH = 0x1c, ABS_VOLUME = 0x20, ABS_MISC = 0x28, /// "MT slot being modified" ABS_MT_SLOT = 0x2f, /// "Major axis of touching ellipse" ABS_MT_TOUCH_MAJOR = 0x30, /// "Minor axis (omit if circular)" ABS_MT_TOUCH_MINOR = 0x31, /// "Major axis of approaching ellipse" ABS_MT_WIDTH_MAJOR = 0x32, /// "Minor axis (omit if circular)" ABS_MT_WIDTH_MINOR = 0x33, /// "Ellipse orientation" ABS_MT_ORIENTATION = 0x34, /// "Center X touch position" ABS_MT_POSITION_X = 0x35, /// "Center Y touch position" ABS_MT_POSITION_Y = 0x36, /// "Type of touching device" ABS_MT_TOOL_TYPE = 0x37, /// "Group a set of packets as a blob" ABS_MT_BLOB_ID = 0x38, /// "Unique ID of the initiated contact" ABS_MT_TRACKING_ID = 0x39, /// "Pressure on contact area" ABS_MT_PRESSURE = 0x3a, /// "Contact over distance" ABS_MT_DISTANCE = 0x3b, /// "Center X tool position" ABS_MT_TOOL_X = 0x3c, /// "Center Y tool position" ABS_MT_TOOL_Y = 0x3d, ); impl AbsoluteAxisCode { pub(crate) const COUNT: usize = ABS_CNT; } /// An event type corresponding to a physical or virtual switch. #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct SwitchCode(pub u16); evdev_enum!( SwitchCode, Array, /// "set = lid shut" SW_LID = 0x00, /// "set = tablet mode" SW_TABLET_MODE = 0x01, /// "set = inserted" SW_HEADPHONE_INSERT = 0x02, /// "rfkill master switch, type 'any'" SW_RFKILL_ALL = 0x03, /// "set = inserted" SW_MICROPHONE_INSERT = 0x04, /// "set = plugged into doc" SW_DOCK = 0x05, /// "set = inserted" SW_LINEOUT_INSERT = 0x06, /// "set = mechanical switch set" SW_JACK_PHYSICAL_INSERT = 0x07, /// "set = inserted" SW_VIDEOOUT_INSERT = 0x08, /// "set = lens covered" SW_CAMERA_LENS_COVER = 0x09, /// "set = keypad slide out" SW_KEYPAD_SLIDE = 0x0a, /// "set = front proximity sensor active" SW_FRONT_PROXIMITY = 0x0b, /// "set = rotate locked/disabled" SW_ROTATE_LOCK = 0x0c, /// "set = inserted" SW_LINEIN_INSERT = 0x0d, /// "set = device disabled" SW_MUTE_DEVICE = 0x0e, /// "set = pen inserted" SW_PEN_INSERTED = 0x0f, /// "set = cover closed" SW_MACHINE_COVER = 0x10, ); impl SwitchCode { pub(crate) const COUNT: usize = SW_CNT; } /// LEDs specified by USB HID. #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct LedCode(pub u16); evdev_enum!( LedCode, Array, LED_NUML = 0x00, LED_CAPSL = 0x01, LED_SCROLLL = 0x02, LED_COMPOSE = 0x03, LED_KANA = 0x04, /// "Stand-by" LED_SLEEP = 0x05, LED_SUSPEND = 0x06, LED_MUTE = 0x07, /// "Generic indicator" LED_MISC = 0x08, /// "Message waiting" LED_MAIL = 0x09, /// "External power connected" LED_CHARGING = 0x0a, ); impl LedCode { pub(crate) const COUNT: usize = LED_CNT; } /// Various miscellaneous event types. #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct MiscCode(pub u16); evdev_enum!( MiscCode, Array, /// Serial number, only exported for tablets ("Transducer Serial Number") MSC_SERIAL = 0x00, /// Only used by the PowerMate driver, right now. MSC_PULSELED = 0x01, /// Completely unused. MSC_GESTURE = 0x02, /// "Raw" event, rarely used. MSC_RAW = 0x03, /// Key scancode MSC_SCAN = 0x04, /// Completely unused. MSC_TIMESTAMP = 0x05, ); impl MiscCode { pub(crate) const COUNT: usize = MSC_CNT; } /// Force feedback effect types #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct FFEffectCode(pub u16); evdev_enum!( FFEffectCode, Array, /// Rumble effects. FF_RUMBLE = 0x50, /// Can render periodic effects with any of the waveforms. FF_PERIODIC = 0x51, /// Can render constant force effects. FF_CONSTANT = 0x52, /// Can simulate the presence of a spring. FF_SPRING = 0x53, /// Can simulate friction. FF_FRICTION = 0x54, /// Can simulate damper effects. FF_DAMPER = 0x55, /// Can simulate inertia. FF_INERTIA = 0x56, /// Can render ramp effects. FF_RAMP = 0x57, /// Square waveform. FF_SQUARE = 0x58, /// Triangle waveform. FF_TRIANGLE = 0x59, /// Sine waveform. FF_SINE = 0x5a, /// Sawtooth up waveform. FF_SAW_UP = 0x5b, /// Sawtooth down waveform. FF_SAW_DOWN = 0x5c, /// Custom waveform. FF_CUSTOM = 0x5d, /// The gain is adjustable. FF_GAIN = 0x60, /// The autocenter is adjustable. FF_AUTOCENTER = 0x61, ); impl FFEffectCode { pub(crate) const COUNT: usize = FF_CNT; } /// Force feedback effect status #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct FFStatusCode(pub u16); evdev_enum!( FFStatusCode, Array, /// The force feedback event is currently stopped. FF_STATUS_STOPPED = 0x00, /// The force feedback event is currently playing. FF_STATUS_PLAYING = 0x01, ); impl FFStatusCode { pub(crate) const COUNT: usize = 2; } #[derive(Copy, Clone, PartialEq, Eq)] pub struct RepeatCode(pub u16); evdev_enum!(RepeatCode, REP_DELAY = 0x00, REP_PERIOD = 0x01,); // impl RepeatType { // pub(crate) const COUNT: usize = libc::REP_CNT; // } /// A type associated with simple sounds, such as beeps or tones. #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct SoundCode(pub u16); evdev_enum!( SoundCode, Array, SND_CLICK = 0x00, SND_BELL = 0x01, SND_TONE = 0x02, ); impl SoundCode { pub(crate) const COUNT: usize = SND_CNT; } /// A uinput event published by the kernel into the events stream for uinput devices. #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct UInputCode(pub u16); evdev_enum!( UInputCode, /// The virtual uinput device is uploading a force feedback effect. UI_FF_UPLOAD = 1, /// The virtual uinput device is erasing a force feedback event. UI_FF_ERASE = 2, ); // some more structs without any constats. They are only there to // porvide a consitatnt type system and simple code generation. #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct PowerCode(pub u16); #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct OtherCode(pub u16, pub u16); evdev-0.13.1/src/device_state.rs000064400000000000000000000104601046102023000146140ustar 00000000000000use crate::compat::input_absinfo; use crate::{constants::*, raw_stream::RawDevice}; use crate::{AttributeSet, AttributeSetRef, EventSummary, InputEvent, KeyCode}; use std::time::SystemTime; /// A **cached** representation of device state at a certain time. #[derive(Debug)] pub struct DeviceState { /// The state corresponds to kernel state at this timestamp. pub(crate) timestamp: SystemTime, /// Set = key pressed pub(crate) key_vals: Option>, pub(crate) abs_vals: Option>, /// Set = switch enabled (closed) pub(crate) switch_vals: Option>, /// Set = LED lit pub(crate) led_vals: Option>, } // manual Clone impl for clone_from optimization impl Clone for DeviceState { fn clone(&self) -> Self { Self { timestamp: self.timestamp, key_vals: self.key_vals.clone(), abs_vals: self.abs_vals.clone(), switch_vals: self.switch_vals.clone(), led_vals: self.led_vals.clone(), } } fn clone_from(&mut self, other: &Self) { self.timestamp.clone_from(&other.timestamp); self.key_vals.clone_from(&other.key_vals); self.abs_vals.clone_from(&other.abs_vals); self.switch_vals.clone_from(&other.switch_vals); self.led_vals.clone_from(&other.led_vals); } } impl DeviceState { /// Create an empty `DeviceState`. The `{abs,key,etc}_vals` for the returned state will return /// `Some` if `supported_events()` contains that `EventType`. pub(crate) fn new(device: &RawDevice) -> Self { let supports = device.supported_events(); let key_vals = if supports.contains(EventType::KEY) { Some(AttributeSet::new()) } else { None }; let abs_vals = if supports.contains(EventType::ABSOLUTE) { Some(Box::new(crate::raw_stream::ABS_VALS_INIT)) } else { None }; let switch_vals = if supports.contains(EventType::SWITCH) { Some(AttributeSet::new()) } else { None }; let led_vals = if supports.contains(EventType::LED) { Some(AttributeSet::new()) } else { None }; DeviceState { timestamp: std::time::UNIX_EPOCH, key_vals, abs_vals, switch_vals, led_vals, } } /// Returns the time when this snapshot was taken. pub fn timestamp(&self) -> SystemTime { self.timestamp } /// Returns the set of keys pressed when the snapshot was taken. /// /// Returns `None` if keys are not supported by this device. pub fn key_vals(&self) -> Option<&AttributeSetRef> { self.key_vals.as_deref() } /// Returns the set of absolute axis measurements when the snapshot was taken. /// /// Returns `None` if not supported by this device. pub fn abs_vals(&self) -> Option<&[input_absinfo]> { self.abs_vals.as_deref().map(|v| &v[..]) } /// Returns the set of switches triggered when the snapshot was taken. /// /// Returns `None` if switches are not supported by this device. pub fn switch_vals(&self) -> Option<&AttributeSetRef> { self.switch_vals.as_deref() } /// Returns the set of LEDs turned on when the snapshot was taken. /// /// Returns `None` if LEDs are not supported by this device. pub fn led_vals(&self) -> Option<&AttributeSetRef> { self.led_vals.as_deref() } #[inline] pub(crate) fn process_event(&mut self, ev: InputEvent) { match ev.destructure() { EventSummary::Key(_, code, _) => { let keys = self .key_vals .as_deref_mut() .expect("got a key event despite not supporting keys"); keys.set(code, ev.value() != 0); } EventSummary::AbsoluteAxis(_, axis, _) => { let axes = self .abs_vals .as_deref_mut() .expect("got an abs event despite not supporting absolute axes"); axes[axis.0 as usize].value = ev.value(); } _ => {} } } } evdev-0.13.1/src/event_variants.rs000064400000000000000000000176021046102023000152120ustar 00000000000000//! The event_variants module contains new-type wrappers around [`InputEvent`] //! for each known [`EventType`]. //! //! These event variants guarantee that the underlying `InputEvent` has the //! corresponding type. They may also contain additional methods for the //! specific type and convenient shortcut methods for event creation. //! An `InputEvent` can be converted to the corresponding event variant with //! the [`InputEvent::destructure()`] method. Each event variant implements //! `Into` and `Deref` for easy back conversion. use std::fmt; use std::ops::Deref; use std::time::SystemTime; use crate::compat::input_event; use crate::constants::{ AbsoluteAxisCode, FFStatusCode, LedCode, MiscCode, OtherCode, PowerCode, RelativeAxisCode, RepeatCode, SoundCode, SwitchCode, SynchronizationCode, UInputCode, }; use crate::scancodes::KeyCode; use crate::{systime_to_timeval, EventType, FFEffectCode}; use crate::{EventSummary, InputEvent}; #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] /// A bookkeeping event. Usually not important to applications. /// [`EventType::SYNCHRONIZATION`] pub struct SynchronizationEvent(InputEvent); #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] /// [`EventType::KEY`] pub struct KeyEvent(InputEvent); #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] /// [`EventType::RELATIVE`] pub struct RelativeAxisEvent(InputEvent); #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] /// [`EventType::ABSOLUTE`] pub struct AbsoluteAxisEvent(InputEvent); #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] /// [`EventType::MISC`] pub struct MiscEvent(InputEvent); #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] /// [`EventType::SWITCH`] pub struct SwitchEvent(InputEvent); #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] /// [`EventType::LED`] pub struct LedEvent(InputEvent); #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] /// [`EventType::SOUND`] pub struct SoundEvent(InputEvent); #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] /// [`EventType::REPEAT`] pub struct RepeatEvent(InputEvent); #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] /// [`EventType::FORCEFEEDBACK`] pub struct FFEvent(InputEvent); #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] /// [`EventType::POWER`] pub struct PowerEvent(InputEvent); #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] /// [`EventType::FORCEFEEDBACKSTATUS`] pub struct FFStatusEvent(InputEvent); #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] /// [`EventType::UINPUT`] pub struct UInputEvent(InputEvent); #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] /// An event not covered by any other variant. pub struct OtherEvent(pub(crate) InputEvent); macro_rules! input_event_newtype { ($name:ty) => { impl AsRef for $name { fn as_ref(&self) -> &input_event { &self.0.as_ref() } } impl AsRef for $name { fn as_ref(&self) -> &InputEvent { &self.0 } } // never implement the other direction! impl From<$name> for InputEvent { fn from(event: $name) -> Self { event.0 } } impl Deref for $name { type Target = InputEvent; fn deref(&self) -> &InputEvent { &self.0 } } }; ($name:ty, $evdev_type:path, $kind:path) => { impl $name { pub fn new($kind(code): $kind, value: i32) -> Self { let raw = input_event { time: libc::timeval { tv_sec: 0, tv_usec: 0, }, type_: $evdev_type.0, code, value, }; Self::from_raw(raw) } pub fn new_now($kind(code): $kind, value: i32) -> Self { let raw = input_event { time: systime_to_timeval(&SystemTime::now()), type_: $evdev_type.0, code, value, }; Self::from_raw(raw) } pub fn destructure(&self) -> ($kind, i32) { (self.code(), self.value()) } pub fn code(&self) -> $kind { $kind(self.0.code()) } // must be kept internal fn from_raw(raw: input_event) -> Self { match EventType(raw.type_) { $evdev_type => Self(InputEvent(raw)), _ => unreachable!(), } } // must be kept internal pub(crate) fn from_event(event: InputEvent) -> Self { match event.event_type() { $evdev_type => Self(event), _ => unreachable!(), } } } impl fmt::Debug for $name { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut debug = f.debug_struct(stringify!($name)); debug.field("time", &self.timestamp()); debug.field("code", &self.code()); debug.field("value", &self.value()).finish() } } input_event_newtype!($name); }; ($name:ty, $evdev_type:path, $kind:path, $summary:path) => { impl From<$name> for EventSummary { fn from(event: $name) -> EventSummary { let (kind, value) = event.destructure(); $summary(event, kind, value) } } input_event_newtype!($name, $evdev_type, $kind); }; } input_event_newtype!( SynchronizationEvent, EventType::SYNCHRONIZATION, SynchronizationCode, EventSummary::Synchronization ); input_event_newtype!(KeyEvent, EventType::KEY, KeyCode, EventSummary::Key); input_event_newtype!( RelativeAxisEvent, EventType::RELATIVE, RelativeAxisCode, EventSummary::RelativeAxis ); input_event_newtype!( AbsoluteAxisEvent, EventType::ABSOLUTE, AbsoluteAxisCode, EventSummary::AbsoluteAxis ); input_event_newtype!(MiscEvent, EventType::MISC, MiscCode, EventSummary::Misc); input_event_newtype!( SwitchEvent, EventType::SWITCH, SwitchCode, EventSummary::Switch ); input_event_newtype!(LedEvent, EventType::LED, LedCode, EventSummary::Led); input_event_newtype!(SoundEvent, EventType::SOUND, SoundCode, EventSummary::Sound); input_event_newtype!( RepeatEvent, EventType::REPEAT, RepeatCode, EventSummary::Repeat ); input_event_newtype!( FFEvent, EventType::FORCEFEEDBACK, FFEffectCode, EventSummary::ForceFeedback ); input_event_newtype!(PowerEvent, EventType::POWER, PowerCode, EventSummary::Power); input_event_newtype!( FFStatusEvent, EventType::FORCEFEEDBACKSTATUS, FFStatusCode, EventSummary::ForceFeedbackStatus ); input_event_newtype!( UInputEvent, EventType::UINPUT, UInputCode, EventSummary::UInput ); input_event_newtype!(OtherEvent); impl OtherEvent { pub fn kind(&self) -> OtherCode { OtherCode(self.event_type().0, self.code()) } pub fn destructure(&self) -> (OtherCode, i32) { (self.kind(), self.value()) } } impl From for EventSummary { fn from(event: OtherEvent) -> Self { let (kind, value) = event.destructure(); EventSummary::Other(event, kind, value) } } impl fmt::Debug for OtherEvent { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut debug = f.debug_struct("OtherEvent"); debug.field("time", &self.timestamp()); debug.field("type", &self.event_type()); debug.field("code", &self.code()); debug.field("value", &self.value()).finish() } } evdev-0.13.1/src/ff.rs000064400000000000000000000276321046102023000125610ustar 00000000000000use crate::attribute_set::EvdevEnum; use crate::compat::{ff_condition_effect, ff_envelope, ff_replay, ff_trigger}; use crate::constants::FFEffectCode; use crate::sys; /// Describes a generic force feedback effect envelope. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct FFEnvelope { /// How long the attack should last in milliseconds. pub attack_length: u16, /// The level of the attack at the beginning of the attack. pub attack_level: u16, /// How long the fade should last in milliseconds. pub fade_length: u16, /// The level of the fade at the end of the fade. pub fade_level: u16, } impl From for FFEnvelope { fn from(value: ff_envelope) -> Self { Self { attack_length: value.attack_length, attack_level: value.attack_level, fade_length: value.fade_length, fade_level: value.fade_level, } } } impl From for ff_envelope { fn from(other: FFEnvelope) -> Self { ff_envelope { attack_length: other.attack_length, attack_level: other.attack_level, fade_length: other.fade_length, fade_level: other.fade_level, } } } /// Describes the waveform for periodic force feedback effects. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum FFWaveform { /// Square waveform. Square, /// Triangle waveform. Triangle, /// Sine waveform. Sine, /// Sawtooth up waveform. SawUp, /// Sawtooth down waveform. SawDown, } impl From for FFEffectCode { fn from(other: FFWaveform) -> Self { match other { FFWaveform::Square => FFEffectCode::FF_SQUARE, FFWaveform::Triangle => FFEffectCode::FF_TRIANGLE, FFWaveform::Sine => FFEffectCode::FF_SINE, FFWaveform::SawUp => FFEffectCode::FF_SAW_UP, FFWaveform::SawDown => FFEffectCode::FF_SAW_DOWN, } } } /// Describes a spring or friction force feedback effect. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct FFCondition { /// The maximum level when the joystick is moved all the way to the right. pub right_saturation: u16, /// The maximum level when the joystick is moved all the way to the left. pub left_saturation: u16, /// The coefficient that controls how fast the force grows when the joystick moves to the /// right. pub right_coefficient: i16, /// The coefficient that controls how fast the force grows when the joystick moves to the left. pub left_coefficient: i16, /// The size of the dead zone, which is the zone where no force is produced. pub deadband: u16, /// The position of the dead zone. pub center: i16, } impl From for FFCondition { fn from(value: ff_condition_effect) -> Self { Self { right_saturation: value.right_saturation, left_saturation: value.left_saturation, right_coefficient: value.right_coeff, left_coefficient: value.left_coeff, deadband: value.deadband, center: value.center, } } } impl From for ff_condition_effect { fn from(other: FFCondition) -> Self { ff_condition_effect { right_saturation: other.right_saturation, left_saturation: other.left_saturation, right_coeff: other.right_coefficient, left_coeff: other.left_coefficient, deadband: other.deadband, center: other.center, } } } #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum FFEffectKind { Damper, Inertia, Constant { /// The strength of the effect. level: i16, /// Envelope data. envelope: FFEnvelope, }, Ramp { /// The strength at the beginning of the effect. start_level: i16, /// The strength at the end of the effect. end_level: i16, /// Envelope data. envelope: FFEnvelope, }, Periodic { /// The kind of waveform to use for the force feedback effect. waveform: FFWaveform, /// The period of the wave in milliseconds. period: u16, /// The peak value or amplitude of the wave. magnitude: i16, /// The mean value of the wave (roughly). offset: i16, /// The horizontal shift. phase: u16, /// Envelope data. envelope: FFEnvelope, }, Spring { /// Condition data for each axis. condition: [FFCondition; 2], }, Friction { /// Condition data for each axis. condition: [FFCondition; 2], }, Rumble { /// The magnitude of the heavy motor. strong_magnitude: u16, /// The magnitude of the light motor. weak_magnitude: u16, }, } impl From for FFEffectCode { fn from(other: FFEffectKind) -> Self { match other { FFEffectKind::Damper => FFEffectCode::FF_DAMPER, FFEffectKind::Inertia => FFEffectCode::FF_INERTIA, FFEffectKind::Constant { .. } => FFEffectCode::FF_CONSTANT, FFEffectKind::Ramp { .. } => FFEffectCode::FF_RAMP, FFEffectKind::Periodic { .. } => FFEffectCode::FF_PERIODIC, FFEffectKind::Spring { .. } => FFEffectCode::FF_SPRING, FFEffectKind::Friction { .. } => FFEffectCode::FF_FRICTION, FFEffectKind::Rumble { .. } => FFEffectCode::FF_RUMBLE, } } } /// Trigger information for the force feedback effect. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct FFTrigger { /// The button number that triggers the force feedback effect. pub button: u16, /// How long to wait before the force feedback effect can be triggered again in milliseconds. pub interval: u16, } impl From for FFTrigger { fn from(value: ff_trigger) -> Self { Self { button: value.button, interval: value.interval, } } } impl From for ff_trigger { fn from(other: FFTrigger) -> Self { ff_trigger { button: other.button, interval: other.interval, } } } /// Scheduling information for the force feedback effect. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct FFReplay { /// How long the force feedback effect should last in milliseconds. pub length: u16, /// How long to wait before the force feedback effect should play in milliseconds. pub delay: u16, } impl From for FFReplay { fn from(value: ff_replay) -> Self { Self { length: value.length, delay: value.delay, } } } impl From for ff_replay { fn from(other: FFReplay) -> Self { ff_replay { length: other.length, delay: other.delay, } } } #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct FFEffectData { /// The direction of the force feedback effect. pub direction: u16, /// Trigger conditions. pub trigger: FFTrigger, /// Scheduling of the effect. pub replay: FFReplay, /// The type of force feedback effect and any associated parameters. pub kind: FFEffectKind, } impl From for FFEffectData { fn from(value: sys::ff_effect) -> Self { let kind = match FFEffectCode::from_index(value.type_ as usize) { FFEffectCode::FF_DAMPER => FFEffectKind::Damper, FFEffectCode::FF_INERTIA => FFEffectKind::Inertia, FFEffectCode::FF_CONSTANT => { let constant = unsafe { value.u.constant }; FFEffectKind::Constant { level: constant.level, envelope: constant.envelope.into(), } } FFEffectCode::FF_RAMP => { let ramp = unsafe { value.u.ramp }; FFEffectKind::Ramp { start_level: ramp.start_level, end_level: ramp.end_level, envelope: ramp.envelope.into(), } } FFEffectCode::FF_PERIODIC => { let periodic = unsafe { value.u.periodic }; FFEffectKind::Periodic { waveform: match FFEffectCode::from_index(periodic.waveform as usize) { FFEffectCode::FF_SQUARE => FFWaveform::Square, FFEffectCode::FF_TRIANGLE => FFWaveform::Triangle, FFEffectCode::FF_SINE => FFWaveform::Sine, FFEffectCode::FF_SAW_UP => FFWaveform::SawUp, FFEffectCode::FF_SAW_DOWN => FFWaveform::SawDown, _ => unreachable!(), }, period: periodic.period, magnitude: periodic.magnitude, offset: periodic.offset, phase: periodic.phase, envelope: periodic.envelope.into(), } } FFEffectCode::FF_SPRING => { let condition = unsafe { value.u.condition }; FFEffectKind::Spring { condition: [condition[0].into(), condition[1].into()], } } FFEffectCode::FF_FRICTION => { let condition = unsafe { value.u.condition }; FFEffectKind::Friction { condition: [condition[0].into(), condition[1].into()], } } FFEffectCode::FF_RUMBLE => { let rumble = unsafe { value.u.rumble }; FFEffectKind::Rumble { strong_magnitude: rumble.strong_magnitude, weak_magnitude: rumble.weak_magnitude, } } _ => unreachable!(), }; Self { direction: value.direction, trigger: value.trigger.into(), replay: value.replay.into(), kind, } } } impl From for sys::ff_effect { fn from(other: FFEffectData) -> Self { let mut effect: sys::ff_effect = unsafe { std::mem::zeroed() }; let type_: FFEffectCode = other.kind.into(); effect.type_ = type_.0; effect.direction = other.direction; effect.trigger = other.trigger.into(); effect.replay = other.replay.into(); match other.kind { FFEffectKind::Constant { level, envelope } => { effect.u.constant.level = level; effect.u.constant.envelope = envelope.into(); } FFEffectKind::Ramp { start_level, end_level, envelope, } => { effect.u.ramp.start_level = start_level; effect.u.ramp.end_level = end_level; effect.u.ramp.envelope = envelope.into(); } FFEffectKind::Periodic { waveform, period, magnitude, offset, phase, envelope, } => { let waveform: FFEffectCode = waveform.into(); effect.u.periodic.waveform = waveform.0; effect.u.periodic.period = period; effect.u.periodic.magnitude = magnitude; effect.u.periodic.offset = offset; effect.u.periodic.phase = phase; effect.u.periodic.envelope = envelope.into(); } FFEffectKind::Spring { condition } | FFEffectKind::Friction { condition } => { effect.u.condition = [condition[0].into(), condition[1].into()]; } FFEffectKind::Rumble { strong_magnitude, weak_magnitude, } => { effect.u.rumble.strong_magnitude = strong_magnitude; effect.u.rumble.weak_magnitude = weak_magnitude; } _ => (), } effect } } evdev-0.13.1/src/inputid.rs000064400000000000000000000057641046102023000136440ustar 00000000000000use crate::compat::input_id; use std::fmt; #[derive(Clone, Hash, Eq, PartialEq)] #[repr(transparent)] pub struct InputId(pub(crate) input_id); impl From for InputId { #[inline] fn from(id: input_id) -> Self { Self(id) } } impl AsRef for InputId { #[inline] fn as_ref(&self) -> &input_id { &self.0 } } impl InputId { pub fn bus_type(&self) -> BusType { BusType(self.0.bustype) } pub fn vendor(&self) -> u16 { self.0.vendor } pub fn product(&self) -> u16 { self.0.product } pub fn version(&self) -> u16 { self.0.version } /// Crate a new InputId, useful for customizing virtual input devices. pub fn new(bus_type: BusType, vendor: u16, product: u16, version: u16) -> Self { Self::from(input_id { bustype: bus_type.0, vendor, product, version, }) } } impl fmt::Debug for InputId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("InputId") .field("bus_type", &self.bus_type()) .field("vendor", &format_args!("{:#x}", self.vendor())) .field("product", &format_args!("{:#x}", self.product())) .field("version", &format_args!("{:#x}", self.version())) .finish() } } /// The bus type of an [`InputId`]. #[derive(Copy, Clone, PartialEq, Eq)] pub struct BusType(pub u16); evdev_enum!( BusType, BUS_PCI = 0x01, BUS_ISAPNP = 0x02, BUS_USB = 0x03, BUS_HIL = 0x04, BUS_BLUETOOTH = 0x05, BUS_VIRTUAL = 0x06, BUS_ISA = 0x10, BUS_I8042 = 0x11, BUS_XTKBD = 0x12, BUS_RS232 = 0x13, BUS_GAMEPORT = 0x14, BUS_PARPORT = 0x15, BUS_AMIGA = 0x16, BUS_ADB = 0x17, BUS_I2C = 0x18, BUS_HOST = 0x19, BUS_GSC = 0x1A, BUS_ATARI = 0x1B, BUS_SPI = 0x1C, BUS_RMI = 0x1D, BUS_CEC = 0x1E, BUS_INTEL_ISHTP = 0x1F, ); impl fmt::Display for BusType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = match *self { Self::BUS_PCI => "PCI", Self::BUS_ISAPNP => "ISA Plug 'n Play", Self::BUS_USB => "USB", Self::BUS_HIL => "HIL", Self::BUS_BLUETOOTH => "Bluetooth", Self::BUS_VIRTUAL => "Virtual", Self::BUS_ISA => "ISA", Self::BUS_I8042 => "i8042", Self::BUS_XTKBD => "XTKBD", Self::BUS_RS232 => "RS232", Self::BUS_GAMEPORT => "Gameport", Self::BUS_PARPORT => "Parallel Port", Self::BUS_AMIGA => "Amiga", Self::BUS_ADB => "ADB", Self::BUS_I2C => "I2C", Self::BUS_HOST => "Host", Self::BUS_GSC => "GSC", Self::BUS_ATARI => "Atari", Self::BUS_SPI => "SPI", Self::BUS_RMI => "RMI", Self::BUS_CEC => "CEC", Self::BUS_INTEL_ISHTP => "Intel ISHTP", _ => "Unknown", }; f.write_str(s) } } evdev-0.13.1/src/lib.rs000064400000000000000000000521611046102023000127270ustar 00000000000000//! Linux event device handling. //! //! The Linux kernel's "evdev" subsystem exposes input devices to userspace in a generic, //! consistent way. I'll try to explain the device model as completely as possible. The upstream //! kernel documentation is split across two files: //! //! - //! - //! //! The `evdev` kernel system exposes input devices as character devices in `/dev/input`, //! typically `/dev/input/eventX` where `X` is an integer. //! Userspace applications can use `ioctl` system calls to interact with these devices. //! Libraries such as this one abstract away the low level calls to provide a high level //! interface. //! //! Applications can interact with `uinput` by writing to `/dev/uinput` to create virtual //! devices and send events to the virtual devices. //! Virtual devices are created in `/sys/devices/virtual/input`. //! //! # Devices //! //! Devices can be opened directly via their path: //! ```no_run //! # fn main() -> Result<(), Box> { //! use evdev::Device; //! let device = Device::open("/dev/input/event0")?; //! # Ok(()) //! # } //! ``` //! This approach requires the calling process to have the appropriate privileges to //! open the device node (typically this requires running as root user). //! Alternatively a device can be created from an already open file descriptor. This approach //! is useful where the file descriptor is provided by an external privileged process //! (e.g. systemd's logind): //! //! ```no_run //! # fn main() -> Result<(), Box> { //! use evdev::Device; //! use std::fs::File; //! use std::os::fd::OwnedFd; //! let f = File::open("/dev/input/event0")?; //! let fd = OwnedFd::from(f); //! let device = Device::from_fd(fd)?; //! # Ok(()) //! # } //! ``` //! //! # Input Events //! //! Devices emit events, represented by the [`InputEvent`] struct. //! A input event has three main fields: event [type](InputEvent::event_type), [code](InputEvent::code) //! and [value](InputEvent::value) //! //! The kernel documentation specifies different event types, reperesented by the [`EventType`] struct. //! Each device can support a subset of those types. See [`Device::supported_events()`]. //! For each of the known event types there is a new-type wrapper around [`InputEvent`] //! in [`event_variants`] see the module documenation for more info about those. //! //! For most event types the kernel documentation also specifies a set of codes, represented by a new-type //! e.g. [`KeyCode`]. The individual codes of a [`EventType`] that a device supports can be retrieved //! through the `Device::supported_*()` methods, e.g. [`Device::supported_keys()`]: //! ```no_run //! # fn main() -> Result<(), Box> { //! use evdev::{Device, KeyCode}; //! let device = Device::open("/dev/input/event0")?; //! // check if the device has an ENTER key //! if device.supported_keys().map_or(false, |keys| keys.contains(KeyCode::KEY_ENTER)) { //! println!("are you prepared to ENTER the world of evdev?"); //! } else { //! println!(":("); //! } //! # Ok(()) //! # } //! ``` //! A [`InputEvent`] with a type of [`EventType::KEY`] a code of [`KeyCode::KEY_ENTER`] and a //! value of 1 is emitted when the Enter key is pressed. //! //! All events (even single events) are sent in batches followed by a synchronization event: //! `EV_SYN / SYN_REPORT / 0`. //! Events are grouped into batches based on if they are related and occur simultaneously, //! for example movement of a mouse triggers a movement event for the `X` and `Y` axes //! separately in a batch of 2 events. //! //! The evdev crate exposes functions to query the current state of a device from the kernel, as //! well as a function that can be called continuously to provide an iterator over update events //! as they arrive. //! //! ## Matching Events //! //! When reading from an input Device it is often useful to check which type/code or value //! the event has. This library provides the [`EventSummary`] enum which can be used to //! match specific events. Calling [`InputEvent::destructure`] will return that enum. //! //! ```no_run //! # fn main() -> Result<(), Box> { //! use evdev::*; //! let mut device = Device::open("/dev/input/event0")?; //! loop { //! for event in device.fetch_events().unwrap(){ //! match event.destructure(){ //! EventSummary::Key(ev, KeyCode::KEY_A, 1) => { //! println!("Key 'a' was pressed, got event: {:?}", ev); //! }, //! EventSummary::Key(_, key_type, 0) => { //! println!("Key {:?} was released", key_type); //! }, //! EventSummary::AbsoluteAxis(_, axis, value) => { //! println!("The Axis {:?} was moved to {}", axis, value); //! }, //! _ => println!("got a different event!") //! } //! } //! } //! # unreachable!() //! # } //! ``` //! //! # Synchronizing versus Raw modes //! //! This library can be used in either Raw or Synchronizing modes, which correspond roughly to //! evdev's `LIBEVDEV_READ_FLAG_NORMAL` and `LIBEVDEV_READ_FLAG_SYNC` modes, respectively. //! In both modes, calling `fetch_events` and driving the resulting iterator to completion //! will provide a stream of real-time events from the underlying kernel device state. //! As the state changes, the kernel will write events into a ring buffer. If the buffer becomes full, the //! kernel will *drop* events from the ring buffer and leave an event telling userspace that it //! did so. At this point, if the application were using the events it received to update its //! internal idea of what state the hardware device is in, it will be wrong: it is missing some //! events. //! //! In synchronous mode, this library tries to ease that pain by removing the corrupted events //! and injecting fake events as if the device had updated normally. Note that this is best-effort; //! events can never be recovered once lost. This synchronization comes at a performance cost: each //! set of input events read from the kernel in turn updates an internal state buffer, and events //! must be internally held back until the end of each frame. If this latency is unacceptable or //! for any reason you want to see every event directly, a raw stream reader is also provided. //! //! As an example of how synchronization behaves, if a switch is toggled twice there will be two switch events //! in the buffer. However, if the kernel needs to drop events, when the device goes to synchronize //! state with the kernel only one (or zero, if the switch is in the same state as it was before //! the sync) switch events will be visible in the stream. //! //! This cache can also be queried. For example, the [`DeviceState::led_vals`] method will tell you which //! LEDs are currently lit on the device. As calling code consumes each iterator, this state will be //! updated, and it will be fully re-synchronized with the kernel if the stream drops any events. //! //! It is recommended that you dedicate a thread to processing input events, or use epoll or an //! async runtime with the fd returned by `::as_raw_fd` to process events when //! they are ready. //! //! For demonstrations of how to use this library in blocking, nonblocking, and async (tokio) modes, //! please reference the "examples" directory. // should really be cfg(target_os = "linux") and maybe also android? #![cfg(unix)] // Flag items' docs' with their required feature flags, but only on docsrs so // that local docs can still be built on stable toolchains. // As of the time of writing, the stabilization plan is such that: // - Once stabilized, this attribute should be replaced with #![doc(auto_cfg)] // - Then in edition 2024, doc(auto_cfg) will become the default and the // attribute can be removed entirely // (see https://github.com/rust-lang/rust/pull/100883#issuecomment-1264470491) #![cfg_attr(docsrs, feature(doc_auto_cfg))] // has to be first for its macro #[macro_use] mod attribute_set; mod compat; mod constants; mod device_state; pub mod event_variants; mod ff; mod inputid; pub mod raw_stream; mod scancodes; mod sync_stream; mod sys; pub mod uinput; use crate::compat::{input_absinfo, input_event, uinput_abs_setup}; use std::fmt::{self, Display}; use std::io; use std::os::fd::{AsFd, AsRawFd, OwnedFd}; use std::path::PathBuf; use std::time::{Duration, SystemTime}; pub use attribute_set::{AttributeSet, AttributeSetRef, EvdevEnum}; pub use constants::*; pub use device_state::DeviceState; pub use event_variants::*; pub use ff::*; pub use inputid::*; pub use scancodes::*; pub use sync_stream::*; macro_rules! common_trait_impls { ($raw:ty, $wrapper:ty) => { impl From<$raw> for $wrapper { fn from(raw: $raw) -> Self { Self(raw) } } impl From<$wrapper> for $raw { fn from(wrapper: $wrapper) -> Self { wrapper.0 } } impl AsRef<$raw> for $wrapper { fn as_ref(&self) -> &$raw { &self.0 } } }; } const EVENT_BATCH_SIZE: usize = 32; /// A convenience mapping for matching a [`InputEvent`] while simultaniously checking its kind `(type, code)` /// and capturing the value /// /// Note This enum can not enforce that `InputEvent.code() == ` enum variant(code). /// It is suggested to not construct this enum and instead use [`InputEvent::destructure`] to obtain instances. #[derive(Debug)] pub enum EventSummary { Synchronization(SynchronizationEvent, SynchronizationCode, i32), Key(KeyEvent, KeyCode, i32), RelativeAxis(RelativeAxisEvent, RelativeAxisCode, i32), AbsoluteAxis(AbsoluteAxisEvent, AbsoluteAxisCode, i32), Misc(MiscEvent, MiscCode, i32), Switch(SwitchEvent, SwitchCode, i32), Led(LedEvent, LedCode, i32), Sound(SoundEvent, SoundCode, i32), Repeat(RepeatEvent, RepeatCode, i32), ForceFeedback(FFEvent, FFEffectCode, i32), Power(PowerEvent, PowerCode, i32), ForceFeedbackStatus(FFStatusEvent, FFStatusCode, i32), UInput(UInputEvent, UInputCode, i32), Other(OtherEvent, OtherCode, i32), } impl From for EventSummary { fn from(value: InputEvent) -> Self { match value.event_type() { EventType::SYNCHRONIZATION => SynchronizationEvent::from_event(value).into(), EventType::KEY => KeyEvent::from_event(value).into(), EventType::RELATIVE => RelativeAxisEvent::from_event(value).into(), EventType::ABSOLUTE => AbsoluteAxisEvent::from_event(value).into(), EventType::MISC => MiscEvent::from_event(value).into(), EventType::SWITCH => SwitchEvent::from_event(value).into(), EventType::LED => LedEvent::from_event(value).into(), EventType::SOUND => SoundEvent::from_event(value).into(), EventType::REPEAT => RepeatEvent::from_event(value).into(), EventType::FORCEFEEDBACK => FFEvent::from_event(value).into(), EventType::POWER => PowerEvent::from_event(value).into(), EventType::FORCEFEEDBACKSTATUS => FFStatusEvent::from_event(value).into(), EventType::UINPUT => UInputEvent::from_event(value).into(), _ => OtherEvent(value).into(), } } } /// A wrapped `input_absinfo` returned by EVIOCGABS and used with uinput to set up absolute /// axes /// /// `input_absinfo` is a struct containing six fields: /// - `value: s32` /// - `minimum: s32` /// - `maximum: s32` /// - `fuzz: s32` /// - `flat: s32` /// - `resolution: s32` /// #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] pub struct AbsInfo(input_absinfo); impl AbsInfo { #[inline] pub fn value(&self) -> i32 { self.0.value } #[inline] pub fn minimum(&self) -> i32 { self.0.minimum } #[inline] pub fn maximum(&self) -> i32 { self.0.maximum } #[inline] pub fn fuzz(&self) -> i32 { self.0.fuzz } #[inline] pub fn flat(&self) -> i32 { self.0.flat } #[inline] pub fn resolution(&self) -> i32 { self.0.resolution } /// Creates a new AbsInfo, particurarily useful for uinput pub fn new( value: i32, minimum: i32, maximum: i32, fuzz: i32, flat: i32, resolution: i32, ) -> Self { AbsInfo(input_absinfo { value, minimum, maximum, fuzz, flat, resolution, }) } } common_trait_impls!(input_absinfo, AbsInfo); /// A wrapped `uinput_abs_setup`, used to set up analogue axes with uinput /// /// `uinput_abs_setup` is a struct containing two fields: /// - `code: u16` /// - `absinfo: input_absinfo` #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] pub struct UinputAbsSetup(uinput_abs_setup); impl UinputAbsSetup { #[inline] pub fn code(&self) -> u16 { self.0.code } #[inline] pub fn absinfo(&self) -> AbsInfo { AbsInfo(self.0.absinfo) } /// Creates new UinputAbsSetup pub fn new(code: AbsoluteAxisCode, absinfo: AbsInfo) -> Self { UinputAbsSetup(uinput_abs_setup { code: code.0, absinfo: absinfo.0, }) } } common_trait_impls!(uinput_abs_setup, UinputAbsSetup); /// A wrapped `input_event` returned by the input device via the kernel. /// /// `input_event` is a struct containing four fields: /// - `time: timeval` /// - `type_: u16` /// - `code: u16` /// - `value: s32` /// /// The meaning of the "code" and "value" fields will depend on the underlying type of event. #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[repr(transparent)] pub struct InputEvent(input_event); common_trait_impls!(input_event, InputEvent); impl InputEvent { /// Returns the timestamp associated with the event. #[inline] pub fn timestamp(&self) -> SystemTime { timeval_to_systime(&self.0.time) } /// Returns the type of event this describes, e.g. Key, Switch, etc. #[inline] pub fn event_type(&self) -> EventType { EventType(self.0.type_) } /// Returns the raw "code" field directly from input_event. #[inline] pub fn code(&self) -> u16 { self.0.code } /// Returns the raw "value" field directly from input_event. /// /// For keys and switches the values 0 and 1 map to pressed and not pressed respectively. /// For axes, the values depend on the hardware and driver implementation. #[inline] pub fn value(&self) -> i32 { self.0.value } /// A convenience function to destructure the InputEvent into a [`EventSummary`]. /// /// # Example /// ``` /// use evdev::*; /// let event = InputEvent::new(1, KeyCode::KEY_A.0, 1); /// match event.destructure() { /// EventSummary::Key(KeyEvent, KeyCode::KEY_A, 1) => (), /// _=> panic!(), /// } /// ``` pub fn destructure(self) -> EventSummary { self.into() } /// Create a new InputEvent. Only really useful for emitting events on virtual devices. pub fn new(type_: u16, code: u16, value: i32) -> Self { let raw = input_event { time: libc::timeval { tv_sec: 0, tv_usec: 0, }, type_, code, value, }; Self(raw) } /// Create a new InputEvent with the time field set to "now" on the system clock. /// /// Note that this isn't usually necessary simply for emitting events on a virtual device, as /// even though [`InputEvent::new`] creates an `input_event` with the time field as zero, /// the kernel will update `input_event.time` when it emits the events to any programs reading /// the event "file". pub fn new_now(type_: u16, code: u16, value: i32) -> Self { let raw = input_event { time: systime_to_timeval(&SystemTime::now()), type_, code, value, }; Self(raw) } } impl fmt::Debug for InputEvent { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let summary = self.destructure(); let code: &dyn fmt::Debug = match &summary { EventSummary::Synchronization(_, code, _) => code, EventSummary::Key(_, code, _) => code, EventSummary::RelativeAxis(_, code, _) => code, EventSummary::AbsoluteAxis(_, code, _) => code, EventSummary::Misc(_, code, _) => code, EventSummary::Switch(_, code, _) => code, EventSummary::Led(_, code, _) => code, EventSummary::Sound(_, code, _) => code, EventSummary::Repeat(_, code, _) => code, EventSummary::ForceFeedback(_, code, _) => code, EventSummary::Power(_, code, _) => code, EventSummary::ForceFeedbackStatus(_, code, _) => code, EventSummary::UInput(_, code, _) => code, EventSummary::Other(_, code, _) => &code.1, }; f.debug_struct("InputEvent") .field("time", &self.timestamp()) .field("type", &self.event_type()) .field("code", code) .field("value", &self.value()) .finish() } } /// Crawls `/dev/input` for evdev devices. /// /// Will not bubble up any errors in opening devices or traversing the directory. Instead returns /// an empty iterator or omits the devices that could not be opened. pub fn enumerate() -> EnumerateDevices { EnumerateDevices { inner: raw_stream::enumerate(), } } /// An iterator over currently connected evdev devices. pub struct EnumerateDevices { inner: raw_stream::EnumerateDevices, } impl Iterator for EnumerateDevices { type Item = (PathBuf, Device); fn next(&mut self) -> Option<(PathBuf, Device)> { self.inner .next() .map(|(pb, dev)| (pb, Device::from_raw_device(dev))) } } /// A safe Rust version of clock_gettime against CLOCK_REALTIME fn systime_to_timeval(time: &SystemTime) -> libc::timeval { let (sign, dur) = match time.duration_since(SystemTime::UNIX_EPOCH) { Ok(dur) => (1, dur), Err(e) => (-1, e.duration()), }; libc::timeval { tv_sec: dur.as_secs() as libc::time_t * sign, tv_usec: dur.subsec_micros() as libc::suseconds_t, } } fn timeval_to_systime(tv: &libc::timeval) -> SystemTime { let dur = Duration::new(tv.tv_sec as u64, tv.tv_usec as u32 * 1000); if tv.tv_sec >= 0 { SystemTime::UNIX_EPOCH + dur } else { SystemTime::UNIX_EPOCH - dur } } /// SAFETY: T must not have any padding or otherwise uninitialized bytes inside of it pub(crate) unsafe fn cast_to_bytes(mem: &T) -> &[u8] { std::slice::from_raw_parts(mem as *const T as *const u8, std::mem::size_of_val(mem)) } /// An error type for the `FromStr` implementation for enum-like types in this crate. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct EnumParseError(()); impl Display for EnumParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "failed to parse Key from string") } } impl std::error::Error for EnumParseError {} fn fd_write_all(fd: std::os::fd::BorrowedFd<'_>, mut data: &[u8]) -> nix::Result<()> { loop { match nix::unistd::write(fd, data) { Ok(0) => return Ok(()), Ok(n) => data = &data[n..], Err(nix::Error::EINTR) => {} Err(e) => return Err(e), } } } fn write_events(fd: std::os::fd::BorrowedFd<'_>, events: &[InputEvent]) -> nix::Result<()> { let bytes = unsafe { cast_to_bytes(events) }; fd_write_all(fd, bytes) } /// Represents a force feedback effect that has been successfully uploaded to the device for /// playback. #[derive(Debug)] pub struct FFEffect { fd: OwnedFd, id: u16, } impl FFEffect { /// Returns the effect ID. pub fn id(&self) -> u16 { self.id } /// Plays the force feedback effect with the `count` argument specifying how often the effect /// should be played. pub fn play(&mut self, count: i32) -> io::Result<()> { let events = [*FFEvent::new(FFEffectCode(self.id), count)]; crate::write_events(self.fd.as_fd(), &events)?; Ok(()) } /// Stops playback of the force feedback effect. pub fn stop(&mut self) -> io::Result<()> { let events = [*FFEvent::new(FFEffectCode(self.id), 0)]; crate::write_events(self.fd.as_fd(), &events)?; Ok(()) } /// Updates the force feedback effect. pub fn update(&mut self, data: FFEffectData) -> io::Result<()> { let mut effect: sys::ff_effect = data.into(); effect.id = self.id as i16; unsafe { sys::eviocsff(self.fd.as_raw_fd(), &effect)? }; Ok(()) } } impl Drop for FFEffect { fn drop(&mut self) { let _ = unsafe { sys::eviocrmff(self.fd.as_raw_fd(), self.id as _) }; } } /// Auto-repeat settings for a device. #[derive(Debug, Clone)] #[repr(C)] pub struct AutoRepeat { /// The duration, in milliseconds, that a key needs to be held down before /// it begins to auto-repeat. pub delay: u32, /// The duration, in milliseconds, between auto-repetitions of a held-down key. pub period: u32, } evdev-0.13.1/src/raw_stream.rs000064400000000000000000000710731046102023000143300ustar 00000000000000//! A device implementation with no userspace synchronization performed. use std::fs::{File, OpenOptions}; use std::mem::MaybeUninit; use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; use std::path::{Path, PathBuf}; use std::{io, mem}; use crate::compat::{input_absinfo, input_event, input_id, input_keymap_entry}; use crate::constants::*; use crate::ff::*; use crate::{ sys, AbsInfo, AttributeSet, AttributeSetRef, AutoRepeat, FFEffect, FFEffectCode, FFEvent, InputEvent, InputId, KeyCode, }; fn ioctl_get_cstring( f: unsafe fn(RawFd, &mut [u8]) -> nix::Result, fd: RawFd, ) -> Option> { let mut buf = vec![0; 256]; match unsafe { f(fd, buf.as_mut_slice()) } { Ok(len) if len as usize > buf.capacity() => { panic!("ioctl_get_cstring call overran the provided buffer!"); } Ok(len) if len > 1 => { // Our ioctl string functions apparently return the number of bytes written, including // trailing \0. buf.truncate(len as usize); assert_eq!(buf.pop().unwrap(), 0); Some(buf) } _ => None, } } fn bytes_into_string_lossy(v: Vec) -> String { String::from_utf8(v).unwrap_or_else(|v| String::from_utf8_lossy(v.as_bytes()).into_owned()) } const ABSINFO_ZERO: input_absinfo = input_absinfo { value: 0, minimum: 0, maximum: 0, fuzz: 0, flat: 0, resolution: 0, }; pub(crate) const ABS_VALS_INIT: [input_absinfo; AbsoluteAxisCode::COUNT] = [ABSINFO_ZERO; AbsoluteAxisCode::COUNT]; const INPUT_KEYMAP_BY_INDEX: u8 = 1; /// A physical or virtual device supported by evdev. /// /// Each device corresponds to a path typically found in `/dev/input`, and supports access via /// one or more "types". For example, an optical mouse has buttons that are represented by "keys", /// and reflects changes in its position via "relative axis" reports. /// /// If events are dropped from the kernel's internal buffer, no synchronization will be done to /// fetch the device's state, meaning the state as observed through events may drift from /// the actual state of the device. #[derive(Debug)] pub struct RawDevice { fd: OwnedFd, ty: AttributeSet, name: Option, phys: Option, uniq: Option, id: input_id, props: AttributeSet, driver_version: (u8, u8, u8), supported_keys: Option>, supported_relative: Option>, supported_absolute: Option>, supported_switch: Option>, supported_led: Option>, supported_misc: Option>, supported_ff: Option>, auto_repeat: Option, max_ff_effects: usize, // ff: Option>, // ff_stat: Option, supported_snd: Option>, pub(crate) event_buf: Vec, grabbed: bool, } impl RawDevice { /// Opens a device, given its system path. /// /// Paths are typically something like `/dev/input/event0`. #[inline(always)] pub fn open(path: impl AsRef) -> io::Result { Self::_open(path.as_ref()) } fn _open(path: &Path) -> io::Result { let mut options = OpenOptions::new(); // Try to load read/write, then fall back to read-only. let fd: OwnedFd = options .read(true) .write(true) .open(path) .or_else(|_| options.write(false).open(path))? .into(); Self::from_fd(fd) } /// Opens a device, given an already opened file descriptor pub fn from_fd(fd: OwnedFd) -> io::Result { let ty = { let mut ty = AttributeSet::::new(); unsafe { sys::eviocgbit_type(fd.as_raw_fd(), ty.as_mut_raw_slice())? }; ty }; let name = ioctl_get_cstring(sys::eviocgname, fd.as_raw_fd()).map(bytes_into_string_lossy); let phys = ioctl_get_cstring(sys::eviocgphys, fd.as_raw_fd()).map(bytes_into_string_lossy); let uniq = ioctl_get_cstring(sys::eviocguniq, fd.as_raw_fd()).map(bytes_into_string_lossy); let id = unsafe { let mut id = MaybeUninit::uninit(); sys::eviocgid(fd.as_raw_fd(), id.as_mut_ptr())?; id.assume_init() }; let mut driver_version: i32 = 0; unsafe { sys::eviocgversion(fd.as_raw_fd(), &mut driver_version)?; } let driver_version = ( ((driver_version >> 16) & 0xff) as u8, ((driver_version >> 8) & 0xff) as u8, (driver_version & 0xff) as u8, ); let props = { let mut props = AttributeSet::::new(); unsafe { sys::eviocgprop(fd.as_raw_fd(), props.as_mut_raw_slice())? }; props }; // FIXME: handle old kernel let supported_keys = if ty.contains(EventType::KEY) { let mut keys = AttributeSet::::new(); unsafe { sys::eviocgbit_key(fd.as_raw_fd(), keys.as_mut_raw_slice())? }; Some(keys) } else { None }; let supported_relative = if ty.contains(EventType::RELATIVE) { let mut rel = AttributeSet::::new(); unsafe { sys::eviocgbit_relative(fd.as_raw_fd(), rel.as_mut_raw_slice())? }; Some(rel) } else { None }; let supported_absolute = if ty.contains(EventType::ABSOLUTE) { let mut abs = AttributeSet::::new(); unsafe { sys::eviocgbit_absolute(fd.as_raw_fd(), abs.as_mut_raw_slice())? }; Some(abs) } else { None }; let supported_switch = if ty.contains(EventType::SWITCH) { let mut switch = AttributeSet::::new(); unsafe { sys::eviocgbit_switch(fd.as_raw_fd(), switch.as_mut_raw_slice())? }; Some(switch) } else { None }; let supported_led = if ty.contains(EventType::LED) { let mut led = AttributeSet::::new(); unsafe { sys::eviocgbit_led(fd.as_raw_fd(), led.as_mut_raw_slice())? }; Some(led) } else { None }; let supported_misc = if ty.contains(EventType::MISC) { let mut misc = AttributeSet::::new(); unsafe { sys::eviocgbit_misc(fd.as_raw_fd(), misc.as_mut_raw_slice())? }; Some(misc) } else { None }; let supported_ff = if ty.contains(EventType::FORCEFEEDBACK) { let mut ff = AttributeSet::::new(); unsafe { sys::eviocgbit_ff(fd.as_raw_fd(), ff.as_mut_raw_slice())? }; Some(ff) } else { None }; let max_ff_effects = if ty.contains(EventType::FORCEFEEDBACK) { let mut max_ff_effects = 0; unsafe { sys::eviocgeffects(fd.as_raw_fd(), &mut max_ff_effects)? }; usize::try_from(max_ff_effects).unwrap_or(0) } else { 0 }; let supported_snd = if ty.contains(EventType::SOUND) { let mut snd = AttributeSet::::new(); unsafe { sys::eviocgbit_sound(fd.as_raw_fd(), snd.as_mut_raw_slice())? }; Some(snd) } else { None }; let auto_repeat = if ty.contains(EventType::REPEAT) { let mut auto_repeat: AutoRepeat = AutoRepeat { delay: 0, period: 0, }; unsafe { sys::eviocgrep( fd.as_raw_fd(), &mut auto_repeat as *mut AutoRepeat as *mut [u32; 2], )?; } Some(auto_repeat) } else { None }; Ok(RawDevice { fd, ty, name, phys, uniq, id, props, driver_version, supported_keys, supported_relative, supported_absolute, supported_switch, supported_led, supported_misc, supported_ff, supported_snd, auto_repeat, max_ff_effects, event_buf: Vec::new(), grabbed: false, }) } /// Returns the device's name as read from the kernel. pub fn name(&self) -> Option<&str> { self.name.as_deref() } /// Returns the device's physical location, either as set by the caller or as read from the kernel. pub fn physical_path(&self) -> Option<&str> { self.phys.as_deref() } /// Returns the user-defined "unique name" of the device, if one has been set. pub fn unique_name(&self) -> Option<&str> { self.uniq.as_deref() } /// Returns a struct containing bustype, vendor, product, and version identifiers pub fn input_id(&self) -> InputId { InputId::from(self.id) } /// Returns the current auto repeat settings pub fn get_auto_repeat(&self) -> Option { self.auto_repeat.clone() } /// Returns the set of supported "properties" for the device (see `INPUT_PROP_*` in kernel headers) pub fn properties(&self) -> &AttributeSetRef { &self.props } /// Returns a tuple of the driver version containing major, minor, rev pub fn driver_version(&self) -> (u8, u8, u8) { self.driver_version } /// Returns a set of the event types supported by this device (Key, Switch, etc) /// /// If you're interested in the individual keys or switches supported, it's probably easier /// to just call the appropriate `supported_*` function instead. pub fn supported_events(&self) -> &AttributeSetRef { &self.ty } /// Returns the set of supported keys reported by the device. /// /// For keyboards, this is the set of all possible keycodes the keyboard may emit. Controllers, /// mice, and other peripherals may also report buttons as keys. /// /// # Examples /// /// ```no_run /// # fn main() -> Result<(), Box> { /// use evdev::{Device, KeyCode}; /// let device = Device::open("/dev/input/event0")?; /// /// // Does this device have an ENTER key? /// let supported = device.supported_keys().map_or(false, |keys| keys.contains(KeyCode::KEY_ENTER)); /// # Ok(()) /// # } /// ``` pub fn supported_keys(&self) -> Option<&AttributeSetRef> { self.supported_keys.as_deref() } /// Returns the set of supported "relative axes" reported by the device. /// /// Standard mice will generally report `REL_X` and `REL_Y` along with wheel if supported. /// /// # Examples /// /// ```no_run /// # fn main() -> Result<(), Box> { /// use evdev::{Device, RelativeAxisCode}; /// let device = Device::open("/dev/input/event0")?; /// /// // Does the device have a scroll wheel? /// let supported = device /// .supported_relative_axes() /// .map_or(false, |axes| axes.contains(RelativeAxisCode::REL_WHEEL)); /// # Ok(()) /// # } /// ``` pub fn supported_relative_axes(&self) -> Option<&AttributeSetRef> { self.supported_relative.as_deref() } /// Returns the set of supported "absolute axes" reported by the device. /// /// These are most typically supported by joysticks and touchpads. /// /// # Examples /// /// ```no_run /// # fn main() -> Result<(), Box> { /// use evdev::{Device, AbsoluteAxisCode}; /// let device = Device::open("/dev/input/event0")?; /// /// // Does the device have an absolute X axis? /// let supported = device /// .supported_absolute_axes() /// .map_or(false, |axes| axes.contains(AbsoluteAxisCode::ABS_X)); /// # Ok(()) /// # } /// ``` pub fn supported_absolute_axes(&self) -> Option<&AttributeSetRef> { self.supported_absolute.as_deref() } /// Returns the set of supported switches reported by the device. /// /// These are typically used for things like software switches on laptop lids (which the /// system reacts to by suspending or locking), or virtual switches to indicate whether a /// headphone jack is plugged in (used to disable external speakers). /// /// # Examples /// /// ```no_run /// # fn main() -> Result<(), Box> { /// use evdev::{Device, SwitchCode}; /// let device = Device::open("/dev/input/event0")?; /// /// // Does the device report a laptop lid switch? /// let supported = device /// .supported_switches() /// .map_or(false, |axes| axes.contains(SwitchCode::SW_LID)); /// # Ok(()) /// # } /// ``` pub fn supported_switches(&self) -> Option<&AttributeSetRef> { self.supported_switch.as_deref() } /// Returns a set of supported LEDs on the device. /// /// Most commonly these are state indicator lights for things like Scroll Lock, but they /// can also be found in cameras and other devices. pub fn supported_leds(&self) -> Option<&AttributeSetRef> { self.supported_led.as_deref() } /// Returns a set of supported "miscellaneous" capabilities. /// /// Aside from vendor-specific key scancodes, most of these are uncommon. pub fn misc_properties(&self) -> Option<&AttributeSetRef> { self.supported_misc.as_deref() } /// Returns the set of supported force feedback effects supported by a device. pub fn supported_ff(&self) -> Option<&AttributeSetRef> { self.supported_ff.as_deref() } /// Returns the maximum number of force feedback effects that can be played simultaneously. pub fn max_ff_effects(&self) -> usize { self.max_ff_effects } /// Returns the set of supported simple sounds supported by a device. /// /// You can use these to make really annoying beep sounds come from an internal self-test /// speaker, for instance. pub fn supported_sounds(&self) -> Option<&AttributeSetRef> { self.supported_snd.as_deref() } /// Read a maximum of `num` events into the internal buffer. If the underlying fd is not /// O_NONBLOCK, this will block. /// /// Returns the number of events that were read, or an error. pub(crate) fn fill_events(&mut self) -> io::Result { let fd = self.as_raw_fd(); self.event_buf.reserve(crate::EVENT_BATCH_SIZE); let spare_capacity = self.event_buf.spare_capacity_mut(); let spare_capacity_size = std::mem::size_of_val(spare_capacity); // use libc::read instead of nix::unistd::read b/c we need to pass an uninitialized buf let res = unsafe { libc::read(fd, spare_capacity.as_mut_ptr() as _, spare_capacity_size) }; let bytes_read = nix::errno::Errno::result(res)?; let num_read = bytes_read as usize / mem::size_of::(); unsafe { let len = self.event_buf.len(); self.event_buf.set_len(len + num_read); } Ok(num_read) } /// Fetches and returns events from the kernel ring buffer without doing synchronization on /// SYN_DROPPED. /// /// By default this will block until events are available. Typically, users will want to call /// this in a tight loop within a thread. pub fn fetch_events(&mut self) -> io::Result + '_> { self.fill_events()?; Ok(self.event_buf.drain(..).map(InputEvent::from)) } /// Retrieve the current keypress state directly via kernel syscall. #[inline] pub fn get_key_state(&self) -> io::Result> { let mut key_vals = AttributeSet::new(); self.update_key_state(&mut key_vals)?; Ok(key_vals) } /// Retrieve the current absolute axis state directly via kernel syscall. #[inline] pub fn get_abs_state(&self) -> io::Result<[input_absinfo; AbsoluteAxisCode::COUNT]> { let mut abs_vals: [input_absinfo; AbsoluteAxisCode::COUNT] = ABS_VALS_INIT; self.update_abs_state(&mut abs_vals)?; Ok(abs_vals) } /// Get the AbsInfo for each supported AbsoluteAxis pub fn get_absinfo( &self, ) -> io::Result + '_> { let raw_absinfo = self.get_abs_state()?; Ok(self .supported_absolute_axes() .into_iter() .flat_map(AttributeSetRef::iter) .map(move |axes| (axes, AbsInfo(raw_absinfo[axes.0 as usize])))) } /// Retrieve the current switch state directly via kernel syscall. #[inline] pub fn get_switch_state(&self) -> io::Result> { let mut switch_vals = AttributeSet::new(); self.update_switch_state(&mut switch_vals)?; Ok(switch_vals) } /// Retrieve the current LED state directly via kernel syscall. #[inline] pub fn get_led_state(&self) -> io::Result> { let mut led_vals = AttributeSet::new(); self.update_led_state(&mut led_vals)?; Ok(led_vals) } /// Fetch the current kernel key state directly into the provided buffer. /// If you don't already have a buffer, you probably want /// [`get_key_state`](Self::get_key_state) instead. #[inline] pub fn update_key_state(&self, key_vals: &mut AttributeSet) -> io::Result<()> { unsafe { sys::eviocgkey(self.as_raw_fd(), key_vals.as_mut_raw_slice())? }; Ok(()) } /// Fetch the current kernel absolute axis state directly into the provided buffer. /// If you don't already have a buffer, you probably want /// [`get_abs_state`](Self::get_abs_state) instead. #[inline] pub fn update_abs_state( &self, abs_vals: &mut [input_absinfo; AbsoluteAxisCode::COUNT], ) -> io::Result<()> { if let Some(supported_abs) = self.supported_absolute_axes() { for AbsoluteAxisCode(idx) in supported_abs.iter() { // ignore multitouch, we'll handle that later. // // handling later removed. not sure what the intention of "handling that later" was // the abs data seems to be fine (tested ABS_MT_POSITION_X/Y) unsafe { sys::eviocgabs(self.as_raw_fd(), idx as u32, &mut abs_vals[idx as usize])? }; } } Ok(()) } /// Fetch the current kernel switch state directly into the provided buffer. /// If you don't already have a buffer, you probably want /// [`get_switch_state`](Self::get_switch_state) instead. #[inline] pub fn update_switch_state( &self, switch_vals: &mut AttributeSet, ) -> io::Result<()> { unsafe { sys::eviocgsw(self.as_raw_fd(), switch_vals.as_mut_raw_slice())? }; Ok(()) } /// Fetch the current kernel LED state directly into the provided buffer. /// If you don't already have a buffer, you probably want /// [`get_led_state`](Self::get_led_state) instead. #[inline] pub fn update_led_state(&self, led_vals: &mut AttributeSet) -> io::Result<()> { unsafe { sys::eviocgled(self.as_raw_fd(), led_vals.as_mut_raw_slice())? }; Ok(()) } /// Update the auto repeat delays #[inline] pub fn update_auto_repeat(&mut self, repeat: &AutoRepeat) -> io::Result<()> { unsafe { sys::eviocsrep( self.as_raw_fd(), repeat as *const AutoRepeat as *const [u32; 2], )?; } self.auto_repeat = Some(repeat.clone()); Ok(()) } /// Retrieve the scancode for a keycode, if any pub fn get_scancode_by_keycode(&self, keycode: u32) -> io::Result> { let mut keymap = input_keymap_entry { flags: 0, len: 0, index: 0, keycode, scancode: [0u8; 32], }; unsafe { sys::eviocgkeycode_v2(self.as_raw_fd(), &mut keymap)? }; Ok(keymap.scancode[..keymap.len as usize].to_vec()) } /// Retrieve the keycode and scancode by index, starting at 0 pub fn get_scancode_by_index(&self, index: u16) -> io::Result<(u32, Vec)> { let mut keymap = input_keymap_entry { flags: INPUT_KEYMAP_BY_INDEX, len: 0, index, keycode: 0, scancode: [0u8; 32], }; unsafe { sys::eviocgkeycode_v2(self.as_raw_fd(), &mut keymap)? }; Ok(( keymap.keycode, keymap.scancode[..keymap.len as usize].to_vec(), )) } /// Update a scancode by index. The return value is the previous keycode pub fn update_scancode_by_index( &self, index: u16, keycode: u32, scancode: &[u8], ) -> io::Result { let len = scancode.len(); let mut keymap = input_keymap_entry { flags: INPUT_KEYMAP_BY_INDEX, len: len as u8, index, keycode, scancode: [0u8; 32], }; keymap.scancode[..len].copy_from_slice(scancode); let keycode = unsafe { sys::eviocskeycode_v2(self.as_raw_fd(), &keymap)? }; Ok(keycode as u32) } /// Update a scancode. The return value is the previous keycode pub fn update_scancode(&self, keycode: u32, scancode: &[u8]) -> io::Result { let len = scancode.len(); let mut keymap = input_keymap_entry { flags: 0, len: len as u8, index: 0, keycode, scancode: [0u8; 32], }; keymap.scancode[..len].copy_from_slice(scancode); let keycode = unsafe { sys::eviocskeycode_v2(self.as_raw_fd(), &keymap)? }; Ok(keycode as u32) } #[cfg(feature = "tokio")] #[inline] pub fn into_event_stream(self) -> io::Result { EventStream::new(self) } pub fn grab(&mut self) -> io::Result<()> { if !self.grabbed { unsafe { sys::eviocgrab(self.as_raw_fd(), 1)?; } self.grabbed = true; } Ok(()) } pub fn ungrab(&mut self) -> io::Result<()> { if self.grabbed { unsafe { sys::eviocgrab(self.as_raw_fd(), 0)?; } self.grabbed = false; } Ok(()) } /// Whether the device is currently grabbed for exclusive use or not. pub fn is_grabbed(&self) -> bool { self.grabbed } /// Send an event to the device. /// /// Events that are typically sent to devices are /// [EventType::LED] (turn device LEDs on and off), /// [EventType::SOUND] (play a sound on the device) /// and [EventType::FORCEFEEDBACK] (play force feedback effects on the device, i.e. rumble). pub fn send_events(&mut self, events: &[InputEvent]) -> io::Result<()> { crate::write_events(self.fd.as_fd(), events)?; Ok(()) } /// Uploads a force feedback effect to the device. pub fn upload_ff_effect(&mut self, data: FFEffectData) -> io::Result { let mut effect: sys::ff_effect = data.into(); effect.id = -1; unsafe { sys::eviocsff(self.fd.as_raw_fd(), &effect)? }; let fd = self.fd.try_clone()?; let id = effect.id as u16; Ok(FFEffect { fd, id }) } /// Sets the force feedback gain, i.e. how strong the force feedback effects should be for the /// device. A gain of 0 means no gain, whereas `u16::MAX` is the maximum gain. pub fn set_ff_gain(&mut self, value: u16) -> io::Result<()> { let events = [*FFEvent::new(FFEffectCode::FF_GAIN, value.into())]; crate::write_events(self.fd.as_fd(), &events)?; Ok(()) } /// Enables or disables autocenter for the force feedback device. pub fn set_ff_autocenter(&mut self, value: u16) -> io::Result<()> { let events = [*FFEvent::new(FFEffectCode::FF_AUTOCENTER, value.into())]; crate::write_events(self.fd.as_fd(), &events)?; Ok(()) } } impl AsFd for RawDevice { fn as_fd(&self) -> BorrowedFd<'_> { self.fd.as_fd() } } impl AsRawFd for RawDevice { fn as_raw_fd(&self) -> RawFd { self.fd.as_raw_fd() } } impl TryFrom for RawDevice { type Error = io::Error; fn try_from(file: File) -> Result { Self::from_fd(file.into()) } } /// Crawls `/dev/input` for evdev devices. /// /// Will not bubble up any errors in opening devices or traversing the directory. Instead returns /// an empty iterator or omits the devices that could not be opened. pub fn enumerate() -> EnumerateDevices { EnumerateDevices { readdir: std::fs::read_dir("/dev/input").ok(), } } /// An iterator over currently connected evdev devices. pub struct EnumerateDevices { readdir: Option, } impl Iterator for EnumerateDevices { type Item = (PathBuf, RawDevice); fn next(&mut self) -> Option<(PathBuf, RawDevice)> { use std::os::unix::ffi::OsStrExt; let readdir = self.readdir.as_mut()?; loop { if let Ok(entry) = readdir.next()? { let path = entry.path(); let fname = path.file_name().unwrap(); if fname.as_bytes().starts_with(b"event") { if let Ok(dev) = RawDevice::open(&path) { return Some((path, dev)); } } } } } } #[cfg(feature = "tokio")] mod tokio_stream { use super::*; use std::future::poll_fn; use std::task::{ready, Context, Poll}; use tokio::io::unix::AsyncFd; /// An asynchronous stream of input events. /// /// This can be used by calling [`stream.next_event().await?`](Self::next_event), or if you /// need to pass it as a stream somewhere, the [`futures::Stream`](Stream) implementation. /// There's also a lower-level [`Self::poll_event`] function if you need to fetch an event from /// inside a `Future::poll` impl. pub struct EventStream { device: AsyncFd, index: usize, } impl Unpin for EventStream {} impl EventStream { pub(crate) fn new(device: RawDevice) -> io::Result { use nix::fcntl; fcntl::fcntl(device.as_raw_fd(), fcntl::F_SETFL(fcntl::OFlag::O_NONBLOCK))?; let device = AsyncFd::new(device)?; Ok(Self { device, index: 0 }) } /// Returns a reference to the underlying device pub fn device(&self) -> &RawDevice { self.device.get_ref() } /// Returns a mutable reference to the underlying device. pub fn device_mut(&mut self) -> &mut RawDevice { self.device.get_mut() } /// Try to wait for the next event in this stream. Any errors are likely to be fatal, i.e. /// any calls afterwards will likely error as well. pub async fn next_event(&mut self) -> io::Result { poll_fn(|cx| self.poll_event(cx)).await } /// A lower-level function for directly polling this stream. pub fn poll_event(&mut self, cx: &mut Context<'_>) -> Poll> { 'outer: loop { if let Some(&ev) = self.device.get_ref().event_buf.get(self.index) { self.index += 1; return Poll::Ready(Ok(InputEvent::from(ev))); } self.device.get_mut().event_buf.clear(); self.index = 0; loop { let mut guard = ready!(self.device.poll_read_ready_mut(cx))?; let res = guard.try_io(|device| device.get_mut().fill_events()); match res { Ok(res) => { let _ = res?; continue 'outer; } Err(_would_block) => continue, } } } } } #[cfg(feature = "stream-trait")] impl futures_core::Stream for EventStream { type Item = io::Result; fn poll_next( self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { self.get_mut().poll_event(cx).map(Some) } } } #[cfg(feature = "tokio")] pub use tokio_stream::EventStream; evdev-0.13.1/src/scancodes.rs000064400000000000000000000402231046102023000141170ustar 00000000000000use crate::compat::KEY_CNT; /// Scancodes for key presses. /// /// Values correspond to [/usr/include/linux/input-event-codes.h](https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h) /// /// Each associated constant for this struct represents a distinct key. #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] #[repr(transparent)] pub struct KeyCode(pub u16); impl KeyCode { #[inline] pub const fn new(code: u16) -> Self { Self(code) } #[inline] pub const fn code(self) -> u16 { self.0 } pub(crate) const COUNT: usize = KEY_CNT; } evdev_enum!( KeyCode, box Array, KEY_RESERVED = 0, KEY_ESC = 1, KEY_1 = 2, KEY_2 = 3, KEY_3 = 4, KEY_4 = 5, KEY_5 = 6, KEY_6 = 7, KEY_7 = 8, KEY_8 = 9, KEY_9 = 10, KEY_0 = 11, KEY_MINUS = 12, KEY_EQUAL = 13, KEY_BACKSPACE = 14, KEY_TAB = 15, KEY_Q = 16, KEY_W = 17, KEY_E = 18, KEY_R = 19, KEY_T = 20, KEY_Y = 21, KEY_U = 22, KEY_I = 23, KEY_O = 24, KEY_P = 25, KEY_LEFTBRACE = 26, KEY_RIGHTBRACE = 27, KEY_ENTER = 28, KEY_LEFTCTRL = 29, KEY_A = 30, KEY_S = 31, KEY_D = 32, KEY_F = 33, KEY_G = 34, KEY_H = 35, KEY_J = 36, KEY_K = 37, KEY_L = 38, KEY_SEMICOLON = 39, KEY_APOSTROPHE = 40, KEY_GRAVE = 41, KEY_LEFTSHIFT = 42, KEY_BACKSLASH = 43, KEY_Z = 44, KEY_X = 45, KEY_C = 46, KEY_V = 47, KEY_B = 48, KEY_N = 49, KEY_M = 50, KEY_COMMA = 51, KEY_DOT = 52, KEY_SLASH = 53, KEY_RIGHTSHIFT = 54, KEY_KPASTERISK = 55, KEY_LEFTALT = 56, KEY_SPACE = 57, KEY_CAPSLOCK = 58, KEY_F1 = 59, KEY_F2 = 60, KEY_F3 = 61, KEY_F4 = 62, KEY_F5 = 63, KEY_F6 = 64, KEY_F7 = 65, KEY_F8 = 66, KEY_F9 = 67, KEY_F10 = 68, KEY_NUMLOCK = 69, KEY_SCROLLLOCK = 70, KEY_KP7 = 71, KEY_KP8 = 72, KEY_KP9 = 73, KEY_KPMINUS = 74, KEY_KP4 = 75, KEY_KP5 = 76, KEY_KP6 = 77, KEY_KPPLUS = 78, KEY_KP1 = 79, KEY_KP2 = 80, KEY_KP3 = 81, KEY_KP0 = 82, KEY_KPDOT = 83, KEY_ZENKAKUHANKAKU = 85, KEY_102ND = 86, KEY_F11 = 87, KEY_F12 = 88, KEY_RO = 89, KEY_KATAKANA = 90, KEY_HIRAGANA = 91, KEY_HENKAN = 92, KEY_KATAKANAHIRAGANA = 93, KEY_MUHENKAN = 94, KEY_KPJPCOMMA = 95, KEY_KPENTER = 96, KEY_RIGHTCTRL = 97, KEY_KPSLASH = 98, KEY_SYSRQ = 99, KEY_RIGHTALT = 100, KEY_LINEFEED = 101, KEY_HOME = 102, KEY_UP = 103, KEY_PAGEUP = 104, KEY_LEFT = 105, KEY_RIGHT = 106, KEY_END = 107, KEY_DOWN = 108, KEY_PAGEDOWN = 109, KEY_INSERT = 110, KEY_DELETE = 111, KEY_MACRO = 112, KEY_MUTE = 113, KEY_VOLUMEDOWN = 114, KEY_VOLUMEUP = 115, KEY_POWER = 116, /* SC System Power Down */ KEY_KPEQUAL = 117, KEY_KPPLUSMINUS = 118, KEY_PAUSE = 119, KEY_SCALE = 120, /* AL Compiz Scale (Expose) */ KEY_KPCOMMA = 121, KEY_HANGEUL = 122, KEY_HANJA = 123, KEY_YEN = 124, KEY_LEFTMETA = 125, KEY_RIGHTMETA = 126, KEY_COMPOSE = 127, KEY_STOP = 128, /* AC Stop */ KEY_AGAIN = 129, KEY_PROPS = 130, /* AC Properties */ KEY_UNDO = 131, /* AC Undo */ KEY_FRONT = 132, KEY_COPY = 133, /* AC Copy */ KEY_OPEN = 134, /* AC Open */ KEY_PASTE = 135, /* AC Paste */ KEY_FIND = 136, /* AC Search */ KEY_CUT = 137, /* AC Cut */ KEY_HELP = 138, /* AL Integrated Help Center */ KEY_MENU = 139, /* Menu (show menu) */ KEY_CALC = 140, /* AL Calculator */ KEY_SETUP = 141, KEY_SLEEP = 142, /* SC System Sleep */ KEY_WAKEUP = 143, /* System Wake Up */ KEY_FILE = 144, /* AL Local Machine Browser */ KEY_SENDFILE = 145, KEY_DELETEFILE = 146, KEY_XFER = 147, KEY_PROG1 = 148, KEY_PROG2 = 149, KEY_WWW = 150, /* AL Internet Browser */ KEY_MSDOS = 151, KEY_COFFEE = 152, /* AL Terminal Lock/Screensaver */ KEY_DIRECTION = 153, KEY_ROTATE_DISPLAY = 153, KEY_CYCLEWINDOWS = 154, KEY_MAIL = 155, KEY_BOOKMARKS = 156, /* AC Bookmarks */ KEY_COMPUTER = 157, KEY_BACK = 158, /* AC Back */ KEY_FORWARD = 159, /* AC Forward */ KEY_CLOSECD = 160, KEY_EJECTCD = 161, KEY_EJECTCLOSECD = 162, KEY_NEXTSONG = 163, KEY_PLAYPAUSE = 164, KEY_PREVIOUSSONG = 165, KEY_STOPCD = 166, KEY_RECORD = 167, KEY_REWIND = 168, KEY_PHONE = 169, /* Media Select Telephone */ KEY_ISO = 170, KEY_CONFIG = 171, /* AL Consumer Control Configuration */ KEY_HOMEPAGE = 172, /* AC Home */ KEY_REFRESH = 173, /* AC Refresh */ KEY_EXIT = 174, /* AC Exit */ KEY_MOVE = 175, KEY_EDIT = 176, KEY_SCROLLUP = 177, KEY_SCROLLDOWN = 178, KEY_KPLEFTPAREN = 179, KEY_KPRIGHTPAREN = 180, KEY_NEW = 181, /* AC New */ KEY_REDO = 182, /* AC Redo/Repeat */ KEY_F13 = 183, KEY_F14 = 184, KEY_F15 = 185, KEY_F16 = 186, KEY_F17 = 187, KEY_F18 = 188, KEY_F19 = 189, KEY_F20 = 190, KEY_F21 = 191, KEY_F22 = 192, KEY_F23 = 193, KEY_F24 = 194, KEY_PLAYCD = 200, KEY_PAUSECD = 201, KEY_PROG3 = 202, KEY_PROG4 = 203, KEY_DASHBOARD = 204, /* AL Dashboard */ KEY_SUSPEND = 205, KEY_CLOSE = 206, /* AC Close */ KEY_PLAY = 207, KEY_FASTFORWARD = 208, KEY_BASSBOOST = 209, KEY_PRINT = 210, /* AC Print */ KEY_HP = 211, KEY_CAMERA = 212, KEY_SOUND = 213, KEY_QUESTION = 214, KEY_EMAIL = 215, KEY_CHAT = 216, KEY_SEARCH = 217, KEY_CONNECT = 218, KEY_FINANCE = 219, KEY_SPORT = 220, KEY_SHOP = 221, KEY_ALTERASE = 222, KEY_CANCEL = 223, KEY_BRIGHTNESSDOWN = 224, KEY_BRIGHTNESSUP = 225, KEY_MEDIA = 226, KEY_SWITCHVIDEOMODE = 227, KEY_KBDILLUMTOGGLE = 228, KEY_KBDILLUMDOWN = 229, KEY_KBDILLUMUP = 230, KEY_SEND = 231, KEY_REPLY = 232, KEY_FORWARDMAIL = 233, KEY_SAVE = 234, KEY_DOCUMENTS = 235, KEY_BATTERY = 236, KEY_BLUETOOTH = 237, KEY_WLAN = 238, KEY_UWB = 239, KEY_UNKNOWN = 240, KEY_VIDEO_NEXT = 241, KEY_VIDEO_PREV = 242, KEY_BRIGHTNESS_CYCLE = 243, KEY_BRIGHTNESS_AUTO = 244, KEY_DISPLAY_OFF = 245, KEY_WWAN = 246, KEY_RFKILL = 247, KEY_MICMUTE = 248, BTN_0 = 0x100, BTN_1 = 0x101, BTN_2 = 0x102, BTN_3 = 0x103, BTN_4 = 0x104, BTN_5 = 0x105, BTN_6 = 0x106, BTN_7 = 0x107, BTN_8 = 0x108, BTN_9 = 0x109, BTN_LEFT = 0x110, BTN_RIGHT = 0x111, BTN_MIDDLE = 0x112, BTN_SIDE = 0x113, BTN_EXTRA = 0x114, BTN_FORWARD = 0x115, BTN_BACK = 0x116, BTN_TASK = 0x117, BTN_TRIGGER = 0x120, BTN_THUMB = 0x121, BTN_THUMB2 = 0x122, BTN_TOP = 0x123, BTN_TOP2 = 0x124, BTN_PINKIE = 0x125, BTN_BASE = 0x126, BTN_BASE2 = 0x127, BTN_BASE3 = 0x128, BTN_BASE4 = 0x129, BTN_BASE5 = 0x12a, BTN_BASE6 = 0x12b, BTN_DEAD = 0x12f, BTN_SOUTH = 0x130, BTN_EAST = 0x131, BTN_C = 0x132, BTN_NORTH = 0x133, BTN_WEST = 0x134, BTN_Z = 0x135, BTN_TL = 0x136, BTN_TR = 0x137, BTN_TL2 = 0x138, BTN_TR2 = 0x139, BTN_SELECT = 0x13a, BTN_START = 0x13b, BTN_MODE = 0x13c, BTN_THUMBL = 0x13d, BTN_THUMBR = 0x13e, BTN_TOOL_PEN = 0x140, BTN_TOOL_RUBBER = 0x141, BTN_TOOL_BRUSH = 0x142, BTN_TOOL_PENCIL = 0x143, BTN_TOOL_AIRBRUSH = 0x144, BTN_TOOL_FINGER = 0x145, BTN_TOOL_MOUSE = 0x146, BTN_TOOL_LENS = 0x147, BTN_TOOL_QUINTTAP = 0x148, /* Five fingers on trackpad */ BTN_TOUCH = 0x14a, BTN_STYLUS = 0x14b, BTN_STYLUS2 = 0x14c, BTN_TOOL_DOUBLETAP = 0x14d, BTN_TOOL_TRIPLETAP = 0x14e, BTN_TOOL_QUADTAP = 0x14f, /* Four fingers on trackpad */ BTN_GEAR_DOWN = 0x150, BTN_GEAR_UP = 0x151, KEY_OK = 0x160, KEY_SELECT = 0x161, KEY_GOTO = 0x162, KEY_CLEAR = 0x163, KEY_POWER2 = 0x164, KEY_OPTION = 0x165, KEY_INFO = 0x166, /* AL OEM Features/Tips/Tutorial */ KEY_TIME = 0x167, KEY_VENDOR = 0x168, KEY_ARCHIVE = 0x169, KEY_PROGRAM = 0x16a, /* Media Select Program Guide */ KEY_CHANNEL = 0x16b, KEY_FAVORITES = 0x16c, KEY_EPG = 0x16d, KEY_PVR = 0x16e, /* Media Select Home */ KEY_MHP = 0x16f, KEY_LANGUAGE = 0x170, KEY_TITLE = 0x171, KEY_SUBTITLE = 0x172, KEY_ANGLE = 0x173, KEY_ZOOM = 0x174, KEY_FULL_SCREEN = 0x174, KEY_MODE = 0x175, KEY_KEYBOARD = 0x176, KEY_SCREEN = 0x177, KEY_PC = 0x178, /* Media Select Computer */ KEY_TV = 0x179, /* Media Select TV */ KEY_TV2 = 0x17a, /* Media Select Cable */ KEY_VCR = 0x17b, /* Media Select VCR */ KEY_VCR2 = 0x17c, /* VCR Plus */ KEY_SAT = 0x17d, /* Media Select Satellite */ KEY_SAT2 = 0x17e, KEY_CD = 0x17f, /* Media Select CD */ KEY_TAPE = 0x180, /* Media Select Tape */ KEY_RADIO = 0x181, KEY_TUNER = 0x182, /* Media Select Tuner */ KEY_PLAYER = 0x183, KEY_TEXT = 0x184, KEY_DVD = 0x185, /* Media Select DVD */ KEY_AUX = 0x186, KEY_MP3 = 0x187, KEY_AUDIO = 0x188, /* AL Audio Browser */ KEY_VIDEO = 0x189, /* AL Movie Browser */ KEY_DIRECTORY = 0x18a, KEY_LIST = 0x18b, KEY_MEMO = 0x18c, /* Media Select Messages */ KEY_CALENDAR = 0x18d, KEY_RED = 0x18e, KEY_GREEN = 0x18f, KEY_YELLOW = 0x190, KEY_BLUE = 0x191, KEY_CHANNELUP = 0x192, /* Channel Increment */ KEY_CHANNELDOWN = 0x193, /* Channel Decrement */ KEY_FIRST = 0x194, KEY_LAST = 0x195, /* Recall Last */ KEY_AB = 0x196, KEY_NEXT = 0x197, KEY_RESTART = 0x198, KEY_SLOW = 0x199, KEY_SHUFFLE = 0x19a, KEY_BREAK = 0x19b, KEY_PREVIOUS = 0x19c, KEY_DIGITS = 0x19d, KEY_TEEN = 0x19e, KEY_TWEN = 0x19f, KEY_VIDEOPHONE = 0x1a0, /* Media Select Video Phone */ KEY_GAMES = 0x1a1, /* Media Select Games */ KEY_ZOOMIN = 0x1a2, /* AC Zoom In */ KEY_ZOOMOUT = 0x1a3, /* AC Zoom Out */ KEY_ZOOMRESET = 0x1a4, /* AC Zoom */ KEY_WORDPROCESSOR = 0x1a5, /* AL Word Processor */ KEY_EDITOR = 0x1a6, /* AL Text Editor */ KEY_SPREADSHEET = 0x1a7, /* AL Spreadsheet */ KEY_GRAPHICSEDITOR = 0x1a8, /* AL Graphics Editor */ KEY_PRESENTATION = 0x1a9, /* AL Presentation App */ KEY_DATABASE = 0x1aa, /* AL Database App */ KEY_NEWS = 0x1ab, /* AL Newsreader */ KEY_VOICEMAIL = 0x1ac, /* AL Voicemail */ KEY_ADDRESSBOOK = 0x1ad, /* AL Contacts/Address Book */ KEY_MESSENGER = 0x1ae, /* AL Instant Messaging */ KEY_DISPLAYTOGGLE = 0x1af, /* Turn display (LCD) on and off */ KEY_SPELLCHECK = 0x1b0, /* AL Spell Check */ KEY_LOGOFF = 0x1b1, /* AL Logoff */ KEY_DOLLAR = 0x1b2, KEY_EURO = 0x1b3, KEY_FRAMEBACK = 0x1b4, /* Consumer - transport controls */ KEY_FRAMEFORWARD = 0x1b5, KEY_CONTEXT_MENU = 0x1b6, /* GenDesc - system context menu */ KEY_MEDIA_REPEAT = 0x1b7, /* Consumer - transport control */ KEY_10CHANNELSUP = 0x1b8, /* 10 channels up (10+) */ KEY_10CHANNELSDOWN = 0x1b9, /* 10 channels down (10-) */ KEY_IMAGES = 0x1ba, /* AL Image Browser */ KEY_PICKUP_PHONE = 0x1bd, KEY_HANGUP_PHONE = 0x1be, KEY_DEL_EOL = 0x1c0, KEY_DEL_EOS = 0x1c1, KEY_INS_LINE = 0x1c2, KEY_DEL_LINE = 0x1c3, KEY_FN = 0x1d0, KEY_FN_ESC = 0x1d1, KEY_FN_F1 = 0x1d2, KEY_FN_F2 = 0x1d3, KEY_FN_F3 = 0x1d4, KEY_FN_F4 = 0x1d5, KEY_FN_F5 = 0x1d6, KEY_FN_F6 = 0x1d7, KEY_FN_F7 = 0x1d8, KEY_FN_F8 = 0x1d9, KEY_FN_F9 = 0x1da, KEY_FN_F10 = 0x1db, KEY_FN_F11 = 0x1dc, KEY_FN_F12 = 0x1dd, KEY_FN_1 = 0x1de, KEY_FN_2 = 0x1df, KEY_FN_D = 0x1e0, KEY_FN_E = 0x1e1, KEY_FN_F = 0x1e2, KEY_FN_S = 0x1e3, KEY_FN_B = 0x1e4, KEY_BRL_DOT1 = 0x1f1, KEY_BRL_DOT2 = 0x1f2, KEY_BRL_DOT3 = 0x1f3, KEY_BRL_DOT4 = 0x1f4, KEY_BRL_DOT5 = 0x1f5, KEY_BRL_DOT6 = 0x1f6, KEY_BRL_DOT7 = 0x1f7, KEY_BRL_DOT8 = 0x1f8, KEY_BRL_DOT9 = 0x1f9, KEY_BRL_DOT10 = 0x1fa, KEY_NUMERIC_0 = 0x200, /* used by phones, remote controls, */ KEY_NUMERIC_1 = 0x201, /* and other keypads */ KEY_NUMERIC_2 = 0x202, KEY_NUMERIC_3 = 0x203, KEY_NUMERIC_4 = 0x204, KEY_NUMERIC_5 = 0x205, KEY_NUMERIC_6 = 0x206, KEY_NUMERIC_7 = 0x207, KEY_NUMERIC_8 = 0x208, KEY_NUMERIC_9 = 0x209, KEY_NUMERIC_STAR = 0x20a, KEY_NUMERIC_POUND = 0x20b, KEY_NUMERIC_A = 0x20c, /* Phone key A - HUT Telephony 0xb9 */ KEY_NUMERIC_B = 0x20d, KEY_NUMERIC_C = 0x20e, KEY_NUMERIC_D = 0x20f, KEY_CAMERA_FOCUS = 0x210, KEY_WPS_BUTTON = 0x211, /* WiFi Protected Setup key */ KEY_TOUCHPAD_TOGGLE = 0x212, /* Request switch touchpad on or off */ KEY_TOUCHPAD_ON = 0x213, KEY_TOUCHPAD_OFF = 0x214, KEY_CAMERA_ZOOMIN = 0x215, KEY_CAMERA_ZOOMOUT = 0x216, KEY_CAMERA_UP = 0x217, KEY_CAMERA_DOWN = 0x218, KEY_CAMERA_LEFT = 0x219, KEY_CAMERA_RIGHT = 0x21a, KEY_ATTENDANT_ON = 0x21b, KEY_ATTENDANT_OFF = 0x21c, KEY_ATTENDANT_TOGGLE = 0x21d, /* Attendant call on or off */ KEY_LIGHTS_TOGGLE = 0x21e, /* Reading light on or off */ BTN_DPAD_UP = 0x220, BTN_DPAD_DOWN = 0x221, BTN_DPAD_LEFT = 0x222, BTN_DPAD_RIGHT = 0x223, KEY_ALS_TOGGLE = 0x230, /* Ambient light sensor */ KEY_BUTTONCONFIG = 0x240, /* AL Button Configuration */ KEY_TASKMANAGER = 0x241, /* AL Task/Project Manager */ KEY_JOURNAL = 0x242, /* AL Log/Journal/Timecard */ KEY_CONTROLPANEL = 0x243, /* AL Control Panel */ KEY_APPSELECT = 0x244, /* AL Select Task/Application */ KEY_SCREENSAVER = 0x245, /* AL Screen Saver */ KEY_VOICECOMMAND = 0x246, /* Listening Voice Command */ KEY_ASSISTANT = 0x247, KEY_KBD_LAYOUT_NEXT = 0x248, KEY_BRIGHTNESS_MIN = 0x250, /* Set Brightness to Minimum */ KEY_BRIGHTNESS_MAX = 0x251, /* Set Brightness to Maximum */ KEY_KBDINPUTASSIST_PREV = 0x260, KEY_KBDINPUTASSIST_NEXT = 0x261, KEY_KBDINPUTASSIST_PREVGROUP = 0x262, KEY_KBDINPUTASSIST_NEXTGROUP = 0x263, KEY_KBDINPUTASSIST_ACCEPT = 0x264, KEY_KBDINPUTASSIST_CANCEL = 0x265, KEY_RIGHT_UP = 0x266, KEY_RIGHT_DOWN = 0x267, KEY_LEFT_UP = 0x268, KEY_LEFT_DOWN = 0x269, KEY_ROOT_MENU = 0x26a, KEY_MEDIA_TOP_MENU = 0x26b, KEY_NUMERIC_11 = 0x26c, KEY_NUMERIC_12 = 0x26d, KEY_AUDIO_DESC = 0x26e, KEY_3D_MODE = 0x26f, KEY_NEXT_FAVORITE = 0x270, KEY_STOP_RECORD = 0x271, KEY_PAUSE_RECORD = 0x272, KEY_VOD = 0x273, /* Video on Demand */ KEY_UNMUTE = 0x274, KEY_FASTREVERSE = 0x275, KEY_SLOWREVERSE = 0x276, KEY_DATA = 0x277, KEY_ONSCREEN_KEYBOARD = 0x278, KEY_PRIVACY_SCREEN_TOGGLE = 0x279, KEY_SELECTIVE_SCREENSHOT = 0x27a, BTN_TRIGGER_HAPPY1 = 0x2c0, BTN_TRIGGER_HAPPY2 = 0x2c1, BTN_TRIGGER_HAPPY3 = 0x2c2, BTN_TRIGGER_HAPPY4 = 0x2c3, BTN_TRIGGER_HAPPY5 = 0x2c4, BTN_TRIGGER_HAPPY6 = 0x2c5, BTN_TRIGGER_HAPPY7 = 0x2c6, BTN_TRIGGER_HAPPY8 = 0x2c7, BTN_TRIGGER_HAPPY9 = 0x2c8, BTN_TRIGGER_HAPPY10 = 0x2c9, BTN_TRIGGER_HAPPY11 = 0x2ca, BTN_TRIGGER_HAPPY12 = 0x2cb, BTN_TRIGGER_HAPPY13 = 0x2cc, BTN_TRIGGER_HAPPY14 = 0x2cd, BTN_TRIGGER_HAPPY15 = 0x2ce, BTN_TRIGGER_HAPPY16 = 0x2cf, BTN_TRIGGER_HAPPY17 = 0x2d0, BTN_TRIGGER_HAPPY18 = 0x2d1, BTN_TRIGGER_HAPPY19 = 0x2d2, BTN_TRIGGER_HAPPY20 = 0x2d3, BTN_TRIGGER_HAPPY21 = 0x2d4, BTN_TRIGGER_HAPPY22 = 0x2d5, BTN_TRIGGER_HAPPY23 = 0x2d6, BTN_TRIGGER_HAPPY24 = 0x2d7, BTN_TRIGGER_HAPPY25 = 0x2d8, BTN_TRIGGER_HAPPY26 = 0x2d9, BTN_TRIGGER_HAPPY27 = 0x2da, BTN_TRIGGER_HAPPY28 = 0x2db, BTN_TRIGGER_HAPPY29 = 0x2dc, BTN_TRIGGER_HAPPY30 = 0x2dd, BTN_TRIGGER_HAPPY31 = 0x2de, BTN_TRIGGER_HAPPY32 = 0x2df, BTN_TRIGGER_HAPPY33 = 0x2e0, BTN_TRIGGER_HAPPY34 = 0x2e1, BTN_TRIGGER_HAPPY35 = 0x2e2, BTN_TRIGGER_HAPPY36 = 0x2e3, BTN_TRIGGER_HAPPY37 = 0x2e4, BTN_TRIGGER_HAPPY38 = 0x2e5, BTN_TRIGGER_HAPPY39 = 0x2e6, BTN_TRIGGER_HAPPY40 = 0x2e7, ); #[test] fn from_str() { use std::str::FromStr; assert_eq!(KeyCode::from_str("KEY_A"), Ok(KeyCode::KEY_A)); assert!(KeyCode::from_str("KEY_FOOBAR").is_err()); } evdev-0.13.1/src/sync_stream.rs000064400000000000000000001014071046102023000145060ustar 00000000000000use crate::compat::{input_absinfo, input_event}; use crate::constants::*; use crate::device_state::DeviceState; use crate::ff::*; use crate::raw_stream::RawDevice; use crate::{ AbsInfo, AttributeSet, AttributeSetRef, AutoRepeat, EventSummary, FFEffect, InputEvent, InputId, KeyCode, }; use nix::fcntl; use std::fs::File; use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; use std::path::Path; use std::time::SystemTime; use std::{fmt, io}; /// A physical or virtual device supported by evdev. /// /// Each device corresponds to a path typically found in `/dev/input`, and supports access via /// one or more "types". For example, an optical mouse has buttons that are represented by "keys", /// and reflects changes in its position via "relative axis" reports. /// /// This type specifically is a wrapper over [`RawDevice`],that synchronizes with the kernel's /// state when events are dropped. /// /// If `fetch_events()` isn't called often enough and the kernel drops events from its internal /// buffer, synthetic events will be injected into the iterator returned by `fetch_events()` and /// [`Device::cached_state()`] will be kept up to date when `fetch_events()` is called. #[derive(Debug)] pub struct Device { raw: RawDevice, prev_state: DeviceState, state: DeviceState, block_dropped: bool, } impl Device { /// Opens a device, given its system path. /// /// Paths are typically something like `/dev/input/event0`. #[inline(always)] pub fn open(path: impl AsRef) -> io::Result { Self::_open(path.as_ref()) } /// Opens a device, given an already opened file descriptor. #[inline(always)] pub fn from_fd(fd: OwnedFd) -> io::Result { RawDevice::from_fd(fd).map(Self::from_raw_device) } #[inline] fn _open(path: &Path) -> io::Result { RawDevice::open(path).map(Self::from_raw_device) } // TODO: should this be public? pub(crate) fn from_raw_device(raw: RawDevice) -> Device { let state = DeviceState::new(&raw); let prev_state = state.clone(); Device { raw, prev_state, state, block_dropped: false, } } /// Returns the synchronization engine's current understanding (cache) of the device state. /// /// Note that this represents the internal cache of the synchronization engine as of the last /// entry that was pulled out. The advantage to calling this instead of invoking /// [`get_key_state`](RawDevice::get_key_state) /// and the like directly is speed: because reading this cache doesn't require any syscalls it's /// easy to do inside a tight loop. The downside is that if the stream is not being driven quickly, /// this can very quickly get desynchronized from the kernel and provide inaccurate data. pub fn cached_state(&self) -> &DeviceState { &self.state } /// Returns the device's name as read from the kernel. pub fn name(&self) -> Option<&str> { self.raw.name() } /// Returns the device's physical location, either as set by the caller or as read from the kernel. pub fn physical_path(&self) -> Option<&str> { self.raw.physical_path() } /// Returns the user-defined "unique name" of the device, if one has been set. pub fn unique_name(&self) -> Option<&str> { self.raw.unique_name() } /// Returns a struct containing bustype, vendor, product, and version identifiers pub fn input_id(&self) -> InputId { self.raw.input_id() } /// Returns a struct containing the delay and period for auto repeat pub fn get_auto_repeat(&self) -> Option { self.raw.get_auto_repeat() } /// Update the delay and period for autorepeat pub fn update_auto_repeat(&mut self, repeat: &AutoRepeat) -> io::Result<()> { self.raw.update_auto_repeat(repeat) } /// Retrieve the scancode for a keycode, if any pub fn get_scancode_by_keycode(&self, keycode: KeyCode) -> io::Result> { self.raw.get_scancode_by_keycode(keycode.code() as u32) } /// Retrieve the keycode and scancode by index, starting at 0 pub fn get_scancode_by_index(&self, index: u16) -> io::Result<(u32, Vec)> { self.raw.get_scancode_by_index(index) } /// Update a scancode. The return value is the previous keycode pub fn update_scancode(&self, keycode: KeyCode, scancode: &[u8]) -> io::Result { self.raw .update_scancode(keycode.code() as u32, scancode) .map(|keycode| KeyCode::new(keycode as u16)) } /// Update a scancode by index. The return value is the previous keycode pub fn update_scancode_by_index( &self, index: u16, keycode: KeyCode, scancode: &[u8], ) -> io::Result { self.raw .update_scancode_by_index(index, keycode.code() as u32, scancode) } /// Returns the set of supported "properties" for the device (see `INPUT_PROP_*` in kernel headers) pub fn properties(&self) -> &AttributeSetRef { self.raw.properties() } /// Returns a tuple of the driver version containing major, minor, rev pub fn driver_version(&self) -> (u8, u8, u8) { self.raw.driver_version() } /// Returns a set of the event types supported by this device (KeyType, Switch, etc) /// /// If you're interested in the individual keys or switches supported, it's probably easier /// to just call the appropriate `supported_*` function instead. pub fn supported_events(&self) -> &AttributeSetRef { self.raw.supported_events() } /// Returns the set of supported keys reported by the device. /// /// For keyboards, this is the set of all possible keycodes the keyboard may emit. Controllers, /// mice, and other peripherals may also report buttons as keys. /// /// # Examples /// /// ```no_run /// # fn main() -> Result<(), Box> { /// use evdev::{Device, KeyCode}; /// let device = Device::open("/dev/input/event0")?; /// /// // Does this device have an ENTER key? /// let supported = device.supported_keys().map_or(false, |keys| keys.contains(KeyCode::KEY_ENTER)); /// # Ok(()) /// # } /// ``` pub fn supported_keys(&self) -> Option<&AttributeSetRef> { self.raw.supported_keys() } /// Returns the set of supported "relative axes" reported by the device. /// /// Standard mice will generally report `REL_X` and `REL_Y` along with wheel if supported. /// /// # Examples /// /// ```no_run /// # fn main() -> Result<(), Box> { /// use evdev::{Device, RelativeAxisCode}; /// let device = Device::open("/dev/input/event0")?; /// /// // Does the device have a scroll wheel? /// let supported = device /// .supported_relative_axes() /// .map_or(false, |axes| axes.contains(RelativeAxisCode::REL_WHEEL)); /// # Ok(()) /// # } /// ``` pub fn supported_relative_axes(&self) -> Option<&AttributeSetRef> { self.raw.supported_relative_axes() } /// Returns the set of supported "absolute axes" reported by the device. /// /// These are most typically supported by joysticks and touchpads. /// /// # Examples /// /// ```no_run /// # fn main() -> Result<(), Box> { /// use evdev::{Device, AbsoluteAxisCode}; /// let device = Device::open("/dev/input/event0")?; /// /// // Does the device have an absolute X axis? /// let supported = device /// .supported_absolute_axes() /// .map_or(false, |axes| axes.contains(AbsoluteAxisCode::ABS_X)); /// # Ok(()) /// # } /// ``` pub fn supported_absolute_axes(&self) -> Option<&AttributeSetRef> { self.raw.supported_absolute_axes() } /// Returns the set of supported switches reported by the device. /// /// These are typically used for things like software switches on laptop lids (which the /// system reacts to by suspending or locking), or virtual switches to indicate whether a /// headphone jack is plugged in (used to disable external speakers). /// /// # Examples /// /// ```no_run /// # fn main() -> Result<(), Box> { /// use evdev::{Device, SwitchCode}; /// let device = Device::open("/dev/input/event0")?; /// /// // Does the device report a laptop lid switch? /// let supported = device /// .supported_switches() /// .map_or(false, |axes| axes.contains(SwitchCode::SW_LID)); /// # Ok(()) /// # } /// ``` pub fn supported_switches(&self) -> Option<&AttributeSetRef> { self.raw.supported_switches() } /// Returns a set of supported LEDs on the device. /// /// Most commonly these are state indicator lights for things like Scroll Lock, but they /// can also be found in cameras and other devices. pub fn supported_leds(&self) -> Option<&AttributeSetRef> { self.raw.supported_leds() } /// Returns a set of supported "miscellaneous" capabilities. /// /// Aside from vendor-specific key scancodes, most of these are uncommon. pub fn misc_properties(&self) -> Option<&AttributeSetRef> { self.raw.misc_properties() } /// Returns the set of supported force feedback effects supported by a device. pub fn supported_ff(&self) -> Option<&AttributeSetRef> { self.raw.supported_ff() } /// Returns the maximum number of force feedback effects that can be played simultaneously. pub fn max_ff_effects(&self) -> usize { self.raw.max_ff_effects() } /// Returns the set of supported simple sounds supported by a device. /// /// You can use these to make really annoying beep sounds come from an internal self-test /// speaker, for instance. pub fn supported_sounds(&self) -> Option<&AttributeSetRef> { self.raw.supported_sounds() } /// Retrieve the current keypress state directly via kernel syscall. pub fn get_key_state(&self) -> io::Result> { self.raw.get_key_state() } /// Retrieve the current absolute axis state directly via kernel syscall. pub fn get_abs_state(&self) -> io::Result<[input_absinfo; AbsoluteAxisCode::COUNT]> { self.raw.get_abs_state() } /// Get the AbsInfo for each supported AbsoluteAxis pub fn get_absinfo( &self, ) -> io::Result + '_> { self.raw.get_absinfo() } /// Retrieve the current switch state directly via kernel syscall. pub fn get_switch_state(&self) -> io::Result> { self.raw.get_switch_state() } /// Retrieve the current LED state directly via kernel syscall. pub fn get_led_state(&self) -> io::Result> { self.raw.get_led_state() } fn sync_state(&mut self, now: SystemTime) -> io::Result<()> { if let Some(ref mut key_vals) = self.state.key_vals { self.raw.update_key_state(key_vals)?; } if let Some(ref mut abs_vals) = self.state.abs_vals { self.raw.update_abs_state(abs_vals)?; } if let Some(ref mut switch_vals) = self.state.switch_vals { self.raw.update_switch_state(switch_vals)?; } if let Some(ref mut led_vals) = self.state.led_vals { self.raw.update_led_state(led_vals)?; } self.state.timestamp = now; Ok(()) } fn fetch_events_inner(&mut self) -> io::Result> { let block_dropped = std::mem::take(&mut self.block_dropped); let sync = if block_dropped { self.prev_state.clone_from(&self.state); let now = SystemTime::now(); self.sync_state(now)?; Some(SyncState::KeyTypes { time: crate::systime_to_timeval(&now), start: KeyCode::new(0), }) } else { None }; self.raw.fill_events()?; Ok(sync) } /// Fetches and returns events from the kernel ring buffer, doing synchronization on SYN_DROPPED. /// /// By default this will block until events are available. Typically, users will want to call /// this in a tight loop within a thread. /// Will insert "fake" events. pub fn fetch_events(&mut self) -> io::Result> { let sync = self.fetch_events_inner()?; Ok(FetchEventsSynced { dev: self, range: 0..0, consumed_to: 0, sync, }) } #[cfg(feature = "tokio")] pub fn into_event_stream(self) -> io::Result { EventStream::new(self) } /// Set `O_NONBLOCK` on this device handle. pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let mut flags = fcntl::OFlag::from_bits_retain(fcntl::fcntl(self.as_raw_fd(), fcntl::F_GETFL)?); flags.set(fcntl::OFlag::O_NONBLOCK, nonblocking); fcntl::fcntl(self.as_raw_fd(), fcntl::F_SETFL(flags))?; Ok(()) } /// Grab the device through a kernel syscall. /// /// This prevents other clients (including kernel-internal ones such as rfkill) from receiving /// events from this device. pub fn grab(&mut self) -> io::Result<()> { self.raw.grab() } /// Ungrab the device through a kernel syscall. pub fn ungrab(&mut self) -> io::Result<()> { self.raw.ungrab() } /// Whether the device is currently grabbed for exclusive use or not. pub fn is_grabbed(&self) -> bool { self.raw.is_grabbed() } /// Send an event to the device. /// /// Events that are typically sent to devices are /// [EventType::LED] (turn device LEDs on and off), /// [EventType::SOUND] (play a sound on the device) /// and [EventType::FORCEFEEDBACK] (play force feedback effects on the device, i.e. rumble). pub fn send_events(&mut self, events: &[InputEvent]) -> io::Result<()> { self.raw.send_events(events) } /// Uploads a force feedback effect to the device. pub fn upload_ff_effect(&mut self, data: FFEffectData) -> io::Result { self.raw.upload_ff_effect(data) } /// Sets the force feedback gain, i.e. how strong the force feedback effects should be for the /// device. A gain of 0 means no gain, whereas `u16::MAX` is the maximum gain. pub fn set_ff_gain(&mut self, value: u16) -> io::Result<()> { self.raw.set_ff_gain(value) } /// Enables or disables autocenter for the force feedback device. pub fn set_ff_autocenter(&mut self, value: u16) -> io::Result<()> { self.raw.set_ff_autocenter(value) } } impl Drop for Device { fn drop(&mut self) { if let Err(error) = self.ungrab() { eprintln!("Failed to ungrab device: {error}"); } } } impl AsFd for Device { fn as_fd(&self) -> BorrowedFd<'_> { self.raw.as_fd() } } impl AsRawFd for Device { fn as_raw_fd(&self) -> RawFd { self.raw.as_raw_fd() } } impl TryFrom for Device { type Error = io::Error; fn try_from(file: File) -> Result { RawDevice::try_from(file).map(Self::from_raw_device) } } /// An iterator over events of a [`Device`], produced by [`Device::fetch_events`]. pub struct FetchEventsSynced<'a> { dev: &'a mut Device, /// The current block of the events we're returning to the consumer. If empty /// (i.e. for any x, range == x..x) then we'll find another block on the next `next()` call. range: std::ops::Range, /// The index into dev.raw.event_buf up to which we'll delete events when dropped. consumed_to: usize, /// Our current synchronization state, i.e. whether we're currently diffing key_vals, /// abs_vals, switch_vals, led_vals, or none of them. sync: Option, } enum SyncState { KeyTypes { time: libc::timeval, start: KeyCode, }, Absolutes { time: libc::timeval, start: AbsoluteAxisCode, }, Switches { time: libc::timeval, start: SwitchCode, }, Leds { time: libc::timeval, start: LedCode, }, } #[inline] fn compensate_events(state: &mut Option, dev: &mut Device) -> Option { let sync = state.as_mut()?; // this macro checks if there are any differences between the old state and the new for the // specific substate(?) that we're checking and if so returns an input_event with the value set // to the value from the up-to-date state macro_rules! try_compensate { ($time:expr, $start:ident : $typ:ident, $evtype:ident, $sync:ident, $supporteds:ident, $state:ty, $get_state:expr, $get_value:expr) => { if let Some(supported_types) = dev.$supporteds() { let types_to_check = supported_types.slice(*$start); let get_state: fn(&DeviceState) -> $state = $get_state; let vals = get_state(&dev.state); let old_vals = get_state(&dev.prev_state); let get_value: fn($state, $typ) -> _ = $get_value; for typ in types_to_check.iter() { let prev = get_value(old_vals, typ); let value = get_value(vals, typ); if prev != value { $start.0 = typ.0 + 1; let ev = InputEvent::from(input_event { time: *$time, type_: EventType::$evtype.0, code: typ.0, value: value as _, }); return Some(ev); } } } }; } loop { // check keys, then abs axes, then switches, then leds match sync { SyncState::KeyTypes { time, start } => { try_compensate!( time, start: KeyCode, KEY, KeyTypes, supported_keys, &AttributeSetRef, |st| st.key_vals().unwrap(), |vals, key| vals.contains(key) ); *sync = SyncState::Absolutes { time: *time, start: AbsoluteAxisCode(0), }; continue; } SyncState::Absolutes { time, start } => { try_compensate!( time, start: AbsoluteAxisCode, ABSOLUTE, Absolutes, supported_absolute_axes, &[input_absinfo], |st| st.abs_vals().unwrap(), |vals, abs| vals[abs.0 as usize].value ); *sync = SyncState::Switches { time: *time, start: SwitchCode(0), }; continue; } SyncState::Switches { time, start } => { try_compensate!( time, start: SwitchCode, SWITCH, Switches, supported_switches, &AttributeSetRef, |st| st.switch_vals().unwrap(), |vals, sw| vals.contains(sw) ); *sync = SyncState::Leds { time: *time, start: LedCode(0), }; continue; } SyncState::Leds { time, start } => { try_compensate!( time, start: LedCode, LED, Leds, supported_leds, &AttributeSetRef, |st| st.led_vals().unwrap(), |vals, led| vals.contains(led) ); let ev = InputEvent::from(input_event { time: *time, type_: EventType::SYNCHRONIZATION.0, code: SynchronizationCode::SYN_REPORT.0, value: 0, }); *state = None; return Some(ev); } } } } impl Iterator for FetchEventsSynced<'_> { type Item = InputEvent; fn next(&mut self) -> Option { // first: check if we need to emit compensatory events due to a SYN_DROPPED we found in the // last batch of blocks if let Some(ev) = compensate_events(&mut self.sync, self.dev) { self.dev.prev_state.process_event(ev); return Some(ev); } let state = &mut self.dev.state; let (res, consumed_to) = sync_events(&mut self.range, &self.dev.raw.event_buf, |ev| { state.process_event(ev) }); if let Some(end) = consumed_to { self.consumed_to = end } match res { Ok(ev) => Some(InputEvent::from(ev)), Err(requires_sync) => { if requires_sync { self.dev.block_dropped = true; } None } } } } impl Drop for FetchEventsSynced<'_> { fn drop(&mut self) { self.dev.raw.event_buf.drain(..self.consumed_to); } } /// Err(true) means the device should sync the state with ioctl #[inline] fn sync_events( range: &mut std::ops::Range, event_buf: &[input_event], mut handle_event: impl FnMut(InputEvent), ) -> (Result, Option) { let mut consumed_to = None; let res = 'outer: loop { if let Some(idx) = range.next() { // we're going through and emitting the events of a block that we checked break Ok(event_buf[idx]); } // find the range of this new block: look for a SYN_REPORT let block_start = range.end; let mut block_dropped = false; for (i, ev) in event_buf.iter().enumerate().skip(block_start) { let ev = InputEvent::from(*ev); match ev.destructure() { EventSummary::Synchronization(_, SynchronizationCode::SYN_DROPPED, _) => { block_dropped = true; } EventSummary::Synchronization(_, SynchronizationCode::SYN_REPORT, _) => { consumed_to = Some(i + 1); if block_dropped { *range = event_buf.len()..event_buf.len(); break 'outer Err(true); } else { *range = block_start..i + 1; continue 'outer; } } _ => handle_event(ev), } } break Err(false); }; (res, consumed_to) } impl fmt::Display for Device { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "{}:", self.name().unwrap_or("Unnamed device"))?; let (maj, min, pat) = self.driver_version(); writeln!(f, " Driver version: {maj}.{min}.{pat}")?; if let Some(ref phys) = self.physical_path() { writeln!(f, " Physical address: {phys:?}")?; } if let Some(ref uniq) = self.unique_name() { writeln!(f, " Unique name: {uniq:?}")?; } let id = self.input_id(); writeln!(f, " Bus: {}", id.bus_type())?; writeln!(f, " Vendor: {:#x}", id.vendor())?; writeln!(f, " Product: {:#x}", id.product())?; writeln!(f, " Version: {:#x}", id.version())?; writeln!(f, " Properties: {:?}", self.properties())?; if let (Some(supported_keys), Some(key_vals)) = (self.supported_keys(), self.state.key_vals()) { writeln!(f, " KeyTypes supported:")?; for key in supported_keys.iter() { let key_idx = key.code() as usize; writeln!( f, " {:?} ({}index {})", key, if key_vals.contains(key) { "pressed, " } else { "" }, key_idx )?; } } if let Some(supported_relative) = self.supported_relative_axes() { writeln!(f, " Relative Axes: {supported_relative:?}")?; } if let (Some(supported_abs), Some(abs_vals)) = (self.supported_absolute_axes(), &self.state.abs_vals) { writeln!(f, " Absolute Axes:")?; for abs in supported_abs.iter() { writeln!( f, " {:?} ({:?}, index {})", abs, abs_vals[abs.0 as usize], abs.0 )?; } } if let Some(supported_misc) = self.misc_properties() { writeln!(f, " Miscellaneous capabilities: {supported_misc:?}")?; } if let (Some(supported_switch), Some(switch_vals)) = (self.supported_switches(), self.state.switch_vals()) { writeln!(f, " Switches:")?; for sw in supported_switch.iter() { writeln!( f, " {:?} ({:?}, index {})", sw, switch_vals.contains(sw), sw.0 )?; } } if let (Some(supported_led), Some(led_vals)) = (self.supported_leds(), self.state.led_vals()) { writeln!(f, " LEDs:")?; for led in supported_led.iter() { writeln!( f, " {:?} ({:?}, index {})", led, led_vals.contains(led), led.0 )?; } } if let Some(supported_snd) = self.supported_sounds() { write!(f, " Sounds:")?; for snd in supported_snd.iter() { writeln!(f, " {:?} (index {})", snd, snd.0)?; } } // if let Some(rep) = self.rep { // writeln!(f, " Repeats: {:?}", rep)?; // } let evs = self.supported_events(); if evs.contains(EventType::FORCEFEEDBACK) { writeln!(f, " Force Feedback supported")?; } if evs.contains(EventType::POWER) { writeln!(f, " Power supported")?; } if evs.contains(EventType::FORCEFEEDBACKSTATUS) { writeln!(f, " Force Feedback status supported")?; } Ok(()) } } #[cfg(feature = "tokio")] mod tokio_stream { use super::*; use std::future::poll_fn; use std::task::{ready, Context, Poll}; use tokio::io::unix::AsyncFd; /// An asynchronous stream of input events. /// /// This can be used by calling [`stream.next_event().await?`](Self::next_event), or if you /// need to pass it as a stream somewhere, the [`futures::Stream`](Stream) implementation. /// There's also a lower-level [`Self::poll_event`] function if you need to fetch an event from /// inside a `Future::poll` impl. pub struct EventStream { device: AsyncFd, event_range: std::ops::Range, consumed_to: usize, sync: Option, } impl Unpin for EventStream {} impl EventStream { pub(crate) fn new(device: Device) -> io::Result { device.set_nonblocking(true)?; let device = AsyncFd::new(device)?; Ok(Self { device, event_range: 0..0, consumed_to: 0, sync: None, }) } /// Returns a reference to the underlying device pub fn device(&self) -> &Device { self.device.get_ref() } /// Returns a mutable reference to the underlying device pub fn device_mut(&mut self) -> &mut Device { self.device.get_mut() } /// Try to wait for the next event in this stream. Any errors are likely to be fatal, i.e. /// any calls afterwards will likely error as well. pub async fn next_event(&mut self) -> io::Result { poll_fn(|cx| self.poll_event(cx)).await } /// A lower-level function for directly polling this stream. pub fn poll_event(&mut self, cx: &mut Context<'_>) -> Poll> { 'outer: loop { let dev = self.device.get_mut(); if let Some(ev) = compensate_events(&mut self.sync, dev) { return Poll::Ready(Ok(ev)); } let state = &mut dev.state; let (res, consumed_to) = sync_events(&mut self.event_range, &dev.raw.event_buf, |ev| { state.process_event(ev) }); if let Some(end) = consumed_to { self.consumed_to = end } match res { Ok(ev) => return Poll::Ready(Ok(InputEvent::from(ev))), Err(requires_sync) => { if requires_sync { dev.block_dropped = true; } } } dev.raw.event_buf.drain(..self.consumed_to); self.consumed_to = 0; loop { let mut guard = ready!(self.device.poll_read_ready_mut(cx))?; let res = guard.try_io(|device| device.get_mut().fetch_events_inner()); match res { Ok(res) => { self.sync = res?; self.event_range = 0..0; continue 'outer; } Err(_would_block) => continue, } } } } } #[cfg(feature = "stream-trait")] impl futures_core::Stream for EventStream { type Item = io::Result; fn poll_next( self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { self.get_mut().poll_event(cx).map(Some) } } } #[cfg(feature = "tokio")] pub use tokio_stream::EventStream; #[cfg(test)] mod tests { use super::*; fn result_events_iter( events: &[input_event], ) -> impl Iterator> + '_ { let mut range = 0..0; std::iter::from_fn(move || { let (res, _) = sync_events(&mut range, events, |_| {}); match res { Ok(x) => Some(Ok(x)), Err(true) => Some(Err(())), Err(false) => None, } }) } fn events_iter(events: &[input_event]) -> impl Iterator + '_ { result_events_iter(events).flatten() } #[allow(non_upper_case_globals)] const time: libc::timeval = libc::timeval { tv_sec: 0, tv_usec: 0, }; const KEY4: input_event = input_event { time, type_: EventType::KEY.0, code: KeyCode::KEY_4.0, value: 1, }; const REPORT: input_event = input_event { time, type_: EventType::SYNCHRONIZATION.0, code: SynchronizationCode::SYN_REPORT.0, value: 0, }; const DROPPED: input_event = input_event { code: SynchronizationCode::SYN_DROPPED.0, ..REPORT }; #[test] fn test_sync_impl() { itertools::assert_equal(events_iter(&[]), vec![]); itertools::assert_equal(events_iter(&[KEY4]), vec![]); itertools::assert_equal(events_iter(&[KEY4, REPORT]), vec![KEY4, REPORT]); itertools::assert_equal(events_iter(&[KEY4, REPORT, KEY4]), vec![KEY4, REPORT]); itertools::assert_equal( result_events_iter(&[KEY4, REPORT, KEY4, DROPPED, REPORT]), vec![Ok(KEY4), Ok(REPORT), Err(())], ); } #[test] fn test_iter_consistency() { // once it sees a SYN_DROPPED, it shouldn't mark the block after it as consumed even if we // keep calling the iterator like an idiot let evs = &[KEY4, REPORT, DROPPED, REPORT, KEY4, REPORT, KEY4]; let mut range = 0..0; let mut next = || sync_events(&mut range, evs, |_| {}); assert_eq!(next(), (Ok(KEY4), Some(2))); assert_eq!(next(), (Ok(REPORT), None)); assert_eq!(next(), (Err(true), Some(4))); assert_eq!(next(), (Err(false), None)); assert_eq!(next(), (Err(false), None)); assert_eq!(next(), (Err(false), None)); } } evdev-0.13.1/src/sys.rs000064400000000000000000000126201046102023000127730ustar 00000000000000#![allow(non_camel_case_types)] use crate::compat::{ ff_condition_effect, ff_constant_effect, ff_periodic_effect, ff_ramp_effect, ff_replay, ff_rumble_effect, ff_trigger, input_absinfo, input_id, input_keymap_entry, uinput_abs_setup, uinput_setup, }; use nix::{ convert_ioctl_res, ioctl_none, ioctl_read, ioctl_read_buf, ioctl_readwrite, ioctl_write_int, ioctl_write_ptr, request_code_read, }; #[repr(C)] #[derive(Clone, Copy)] pub union ff_effect_union { pub constant: ff_constant_effect, pub ramp: ff_ramp_effect, pub periodic: ff_periodic_effect, pub condition: [ff_condition_effect; 2], pub rumble: ff_rumble_effect, } #[repr(C)] #[derive(Clone, Copy)] pub struct ff_effect { pub type_: u16, pub id: i16, pub direction: u16, pub trigger: ff_trigger, pub replay: ff_replay, pub u: ff_effect_union, } #[repr(C)] #[derive(Clone, Copy)] pub struct uinput_ff_upload { pub request_id: u32, pub retval: i32, pub effect: ff_effect, pub old: ff_effect, } #[repr(C)] #[derive(Clone, Copy)] pub struct uinput_ff_erase { pub request_id: u32, pub retval: i32, pub effect_id: u32, } ioctl_read!(eviocgeffects, b'E', 0x84, ::libc::c_int); ioctl_read!(eviocgid, b'E', 0x02, /*struct*/ input_id); ioctl_read!(eviocgkeycode, b'E', 0x04, [::libc::c_uint; 2]); ioctl_read!(eviocgrep, b'E', 0x03, [::libc::c_uint; 2]); ioctl_read!(eviocgversion, b'E', 0x01, ::libc::c_int); ioctl_write_int!(eviocrmff, b'E', 0x81); ioctl_read!(eviocgkeycode_v2, b'E', 0x04, input_keymap_entry); ioctl_write_ptr!(eviocskeycode, b'E', 0x04, [::libc::c_uint; 2]); ioctl_write_ptr!(eviocskeycode_v2, b'E', 0x04, input_keymap_entry); ioctl_write_ptr!(eviocsrep, b'E', 0x03, [::libc::c_uint; 2]); ioctl_read_buf!(eviocgname, b'E', 0x06, u8); ioctl_read_buf!(eviocgphys, b'E', 0x07, u8); ioctl_read_buf!(eviocguniq, b'E', 0x08, u8); ioctl_read_buf!(eviocgprop, b'E', 0x09, u8); ioctl_read_buf!(eviocgmtslots, b'E', 0x0a, u8); ioctl_read_buf!(eviocgkey, b'E', 0x18, u8); ioctl_read_buf!(eviocgled, b'E', 0x19, u8); ioctl_read_buf!(eviocgsnd, b'E', 0x1a, u8); ioctl_read_buf!(eviocgsw, b'E', 0x1b, u8); ioctl_write_ptr!(eviocsff, b'E', 0x80, ff_effect); ioctl_write_int!(eviocgrab, b'E', 0x90); ioctl_write_int!(eviocrevoke, b'E', 0x91); ioctl_write_int!(eviocsclockid, b'E', 0xa0); const UINPUT_IOCTL_BASE: u8 = b'U'; ioctl_write_ptr!(ui_dev_setup, UINPUT_IOCTL_BASE, 3, uinput_setup); ioctl_write_ptr!(ui_abs_setup, UINPUT_IOCTL_BASE, 4, uinput_abs_setup); ioctl_none!(ui_dev_create, UINPUT_IOCTL_BASE, 1); ioctl_write_int!(ui_set_evbit, UINPUT_IOCTL_BASE, 100); ioctl_write_int!(ui_set_keybit, UINPUT_IOCTL_BASE, 101); ioctl_write_int!(ui_set_relbit, UINPUT_IOCTL_BASE, 102); ioctl_write_int!(ui_set_absbit, UINPUT_IOCTL_BASE, 103); ioctl_write_int!(ui_set_mscbit, UINPUT_IOCTL_BASE, 104); ioctl_write_int!(ui_set_ledbit, UINPUT_IOCTL_BASE, 105); ioctl_write_int!(ui_set_sndbit, UINPUT_IOCTL_BASE, 106); ioctl_write_int!(ui_set_ffbit, UINPUT_IOCTL_BASE, 107); ioctl_write_ptr!(ui_set_phys, UINPUT_IOCTL_BASE, 108, libc::c_char); ioctl_write_int!(ui_set_swbit, UINPUT_IOCTL_BASE, 109); ioctl_write_int!(ui_set_propbit, UINPUT_IOCTL_BASE, 110); ioctl_read_buf!(ui_get_sysname, UINPUT_IOCTL_BASE, 300, u8); ioctl_readwrite!(ui_begin_ff_upload, UINPUT_IOCTL_BASE, 200, uinput_ff_upload); ioctl_write_ptr!(ui_end_ff_upload, UINPUT_IOCTL_BASE, 201, uinput_ff_upload); ioctl_readwrite!(ui_begin_ff_erase, UINPUT_IOCTL_BASE, 202, uinput_ff_erase); ioctl_write_ptr!(ui_end_ff_erase, UINPUT_IOCTL_BASE, 203, uinput_ff_erase); macro_rules! eviocgbit_ioctl { ($mac:ident!($name:ident, $ev:ident, $ty:ty)) => { eviocgbit_ioctl!($mac!($name, $crate::EventType::$ev.0, $ty)); }; ($mac:ident!($name:ident, $ev:expr, $ty:ty)) => { $mac!($name, b'E', 0x20 + $ev, $ty); }; } eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_type, 0, u8)); eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_key, KEY, u8)); eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_relative, RELATIVE, u8)); eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_absolute, ABSOLUTE, u8)); eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_misc, MISC, u8)); eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_switch, SWITCH, u8)); eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_led, LED, u8)); eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_sound, SOUND, u8)); eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_repeat, REPEAT, u8)); eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_ff, FORCEFEEDBACK, u8)); eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_power, POWER, u8)); eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_ffstatus, FORCEFEEDBACKSTATUS, u8)); /// ioctl: "get abs value/limits" /// /// `abs` should be one of the "Absolute axes" values defined in the Linux kernel headers. /// In modern (5.11) kernels these are in `include/uapi/linux/input-event-codes.h`, and in older /// kernels these defines can be found in `include/uapi/linux/input.h` /// /// # Panics /// /// Calling this with a value greater than the kernel-defined `ABS_MAX` (typically 0x3f) will panic. /// /// # Safety /// /// 'abs' must be a valid axis number and supported by the device, otherwise the behavior is /// undefined. pub unsafe fn eviocgabs( fd: ::libc::c_int, abs: u32, buf: &mut input_absinfo, ) -> ::nix::Result<::libc::c_int> { assert!(abs <= 0x3f); convert_ioctl_res!(::nix::libc::ioctl( fd, request_code_read!(b'E', 0x40 + abs, ::std::mem::size_of::()), buf as *mut input_absinfo )) } evdev-0.13.1/src/uinput.rs000064400000000000000000000510101046102023000134750ustar 00000000000000//! Virtual device emulation for evdev via uinput. //! //! This is quite useful when testing/debugging devices, or synchronization. use crate::compat::{input_event, input_id, uinput_abs_setup, uinput_setup, UINPUT_MAX_NAME_SIZE}; use crate::ff::FFEffectData; use crate::inputid::{BusType, InputId}; use crate::{ sys, AttributeSetRef, FFEffectCode, InputEvent, KeyCode, MiscCode, PropType, RelativeAxisCode, SwitchCode, SynchronizationEvent, UInputCode, UInputEvent, UinputAbsSetup, }; use std::ffi::{CStr, OsStr}; use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; use std::os::unix::ffi::OsStrExt; use std::path::{Path, PathBuf}; use std::{fs, io}; const UINPUT_PATH: &str = "/dev/uinput"; const SYSFS_PATH: &str = "/sys/devices/virtual/input"; const DEV_PATH: &str = "/dev/input"; /// A builder struct for creating a new uinput virtual device. #[derive(Debug)] pub struct VirtualDeviceBuilder<'a> { fd: OwnedFd, name: &'a [u8], id: Option, ff_effects_max: u32, } /// A builder struct for [`VirtualDevice`]. /// /// Created via [`VirtualDevice::builder()`]. impl<'a> VirtualDeviceBuilder<'a> { #[deprecated(note = "use `VirtualDevice::builder()` instead")] #[doc(hidden)] pub fn new() -> io::Result { // Open in read-write mode. let fd = fs::OpenOptions::new() .read(true) .write(true) .open(UINPUT_PATH)?; Ok(VirtualDeviceBuilder { fd: fd.into(), name: Default::default(), id: None, ff_effects_max: 0, }) } /// Set the display name of this device. #[inline] pub fn name + ?Sized>(mut self, name: &'a S) -> Self { self.name = name.as_ref(); self } /// Set a custom input ID. #[inline] pub fn input_id(mut self, id: InputId) -> Self { self.id = Some(id.0); self } /// Set the device's physical location, e.g. `usb-00:01.2-2.1/input0`. pub fn with_phys(self, path: &CStr) -> io::Result { unsafe { sys::ui_set_phys(self.fd.as_raw_fd(), path.as_ptr())?; } Ok(self) } /// Set the key codes that can be emitted by this device. pub fn with_keys(self, keys: &AttributeSetRef) -> io::Result { // Run ioctls for setting capability bits unsafe { sys::ui_set_evbit( self.fd.as_raw_fd(), crate::EventType::KEY.0 as nix::sys::ioctl::ioctl_param_type, )?; } for bit in keys.iter() { unsafe { sys::ui_set_keybit( self.fd.as_raw_fd(), bit.0 as nix::sys::ioctl::ioctl_param_type, )?; } } Ok(self) } /// Set the absolute axes of this device. pub fn with_absolute_axis(self, axis: &UinputAbsSetup) -> io::Result { unsafe { sys::ui_set_evbit( self.fd.as_raw_fd(), crate::EventType::ABSOLUTE.0 as nix::sys::ioctl::ioctl_param_type, )?; sys::ui_set_absbit( self.fd.as_raw_fd(), axis.code() as nix::sys::ioctl::ioctl_param_type, )?; sys::ui_abs_setup(self.fd.as_raw_fd(), &axis.0 as *const uinput_abs_setup)?; } Ok(self) } /// Set the relative axes of this device. pub fn with_relative_axes(self, axes: &AttributeSetRef) -> io::Result { unsafe { sys::ui_set_evbit( self.fd.as_raw_fd(), crate::EventType::RELATIVE.0 as nix::sys::ioctl::ioctl_param_type, )?; } for bit in axes.iter() { unsafe { sys::ui_set_relbit( self.fd.as_raw_fd(), bit.0 as nix::sys::ioctl::ioctl_param_type, )?; } } Ok(self) } /// Set the properties of this device. pub fn with_properties(self, switches: &AttributeSetRef) -> io::Result { for bit in switches.iter() { unsafe { sys::ui_set_propbit( self.fd.as_raw_fd(), bit.0 as nix::sys::ioctl::ioctl_param_type, )?; } } Ok(self) } /// Set the switch codes that can be emitted by this device. pub fn with_switches(self, switches: &AttributeSetRef) -> io::Result { unsafe { sys::ui_set_evbit( self.fd.as_raw_fd(), crate::EventType::SWITCH.0 as nix::sys::ioctl::ioctl_param_type, )?; } for bit in switches.iter() { unsafe { sys::ui_set_swbit( self.fd.as_raw_fd(), bit.0 as nix::sys::ioctl::ioctl_param_type, )?; } } Ok(self) } /// Set the force-feedback effects that can be emitted by this device. pub fn with_ff(self, ff: &AttributeSetRef) -> io::Result { unsafe { sys::ui_set_evbit( self.fd.as_raw_fd(), crate::EventType::FORCEFEEDBACK.0 as nix::sys::ioctl::ioctl_param_type, )?; } for bit in ff.iter() { unsafe { sys::ui_set_ffbit( self.fd.as_raw_fd(), bit.0 as nix::sys::ioctl::ioctl_param_type, )?; } } Ok(self) } /// Set the maximum number for a force-feedback effect for this device. pub fn with_ff_effects_max(mut self, ff_effects_max: u32) -> Self { self.ff_effects_max = ff_effects_max; self } /// Set the `MiscCode`s of this device. pub fn with_msc(self, misc_set: &AttributeSetRef) -> io::Result { unsafe { sys::ui_set_evbit( self.fd.as_raw_fd(), crate::EventType::MISC.0 as nix::sys::ioctl::ioctl_param_type, )?; } for bit in misc_set.iter() { unsafe { sys::ui_set_mscbit( self.fd.as_raw_fd(), bit.0 as nix::sys::ioctl::ioctl_param_type, )?; } } Ok(self) } /// Finalize and register this device. /// /// # Errors /// Returns an error if device setup or creation fails. pub fn build(self) -> io::Result { // Populate the uinput_setup struct let mut usetup = uinput_setup { id: self.id.unwrap_or(DEFAULT_ID), name: [0; UINPUT_MAX_NAME_SIZE], ff_effects_max: self.ff_effects_max, }; // SAFETY: either casting [u8] to [u8], or [u8] to [i8], which is the same size let name_bytes = unsafe { &*(self.name as *const [u8] as *const [libc::c_char]) }; // Panic if we're doing something really stupid // + 1 for the null terminator; usetup.name was zero-initialized so there will be null // bytes after the part we copy into assert!(name_bytes.len() + 1 < UINPUT_MAX_NAME_SIZE); usetup.name[..name_bytes.len()].copy_from_slice(name_bytes); VirtualDevice::new(self.fd, &usetup) } } const DEFAULT_ID: input_id = input_id { bustype: BusType::BUS_USB.0, vendor: 0x1234, /* sample vendor */ product: 0x5678, /* sample product */ version: 0x111, }; /// A handle to a uinput virtual device. #[derive(Debug)] pub struct VirtualDevice { fd: OwnedFd, pub(crate) event_buf: Vec, } impl VirtualDevice { /// Convenience method for creating a `VirtualDeviceBuilder`. pub fn builder<'a>() -> io::Result> { #[allow(deprecated)] VirtualDeviceBuilder::new() } /// Create a new virtual device. fn new(fd: OwnedFd, usetup: &uinput_setup) -> io::Result { unsafe { sys::ui_dev_setup(fd.as_raw_fd(), usetup)? }; unsafe { sys::ui_dev_create(fd.as_raw_fd())? }; Ok(VirtualDevice { fd, event_buf: vec![], }) } #[inline] fn write_raw(&mut self, events: &[InputEvent]) -> io::Result<()> { crate::write_events(self.fd.as_fd(), events)?; Ok(()) } /// Get the syspath representing this uinput device. /// /// The syspath returned is the one of the input node itself (e.g. /// `/sys/devices/virtual/input/input123`), not the syspath of the device node. pub fn get_syspath(&mut self) -> io::Result { let mut syspath = vec![0u8; 256]; let len = unsafe { sys::ui_get_sysname(self.fd.as_raw_fd(), &mut syspath)? }; syspath.truncate(len as usize - 1); let syspath = OsStr::from_bytes(&syspath); Ok(Path::new(SYSFS_PATH).join(syspath)) } /// Get the syspaths of the corresponding device nodes in /dev/input. pub fn enumerate_dev_nodes_blocking(&mut self) -> io::Result { let path = self.get_syspath()?; let dir = std::fs::read_dir(path)?; Ok(DevNodesBlocking { dir }) } /// Get the syspaths of the corresponding device nodes in /dev/input. #[cfg(feature = "tokio")] pub async fn enumerate_dev_nodes(&mut self) -> io::Result { let path = self.get_syspath()?; let dir = tokio::fs::read_dir(path).await?; Ok(DevNodes { dir }) } /// Post a batch of events to the virtual device. /// /// The batch is automatically terminated with a `SYN_REPORT` event. /// Events from physical devices are batched based on if they occur simultaneously, for example movement /// of a mouse triggers a movement events for the X and Y axes separately in a batch of 2 events. /// /// Single events such as a `KEY` event must still be followed by a `SYN_REPORT`. pub fn emit(&mut self, events: &[InputEvent]) -> io::Result<()> { self.write_raw(events)?; let syn = *SynchronizationEvent::new(crate::SynchronizationCode::SYN_REPORT, 0); self.write_raw(&[syn]) } /// Processes the given [`UInputEvent`] if it is a force feedback upload event, in which case /// this function will start the force feedback upload and claim ownership over the /// [`UInputEvent`] and return a [`FFUploadEvent`] instead. /// /// The returned event allows the user to allocate and set the effect ID as well as access the /// effect data. /// /// # Panics /// /// This function will panic if `event.code()` is not `UI_FF_UPLOAD`. pub fn process_ff_upload(&mut self, event: UInputEvent) -> io::Result { assert_eq!(event.code(), UInputCode::UI_FF_UPLOAD); let mut request: sys::uinput_ff_upload = unsafe { std::mem::zeroed() }; request.request_id = event.value() as u32; unsafe { sys::ui_begin_ff_upload(self.fd.as_raw_fd(), &mut request)? }; request.retval = 0; let fd = self.fd.try_clone()?; Ok(FFUploadEvent { fd, request }) } /// Processes the given [`UInputEvent`] if it is a force feedback erase event, in which case /// this function will start the force feedback erasure and claim ownership over the /// [`UInputEvent`] and return a [`FFEraseEvent`] instead. /// /// The returned event allows the user to access the effect ID, such that it can free any /// memory used for the given effect ID. /// /// # Panics /// /// This function will panic if `event.code()` is not `UI_FF_ERASE`. pub fn process_ff_erase(&mut self, event: UInputEvent) -> io::Result { assert_eq!(event.code(), UInputCode::UI_FF_ERASE); let mut request: sys::uinput_ff_erase = unsafe { std::mem::zeroed() }; request.request_id = event.value() as u32; unsafe { sys::ui_begin_ff_erase(self.fd.as_raw_fd(), &mut request)? }; request.retval = 0; let fd = self.fd.try_clone()?; Ok(FFEraseEvent { fd, request }) } /// Read a maximum of `num` events into the internal buffer. If the underlying fd is not /// O_NONBLOCK, this will block. /// /// Returns the number of events that were read, or an error. pub(crate) fn fill_events(&mut self) -> io::Result { let fd = self.fd.as_raw_fd(); self.event_buf.reserve(crate::EVENT_BATCH_SIZE); let spare_capacity = self.event_buf.spare_capacity_mut(); let spare_capacity_size = std::mem::size_of_val(spare_capacity); // use libc::read instead of nix::unistd::read b/c we need to pass an uninitialized buf let res = unsafe { libc::read(fd, spare_capacity.as_mut_ptr() as _, spare_capacity_size) }; let bytes_read = nix::errno::Errno::result(res)?; let num_read = bytes_read as usize / std::mem::size_of::(); unsafe { let len = self.event_buf.len(); self.event_buf.set_len(len + num_read); } Ok(num_read) } /// Fetches and returns events from the kernel ring buffer without doing synchronization on /// SYN_DROPPED. /// /// By default this will block until events are available. Typically, users will want to call /// this in a tight loop within a thread. pub fn fetch_events(&mut self) -> io::Result + '_> { self.fill_events()?; Ok(self.event_buf.drain(..).map(InputEvent::from)) } #[cfg(feature = "tokio")] #[inline] pub fn into_event_stream(self) -> io::Result { VirtualEventStream::new(self) } } /// This struct is returned from the [VirtualDevice::enumerate_dev_nodes_blocking] function and will yield /// the syspaths corresponding to the virtual device. These are of the form `/dev/input123`. pub struct DevNodesBlocking { dir: std::fs::ReadDir, } impl Iterator for DevNodesBlocking { type Item = io::Result; fn next(&mut self) -> Option { for entry in self.dir.by_ref() { let entry = match entry { Ok(entry) => entry, Err(e) => return Some(Err(e)), }; // Map the directory name to its file name. let file_name = entry.file_name(); // Ignore file names that do not start with event. if !file_name.as_bytes().starts_with(b"event") { continue; } // Construct the path of the form '/dev/input/eventX'. let path = Path::new(DEV_PATH).join(file_name); return Some(Ok(path)); } None } } /// This struct is returned from the [VirtualDevice::enumerate_dev_nodes_blocking] function and /// will yield the syspaths corresponding to the virtual device. These are of the form /// `/dev/input123`. #[cfg(feature = "tokio")] pub struct DevNodes { dir: tokio::fs::ReadDir, } #[cfg(feature = "tokio")] impl DevNodes { /// Returns the next entry in the set of device nodes. pub async fn next_entry(&mut self) -> io::Result> { while let Some(entry) = self.dir.next_entry().await? { // Map the directory name to its file name. let file_name = entry.file_name(); // Ignore file names that do not start with event. if !file_name.as_bytes().starts_with(b"event") { continue; } // Construct the path of the form '/dev/input/eventX'. let path = Path::new(DEV_PATH).join(file_name); return Ok(Some(path)); } Ok(None) } } impl AsFd for VirtualDevice { fn as_fd(&self) -> BorrowedFd<'_> { self.fd.as_fd() } } impl AsRawFd for VirtualDevice { fn as_raw_fd(&self) -> RawFd { self.fd.as_raw_fd() } } /// Represents a force feedback upload event that we are currently processing. pub struct FFUploadEvent { fd: OwnedFd, request: sys::uinput_ff_upload, } impl FFUploadEvent { /// Returns the old effect data. pub fn old_effect(&self) -> FFEffectData { self.request.old.into() } /// Returns the new effect ID. pub fn effect_id(&self) -> i16 { self.request.effect.id } /// Sets the new effect ID. pub fn set_effect_id(&mut self, id: i16) { self.request.effect.id = id; } /// Returns the new effect data. pub fn effect(&self) -> FFEffectData { self.request.effect.into() } /// Returns the currently set return value for the upload event. pub fn retval(&self) -> i32 { self.request.retval } /// Sets the return value to return for the upload event. pub fn set_retval(&mut self, value: i32) { self.request.retval = value; } } impl Drop for FFUploadEvent { fn drop(&mut self) { unsafe { let _ = sys::ui_end_ff_upload(self.fd.as_raw_fd(), &self.request); } } } /// Represents a force feedback erase event that we are currently processing. pub struct FFEraseEvent { fd: OwnedFd, request: sys::uinput_ff_erase, } impl FFEraseEvent { /// Returns the effect ID to erase. pub fn effect_id(&self) -> u32 { self.request.effect_id } /// Returns the currently set return value for the erase event. pub fn retval(&self) -> i32 { self.request.retval } /// Sets the return value to return for the erase event. pub fn set_retval(&mut self, value: i32) { self.request.retval = value; } } impl Drop for FFEraseEvent { fn drop(&mut self) { unsafe { let _ = sys::ui_end_ff_erase(self.fd.as_raw_fd(), &self.request); } } } #[cfg(feature = "tokio")] mod tokio_stream { use super::*; use std::future::poll_fn; use std::task::{ready, Context, Poll}; use tokio::io::unix::AsyncFd; /// An asynchronous stream of input events. /// /// This can be used by calling [`stream.next_event().await?`](Self::next_event), or if you /// need to pass it as a stream somewhere, the [`futures::Stream`](Stream) implementation. /// There's also a lower-level [`Self::poll_event`] function if you need to fetch an event from /// inside a `Future::poll` impl. pub struct VirtualEventStream { device: AsyncFd, index: usize, } impl Unpin for VirtualEventStream {} impl VirtualEventStream { pub(crate) fn new(device: VirtualDevice) -> io::Result { use nix::fcntl; fcntl::fcntl(device.as_raw_fd(), fcntl::F_SETFL(fcntl::OFlag::O_NONBLOCK))?; let device = AsyncFd::new(device)?; Ok(Self { device, index: 0 }) } /// Returns a reference to the underlying device pub fn device(&self) -> &VirtualDevice { self.device.get_ref() } /// Returns a mutable reference to the underlying device. pub fn device_mut(&mut self) -> &mut VirtualDevice { self.device.get_mut() } /// Try to wait for the next event in this stream. Any errors are likely to be fatal, i.e. /// any calls afterwards will likely error as well. pub async fn next_event(&mut self) -> io::Result { poll_fn(|cx| self.poll_event(cx)).await } /// A lower-level function for directly polling this stream. pub fn poll_event(&mut self, cx: &mut Context<'_>) -> Poll> { 'outer: loop { if let Some(&ev) = self.device.get_ref().event_buf.get(self.index) { self.index += 1; return Poll::Ready(Ok(InputEvent::from(ev))); } self.device.get_mut().event_buf.clear(); self.index = 0; loop { let mut guard = ready!(self.device.poll_read_ready_mut(cx))?; let res = guard.try_io(|device| device.get_mut().fill_events()); match res { Ok(res) => { let _ = res?; continue 'outer; } Err(_would_block) => continue, } } } } } #[cfg(feature = "stream-trait")] impl futures_core::Stream for VirtualEventStream { type Item = io::Result; fn poll_next( self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { self.get_mut().poll_event(cx).map(Some) } } } #[cfg(feature = "tokio")] pub use tokio_stream::VirtualEventStream; evdev-0.13.1/tests/virtual_device.rs000064400000000000000000000031571046102023000155420ustar 00000000000000#![cfg(feature = "tokio")] use std::error::Error; use std::thread::sleep; use std::time::Duration; use tokio::time::timeout; use evdev::{uinput::VirtualDevice, AttributeSet, EventType, InputEvent, KeyCode}; #[tokio::test] async fn test_virtual_device_actually_emits() -> Result<(), Box> { let mut keys = AttributeSet::::new(); let virtual_device_name = "fake-keyboard"; keys.insert(KeyCode::KEY_ESC); let mut device = VirtualDevice::builder()? .name(virtual_device_name) .with_keys(&keys)? .build() .unwrap(); let mut maybe_device = None; sleep(Duration::from_millis(500)); for (_i, d) in evdev::enumerate() { println!("{:?}", d.name()); if d.name() == Some(virtual_device_name) { maybe_device = Some(d); break; } } assert!(maybe_device.is_some()); let listen_device = maybe_device.unwrap(); let type_ = EventType::KEY; let code = KeyCode::KEY_ESC.code(); // listen for events on the listen device let listener = tokio::spawn(async move { // try to read the key code that will be sent through virtual device let mut events = listen_device.into_event_stream()?; events.next_event().await }); // emit a key code through virtual device let down_event = InputEvent::new(type_.0, code, 10); device.emit(&[down_event]).unwrap(); let event = timeout(Duration::from_secs(1), listener).await???; assert_eq!(down_event.event_type(), event.event_type()); assert_eq!(down_event.code(), event.code()); // wait for listener Ok(()) }