reis-0.2.0/.cargo_vcs_info.json0000644000000001360000000000100117750ustar { "git": { "sha1": "87226f44ef0386325527fc7391b15dfb99efc1a4" }, "path_in_vcs": "" }reis-0.2.0/.github/workflows/ci.yaml000064400000000000000000000030241046102023000154400ustar 00000000000000name: CI on: pull_request: push: jobs: check-and-build: strategy: matrix: rust_version: ['1.65.0', stable] runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: hecrj/setup-rust-action@v1 with: rust-version: ${{ matrix.rust_version }} components: clippy, rustfmt - run: sudo apt-get -qq update - run: sudo apt-get install -y libxkbcommon-dev - run: cargo fmt --all -- --check - run: cargo clippy --all-features -- -Dwarnings - run: cargo build --all-features if: matrix.rust_version != 'stable' - run: cargo build --all-features --examples if: matrix.rust_version == 'stable' - uses: actions/upload-artifact@v3 with: name: reis-demo-server path: target/debug/examples/reis-demo-server libei-test: runs-on: ubuntu-latest needs: check-and-build steps: - run: git clone https://gitlab.freedesktop.org/libinput/libei . - uses: actions/download-artifact@v3 with: name: reis-demo-server - run: chmod +x reis-demo-server - run: sudo apt-get -qq update - run: sudo apt-get install -y meson libsystemd-dev python3-pytest python3-structlog python3-attr python3-dbusmock python3-pip - run: pip3 install strenum - run: meson setup build - run: meson compile -C build - run: | mkdir xdg export XDG_RUNTIME_DIR=$PWD/xdg export LIBEI_TEST_SERVER=$PWD/reis-demo-server export LIBEI_TEST_SOCKET=$PWD/xdg/eis-0 cd build && pytest-3 reis-0.2.0/.gitignore000064400000000000000000000000241046102023000125510ustar 00000000000000/target /Cargo.lock reis-0.2.0/Cargo.lock0000644000001122320000000000100077510ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "ashpd" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" dependencies = [ "async-fs", "async-net", "enumflags2", "futures-channel", "futures-util", "rand", "serde", "serde_repr", "url", "zbus", ] [[package]] name = "async-broadcast" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "258b52a1aa741b9f09783b2d86cf0aeeb617bbf847f6933340a39644227acbdb" dependencies = [ "event-listener 5.2.0", "event-listener-strategy 0.5.0", "futures-core", "pin-project-lite", ] [[package]] name = "async-channel" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", "event-listener 5.2.0", "event-listener-strategy 0.5.0", "futures-core", "pin-project-lite", ] [[package]] name = "async-executor" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" dependencies = [ "async-lock 3.3.0", "async-task", "concurrent-queue", "fastrand", "futures-lite", "slab", ] [[package]] name = "async-fs" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1" dependencies = [ "async-lock 3.3.0", "blocking", "futures-lite", ] [[package]] name = "async-io" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" dependencies = [ "async-lock 3.3.0", "cfg-if", "concurrent-queue", "futures-io", "futures-lite", "parking", "polling", "rustix", "slab", "tracing", "windows-sys 0.52.0", ] [[package]] name = "async-lock" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ "event-listener 2.5.3", ] [[package]] name = "async-lock" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ "event-listener 4.0.3", "event-listener-strategy 0.4.0", "pin-project-lite", ] [[package]] name = "async-net" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" dependencies = [ "async-io", "blocking", "futures-lite", ] [[package]] name = "async-process" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "451e3cf68011bd56771c79db04a9e333095ab6349f7e47592b788e9b98720cc8" dependencies = [ "async-channel", "async-io", "async-lock 3.3.0", "async-signal", "blocking", "cfg-if", "event-listener 5.2.0", "futures-lite", "rustix", "windows-sys 0.52.0", ] [[package]] name = "async-recursion" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ "proc-macro2", "quote", "syn 2.0.53", ] [[package]] name = "async-signal" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" dependencies = [ "async-io", "async-lock 2.8.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", "rustix", "signal-hook-registry", "slab", "windows-sys 0.48.0", ] [[package]] name = "async-task" version = "4.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" version = "0.1.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "461abc97219de0eaaf81fe3ef974a540158f3d079c2ab200f891f1a2ef201e85" dependencies = [ "proc-macro2", "quote", "syn 2.0.53", ] [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "blocking" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ "async-channel", "async-lock 3.3.0", "async-task", "fastrand", "futures-io", "futures-lite", "piper", "tracing", ] [[package]] name = "calloop" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ "bitflags", "log", "polling", "rustix", "slab", "thiserror", ] [[package]] name = "cc" version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" [[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.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] name = "concurrent-queue" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ "crossbeam-utils", ] [[package]] name = "cpufeatures" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crossbeam-utils" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", ] [[package]] name = "derivative" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", ] [[package]] name = "endi" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" [[package]] name = "enumflags2" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" dependencies = [ "enumflags2_derive", "serde", ] [[package]] name = "enumflags2_derive" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" dependencies = [ "proc-macro2", "quote", "syn 2.0.53", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "event-listener" version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] [[package]] name = "event-listener" version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] [[package]] name = "event-listener-strategy" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ "event-listener 4.0.3", "pin-project-lite", ] [[package]] name = "event-listener-strategy" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" dependencies = [ "event-listener 5.2.0", "pin-project-lite", ] [[package]] name = "fastrand" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "futures" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ "fastrand", "futures-core", "futures-io", "parking", "pin-project-lite", ] [[package]] name = "futures-macro" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", "syn 2.0.53", ] [[package]] name = "futures-sink" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", ] [[package]] name = "getrandom" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "idna" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", ] [[package]] name = "indexmap" version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "linux-raw-sys" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memmap2" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" dependencies = [ "libc", ] [[package]] name = "memoffset" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] [[package]] name = "miniz_oxide" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi", "windows-sys 0.48.0", ] [[package]] name = "nix" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags", "cfg-if", "cfg_aliases", "libc", "memoffset", ] [[package]] name = "object" version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "ordered-stream" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" dependencies = [ "futures-core", "pin-project-lite", ] [[package]] name = "parking" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" dependencies = [ "atomic-waker", "fastrand", "futures-io", ] [[package]] name = "polling" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24f040dee2588b4963afb4e420540439d126f73fdacf4a9c486a96d840bac3c9" dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", "rustix", "tracing", "windows-sys 0.52.0", ] [[package]] name = "pollster" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-crate" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "regex" version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reis" version = "0.2.0" dependencies = [ "ashpd", "calloop", "futures", "futures-executor", "once_cell", "pollster", "rustix", "signal-hook", "tokio", "xkbcommon", ] [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "serde" version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", "syn 2.0.53", ] [[package]] name = "serde_repr" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", "syn 2.0.53", ] [[package]] name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "signal-hook" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", ] [[package]] name = "signal-hook-registry" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "socket2" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", "rustix", "windows-sys 0.52.0", ] [[package]] name = "thiserror" version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", "syn 2.0.53", ] [[package]] name = "tinyvec" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "libc", "mio", "pin-project-lite", "socket2", "tokio-macros", "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", "syn 2.0.53", ] [[package]] name = "toml_datetime" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" [[package]] name = "toml_edit" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap", "toml_datetime", "winnow", ] [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", "syn 2.0.53", ] [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] [[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "uds_windows" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" dependencies = [ "memoffset", "tempfile", "winapi", ] [[package]] name = "unicode-bidi" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "url" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", ] [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.4", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ "windows_aarch64_gnullvm 0.52.4", "windows_aarch64_msvc 0.52.4", "windows_i686_gnu 0.52.4", "windows_i686_msvc 0.52.4", "windows_x86_64_gnu 0.52.4", "windows_x86_64_gnullvm 0.52.4", "windows_x86_64_msvc 0.52.4", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] name = "xdg-home" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" dependencies = [ "libc", "winapi", ] [[package]] name = "xkbcommon" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" dependencies = [ "libc", "memmap2", "xkeysym", ] [[package]] name = "xkeysym" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621" [[package]] name = "zbus" version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9ff46f2a25abd690ed072054733e0bc3157e3d4c45f41bd183dce09c2ff8ab9" dependencies = [ "async-broadcast", "async-executor", "async-fs", "async-io", "async-lock 3.3.0", "async-process", "async-recursion", "async-task", "async-trait", "blocking", "derivative", "enumflags2", "event-listener 5.2.0", "futures-core", "futures-sink", "futures-util", "hex", "nix", "ordered-stream", "rand", "serde", "serde_repr", "sha1", "static_assertions", "tracing", "uds_windows", "windows-sys 0.52.0", "xdg-home", "zbus_macros", "zbus_names", "zvariant", ] [[package]] name = "zbus_macros" version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e0e3852c93dcdb49c9462afe67a2a468f7bd464150d866e861eaf06208633e0" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "regex", "syn 1.0.109", "zvariant_utils", ] [[package]] name = "zbus_names" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" dependencies = [ "serde", "static_assertions", "zvariant", ] [[package]] name = "zvariant" version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c1b3ca6db667bfada0f1ebfc94b2b1759ba25472ee5373d4551bb892616389a" dependencies = [ "endi", "enumflags2", "serde", "static_assertions", "url", "zvariant_derive", ] [[package]] name = "zvariant_derive" version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7a4b236063316163b69039f77ce3117accb41a09567fd24c168e43491e521bc" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "syn 1.0.109", "zvariant_utils", ] [[package]] name = "zvariant_utils" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] reis-0.2.0/Cargo.toml0000644000000033120000000000100077720ustar # 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" name = "reis" version = "0.2.0" description = "Pure Rust implementation of libei/libeis protocol." homepage = "https://github.com/ids1024/reis" readme = "README.md" keywords = [ "libei", "libeis", "wayland", ] license = "MIT" [[example]] name = "receive" required-features = ["tokio"] [[example]] name = "list-devices" required-features = ["tokio"] [[example]] name = "type-text" required-features = ["calloop"] [[example]] name = "reis-demo-server" required-features = ["calloop"] [dependencies.calloop] version = "0.13.0" optional = true [dependencies.futures] version = "0.3.28" optional = true [dependencies.rustix] version = "0.38.3" features = [ "event", "fs", "net", ] [dependencies.tokio] version = "1.31.0" features = ["net"] optional = true [dev-dependencies.ashpd] version = "0.8.1" [dev-dependencies.futures-executor] version = "0.3.29" [dev-dependencies.once_cell] version = "1.17.1" [dev-dependencies.pollster] version = "0.3.0" [dev-dependencies.signal-hook] version = "0.3.17" [dev-dependencies.tokio] version = "1.31.0" features = [ "rt", "macros", ] [dev-dependencies.xkbcommon] version = "0.7.0" [features] calloop = ["dep:calloop"] tokio = [ "dep:tokio", "futures", ] reis-0.2.0/Cargo.toml.orig000064400000000000000000000020421046102023000134520ustar 00000000000000[package] name = "reis" version = "0.2.0" license = "MIT" description = "Pure Rust implementation of libei/libeis protocol." homepage = "https://github.com/ids1024/reis" keywords = ["libei", "libeis", "wayland"] edition = "2021" [dependencies] calloop = { version = "0.13.0", optional = true } rustix = { version = "0.38.3", features = ["event", "fs", "net"] } futures = { version = "0.3.28", optional = true } tokio = { version = "1.31.0", features = ["net"], optional = true } [dev-dependencies] ashpd = "0.8.1" futures-executor = "0.3.29" once_cell = "1.17.1" xkbcommon = "0.7.0" tokio = { version = "1.31.0", features = ["rt", "macros"] } signal-hook = "0.3.17" pollster = "0.3.0" [features] tokio = ["dep:tokio", "futures"] # Experimental and somewhat incomplete calloop = ["dep:calloop"] [[example]] name = "receive" required-features = ["tokio"] [[example]] name = "list-devices" required-features = ["tokio"] [[example]] name = "type-text" required-features = ["calloop"] [[example]] name = "reis-demo-server" required-features = ["calloop"] reis-0.2.0/LICENSE000064400000000000000000000017771046102023000116060ustar 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. reis-0.2.0/README.md000064400000000000000000000007431046102023000120500ustar 00000000000000Reis :rice: provides a Rust version of EI :egg: and EIS :icecream: for emulated input on Wayland. See the upstream project [libei](https://gitlab.freedesktop.org/libinput/libei) for more information. This library is currently **incomplete** and subject to change. It should probably do more to provide a more high level API that handles the things a client/server needs to deal with. Setting the env var `REIS_DEBUG` will make the library print ei messages it sends and receives. reis-0.2.0/examples/list-devices.rs000064400000000000000000000155621046102023000153550ustar 00000000000000use ashpd::desktop::remote_desktop::{DeviceType, RemoteDesktop}; use futures::stream::StreamExt; use once_cell::sync::Lazy; use reis::{ei, tokio::EiEventStream, PendingRequestResult}; use std::{collections::HashMap, io, os::unix::net::UnixStream, process}; static INTERFACES: Lazy> = Lazy::new(|| { let mut m = HashMap::new(); m.insert("ei_connection", 1); m.insert("ei_callback", 1); m.insert("ei_pingpong", 1); m.insert("ei_seat", 1); m.insert("ei_device", 1); m.insert("ei_pointer", 1); m.insert("ei_pointer_absolute", 1); m.insert("ei_scroll", 1); m.insert("ei_button", 1); m.insert("ei_keyboard", 1); m.insert("ei_touchscreen", 1); m }); #[derive(Default)] struct SeatData { name: Option, capabilities: HashMap, devices: Vec, done: bool, } #[derive(Default)] struct DeviceData { name: Option, device_type: Option, interfaces: HashMap, done: bool, } impl DeviceData { fn interface(&self) -> Option { self.interfaces.get(T::NAME)?.clone().downcast() } } struct State { context: ei::Context, connection: ei::Connection, // XXX best way to handle data associated with object? seats: HashMap, // XXX association with seat? devices: HashMap, } impl State { fn handle_event(&mut self, event: ei::Event) { match event { ei::Event::Handshake(handshake, request) => panic!(), ei::Event::Connection(connection, request) => match request { ei::connection::Event::Seat { seat } => { self.seats.insert(seat, Default::default()); } ei::connection::Event::Ping { ping } => { ping.done(0); } _ => {} }, ei::Event::Seat(seat, request) => { let data = self.seats.get_mut(&seat).unwrap(); match request { ei::seat::Event::Name { name } => { data.name = Some(name); } ei::seat::Event::Capability { mask, interface } => { data.capabilities.insert(interface, mask); } ei::seat::Event::Done => { let caps = data.capabilities.values().fold(0, |a, b| a | b); seat.bind(caps); data.done = true; self.connection.sync(1); println!("Seat"); println!(" Name: {:?}", data.name); println!( " Capabiltities: {:?}", data.capabilities.keys().collect::>() ); } ei::seat::Event::Device { device } => { data.devices.push(device.clone()); self.devices.insert(device, Default::default()); } _ => {} } } ei::Event::Device(device, request) => { let data = self.devices.get_mut(&device).unwrap(); match request { ei::device::Event::Name { name } => { data.name = Some(name); } ei::device::Event::DeviceType { device_type } => { data.device_type = Some(device_type); } ei::device::Event::Interface { object } => { data.interfaces .insert(object.interface().to_string(), object); } ei::device::Event::Done => { data.done = true; //self.print_and_exit_if_done(); println!("Device"); println!(" Name: {:?}", data.name); println!(" Type: {:?}", data.device_type); println!( " Interfaces: {:?}", data.interfaces.keys().collect::>() ); } ei::device::Event::Resumed { serial } => {} _ => {} } } ei::Event::Callback(callback, request) => match request { ei::callback::Event::Done { callback_data: _ } => { // TODO: Callback being called after first device, but not later ones? // self.print_and_exit_if_done(); } _ => {} }, _ => {} } let _ = self.context.flush(); } fn print_and_exit_if_done(&self) { if !(self.seats.values().all(|x| x.done) && self.devices.values().all(|x| x.done)) { return; } process::exit(0); } } async fn open_connection() -> ei::Context { if let Some(context) = ei::Context::connect_to_env().unwrap() { context } else { eprintln!("Unable to find ei socket. Trying xdg desktop portal."); let remote_desktop = RemoteDesktop::new().await.unwrap(); let session = remote_desktop.create_session().await.unwrap(); remote_desktop .select_devices( &session, (DeviceType::Keyboard | DeviceType::Pointer | DeviceType::Touchscreen).into(), ) .await .unwrap(); remote_desktop .start(&session, &ashpd::WindowIdentifier::default()) .await .unwrap(); let fd = remote_desktop.connect_to_eis(&session).await.unwrap(); let stream = UnixStream::from(fd); ei::Context::new(stream).unwrap() } } #[tokio::main(flavor = "current_thread")] async fn main() { let context = open_connection().await; let mut events = EiEventStream::new(context.clone()).unwrap(); let handshake_resp = reis::tokio::ei_handshake( &mut events, "list-devices-example", ei::handshake::ContextType::Sender, &INTERFACES, ) .await .unwrap(); let mut state = State { context: context.clone(), connection: handshake_resp.connection, seats: HashMap::new(), devices: HashMap::new(), }; while let Some(result) = events.next().await { let event = match result.unwrap() { PendingRequestResult::Request(event) => event, PendingRequestResult::ParseError(msg) => { todo!() } PendingRequestResult::InvalidObject(object_id) => { // TODO continue; } }; state.handle_event(event); } } reis-0.2.0/examples/receive-synchronous.rs000064400000000000000000000103131046102023000167610ustar 00000000000000use ashpd::desktop::input_capture::{Barrier, Capabilities, InputCapture}; use once_cell::sync::Lazy; use pollster::FutureExt as _; use reis::{ ei, event::{DeviceCapability, EiConvertEventIterator}, }; use std::{collections::HashMap, os::unix::net::UnixStream}; use xkbcommon::xkb; static INTERFACES: Lazy> = Lazy::new(|| { let mut m = HashMap::new(); m.insert("ei_connection", 1); m.insert("ei_callback", 1); m.insert("ei_pingpong", 1); m.insert("ei_seat", 1); m.insert("ei_device", 2); m.insert("ei_pointer", 1); m.insert("ei_pointer_absolute", 1); m.insert("ei_scroll", 1); m.insert("ei_button", 1); m.insert("ei_keyboard", 1); m.insert("ei_touchscreen", 1); m }); async fn open_connection() -> ei::Context { if let Some(context) = ei::Context::connect_to_env().unwrap() { context } else { eprintln!("Unable to find ei socket. Trying xdg desktop portal."); let input_capture = InputCapture::new().await.unwrap(); let session = input_capture .create_session( &ashpd::WindowIdentifier::default(), (Capabilities::Keyboard | Capabilities::Pointer | Capabilities::Touchscreen).into(), ) .await .unwrap() .0; let fd = input_capture.connect_to_eis(&session).await.unwrap(); let stream = UnixStream::from(fd); let zones = input_capture .zones(&session) .await .unwrap() .response() .unwrap(); let barriers = zones .regions() .iter() .enumerate() .map(|(n, region)| { let x = region.x_offset(); let y = region.y_offset(); let w = region.width() as i32; let h = region.height() as i32; Barrier::new(n as u32 + 1, (x, y, x + w - 1, y)) }) .collect::>(); let resp = input_capture .set_pointer_barriers(&session, &barriers, zones.zone_set()) .await .unwrap() .response() .unwrap(); assert_eq!(&resp.failed_barriers(), &[]); eprintln!("Set capture barrier to top edge of screen."); eprintln!("(When input is captured, Esc will exit.)"); input_capture.enable(&session).await.unwrap(); ei::Context::new(stream).unwrap() } } fn main() { let context = open_connection().block_on(); let resp = reis::handshake::ei_handshake_blocking( &context, "receive-example", ei::handshake::ContextType::Receiver, &INTERFACES, ) .unwrap(); let mut events = EiConvertEventIterator::new(context.clone(), resp.serial); while let Some(event) = events.next() { let event = event.unwrap(); println!("{event:?}"); match &event { reis::event::EiEvent::SeatAdded(evt) => { // println!(" capabilities: {:?}", evt.seat); evt.seat.bind_capabilities(&[ DeviceCapability::Pointer, DeviceCapability::PointerAbsolute, DeviceCapability::Keyboard, DeviceCapability::Touch, DeviceCapability::Scroll, DeviceCapability::Button, ]); context.flush(); } reis::event::EiEvent::DeviceAdded(evt) => { println!(" seat: {:?}", evt.device.seat().name()); println!(" type: {:?}", evt.device.device_type()); if let Some(dimensions) = evt.device.dimensions() { println!(" dimensions: {:?}", dimensions); } println!(" regions: {:?}", evt.device.regions()); if let Some(keymap) = evt.device.keymap() { println!(" keymap: {:?}", keymap); } // Interfaces? } reis::event::EiEvent::KeyboardKey(evt) => { // Escape key if evt.key == 1 { std::process::exit(0); } } _ => {} } } } reis-0.2.0/examples/receive.rs000064400000000000000000000105171046102023000143770ustar 00000000000000use ashpd::desktop::input_capture::{Barrier, Capabilities, InputCapture}; use futures::stream::StreamExt; use once_cell::sync::Lazy; use reis::{ ei, event::DeviceCapability, tokio::{EiConvertEventStream, EiEventStream}, }; use std::{collections::HashMap, os::unix::net::UnixStream}; use xkbcommon::xkb; static INTERFACES: Lazy> = Lazy::new(|| { let mut m = HashMap::new(); m.insert("ei_connection", 1); m.insert("ei_callback", 1); m.insert("ei_pingpong", 1); m.insert("ei_seat", 1); m.insert("ei_device", 2); m.insert("ei_pointer", 1); m.insert("ei_pointer_absolute", 1); m.insert("ei_scroll", 1); m.insert("ei_button", 1); m.insert("ei_keyboard", 1); m.insert("ei_touchscreen", 1); m }); async fn open_connection() -> ei::Context { if let Some(context) = ei::Context::connect_to_env().unwrap() { context } else { eprintln!("Unable to find ei socket. Trying xdg desktop portal."); let input_capture = InputCapture::new().await.unwrap(); let session = input_capture .create_session( &ashpd::WindowIdentifier::default(), (Capabilities::Keyboard | Capabilities::Pointer | Capabilities::Touchscreen).into(), ) .await .unwrap() .0; let fd = input_capture.connect_to_eis(&session).await.unwrap(); let stream = UnixStream::from(fd); let zones = input_capture .zones(&session) .await .unwrap() .response() .unwrap(); let barriers = zones .regions() .iter() .enumerate() .map(|(n, region)| { let x = region.x_offset(); let y = region.y_offset(); let w = region.width() as i32; let h = region.height() as i32; Barrier::new(n as u32 + 1, (x, y, x + w - 1, y)) }) .collect::>(); let resp = input_capture .set_pointer_barriers(&session, &barriers, zones.zone_set()) .await .unwrap() .response() .unwrap(); assert_eq!(&resp.failed_barriers(), &[]); eprintln!("Set capture barrier to top edge of screen."); eprintln!("(When input is captured, Esc will exit.)"); input_capture.enable(&session).await.unwrap(); ei::Context::new(stream).unwrap() } } #[tokio::main(flavor = "current_thread")] async fn main() { let context = open_connection().await; let mut events = EiEventStream::new(context.clone()).unwrap(); let resp = reis::tokio::ei_handshake( &mut events, "receive-example", ei::handshake::ContextType::Receiver, &INTERFACES, ) .await .unwrap(); let mut events = EiConvertEventStream::new(events, resp.serial); while let Some(event) = events.next().await { let event = event.unwrap(); println!("{event:?}"); match &event { reis::event::EiEvent::SeatAdded(evt) => { // println!(" capabilities: {:?}", evt.seat); evt.seat.bind_capabilities(&[ DeviceCapability::Pointer, DeviceCapability::PointerAbsolute, DeviceCapability::Keyboard, DeviceCapability::Touch, DeviceCapability::Scroll, DeviceCapability::Button, ]); context.flush(); } reis::event::EiEvent::DeviceAdded(evt) => { println!(" seat: {:?}", evt.device.seat().name()); println!(" type: {:?}", evt.device.device_type()); if let Some(dimensions) = evt.device.dimensions() { println!(" dimensions: {:?}", dimensions); } println!(" regions: {:?}", evt.device.regions()); if let Some(keymap) = evt.device.keymap() { println!(" keymap: {:?}", keymap); } // Interfaces? } reis::event::EiEvent::KeyboardKey(evt) => { // Escape key if evt.key == 1 { std::process::exit(0); } } _ => {} } } } reis-0.2.0/examples/reis-demo-server.rs000064400000000000000000000223011046102023000161370ustar 00000000000000// TODO: Require context_type use once_cell::sync::Lazy; use reis::{ calloop::{ConnectedContextState, EisListenerSource, EisRequestSource, EisRequestSourceEvent}, eis::{self, device::DeviceType}, request::{DeviceCapability, EisRequest}, }; use std::{ collections::HashMap, io, sync::{ atomic::{AtomicBool, Ordering}, Arc, }, time::Duration, }; static SERVER_INTERFACES: Lazy> = Lazy::new(|| { let mut m = HashMap::new(); m.insert("ei_callback", 1); m.insert("ei_connection", 1); m.insert("ei_seat", 1); m.insert("ei_device", 1); m.insert("ei_pingpong", 1); m.insert("ei_keyboard", 1); m.insert("ei_pointer", 1); m.insert("ei_pointer_absolute", 1); m.insert("ei_button", 1); m.insert("ei_scroll", 1); m.insert("ei_touchscreen", 1); m }); struct ContextState { seat: Option, } impl ContextState { fn disconnected( &self, connected_state: &ConnectedContextState, reason: eis::connection::DisconnectReason, explaination: &str, ) -> calloop::PostAction { connected_state.connection.disconnected( connected_state.request_converter.last_serial(), reason, explaination, ); connected_state.context.flush(); calloop::PostAction::Remove } fn protocol_error( &self, connected_state: &ConnectedContextState, explanation: &str, ) -> calloop::PostAction { self.disconnected( connected_state, eis::connection::DisconnectReason::Protocol, explanation, ) } fn handle_request( &mut self, connected_state: &mut ConnectedContextState, request: EisRequest, ) -> calloop::PostAction { match request { EisRequest::Disconnect => { return calloop::PostAction::Remove; } EisRequest::Bind(request) => { let capabilities = request.capabilities; // TODO Handle in converter if capabilities & 0x7e != capabilities { let serial = connected_state.request_converter.next_serial(); request.seat.eis_seat().destroyed(serial); return self.disconnected( connected_state, eis::connection::DisconnectReason::Value, "Invalid capabilities", ); } if connected_state.has_interface("ei_keyboard") && capabilities & 2 << DeviceCapability::Keyboard as u64 != 0 { connected_state.request_converter.add_device( self.seat.as_ref().unwrap(), Some("keyboard"), DeviceType::Virtual, &[DeviceCapability::Keyboard], ); } // XXX button/etc should be on same object if connected_state.has_interface("ei_pointer") && capabilities & 2 << DeviceCapability::Pointer as u64 != 0 { connected_state.request_converter.add_device( self.seat.as_ref().unwrap(), Some("pointer"), DeviceType::Virtual, &[DeviceCapability::Pointer], ); } if connected_state.has_interface("ei_touchscreen") && capabilities & 2 << DeviceCapability::Touch as u64 != 0 { connected_state.request_converter.add_device( self.seat.as_ref().unwrap(), Some("touch"), DeviceType::Virtual, &[DeviceCapability::Touch], ); } if connected_state.has_interface("ei_pointer_absolute") && capabilities & 2 << DeviceCapability::PointerAbsolute as u64 != 0 { connected_state.request_converter.add_device( self.seat.as_ref().unwrap(), Some("pointer-abs"), DeviceType::Virtual, &[DeviceCapability::PointerAbsolute], ); } // TODO create devices; compare against current bitflag } _ => {} } calloop::PostAction::Continue } } struct State { handle: calloop::LoopHandle<'static, Self>, } impl State { fn handle_new_connection(&mut self, context: eis::Context) -> io::Result { println!("New connection: {:?}", context); let source = EisRequestSource::new(context, &SERVER_INTERFACES, 1); let mut context_state = ContextState { seat: None }; self.handle .insert_source(source, move |event, connected_state, state| match event { Ok(event) => Ok(state.handle_request_source_event( &mut context_state, connected_state, event, )), Err(err) => Ok(context_state.protocol_error(connected_state, &err.to_string())), }) .unwrap(); Ok(calloop::PostAction::Continue) } fn connected(&mut self, mut connected_state: ConnectedContextState) { if !connected_state .negotiated_interfaces .contains_key("ei_seat") || !connected_state .negotiated_interfaces .contains_key("ei_device") { connected_state.connection.disconnected( 1, eis::connection::DisconnectReason::Protocol, "Need `ei_seat` and `ei_device`", ); connected_state.context.flush(); } let seat = connected_state.request_converter.add_seat( Some("default"), &[ DeviceCapability::Pointer, DeviceCapability::PointerAbsolute, DeviceCapability::Keyboard, DeviceCapability::Touch, DeviceCapability::Scroll, DeviceCapability::Button, ], ); } fn handle_request_source_event( &mut self, context_state: &mut ContextState, connected_state: &mut ConnectedContextState, event: EisRequestSourceEvent, ) -> calloop::PostAction { match event { EisRequestSourceEvent::Connected => { if !connected_state .negotiated_interfaces .contains_key("ei_seat") || !connected_state .negotiated_interfaces .contains_key("ei_device") { connected_state.connection.disconnected( 1, eis::connection::DisconnectReason::Protocol, "Need `ei_seat` and `ei_device`", ); connected_state.context.flush(); } let seat = connected_state.request_converter.add_seat( Some("default"), &[ DeviceCapability::Pointer, DeviceCapability::PointerAbsolute, DeviceCapability::Keyboard, DeviceCapability::Touch, DeviceCapability::Scroll, DeviceCapability::Button, ], ); context_state.seat = Some(seat); } EisRequestSourceEvent::Request(request) => { let res = context_state.handle_request(connected_state, request); if res != calloop::PostAction::Continue { return res; } } EisRequestSourceEvent::InvalidObject(object_id) => { // Only send if object ID is in range? connected_state .connection .invalid_object(connected_state.request_converter.last_serial(), object_id); } } connected_state.context.flush(); calloop::PostAction::Continue } } fn main() { let mut event_loop = calloop::EventLoop::try_new().unwrap(); let handle = event_loop.handle(); let path = reis::default_socket_path().unwrap(); std::fs::remove_file(&path); // XXX in use? let listener = eis::Listener::bind(&path).unwrap(); let listener_source = EisListenerSource::new(listener); handle .insert_source(listener_source, |context, (), state: &mut State| { state.handle_new_connection(context) }) .unwrap(); let terminate = Arc::new(AtomicBool::new(false)); signal_hook::flag::register(signal_hook::consts::SIGINT, terminate.clone()).unwrap(); let mut state = State { handle }; while !terminate.load(Ordering::Relaxed) { event_loop .dispatch(Duration::from_millis(100), &mut state) .unwrap(); } } reis-0.2.0/examples/type-text.rs000064400000000000000000000260261046102023000147220ustar 00000000000000use ashpd::desktop::remote_desktop::{DeviceType, RemoteDesktop}; use calloop::generic::Generic; use once_cell::sync::Lazy; use reis::{ei, PendingRequestResult}; use std::{collections::HashMap, io, os::unix::net::UnixStream}; use xkbcommon::xkb; static INTERFACES: Lazy> = Lazy::new(|| { let mut m = HashMap::new(); m.insert("ei_callback", 1); m.insert("ei_connection", 1); m.insert("ei_seat", 1); m.insert("ei_device", 1); m.insert("ei_pingpong", 1); m.insert("ei_keyboard", 1); m }); #[derive(Debug, Default)] struct SeatData { name: Option, capabilities: HashMap, } #[derive(Default)] struct DeviceData { name: Option, device_type: Option, interfaces: HashMap, } impl DeviceData { fn interface(&self) -> Option { self.interfaces.get(T::NAME)?.clone().downcast() } } struct State { // XXX best way to handle data associated with object? seats: HashMap, // XXX association with seat? devices: HashMap, running: bool, sequence: u32, last_serial: u32, keymap: Option, } impl State { fn handle_listener_readable( &mut self, context: &mut ei::Context, ) -> io::Result { if let Err(_) = context.read() { return Ok(calloop::PostAction::Remove); } while let Some(result) = context.pending_event() { let request = match result { PendingRequestResult::Request(request) => request, PendingRequestResult::ParseError(msg) => { todo!() } PendingRequestResult::InvalidObject(object_id) => { // TODO continue; } }; match request { ei::Event::Handshake(handshake, request) => match request { ei::handshake::Event::HandshakeVersion { version: _ } => { handshake.handshake_version(1); handshake.name("type-text-example"); handshake.context_type(ei::handshake::ContextType::Sender); for (interface, version) in INTERFACES.iter() { handshake.interface_version(interface, *version); } handshake.finish(); } ei::handshake::Event::InterfaceVersion { name: _, version: _, } => {} ei::handshake::Event::Connection { connection: _, serial, } => { self.last_serial = serial; } _ => {} }, ei::Event::Connection(connection, request) => match request { ei::connection::Event::Seat { seat } => { self.seats.insert(seat, Default::default()); } ei::connection::Event::Ping { ping } => { ping.done(0); } _ => {} }, ei::Event::Seat(seat, request) => { let data = self.seats.get_mut(&seat).unwrap(); match request { ei::seat::Event::Name { name } => { data.name = Some(name); } ei::seat::Event::Capability { mask, interface } => { data.capabilities.insert(interface, mask); } ei::seat::Event::Done => { seat.bind(*data.capabilities.get("ei_keyboard").unwrap()); // XXX } ei::seat::Event::Device { device } => { self.devices.insert(device, Default::default()); } _ => {} } } ei::Event::Device(device, request) => { let data = self.devices.get_mut(&device).unwrap(); match request { ei::device::Event::Name { name } => { data.name = Some(name); } ei::device::Event::DeviceType { device_type } => { data.device_type = Some(device_type); } ei::device::Event::Interface { object } => { data.interfaces .insert(object.interface().to_string(), object); } ei::device::Event::Done => { if let Some(keyboard) = data.interface::() { device.start_emulating(self.sequence, self.last_serial); self.sequence += 1; let keymap = self.keymap.as_ref().unwrap(); let all_keycodes = keymap.min_keycode().raw()..keymap.max_keycode().raw(); let shift_keycode = all_keycodes .clone() .find(|i| { keymap .key_get_syms_by_level(xkb::Keycode::new(*i), 0, 0) .contains(&xkb::Keysym::Shift_L) }) .unwrap(); let s = "Hello world!"; for c in s.chars() { let keysym = xkb::Keysym::from_char(c); let mut keycode = None; let mut shift = false; 'outer: for i in all_keycodes.clone() { for j in 0..=1 { let syms = keymap.key_get_syms_by_level( xkb::Keycode::new(i), 0, j, ); if syms.contains(&keysym) { keycode = Some(i); shift = j == 1; break 'outer; } } } let keycode = keycode.unwrap(); if shift { keyboard .key(shift_keycode - 8, ei::keyboard::KeyState::Press); } keyboard.key(keycode - 8, ei::keyboard::KeyState::Press); keyboard.key(keycode - 8, ei::keyboard::KeyState::Released); if shift { keyboard.key( shift_keycode - 8, ei::keyboard::KeyState::Released, ); } } device.stop_emulating(self.last_serial); self.running = false; } } ei::device::Event::Resumed { serial } => { self.last_serial = serial; } _ => {} } } ei::Event::Keyboard(keyboard, request) => { match request { ei::keyboard::Event::Keymap { keymap_type, size, keymap, } => { // XXX format // flags? // handle multiple keyboard? let context = xkb::Context::new(0); self.keymap = Some( unsafe { xkb::Keymap::new_from_fd( &context, keymap, size as _, xkb::KEYMAP_FORMAT_TEXT_V1, 0, ) } .unwrap() .unwrap(), ); } _ => {} } } _ => {} } } context.flush(); Ok(calloop::PostAction::Continue) } } async fn open_connection() -> ei::Context { if let Some(context) = ei::Context::connect_to_env().unwrap() { context } else { eprintln!("Unable to find ei socket. Trying xdg desktop portal."); let remote_desktop = RemoteDesktop::new().await.unwrap(); let session = remote_desktop.create_session().await.unwrap(); remote_desktop .select_devices(&session, DeviceType::Keyboard.into()) .await .unwrap(); remote_desktop .start(&session, &ashpd::WindowIdentifier::default()) .await .unwrap(); let fd = remote_desktop.connect_to_eis(&session).await.unwrap(); let stream = UnixStream::from(fd); ei::Context::new(stream).unwrap() } } fn main() { let mut event_loop = calloop::EventLoop::try_new().unwrap(); let handle = event_loop.handle(); let context = futures_executor::block_on(open_connection()); // XXX wait for server version? let handshake = context.handshake(); context.flush(); let context_source = Generic::new(context, calloop::Interest::READ, calloop::Mode::Level); handle .insert_source(context_source, |_event, context, state: &mut State| { state.handle_listener_readable(unsafe { context.get_mut() }) }) .unwrap(); let mut state = State { seats: HashMap::new(), devices: HashMap::new(), running: true, last_serial: u32::MAX, sequence: 0, keymap: None, }; while state.running { event_loop.dispatch(None, &mut state).unwrap(); } } reis-0.2.0/src/calloop.rs000064400000000000000000000175321046102023000133630ustar 00000000000000// TODO Define an event source that reads socket and produces eis::Event // - is it easy to compose and wrap with handshaker, event handler? // produce an event of some kind on disconnect/eof? use calloop::{generic::Generic, Interest, Mode, PostAction, Readiness, Token, TokenFactory}; use std::{collections::HashMap, io}; use crate::{ eis, handshake::HandshakeError, request::{self, EisRequestConverter}, PendingRequestResult, }; pub struct EisListenerSource { source: Generic, } impl EisListenerSource { pub fn new(listener: eis::Listener) -> Self { Self { source: Generic::new(listener, Interest::READ, Mode::Level), } } } impl calloop::EventSource for EisListenerSource { type Event = eis::Context; type Metadata = (); type Ret = io::Result; type Error = io::Error; fn process_events( &mut self, readiness: Readiness, token: Token, mut cb: F, ) -> io::Result where F: FnMut(eis::Context, &mut ()) -> io::Result, { self.source .process_events(readiness, token, |_readiness, listener| { if let Some(context) = listener.accept()? { cb(context, &mut ()) } else { Ok(PostAction::Continue) } }) } fn register( &mut self, poll: &mut calloop::Poll, token_factory: &mut TokenFactory, ) -> Result<(), calloop::Error> { self.source.register(poll, token_factory) } fn reregister( &mut self, poll: &mut calloop::Poll, token_factory: &mut TokenFactory, ) -> Result<(), calloop::Error> { self.source.reregister(poll, token_factory) } fn unregister(&mut self, poll: &mut calloop::Poll) -> Result<(), calloop::Error> { self.source.unregister(poll) } } pub struct ConnectedContextState { pub context: eis::Context, pub connection: eis::Connection, pub name: Option, pub context_type: eis::handshake::ContextType, pub negotiated_interfaces: HashMap, pub request_converter: request::EisRequestConverter, } impl ConnectedContextState { // Use type instead of string? pub fn has_interface(&self, interface: &str) -> bool { self.negotiated_interfaces.contains_key(interface) } fn process(&mut self, mut cb: F) -> io::Result where F: FnMut( Result, &mut Self, ) -> io::Result, { if let Err(err) = self.context.read() { cb(Err(request::Error::Io(err)), self)?; return Ok(calloop::PostAction::Remove); } while let Some(result) = self.context.pending_request() { let request = match result { PendingRequestResult::Request(request) => request, PendingRequestResult::ParseError(err) => { cb(Err(request::Error::Parse(err)), self)?; return Ok(calloop::PostAction::Remove); } PendingRequestResult::InvalidObject(object_id) => { let res = cb(Ok(EisRequestSourceEvent::InvalidObject(object_id)), self)?; if res != calloop::PostAction::Continue { return Ok(res); } continue; } }; if let Err(err) = self.request_converter.handle_request(request) { cb(Err(err), self)?; return Ok(calloop::PostAction::Remove); } while let Some(request) = self.request_converter.next_request() { let res = cb(Ok(EisRequestSourceEvent::Request(request)), self)?; if res != calloop::PostAction::Continue { return Ok(res); } } } Ok(calloop::PostAction::Continue) } } fn process_handshake( handshaker: &mut crate::handshake::EisHandshaker<'_>, context: &eis::Context, ) -> Result, HandshakeError> { context.read()?; while let Some(result) = context.pending_request() { let request = crate::handshake::request_result(result)?; if let Some(resp) = handshaker.handle_request(request)? { let request_converter = EisRequestConverter::new(&resp.connection, 1); let connected_state = ConnectedContextState { context: context.clone(), connection: resp.connection, name: resp.name, context_type: resp.context_type, negotiated_interfaces: resp.negotiated_interfaces, request_converter, }; return Ok(Some(connected_state)); } } // XXX let _ = context.flush(); Ok(None) } #[allow(clippy::large_enum_variant)] enum State { Handshake(crate::handshake::EisHandshaker<'static>), Connected(ConnectedContextState), } pub struct EisRequestSource { source: Generic, state: State, } impl EisRequestSource { pub fn new( context: eis::Context, interfaces: &'static HashMap<&'static str, u32>, initial_serial: u32, ) -> Self { let handshaker = crate::handshake::EisHandshaker::new(&context, interfaces, initial_serial); Self { source: Generic::new(context, Interest::READ, Mode::Level), state: State::Handshake(handshaker), } } } impl calloop::EventSource for EisRequestSource { type Event = Result; type Metadata = ConnectedContextState; type Ret = io::Result; type Error = io::Error; fn process_events( &mut self, readiness: Readiness, token: Token, mut cb: F, ) -> io::Result where F: FnMut(Self::Event, &mut ConnectedContextState) -> io::Result, { self.source .process_events(readiness, token, |_readiness, context| { match &mut self.state { State::Handshake(handshaker) => { if let Some(res) = process_handshake(handshaker, context).transpose() { match res { Ok(mut state) => { let res = cb(Ok(EisRequestSourceEvent::Connected), &mut state)?; self.state = State::Connected(state); Ok(res) } Err(err) => { // TODO return handshake errors? eprintln!("Client handshake failed: {}", err); Ok(calloop::PostAction::Remove) } } } else { Ok(calloop::PostAction::Continue) } } State::Connected(state) => state.process(&mut cb), } }) } fn register( &mut self, poll: &mut calloop::Poll, token_factory: &mut TokenFactory, ) -> Result<(), calloop::Error> { self.source.register(poll, token_factory) } fn reregister( &mut self, poll: &mut calloop::Poll, token_factory: &mut TokenFactory, ) -> Result<(), calloop::Error> { self.source.reregister(poll, token_factory) } fn unregister(&mut self, poll: &mut calloop::Poll) -> Result<(), calloop::Error> { self.source.unregister(poll) } } // TODO pub enum EisRequestSourceEvent { Connected, Request(request::EisRequest), InvalidObject(u64), } reis-0.2.0/src/ei.rs000064400000000000000000000027001046102023000123160ustar 00000000000000use std::{ env, io, os::unix::{ io::{AsFd, AsRawFd, BorrowedFd, RawFd}, net::UnixStream, }, }; use crate::{wire::Backend, PendingRequestResult}; // Re-export generate bindings pub use crate::eiproto_ei::*; #[derive(Clone, Debug)] pub struct Context(pub(crate) Backend); impl AsFd for Context { fn as_fd(&self) -> BorrowedFd { self.0.as_fd() } } impl AsRawFd for Context { fn as_raw_fd(&self) -> RawFd { self.0.as_fd().as_raw_fd() } } impl Context { // TODO way to connect pub fn new(socket: UnixStream) -> io::Result { Ok(Self(Backend::new(socket, true)?)) } pub fn connect_to_env() -> io::Result> { let Some(path) = env::var_os("LIBEI_SOCKET") else { // XXX return error type return Ok(None); }; let socket = UnixStream::connect(path)?; Self::new(socket).map(Some) } /// Read any pending data on socket into buffer pub fn read(&self) -> io::Result { self.0.read() } // XXX iterator pub fn pending_event(&self) -> Option> { self.0.pending(Event::parse) } pub fn handshake(&self) -> handshake::Handshake { self.0.object_for_id(0).unwrap().downcast_unchecked() } pub fn flush(&self) -> rustix::io::Result<()> { self.0.flush() } } #[doc(hidden)] pub trait Interface: crate::wire::Interface {} reis-0.2.0/src/eiproto.rs.jinja000064400000000000000000000246451046102023000145100ustar 00000000000000{# ei-scanner jinja template, for `` #} #![allow(unused_imports, unused_parens, clippy::useless_conversion, clippy::double_parens, clippy::match_single_binding, clippy::unused_unit)] // GENERATED FILE {# TODO handle destructor #} {# TODO handle context_type #} use crate::wire; {% macro interface_type(interface) -%} super::{{interface.plainname}}::{{interface.plainname|camel}} {%- endmacro %} {% macro arg_type(arg, owned, generic) -%} {% if arg.enum != None %} {{ arg.enum.name|camel }} {% elif arg.protocol_type == 'string' and owned %} String {% elif arg.protocol_type == 'string' %} &str {% elif arg.protocol_type == 'int32' %} i32 {% elif arg.protocol_type == 'uint32' %} u32 {% elif arg.protocol_type == 'int64' %} i64 {% elif arg.protocol_type == 'uint64' %} u64 {% elif arg.protocol_type == 'object' and owned %} {{interface_type(arg.interface)}} {% elif arg.protocol_type == 'object' %} &{{interface_type(arg.interface)}} {% elif arg.protocol_type == 'new_id' and arg.interface %} {{interface_type(arg.interface)}} {% elif arg.protocol_type == 'new_id' and arg.interface_arg and generic %} {{arg.interface_arg.name|camel}} {% elif arg.protocol_type == 'new_id' and arg.interface_arg %} crate::Object {% elif arg.protocol_type == 'float' %} f32 {% elif arg.protocol_type == 'fd' and owned %} std::os::unix::io::OwnedFd {% elif arg.protocol_type == 'fd' %} std::os::unix::io::BorrowedFd {% else %} unhandled_arg_type_{{arg.protocol_type}} {% endif %} {%- endmacro %} {% macro incoming_enum() -%} {% if extra.eis %} Request {% else %} Event {% endif %} {%- endmacro %} {% macro module() -%} {% if extra.eis %} crate::eis {% else %} crate::ei {% endif %} {%- endmacro %} {% for interface in interfaces %} /** {{interface.description.text|ei_escape_names}} */ pub mod {{interface.plainname}} { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct {{interface.plainname|camel}}(pub(crate) crate::Object); impl {{interface.plainname|camel}} { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for {{interface.plainname|camel}} {} impl wire::Interface for {{interface.plainname|camel}} { const NAME: &'static str = "{{interface.protocol_name}}"; const VERSION: u32 = {{interface.version}}; {% if extra.eis %} const CLIENT_SIDE: bool = false; {% else %} const CLIENT_SIDE: bool = true; {% endif %} type Incoming = {{incoming_enum()}}; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl {{module()}}::Interface for {{interface.plainname|camel}} {} impl {{interface.plainname|camel}} { {% for outgoing in interface.outgoing %} /** {{outgoing.description.text|ei_escape_names}} */ pub fn {{outgoing.name}}< {%- for arg in outgoing.arguments %} {% if arg.interface_arg_for %} {{arg.name|camel}}: {{module()}}::Interface {% endif %} {% endfor -%} >( &self, {%- for arg in outgoing.arguments %} {% if arg.protocol_type != 'new_id' and not arg.interface_arg_for %} {{arg.name}}: {{arg_type(arg, false, false)}}, {% endif %} {% endfor -%} ) -> ( {%- for arg in outgoing.arguments %} {% if arg.protocol_type == 'new_id' %} {{arg_type(arg, true, true)}} {% endif %} {% endfor -%} ) { {%- for arg in outgoing.arguments %} {% if arg.protocol_type == 'new_id' and arg.interface_arg %} let {{arg.name}} = self.0.backend_weak().new_object({{arg.interface_arg.name|camel}}::NAME.to_string(), {{arg.version_arg.name}}); {% elif arg.protocol_type == 'new_id' %} let {{arg.name}} = self.0.backend_weak().new_object("{{arg.interface.protocol_name}}".to_string(), {{arg.version_arg.name}}); {% endif -%} {% endfor -%} let args = &[ {%- for arg in outgoing.arguments %} {% if arg.interface_arg_for %} wire::Arg::{{arg.protocol_type|camel}}({{arg.name|camel}}::NAME), {% else %} wire::Arg::{{arg.protocol_type|camel}}({{arg.name}} {% if arg.protocol_type == 'new_id' %} .id() {% endif %} .into()), {% endif %} {% endfor -%} ]; self.0.request({{outgoing.opcode}}, args); {% if outgoing.is_destructor %} self.0.backend_weak().remove_id(self.0.id()); {% endif %} ( {%- for arg in outgoing.arguments %} {% if arg.protocol_type == 'new_id' %} {% if arg.interface %} {{arg_type(arg, true, false)}}({{arg.name}}) {% elif arg.interface_arg %} {{arg.name}}.downcast_unchecked() {% else -%} unreachable {% endif -%} {% endif -%} {% endfor -%} ) } {% endfor %} } {% for enum in interface.enums %} pub use crate::eiproto_enum::{{interface.plainname}}::{{enum.camel_name}}; {% endfor %} #[non_exhaustive] #[derive(Debug)] pub enum {{incoming_enum()}} { {% for incoming in interface.incoming %} /** {{incoming.description.text|ei_escape_names}} */ {{ incoming.name|camel }} {% if incoming.arguments %} { {% for arg in incoming.arguments %} {% if not (arg.version_arg_for or arg.interface_arg_for) %} /** {{arg.summary}} */ {{arg.name}}: {{arg_type(arg, true, false)}}, {% endif %} {% endfor %} } {% endif %}, {% endfor %} } impl {{incoming_enum()}} { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { {% for incoming in interface.incoming %} {{incoming.opcode}} => Some("{{incoming.name}}"), {% endfor %} _ => None } } pub(super) fn parse(operand: u32, _bytes: &mut wire::ByteStream) -> Result { match operand { {% for incoming in interface.incoming %} {{incoming.opcode}} => { {% for arg in incoming.arguments %} let {{arg.name}} = _bytes.read_arg()?; {% endfor %} Ok(Self::{{ incoming.name|camel }} {% if incoming.arguments %} { {% for arg in incoming.arguments %} {% if arg.version_arg_for or arg.interface_arg_for %} {% elif arg.protocol_type == 'new_id' and arg.interface_arg %} {{arg.name}}: _bytes.backend().new_peer_object({{arg.name}}, {{arg.interface_arg.name}}, {{arg.version_arg.name}})?, {% elif arg.protocol_type == 'new_id' %} {{arg.name}}: _bytes.backend().new_peer_interface({{arg.name}}, {{arg.version_arg.name}})?, {% else %} {{arg.name}}, {% endif %} {% endfor %} } {% endif %} ) } {% endfor %} opcode => Err(wire::ParseError::InvalidOpcode("{{interface.plainname}}", opcode)), } } #[allow(unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns)] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { {% for incoming in interface.incoming %} {% if incoming.arguments %} Self::{{ incoming.name|camel }} { {% for arg in incoming.arguments %} {% if not (arg.version_arg_for or arg.interface_arg_for) %} {{arg.name}}, {% endif %} {% endfor %} } => { {% for arg in incoming.arguments %} {% if not (arg.version_arg_for or arg.interface_arg_for) %} args.push({{arg.name}}.as_arg()); {% endif %} {% endfor %} } {% else %} Self::{{ incoming.name|camel }} => {} {% endif %} {% endfor %} _ => unreachable!() } args } } } pub use {{interface.plainname}}::{{interface.plainname|camel}}; {% endfor %} #[non_exhaustive] #[derive(Debug)] pub enum {{incoming_enum()}} { {% for interface in interfaces %} {{interface.plainname|camel}}({{interface.plainname}}::{{interface.plainname|camel}}, {{interface.plainname}}::{{incoming_enum()}}), {% endfor %} } impl {{incoming_enum()}} { pub(crate) fn op_name(interface: &str, operand: u32) -> Option<&'static str> { match interface { {% for interface in interfaces %} "{{interface.protocol_name}}" => {{interface.plainname}}::{{incoming_enum()}}::op_name(operand), {% endfor %} _ => None, } } pub(crate) fn parse(object: crate::Object, operand: u32, bytes: &mut wire::ByteStream) -> Result { match object.interface() { {% for interface in interfaces %} "{{interface.protocol_name}}" => Ok(Self::{{interface.plainname|camel}}( object.downcast_unchecked(), {{interface.plainname}}::{{incoming_enum()}}::parse(operand, bytes)?) ), {% endfor %} intr => Err(wire::ParseError::InvalidInterface(intr.to_owned())), } } } impl wire::MessageEnum for {{incoming_enum()}} { fn args(&self) -> Vec> { match self { {% for interface in interfaces %} Self::{{interface.plainname|camel}}(_, x) => x.args(), {% endfor %} } } } reis-0.2.0/src/eiproto_ei.rs000064400000000000000000003071001046102023000140610ustar 00000000000000#![allow( unused_imports, unused_parens, clippy::useless_conversion, clippy::double_parens, clippy::match_single_binding, clippy::unused_unit )] // GENERATED FILE use crate::wire; /** This is a special interface to setup the client as seen by the EIS implementation. The object for this interface has the fixed object id 0 and only exists until the connection has been set up, see the `ei_handshake.connection` event. The `ei_handshake` version is 1 until: - the EIS implementation sends the interface_version event with a version other than 1, and, in response, - the client sends the interface_version request with a version equal or lower to the EIS implementation version. The EIS implementation must send the interface_version event immediately once the physical connection has been established. Once the `ei_connection.connection` event has been sent the handshake is destroyed by the EIS implementation. */ pub mod handshake { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Handshake(pub(crate) crate::Object); impl Handshake { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Handshake {} impl wire::Interface for Handshake { const NAME: &'static str = "ei_handshake"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = true; type Incoming = Event; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::ei::Interface for Handshake {} impl Handshake { /** Notifies the EIS implementation that this client supports the given version of the `ei_handshake` interface. The version number must be less than or equal to the version in the handshake_version event sent by the EIS implementation when the connection was established. Immediately after sending this request, the client must assume the negotiated version number for the `ei_handshake` interface and the EIS implementation may send events and process requests matching that version. This request must be sent exactly once and it must be the first request the client sends. */ pub fn handshake_version(&self, version: u32) -> () { let args = &[wire::Arg::Uint32(version.into())]; self.0.request(0, args); () } /** Notify the EIS implementation that configuration is complete. In the future (and possibly after requiring user interaction), the EIS implementation responds by sending the `ei_handshake.connection` event. */ pub fn finish(&self) -> () { let args = &[]; self.0.request(1, args); () } /** Notify the EIS implementation of the type of this context. The context types defines whether the client will send events to or receive events from the EIS implementation. Depending on the context type, certain requests must not be used and some events must not be sent by the EIS implementation. This request is optional, the default client type is context_type.receiver. This request must not be sent more than once and must be sent before `ei_handshake.finish.` */ pub fn context_type(&self, context_type: ContextType) -> () { let args = &[wire::Arg::Uint32(context_type.into())]; self.0.request(2, args); () } /** Notify the EIS implementation of the client name. The name is a human-presentable UTF-8 string and should represent the client name as accurately as possible. This name may be presented to the user for identification of this client (e.g. to confirm the client has permissions to connect). There is no requirement for the EIS implementation to use this name. For example, where the client is managed through an XDG Desktop Portal an EIS implementation would typically use client identification information sent by the portal instead. This request is optional, the default client name is implementation-defined. This request must not be sent more than once and must be sent before `ei_handshake.finish.` */ pub fn name(&self, name: &str) -> () { let args = &[wire::Arg::String(name.into())]; self.0.request(3, args); () } /** Notify the EIS implementation that the client supports the given named interface with the given maximum version number. Future objects created by the EIS implementation will use the respective interface version (or any lesser version). This request must be sent for the "`ei_connection`" interface, failing to do so will result in the EIS implementation disconnecting the client on `ei_handshake.finish.` This request must not be sent for the "`ei_handshake`" interface, use the `ei_handshake.handshake_version` request instead. Note that an EIS implementation may consider some interfaces to be required and immediately `ei_connection.disconnect` a client not supporting those interfaces. This request must not be sent more than once per interface and must be sent before `ei_handshake.finish.` */ pub fn interface_version(&self, name: &str, version: u32) -> () { let args = &[ wire::Arg::String(name.into()), wire::Arg::Uint32(version.into()), ]; self.0.request(4, args); () } } pub use crate::eiproto_enum::handshake::ContextType; #[non_exhaustive] #[derive(Debug)] pub enum Event { /** This event is sent exactly once and immediately after connection to the EIS implementation. In response, the client must send the `ei_handshake.handshake_version` request with any version up to including the version provided in this event. See the `ei_handshake.handshake_version` request for details on what happens next. */ HandshakeVersion { /** the interface version */ version: u32, }, /** Notifies the client that the EIS implementation supports the given named interface with the given maximum version number. This event must be sent by the EIS implementation for any interfaces that supports client-created objects (e.g. "`ei_callback`") before the `ei_handshake.connection` event. The client must not assume those interfaces are supported unless and until those versions have been received. This request must not be sent for the "`ei_handshake`" interface, use the handshake_version event instead. This event may be sent by the EIS implementation for any other supported interface (but not necessarily all supported interfaces) before the `ei_handshake.connection` event. */ InterfaceVersion { /** the interface name */ name: String, /** the interface version */ version: u32, }, /** Provides the client with the connection object that is the top-level object for all future requests and events. This event is sent exactly once at some unspecified time after the client sends the `ei_handshake.finish` request to the EIS implementation. The `ei_handshake` object will be destroyed by the EIS implementation immediately after this event has been sent, a client must not attempt to use it after that point. The version sent by the EIS implementation is the version of the "`ei_connection`" interface as announced by `ei_handshake.interface_version`, or any lower version. The serial number is the start value of the EIS implementation's serial number sequence. Clients must not assume any specific value for this serial number. Any future serial number in any event is monotonically increasing by an unspecified amount. */ Connection { /** this event's serial number */ serial: u32, /** the connection object */ connection: super::connection::Connection, }, } impl Event { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("handshake_version"), 1 => Some("interface_version"), 2 => Some("connection"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => { let version = _bytes.read_arg()?; Ok(Self::HandshakeVersion { version }) } 1 => { let name = _bytes.read_arg()?; let version = _bytes.read_arg()?; Ok(Self::InterfaceVersion { name, version }) } 2 => { let serial = _bytes.read_arg()?; let connection = _bytes.read_arg()?; let version = _bytes.read_arg()?; Ok(Self::Connection { serial, connection: _bytes.backend().new_peer_interface(connection, version)?, }) } opcode => Err(wire::ParseError::InvalidOpcode("handshake", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::HandshakeVersion { version } => { args.push(version.as_arg()); } Self::InterfaceVersion { name, version } => { args.push(name.as_arg()); args.push(version.as_arg()); } Self::Connection { serial, connection } => { args.push(serial.as_arg()); args.push(connection.as_arg()); } _ => unreachable!(), } args } } } pub use handshake::Handshake; /** The core connection object. This is the top-level object for any communication with the EIS implementation. Note that for a client to receive this object, it must announce support for this interface in `ei_handshake.interface_version.` */ pub mod connection { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Connection(pub(crate) crate::Object); impl Connection { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Connection {} impl wire::Interface for Connection { const NAME: &'static str = "ei_connection"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = true; type Incoming = Event; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::ei::Interface for Connection {} impl Connection { /** The sync request asks the EIS implementation to emit the 'done' event on the returned `ei_callback` object. Since requests are handled in-order and events are delivered in-order, this can be used as a synchronization point to ensure all previous requests and the resulting events have been handled. The object returned by this request will be destroyed by the EIS implementation after the callback is fired and as such the client must not attempt to use it after that point. The callback_data in the `ei_callback.done` event is always zero. Note that for a client to use this request it must announce support for the "`ei_callback`" interface in `ei_handshake.interface_version.` It is a protocol violation to request sync without having announced the "`ei_callback`" interface and the EIS implementation must disconnect the client. */ pub fn sync(&self, version: u32) -> (super::callback::Callback) { let callback = self .0 .backend_weak() .new_object("ei_callback".to_string(), version); let args = &[ wire::Arg::NewId(callback.id().into()), wire::Arg::Uint32(version.into()), ]; self.0.request(0, args); (super::callback::Callback(callback)) } /** A request to the EIS implementation that this client should be disconnected. This is a courtesy request to allow the EIS implementation to distinquish between a client disconnecting on purpose and one disconnecting through the socket becoming invalid. Immediately after sending this request, the client may destroy the `ei_connection` object and it should close the socket. The EIS implementation will treat the connection as already disconnected on receipt and does not send the `ei_connection.disconnect` event in response to this request. */ pub fn disconnect(&self) -> () { let args = &[]; self.0.request(1, args); self.0.backend_weak().remove_id(self.0.id()); () } } pub use crate::eiproto_enum::connection::DisconnectReason; #[non_exhaustive] #[derive(Debug)] pub enum Event { /** This event may be sent by the EIS implementation immediately before the client is disconnected. The last_serial argument is set to the last serial number used in a request by the client or zero if the client has not yet issued a request. Where a client is disconnected by EIS on purpose, for example after a user interaction, the reason is disconnect_reason.disconnected (i.e. zero) and the explanation is NULL. Where a client is disconnected due to some invalid request or other protocol error, the reason is one of disconnect_reason (i.e. nonzero) and explanation may contain a string explaining why. This string is intended to help debugging only and is not guaranteed to stay constant. The `ei_connection` object will be destroyed by the EIS implementation immediately after this event has been sent, a client must not attempt to use it after that point. There is no guarantee this event is sent - the connection may be closed without a disconnection event. */ Disconnected { /** the last serial sent by the EIS implementation */ last_serial: u32, /** the reason for being disconnected */ reason: DisconnectReason, /** an explanation for debugging purposes */ explanation: String, }, /** Notification that a new seat has been added. A seat is a set of input devices that logically belong together. This event is only sent if the client announced support for the "`ei_seat`" interface in `ei_handshake.interface_version.` The interface version is equal or less to the client-supported version in `ei_handshake.interface_version` for the "`ei_seat`" interface. */ Seat { /** */ seat: super::seat::Seat, }, /** Notification that an object ID used in an earlier request was invalid and does not exist. This event is sent by the EIS implementation when an object that does not exist as seen by the EIS implementation. The protocol is asynchronous and this may occur e.g. when the EIS implementation destroys an object at the same time as the client requests functionality from that object. For example, an EIS implementation may send `ei_device.destroyed` and destroy the device's resources (and protocol object) at the same time as the client attempts to `ei_device.start_emulating` on that object. It is the client's responsibilty to unwind any state changes done to the object since the last successful message. */ InvalidObject { /** the last serial sent by the EIS implementation */ last_serial: u32, /** */ invalid_id: u64, }, /** The ping event asks the client to emit the 'done' event on the provided `ei_callback` object. Since requests are handled in-order and events are delivered in-order, this can be used as a synchronization point to ensure all previous requests and the resulting events have been handled. The object returned by this request must be destroyed by the ei client implementation after the callback is fired and as such the client must not attempt to use it after that point. The callback_data in the resulting `ei_pingpong.done` request is ignored by the EIS implementation. Note that for a EIS implementation to use this request the client must announce support for this interface in `ei_handshake.interface_version.` It is a protocol violation to send this event to a client without the "`ei_pingpong`" interface. */ Ping { /** callback object for the ping request */ ping: super::pingpong::Pingpong, }, } impl Event { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("disconnected"), 1 => Some("seat"), 2 => Some("invalid_object"), 3 => Some("ping"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => { let last_serial = _bytes.read_arg()?; let reason = _bytes.read_arg()?; let explanation = _bytes.read_arg()?; Ok(Self::Disconnected { last_serial, reason, explanation, }) } 1 => { let seat = _bytes.read_arg()?; let version = _bytes.read_arg()?; Ok(Self::Seat { seat: _bytes.backend().new_peer_interface(seat, version)?, }) } 2 => { let last_serial = _bytes.read_arg()?; let invalid_id = _bytes.read_arg()?; Ok(Self::InvalidObject { last_serial, invalid_id, }) } 3 => { let ping = _bytes.read_arg()?; let version = _bytes.read_arg()?; Ok(Self::Ping { ping: _bytes.backend().new_peer_interface(ping, version)?, }) } opcode => Err(wire::ParseError::InvalidOpcode("connection", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Disconnected { last_serial, reason, explanation, } => { args.push(last_serial.as_arg()); args.push(reason.as_arg()); args.push(explanation.as_arg()); } Self::Seat { seat } => { args.push(seat.as_arg()); } Self::InvalidObject { last_serial, invalid_id, } => { args.push(last_serial.as_arg()); args.push(invalid_id.as_arg()); } Self::Ping { ping } => { args.push(ping.as_arg()); } _ => unreachable!(), } args } } } pub use connection::Connection; /** Interface for ensuring a roundtrip to the EIS implementation. Clients can handle the 'done' event to get notified when the related request that created the `ei_callback` object is done. Note that for a client to receive objects of this type, it must announce support for this interface in `ei_handshake.interface_version.` */ pub mod callback { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Callback(pub(crate) crate::Object); impl Callback { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Callback {} impl wire::Interface for Callback { const NAME: &'static str = "ei_callback"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = true; type Incoming = Event; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::ei::Interface for Callback {} impl Callback {} #[non_exhaustive] #[derive(Debug)] pub enum Event { /** Notify the client when the related request is done. Immediately after this event the `ei_callback` object is destroyed by the EIS implementation and as such the client must not attempt to use it after that point. */ Done { /** request-specific data for the callback */ callback_data: u64, }, } impl Event { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("done"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => { let callback_data = _bytes.read_arg()?; Ok(Self::Done { callback_data }) } opcode => Err(wire::ParseError::InvalidOpcode("callback", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Done { callback_data } => { args.push(callback_data.as_arg()); } _ => unreachable!(), } args } } } pub use callback::Callback; /** Interface for ensuring a roundtrip to the client implementation. This interface is identical to `ei_callback` but is intended for the EIS implementation to enforce a roundtrip to the client. Note that for a client to receive objects of this type, it must announce support for this interface in `ei_handshake.interface_version.` */ pub mod pingpong { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Pingpong(pub(crate) crate::Object); impl Pingpong { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Pingpong {} impl wire::Interface for Pingpong { const NAME: &'static str = "ei_pingpong"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = true; type Incoming = Event; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::ei::Interface for Pingpong {} impl Pingpong { /** Notify the EIS implementation when the related event is done. Immediately after this request the `ei_pingpong` object is destroyed by the client and as such must not be used any further. */ pub fn done(&self, callback_data: u64) -> () { let args = &[wire::Arg::Uint64(callback_data.into())]; self.0.request(0, args); self.0.backend_weak().remove_id(self.0.id()); () } } #[non_exhaustive] #[derive(Debug)] pub enum Event {} impl Event { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { opcode => Err(wire::ParseError::InvalidOpcode("pingpong", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { _ => unreachable!(), } args } } } pub use pingpong::Pingpong; /** An `ei_seat` represents a set of input devices that logically belong together. In most cases only one seat is present and all input devices on that seat share the same pointer and keyboard focus. A seat has potential capabilities, a client is expected to bind to those capabilities. The EIS implementation then creates logical input devices based on the capabilities the client is interested in. Immediately after creation of the `ei_seat` object, the EIS implementation sends a burst of events with information about this seat. This burst of events is terminated by the `ei_seat.done` event. Note that for a client to receive objects of this type, it must announce support for this interface in `ei_handshake.interface_version.` */ pub mod seat { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Seat(pub(crate) crate::Object); impl Seat { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Seat {} impl wire::Interface for Seat { const NAME: &'static str = "ei_seat"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = true; type Incoming = Event; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::ei::Interface for Seat {} impl Seat { /** Notification that the client is no longer interested in this seat. The EIS implementation will release any resources related to this seat and send the `ei_seat.destroyed` event once complete. Note that releasing a seat does not guarantee another seat becomes available. In other words, in most single-seat cases, releasing the seat means the connection becomes effectively inert. */ pub fn release(&self) -> () { let args = &[]; self.0.request(0, args); () } /** Bind to the bitmask of capabilities given. The bitmask is zero or more of the masks representing an interface as provided in the `ei_seat.capability` event. See the `ei_seat.capability` event documentation for examples. Binding masks that are not supported in the `ei_device`'s interface version is a client bug and may result in disconnection. A client may send this request multiple times to adjust the capabilities it is interested in. If previously-bound capabilities are dropped by the client, the EIS implementation may `ei_device.remove` devices that have these capabilities. */ pub fn bind(&self, capabilities: u64) -> () { let args = &[wire::Arg::Uint64(capabilities.into())]; self.0.request(1, args); () } } #[non_exhaustive] #[derive(Debug)] pub enum Event { /** This seat has been removed and a client should release all associated resources. This `ei_seat` object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ Destroyed { /** this event's serial number */ serial: u32, }, /** The name of this seat, if any. This event is optional and sent once immediately after object creation. It is a protocol violation to send this event after the `ei_seat.done` event. */ Name { /** the seat name */ name: String, }, /** A notification that this seat supports devices with the given interface. The interface is mapped to a bitmask by the EIS implementation. A client may then binary OR these bitmasks in `ei_seat.bind.` In response, the EIS implementation may then create device based on those bound capabilities. For example, an EIS implementation may map "`ei_pointer`" to 0x1, "`ei_keyboard`" to 0x4 and "`ei_touchscreen`" to 0x8. A client may then `ei_seat.bind`(0xc) to bind to keyboard and touchscreen but not pointer. Note that as shown in this example the set of masks may be sparse. The value of the mask is contant for the lifetime of the seat but may differ between seats. Note that seat capabilities only represent a mask of possible capabilities on devices in this seat. A capability that is not available on the seat cannot ever be available on any device in this seat. For example, a seat that only has the pointer and keyboard capabilities can never have a device with the touchscreen capability. It is up to the EIS implementation to decide how many (if any) devices with any given capability exist in this seat. Only interfaces that the client announced during `ei_handshake.interface_version` can be a seat capability. This event is sent multiple times - once per supported interface. The set of capabilities is constant for the lifetime of the seat. It is a protocol violation to send this event after the `ei_seat.done` event. */ Capability { /** the mask representing this capability */ mask: u64, /** the interface name for this capability */ interface: String, }, /** Notification that the initial burst of events is complete and the client can set up this seat now. It is a protocol violation to send this event more than once. */ Done, /** Notification that a new device has been added. This event is only sent if the client announced support for the "`ei_device`" interface in `ei_handshake.interface_version.` The interface version is equal or less to the client-supported version in `ei_handshake.interface_version` for the "`ei_device`" interface. */ Device { /** the new device */ device: super::device::Device, }, } impl Event { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("destroyed"), 1 => Some("name"), 2 => Some("capability"), 3 => Some("done"), 4 => Some("device"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => { let serial = _bytes.read_arg()?; Ok(Self::Destroyed { serial }) } 1 => { let name = _bytes.read_arg()?; Ok(Self::Name { name }) } 2 => { let mask = _bytes.read_arg()?; let interface = _bytes.read_arg()?; Ok(Self::Capability { mask, interface }) } 3 => Ok(Self::Done), 4 => { let device = _bytes.read_arg()?; let version = _bytes.read_arg()?; Ok(Self::Device { device: _bytes.backend().new_peer_interface(device, version)?, }) } opcode => Err(wire::ParseError::InvalidOpcode("seat", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Destroyed { serial } => { args.push(serial.as_arg()); } Self::Name { name } => { args.push(name.as_arg()); } Self::Capability { mask, interface } => { args.push(mask.as_arg()); args.push(interface.as_arg()); } Self::Done => {} Self::Device { device } => { args.push(device.as_arg()); } _ => unreachable!(), } args } } } pub use seat::Seat; /** An `ei_device` represents a single logical input devices. Like physical input devices an `ei_device` may have multiple capabilities and may e.g. function as pointer and keyboard. Depending on the `ei_handshake.context_type`, an `ei_device` can emulate events via client requests or receive events. It is a protocol violation to emulate certain events on a receiver device, or for the EIS implementation to send certain events to the device. See the individual request/event documentation for details. Note that for a client to receive objects of this type, it must announce support for this interface in `ei_handshake.interface_version.` */ pub mod device { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Device(pub(crate) crate::Object); impl Device { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Device {} impl wire::Interface for Device { const NAME: &'static str = "ei_device"; const VERSION: u32 = 2; const CLIENT_SIDE: bool = true; type Incoming = Event; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::ei::Interface for Device {} impl Device { /** Notification that the client is no longer interested in this device. Note that releasing a device does not guarantee another device becomes available. The EIS implementation will release any resources related to this device and send the `ei_device.destroyed` event once complete. */ pub fn release(&self) -> () { let args = &[]; self.0.request(0, args); () } /** Notify the EIS implementation that the given device is about to start sending events. This should be seen more as a transactional boundary than a time-based boundary. The primary use-cases for this are to allow for setup on the EIS implementation side and/or UI updates to indicate that a device is sending events now and for out-of-band information to sync with a given event sequence. There is no actual requirement that events start immediately once emulation starts and there is no requirement that a client calls `ei_device.stop_emulating` after the most recent events. For example, in a remote desktop use-case the client would call `ei_device.start_emulating` once the remote desktop session starts (rather than when the device sends events) and `ei_device.stop_emulating` once the remote desktop session stops. The sequence number identifies this transaction between start/stop emulating. It must go up by at least 1 on each call to `ei_device.start_emulating.` Wraparound must be handled by the EIS implementation but callers must ensure that detection of wraparound is possible. It is a protocol violation to request `ei_device.start_emulating` after `ei_device.start_emulating` without an intermediate stop_emulating. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than sender. */ pub fn start_emulating(&self, last_serial: u32, sequence: u32) -> () { let args = &[ wire::Arg::Uint32(last_serial.into()), wire::Arg::Uint32(sequence.into()), ]; self.0.request(1, args); () } /** Notify the EIS implementation that the given device is no longer sending events. See `ei_device.start_emulating` for details. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than sender. */ pub fn stop_emulating(&self, last_serial: u32) -> () { let args = &[wire::Arg::Uint32(last_serial.into())]; self.0.request(2, args); () } /** Generate a frame event to group the current set of events into a logical hardware event. This function must be called after one or more events on any of `ei_pointer`, `ei_pointer_absolute`, `ei_scroll`, `ei_button`, `ei_keyboard` or `ei_touchscreen` has been requested by the EIS implementation. The EIS implementation should not process changes to the device state until the `ei_device.frame` event. For example, pressing and releasing a key within the same frame is a logical noop. The given timestamp applies to all events in the current frame. The timestamp must be in microseconds of CLOCK_MONOTONIC. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than sender. */ pub fn frame(&self, last_serial: u32, timestamp: u64) -> () { let args = &[ wire::Arg::Uint32(last_serial.into()), wire::Arg::Uint64(timestamp.into()), ]; self.0.request(3, args); () } } pub use crate::eiproto_enum::device::DeviceType; #[non_exhaustive] #[derive(Debug)] pub enum Event { /** This device has been removed and a client should release all associated resources. This `ei_device` object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ Destroyed { /** this event's serial number */ serial: u32, }, /** The name of this device, if any. This event is optional and sent once immediately after object creation. It is a protocol violation to send this event after the `ei_device.done` event. */ Name { /** the device name */ name: String, }, /** The device type, one of virtual or physical. Devices of type `ei_device.device_type.physical` are supported only clients of type `ei_handshake.context_type.receiver.` This event is sent once immediately after object creation. It is a protocol violation to send this event after the `ei_device.done` event. */ DeviceType { /** the device type */ device_type: DeviceType, }, /** The device dimensions in mm. This event is optional and sent once immediately after object creation. This event is only sent for devices of `ei_device.device_type.physical.` It is a protocol violation to send this event after the `ei_device.done` event. */ Dimensions { /** the device physical width in mm */ width: u32, /** the device physical height in mm */ height: u32, }, /** Notifies the client of one region. The number of regions is constant for a device and all regions are announced immediately after object creation. A region is rectangular and defined by an x/y offset and a width and a height. A region defines the area on an EIS desktop layout that is accessible by this device - this region may not be the full area of the desktop. Input events may only be sent for points within the regions. The use of regions is private to the EIS compositor and coordinates may not match the size of the actual desktop. For example, a compositor may set a 1920x1080 region to represent a 4K monitor and transparently map input events into the respective true pixels. Absolute devices may have different regions, it is up to the libei client to send events through the correct device to target the right pixel. For example, a dual-head setup my have two absolute devices, the first with a zero offset region spanning the left screen, the second with a nonzero offset spanning the right screen. The physical scale denotes a constant factor that needs to be applied to any relative movement on this region for that movement to match the same *physical* movement on another region. It is an EIS implementation bug to advertise the absolute pointer capability on a device_type.virtual device without advertising an `ei_region` for this device. This event is optional and sent immediately after object creation. Where a device has multiple regions, this event is sent once for each region. It is a protocol violation to send this event after the `ei_device.done` event. */ Region { /** region x offset in logical pixels */ offset_x: u32, /** region y offset in logical pixels */ offset_y: u32, /** region width in logical pixels */ width: u32, /** region height in logical pixels */ hight: u32, /** the physical scale for this region */ scale: f32, }, /** Notification that a new device has a sub-interface. This event may be sent for the - "`ei_pointer`" interface if the device has the `ei_device.capabilities.pointer` capability - "`ei_pointer_absolute`" interface if the device has the `ei_device.capabilities.pointer_absolute` capability - "`ei_scroll`" interface if the device has the `ei_device.capabilities.scroll` capability - "`ei_button`" interface if the device has the `ei_device.capabilities.button` capability - "`ei_keyboard`" interface if the device has the `ei_device.capabilities.keyboard` capability - "`ei_touchscreen`" interface if the device has the `ei_device.capabilities.touchscreen` capability The interface version is equal or less to the client-supported version in `ei_handshake.interface_version` for the respective interface. This event is optional and sent immediately after object creation and at most once per interface. It is a protocol violation to send this event after the `ei_device.done` event. */ Interface { /** */ object: crate::Object, }, /** Notification that the initial burst of events is complete and the client can set up this device now. It is a protocol violation to send this event more than once per device. */ Done, /** Notification that the device has been resumed by the EIS implementation and (depending on the `ei_handshake.context_type`) the client may request `ei_device.start_emulating` or the EIS implementation may `ei_device.start_emulating` events. It is a client bug to request emulation of events on a device that is not resumed. The EIS implementation may silently discard such events. A newly advertised device is in the `ei_device.paused` state. */ Resumed { /** this event's serial number */ serial: u32, }, /** Notification that the device has been paused by the EIS implementation and no futher events will be accepted on this device until it is resumed again. For devices of `ei_device_setup.context_type` sender, the client thus does not need to request `ei_device.stop_emulating` and may request `ei_device.start_emulating` after a subsequent `ei_device.resumed.` For devices of `ei_device_setup.context_type` receiver and where the EIS implementation did not send a `ei_device.stop_emulating` prior to this event, the device may send a `ei_device.start_emulating` event after a subsequent `ei_device.resumed` event. Pausing a device resets the logical state of the device to neutral. This includes: - any buttons or keys logically down are released - any modifiers logically down are released - any touches logically down are released It is a client bug to request emulation of events on a device that is not resumed. The EIS implementation may silently discard such events. A newly advertised device is in the `ei_device.paused` state. */ Paused { /** this event's serial number */ serial: u32, }, /** See the `ei_device.start_emulating` request for details. It is a protocol violation to send this event for a client of an `ei_handshake.context_type` other than receiver. */ StartEmulating { /** this event's serial number */ serial: u32, /** */ sequence: u32, }, /** See the `ei_device.stop_emulating` request for details. It is a protocol violation to send this event for a client of an `ei_handshake.context_type` other than receiver. */ StopEmulating { /** this event's serial number */ serial: u32, }, /** See the `ei_device.frame` request for details. It is a protocol violation to send this event for a client of an `ei_handshake.context_type` other than receiver. */ Frame { /** this event's serial number */ serial: u32, /** timestamp in microseconds */ timestamp: u64, }, /** Notifies the client that the region specified in the next `ei_device.region` event is to be assigned the given mapping_id. This ID can be used by the client to identify an external resource that has a relationship with this region. For example the client may receive a data stream with the video data that this region represents. By attaching the same identifier to the data stream and this region the EIS implementation can inform the client that the video data stream and the region represent paired data. This event is optional and sent immediately after object creation but before the corresponding `ei_device.region` event. Where a device has multiple regions, this event may be sent zero or one time for each region. It is a protocol violation to send this event after the `ei_device.done` event or to send this event without a corresponding following `ei_device.region` event. */ RegionMappingId { /** region mapping id */ mapping_id: String, }, } impl Event { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("destroyed"), 1 => Some("name"), 2 => Some("device_type"), 3 => Some("dimensions"), 4 => Some("region"), 5 => Some("interface"), 6 => Some("done"), 7 => Some("resumed"), 8 => Some("paused"), 9 => Some("start_emulating"), 10 => Some("stop_emulating"), 11 => Some("frame"), 12 => Some("region_mapping_id"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => { let serial = _bytes.read_arg()?; Ok(Self::Destroyed { serial }) } 1 => { let name = _bytes.read_arg()?; Ok(Self::Name { name }) } 2 => { let device_type = _bytes.read_arg()?; Ok(Self::DeviceType { device_type }) } 3 => { let width = _bytes.read_arg()?; let height = _bytes.read_arg()?; Ok(Self::Dimensions { width, height }) } 4 => { let offset_x = _bytes.read_arg()?; let offset_y = _bytes.read_arg()?; let width = _bytes.read_arg()?; let hight = _bytes.read_arg()?; let scale = _bytes.read_arg()?; Ok(Self::Region { offset_x, offset_y, width, hight, scale, }) } 5 => { let object = _bytes.read_arg()?; let interface_name = _bytes.read_arg()?; let version = _bytes.read_arg()?; Ok(Self::Interface { object: _bytes.backend().new_peer_object( object, interface_name, version, )?, }) } 6 => Ok(Self::Done), 7 => { let serial = _bytes.read_arg()?; Ok(Self::Resumed { serial }) } 8 => { let serial = _bytes.read_arg()?; Ok(Self::Paused { serial }) } 9 => { let serial = _bytes.read_arg()?; let sequence = _bytes.read_arg()?; Ok(Self::StartEmulating { serial, sequence }) } 10 => { let serial = _bytes.read_arg()?; Ok(Self::StopEmulating { serial }) } 11 => { let serial = _bytes.read_arg()?; let timestamp = _bytes.read_arg()?; Ok(Self::Frame { serial, timestamp }) } 12 => { let mapping_id = _bytes.read_arg()?; Ok(Self::RegionMappingId { mapping_id }) } opcode => Err(wire::ParseError::InvalidOpcode("device", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Destroyed { serial } => { args.push(serial.as_arg()); } Self::Name { name } => { args.push(name.as_arg()); } Self::DeviceType { device_type } => { args.push(device_type.as_arg()); } Self::Dimensions { width, height } => { args.push(width.as_arg()); args.push(height.as_arg()); } Self::Region { offset_x, offset_y, width, hight, scale, } => { args.push(offset_x.as_arg()); args.push(offset_y.as_arg()); args.push(width.as_arg()); args.push(hight.as_arg()); args.push(scale.as_arg()); } Self::Interface { object } => { args.push(object.as_arg()); } Self::Done => {} Self::Resumed { serial } => { args.push(serial.as_arg()); } Self::Paused { serial } => { args.push(serial.as_arg()); } Self::StartEmulating { serial, sequence } => { args.push(serial.as_arg()); args.push(sequence.as_arg()); } Self::StopEmulating { serial } => { args.push(serial.as_arg()); } Self::Frame { serial, timestamp } => { args.push(serial.as_arg()); args.push(timestamp.as_arg()); } Self::RegionMappingId { mapping_id } => { args.push(mapping_id.as_arg()); } _ => unreachable!(), } args } } } pub use device::Device; /** Interface for pointer motion requests and events. This interface is available on devices with the `ei_device.capability` pointer. This interface is only provided once per device and where a client requests `ei_pointer.release` the interface does not get re-initialized. An EIS implementation may adjust the behavior of the device (including removing the device) if the interface is releasd. Note that for a client to receive objects of this type, it must announce support for this interface in `ei_handshake.interface_version.` */ pub mod pointer { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Pointer(pub(crate) crate::Object); impl Pointer { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Pointer {} impl wire::Interface for Pointer { const NAME: &'static str = "ei_pointer"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = true; type Incoming = Event; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::ei::Interface for Pointer {} impl Pointer { /** Notification that the client is no longer interested in this pointer. The EIS implementation will release any resources related to this pointer and send the `ei_pointer.destroyed` event once complete. */ pub fn release(&self) -> () { let args = &[]; self.0.request(0, args); () } /** Generate a relative motion event on this pointer. It is a client bug to send this request more than once within the same `ei_device.frame.` It is a client bug to send this request on a device without the `ei_device.capabilities.pointer` capability. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than sender. */ pub fn motion_relative(&self, x: f32, y: f32) -> () { let args = &[wire::Arg::Float(x.into()), wire::Arg::Float(y.into())]; self.0.request(1, args); () } } #[non_exhaustive] #[derive(Debug)] pub enum Event { /** This object has been removed and a client should release all associated resources. This object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ Destroyed { /** this event's serial number */ serial: u32, }, /** See the `ei_pointer.motion_relative` request for details. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than receiver. */ MotionRelative { /** */ x: f32, /** */ y: f32, }, } impl Event { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("destroyed"), 1 => Some("motion_relative"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => { let serial = _bytes.read_arg()?; Ok(Self::Destroyed { serial }) } 1 => { let x = _bytes.read_arg()?; let y = _bytes.read_arg()?; Ok(Self::MotionRelative { x, y }) } opcode => Err(wire::ParseError::InvalidOpcode("pointer", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Destroyed { serial } => { args.push(serial.as_arg()); } Self::MotionRelative { x, y } => { args.push(x.as_arg()); args.push(y.as_arg()); } _ => unreachable!(), } args } } } pub use pointer::Pointer; /** Interface for absolute pointer requests and events. This interface is available on devices with the `ei_device.capability` pointer_absolute. This interface is only provided once per device and where a client requests `ei_pointer_absolute.release` the interface does not get re-initialized. An EIS implementation may adjust the behavior of the device (including removing the device) if the interface is releasd. Note that for a client to receive objects of this type, it must announce support for this interface in `ei_handshake.interface_version.` */ pub mod pointer_absolute { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct PointerAbsolute(pub(crate) crate::Object); impl PointerAbsolute { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for PointerAbsolute {} impl wire::Interface for PointerAbsolute { const NAME: &'static str = "ei_pointer_absolute"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = true; type Incoming = Event; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::ei::Interface for PointerAbsolute {} impl PointerAbsolute { /** Notification that the client is no longer interested in this object. The EIS implementation will release any resources related to this object and send the `ei_pointer_absolute.destroyed` event once complete. */ pub fn release(&self) -> () { let args = &[]; self.0.request(0, args); () } /** Generate an absolute motion event on this pointer. The x/y coordinates must be within the device's regions or the event is silently discarded. It is a client bug to send this request more than once within the same `ei_device.frame.` It is a client bug to send this request on a device without the `ei_device.capabilities.pointer_absolute` capability. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than sender. */ pub fn motion_absolute(&self, x: f32, y: f32) -> () { let args = &[wire::Arg::Float(x.into()), wire::Arg::Float(y.into())]; self.0.request(1, args); () } } #[non_exhaustive] #[derive(Debug)] pub enum Event { /** This object has been removed and a client should release all associated resources. This object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ Destroyed { /** this event's serial number */ serial: u32, }, /** See the `ei_pointer_absolute.motion_absolute` request for details. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than receiver. */ MotionAbsolute { /** */ x: f32, /** */ y: f32, }, } impl Event { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("destroyed"), 1 => Some("motion_absolute"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => { let serial = _bytes.read_arg()?; Ok(Self::Destroyed { serial }) } 1 => { let x = _bytes.read_arg()?; let y = _bytes.read_arg()?; Ok(Self::MotionAbsolute { x, y }) } opcode => Err(wire::ParseError::InvalidOpcode("pointer_absolute", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Destroyed { serial } => { args.push(serial.as_arg()); } Self::MotionAbsolute { x, y } => { args.push(x.as_arg()); args.push(y.as_arg()); } _ => unreachable!(), } args } } } pub use pointer_absolute::PointerAbsolute; /** Interface for scroll requests and events. This interface is available on devices with the `ei_device.capability` scroll. This interface is only provided once per device and where a client requests `ei_scroll.release` the interface does not get re-initialized. An EIS implementation may adjust the behavior of the device (including removing the device) if the interface is releasd. Note that for a client to receive objects of this type, it must announce support for this interface in `ei_handshake.interface_version.` */ pub mod scroll { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Scroll(pub(crate) crate::Object); impl Scroll { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Scroll {} impl wire::Interface for Scroll { const NAME: &'static str = "ei_scroll"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = true; type Incoming = Event; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::ei::Interface for Scroll {} impl Scroll { /** Notification that the client is no longer interested in this object. The EIS implementation will release any resources related to this object and send the `ei_scroll.destroyed` event once complete. */ pub fn release(&self) -> () { let args = &[]; self.0.request(0, args); () } /** Generate a a smooth (pixel-precise) scroll event on this pointer. Clients must not send `ei_scroll.scroll_discrete` events for the same event, the EIS implementation is responsible for emulation of discrete scroll events. It is a client bug to send this request more than once within the same `ei_device.frame.` It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than sender. */ pub fn scroll(&self, x: f32, y: f32) -> () { let args = &[wire::Arg::Float(x.into()), wire::Arg::Float(y.into())]; self.0.request(1, args); () } /** Generate a a discrete (e.g. wheel) scroll event on this pointer. Clients must not send `ei_scroll.scroll` events for the same event, the EIS implementation is responsible for emulation of smooth scroll events. A discrete scroll event is based logical scroll units (equivalent to one mouse wheel click). The value for one scroll unit is 120, a fraction or multiple thereof represents a fraction or multiple of a wheel click. It is a client bug to send this request more than once within the same `ei_device.frame.` It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than sender. */ pub fn scroll_discrete(&self, x: i32, y: i32) -> () { let args = &[wire::Arg::Int32(x.into()), wire::Arg::Int32(y.into())]; self.0.request(2, args); () } /** Generate a a scroll stop or cancel event on this pointer. A scroll stop event notifies the EIS implementation that the interaction causing a scroll motion previously triggered with `ei_scroll.scroll` or `ei_scroll.scroll_discrete` has stopped. For example, if all fingers are lifted off a touchpad, two-finger scrolling has logically stopped. The EIS implementation may use this information to e.g. start kinetic scrolling previously based on the previous finger speed. If is_cancel is nonzero, the event represents a cancellation of the current interaction. This indicates that the interaction has stopped to the point where further (server-emulated) scroll events from this device are wrong. It is a client bug to send this request more than once within the same `ei_device.frame.` It is a client bug to send this request for an axis that had a a nonzero value in either `ei_scroll.scroll` or `ei_scroll.scroll_discrete` in the current frame. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than sender. */ pub fn scroll_stop(&self, x: u32, y: u32, is_cancel: u32) -> () { let args = &[ wire::Arg::Uint32(x.into()), wire::Arg::Uint32(y.into()), wire::Arg::Uint32(is_cancel.into()), ]; self.0.request(3, args); () } } #[non_exhaustive] #[derive(Debug)] pub enum Event { /** This object has been removed and a client should release all associated resources. This object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ Destroyed { /** this event's serial number */ serial: u32, }, /** See the `ei_scroll.scroll` request for details. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than receiver. */ Scroll { /** */ x: f32, /** */ y: f32, }, /** See the `ei_scroll.scroll_discrete` request for details. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than receiver. */ ScrollDiscrete { /** */ x: i32, /** */ y: i32, }, /** See the `ei_scroll.scroll_stop` request for details. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than receiver. */ ScrollStop { /** */ x: u32, /** */ y: u32, /** */ is_cancel: u32, }, } impl Event { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("destroyed"), 1 => Some("scroll"), 2 => Some("scroll_discrete"), 3 => Some("scroll_stop"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => { let serial = _bytes.read_arg()?; Ok(Self::Destroyed { serial }) } 1 => { let x = _bytes.read_arg()?; let y = _bytes.read_arg()?; Ok(Self::Scroll { x, y }) } 2 => { let x = _bytes.read_arg()?; let y = _bytes.read_arg()?; Ok(Self::ScrollDiscrete { x, y }) } 3 => { let x = _bytes.read_arg()?; let y = _bytes.read_arg()?; let is_cancel = _bytes.read_arg()?; Ok(Self::ScrollStop { x, y, is_cancel }) } opcode => Err(wire::ParseError::InvalidOpcode("scroll", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Destroyed { serial } => { args.push(serial.as_arg()); } Self::Scroll { x, y } => { args.push(x.as_arg()); args.push(y.as_arg()); } Self::ScrollDiscrete { x, y } => { args.push(x.as_arg()); args.push(y.as_arg()); } Self::ScrollStop { x, y, is_cancel } => { args.push(x.as_arg()); args.push(y.as_arg()); args.push(is_cancel.as_arg()); } _ => unreachable!(), } args } } } pub use scroll::Scroll; /** Interface for button requests and events. This interface is available on devices with the `ei_device.capability` button. This interface is only provided once per device and where a client requests `ei_button.release` the interface does not get re-initialized. An EIS implementation may adjust the behavior of the device (including removing the device) if the interface is releasd. Note that for a client to receive objects of this type, it must announce support for this interface in `ei_handshake.interface_version.` */ pub mod button { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Button(pub(crate) crate::Object); impl Button { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Button {} impl wire::Interface for Button { const NAME: &'static str = "ei_button"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = true; type Incoming = Event; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::ei::Interface for Button {} impl Button { /** Notification that the client is no longer interested in this object. The EIS implementation will release any resources related to this object and send the `ei_button.destroyed` event once complete. */ pub fn release(&self) -> () { let args = &[]; self.0.request(0, args); () } /** Generate a button event on this pointer. The button codes must match the defines in linux/input-event-codes.h. It is a client bug to send more than one button request for the same button within the same `ei_device.frame.` It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than sender. */ pub fn button(&self, button: u32, state: ButtonState) -> () { let args = &[ wire::Arg::Uint32(button.into()), wire::Arg::Uint32(state.into()), ]; self.0.request(1, args); () } } pub use crate::eiproto_enum::button::ButtonState; #[non_exhaustive] #[derive(Debug)] pub enum Event { /** This pointer has been removed and a client should release all associated resources. This `ei_scroll` object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ Destroyed { /** this event's serial number */ serial: u32, }, /** See the `ei_scroll.button` request for details. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than receiver. It is an EIS implementation bug to send more than one button request for the same button within the same `ei_device.frame.` */ Button { /** */ button: u32, /** */ state: ButtonState, }, } impl Event { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("destroyed"), 1 => Some("button"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => { let serial = _bytes.read_arg()?; Ok(Self::Destroyed { serial }) } 1 => { let button = _bytes.read_arg()?; let state = _bytes.read_arg()?; Ok(Self::Button { button, state }) } opcode => Err(wire::ParseError::InvalidOpcode("button", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Destroyed { serial } => { args.push(serial.as_arg()); } Self::Button { button, state } => { args.push(button.as_arg()); args.push(state.as_arg()); } _ => unreachable!(), } args } } } pub use button::Button; /** Interface for keyboard requests and events. This interface is available on devices with the `ei_device.capability` keyboard. This interface is only provided once per device and where a client requests `ei_keyboard.release` the interface does not get re-initialized. An EIS implementation may adjust the behavior of the device (including removing the device) if the interface is releasd. Note that for a client to receive objects of this type, it must announce support for this interface in `ei_handshake.interface_version.` */ pub mod keyboard { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Keyboard(pub(crate) crate::Object); impl Keyboard { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Keyboard {} impl wire::Interface for Keyboard { const NAME: &'static str = "ei_keyboard"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = true; type Incoming = Event; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::ei::Interface for Keyboard {} impl Keyboard { /** Notification that the client is no longer interested in this keyboard. The EIS implementation will release any resources related to this keyboard and send the `ei_keyboard.destroyed` event once complete. */ pub fn release(&self) -> () { let args = &[]; self.0.request(0, args); () } /** Generate a key event on this keyboard. If the device has an `ei_keyboard.keymap`, the key code corresponds to that keymap. The key codes must match the defines in linux/input-event-codes.h. It is a client bug to send more than one key request for the same key within the same `ei_device.frame.` It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than sender. */ pub fn key(&self, key: u32, state: KeyState) -> () { let args = &[ wire::Arg::Uint32(key.into()), wire::Arg::Uint32(state.into()), ]; self.0.request(1, args); () } } pub use crate::eiproto_enum::keyboard::KeyState; pub use crate::eiproto_enum::keyboard::KeymapType; #[non_exhaustive] #[derive(Debug)] pub enum Event { /** This keyboard has been removed and a client should release all associated resources. This `ei_keyboard` object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ Destroyed { /** this event's serial number */ serial: u32, }, /** Notification that this device has a keymap. Future key events must be interpreted by the client according to this keymap. For clients of `ei_handshake.context_type` sender it is the client's responsibility to send the correct `ei_keyboard.key` keycodes to generate the expected keysym in the EIS implementation. The keymap is constant for the lifetime of the device. This event provides a file descriptor to the client which can be memory-mapped in read-only mode to provide a keyboard mapping description. The fd must be mapped with MAP_PRIVATE by the recipient, as MAP_SHARED may fail. This event is sent immediately after the `ei_keyboard` object is created and before the `ei_device.done` event. It is a protocol violation to send this event after the `ei_device.done` event. */ Keymap { /** the keymap type */ keymap_type: KeymapType, /** the keymap size in bytes */ size: u32, /** file descriptor to the keymap */ keymap: std::os::unix::io::OwnedFd, }, /** See the `ei_keyboard.key` request for details. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than receiver. It is a protocol violation to send a key down event in the same frame as a key up event for the same key in the same frame. */ Key { /** */ key: u32, /** */ state: KeyState, }, /** Notification that the EIS implementation has changed modifier states on this device. Future `ei_keyboard.key` requests must take the new modifier state into account. A client must assume that all modifiers are lifted when it receives an `ei_device.paused` event. The EIS implementation must send this event after `ei_device.resumed` to notify the client of any nonzero modifier state. This event does not reqire an `ei_device.frame` and should be processed immediately by the client. This event is only sent for devices with an `ei_keyboard.keymap.` */ Modifiers { /** this event's serial number */ serial: u32, /** depressed modifiers */ depressed: u32, /** locked modifiers */ locked: u32, /** latched modifiers */ latched: u32, /** the keyboard group (layout) */ group: u32, }, } impl Event { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("destroyed"), 1 => Some("keymap"), 2 => Some("key"), 3 => Some("modifiers"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => { let serial = _bytes.read_arg()?; Ok(Self::Destroyed { serial }) } 1 => { let keymap_type = _bytes.read_arg()?; let size = _bytes.read_arg()?; let keymap = _bytes.read_arg()?; Ok(Self::Keymap { keymap_type, size, keymap, }) } 2 => { let key = _bytes.read_arg()?; let state = _bytes.read_arg()?; Ok(Self::Key { key, state }) } 3 => { let serial = _bytes.read_arg()?; let depressed = _bytes.read_arg()?; let locked = _bytes.read_arg()?; let latched = _bytes.read_arg()?; let group = _bytes.read_arg()?; Ok(Self::Modifiers { serial, depressed, locked, latched, group, }) } opcode => Err(wire::ParseError::InvalidOpcode("keyboard", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Destroyed { serial } => { args.push(serial.as_arg()); } Self::Keymap { keymap_type, size, keymap, } => { args.push(keymap_type.as_arg()); args.push(size.as_arg()); args.push(keymap.as_arg()); } Self::Key { key, state } => { args.push(key.as_arg()); args.push(state.as_arg()); } Self::Modifiers { serial, depressed, locked, latched, group, } => { args.push(serial.as_arg()); args.push(depressed.as_arg()); args.push(locked.as_arg()); args.push(latched.as_arg()); args.push(group.as_arg()); } _ => unreachable!(), } args } } } pub use keyboard::Keyboard; /** Interface for touchscreen requests and events. This interface is available on devices with the `ei_device.capability` touchscreen. This interface is only provided once per device and where a client requests `ei_touchscreen.release` the interface does not get re-initialized. An EIS implementation may adjust the behavior of the device (including removing the device) if the interface is releasd. Note that for a client to receive objects of this type, it must announce support for this interface in `ei_handshake.interface_version.` */ pub mod touchscreen { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Touchscreen(pub(crate) crate::Object); impl Touchscreen { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Touchscreen {} impl wire::Interface for Touchscreen { const NAME: &'static str = "ei_touchscreen"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = true; type Incoming = Event; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::ei::Interface for Touchscreen {} impl Touchscreen { /** Notification that the client is no longer interested in this touch. The EIS implementation will release any resources related to this touch and send the `ei_touch.destroyed` event once complete. */ pub fn release(&self) -> () { let args = &[]; self.0.request(0, args); () } /** Notifies the EIS implementation about a new touch logically down at the given coordinates. The touchid is a unique id for this touch. Touchids may be re-used after `ei_touchscreen.up.` The x/y coordinates must be within the device's regions or the event and future `ei_touchscreen.motion` events with the same touchid are silently discarded. It is a protocol violation to send a touch down in the same frame as a touch motion or touch up. */ pub fn down(&self, touchid: u32, x: f32, y: f32) -> () { let args = &[ wire::Arg::Uint32(touchid.into()), wire::Arg::Float(x.into()), wire::Arg::Float(y.into()), ]; self.0.request(1, args); () } /** Notifies the EIS implementation about an existing touch changing position to the given coordinates. The touchid is the unique id for this touch previously sent with `ei_touchscreen.down.` The x/y coordinates must be within the device's regions or the event is silently discarded. It is a protocol violation to send a touch motion in the same frame as a touch down or touch up. */ pub fn motion(&self, touchid: u32, x: f32, y: f32) -> () { let args = &[ wire::Arg::Uint32(touchid.into()), wire::Arg::Float(x.into()), wire::Arg::Float(y.into()), ]; self.0.request(2, args); () } /** Notifies the EIS implementation about an existing touch being logically up. The touchid is the unique id for this touch previously sent with `ei_touchscreen.down.` The touchid may be re-used after this request. It is a protocol violation to send a touch up in the same frame as a touch motion or touch down. */ pub fn up(&self, touchid: u32) -> () { let args = &[wire::Arg::Uint32(touchid.into())]; self.0.request(3, args); () } } #[non_exhaustive] #[derive(Debug)] pub enum Event { /** This touch has been removed and a client should release all associated resources. This `ei_touchscreen` object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ Destroyed { /** this event's serial number */ serial: u32, }, /** See the `ei_touchscreen.down` request for details. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than receiver. It is a protocol violation to send a touch down in the same frame as a touch motion or touch up. */ Down { /** */ touchid: u32, /** */ x: f32, /** */ y: f32, }, /** See the `ei_touchscreen.motion` request for details. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than receiver. It is a protocol violation to send a touch motion in the same frame as a touch down or touch up. */ Motion { /** */ touchid: u32, /** */ x: f32, /** */ y: f32, }, /** See the `ei_touchscreen.up` request for details. It is a protocol violation to send this request for a client of an `ei_handshake.context_type` other than receiver. It is a protocol violation to send a touch up in the same frame as a touch motion or touch down. */ Up { /** */ touchid: u32, }, } impl Event { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("destroyed"), 1 => Some("down"), 2 => Some("motion"), 3 => Some("up"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => { let serial = _bytes.read_arg()?; Ok(Self::Destroyed { serial }) } 1 => { let touchid = _bytes.read_arg()?; let x = _bytes.read_arg()?; let y = _bytes.read_arg()?; Ok(Self::Down { touchid, x, y }) } 2 => { let touchid = _bytes.read_arg()?; let x = _bytes.read_arg()?; let y = _bytes.read_arg()?; Ok(Self::Motion { touchid, x, y }) } 3 => { let touchid = _bytes.read_arg()?; Ok(Self::Up { touchid }) } opcode => Err(wire::ParseError::InvalidOpcode("touchscreen", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Destroyed { serial } => { args.push(serial.as_arg()); } Self::Down { touchid, x, y } => { args.push(touchid.as_arg()); args.push(x.as_arg()); args.push(y.as_arg()); } Self::Motion { touchid, x, y } => { args.push(touchid.as_arg()); args.push(x.as_arg()); args.push(y.as_arg()); } Self::Up { touchid } => { args.push(touchid.as_arg()); } _ => unreachable!(), } args } } } pub use touchscreen::Touchscreen; #[non_exhaustive] #[derive(Debug)] pub enum Event { Handshake(handshake::Handshake, handshake::Event), Connection(connection::Connection, connection::Event), Callback(callback::Callback, callback::Event), Pingpong(pingpong::Pingpong, pingpong::Event), Seat(seat::Seat, seat::Event), Device(device::Device, device::Event), Pointer(pointer::Pointer, pointer::Event), PointerAbsolute(pointer_absolute::PointerAbsolute, pointer_absolute::Event), Scroll(scroll::Scroll, scroll::Event), Button(button::Button, button::Event), Keyboard(keyboard::Keyboard, keyboard::Event), Touchscreen(touchscreen::Touchscreen, touchscreen::Event), } impl Event { pub(crate) fn op_name(interface: &str, operand: u32) -> Option<&'static str> { match interface { "ei_handshake" => handshake::Event::op_name(operand), "ei_connection" => connection::Event::op_name(operand), "ei_callback" => callback::Event::op_name(operand), "ei_pingpong" => pingpong::Event::op_name(operand), "ei_seat" => seat::Event::op_name(operand), "ei_device" => device::Event::op_name(operand), "ei_pointer" => pointer::Event::op_name(operand), "ei_pointer_absolute" => pointer_absolute::Event::op_name(operand), "ei_scroll" => scroll::Event::op_name(operand), "ei_button" => button::Event::op_name(operand), "ei_keyboard" => keyboard::Event::op_name(operand), "ei_touchscreen" => touchscreen::Event::op_name(operand), _ => None, } } pub(crate) fn parse( object: crate::Object, operand: u32, bytes: &mut wire::ByteStream, ) -> Result { match object.interface() { "ei_handshake" => Ok(Self::Handshake( object.downcast_unchecked(), handshake::Event::parse(operand, bytes)?, )), "ei_connection" => Ok(Self::Connection( object.downcast_unchecked(), connection::Event::parse(operand, bytes)?, )), "ei_callback" => Ok(Self::Callback( object.downcast_unchecked(), callback::Event::parse(operand, bytes)?, )), "ei_pingpong" => Ok(Self::Pingpong( object.downcast_unchecked(), pingpong::Event::parse(operand, bytes)?, )), "ei_seat" => Ok(Self::Seat( object.downcast_unchecked(), seat::Event::parse(operand, bytes)?, )), "ei_device" => Ok(Self::Device( object.downcast_unchecked(), device::Event::parse(operand, bytes)?, )), "ei_pointer" => Ok(Self::Pointer( object.downcast_unchecked(), pointer::Event::parse(operand, bytes)?, )), "ei_pointer_absolute" => Ok(Self::PointerAbsolute( object.downcast_unchecked(), pointer_absolute::Event::parse(operand, bytes)?, )), "ei_scroll" => Ok(Self::Scroll( object.downcast_unchecked(), scroll::Event::parse(operand, bytes)?, )), "ei_button" => Ok(Self::Button( object.downcast_unchecked(), button::Event::parse(operand, bytes)?, )), "ei_keyboard" => Ok(Self::Keyboard( object.downcast_unchecked(), keyboard::Event::parse(operand, bytes)?, )), "ei_touchscreen" => Ok(Self::Touchscreen( object.downcast_unchecked(), touchscreen::Event::parse(operand, bytes)?, )), intr => Err(wire::ParseError::InvalidInterface(intr.to_owned())), } } } impl wire::MessageEnum for Event { fn args(&self) -> Vec> { match self { Self::Handshake(_, x) => x.args(), Self::Connection(_, x) => x.args(), Self::Callback(_, x) => x.args(), Self::Pingpong(_, x) => x.args(), Self::Seat(_, x) => x.args(), Self::Device(_, x) => x.args(), Self::Pointer(_, x) => x.args(), Self::PointerAbsolute(_, x) => x.args(), Self::Scroll(_, x) => x.args(), Self::Button(_, x) => x.args(), Self::Keyboard(_, x) => x.args(), Self::Touchscreen(_, x) => x.args(), } } } reis-0.2.0/src/eiproto_eis.rs000064400000000000000000002740341046102023000142550ustar 00000000000000#![allow( unused_imports, unused_parens, clippy::useless_conversion, clippy::double_parens, clippy::match_single_binding, clippy::unused_unit )] // GENERATED FILE use crate::wire; /** This is a special interface to setup the client as seen by the EIS implementation. The object for this interface has the fixed object id 0 and only exists until the connection has been set up, see the ei_handshake.connection event. The ei_handshake version is 1 until: - the EIS implementation sends the interface_version event with a version other than 1, and, in response, - the client sends the interface_version request with a version equal or lower to the EIS implementation version. The EIS implementation must send the interface_version event immediately once the physical connection has been established. Once the ei_connection.connection event has been sent the handshake is destroyed by the EIS implementation. */ pub mod handshake { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Handshake(pub(crate) crate::Object); impl Handshake { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Handshake {} impl wire::Interface for Handshake { const NAME: &'static str = "ei_handshake"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = false; type Incoming = Request; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::eis::Interface for Handshake {} impl Handshake { /** This event is sent exactly once and immediately after connection to the EIS implementation. In response, the client must send the ei_handshake.handshake_version request with any version up to including the version provided in this event. See the ei_handshake.handshake_version request for details on what happens next. */ pub fn handshake_version(&self, version: u32) -> () { let args = &[wire::Arg::Uint32(version.into())]; self.0.request(0, args); () } /** Notifies the client that the EIS implementation supports the given named interface with the given maximum version number. This event must be sent by the EIS implementation for any interfaces that supports client-created objects (e.g. "ei_callback") before the ei_handshake.connection event. The client must not assume those interfaces are supported unless and until those versions have been received. This request must not be sent for the "ei_handshake" interface, use the handshake_version event instead. This event may be sent by the EIS implementation for any other supported interface (but not necessarily all supported interfaces) before the ei_handshake.connection event. */ pub fn interface_version(&self, name: &str, version: u32) -> () { let args = &[ wire::Arg::String(name.into()), wire::Arg::Uint32(version.into()), ]; self.0.request(1, args); () } /** Provides the client with the connection object that is the top-level object for all future requests and events. This event is sent exactly once at some unspecified time after the client sends the ei_handshake.finish request to the EIS implementation. The ei_handshake object will be destroyed by the EIS implementation immediately after this event has been sent, a client must not attempt to use it after that point. The version sent by the EIS implementation is the version of the "ei_connection" interface as announced by ei_handshake.interface_version, or any lower version. The serial number is the start value of the EIS implementation's serial number sequence. Clients must not assume any specific value for this serial number. Any future serial number in any event is monotonically increasing by an unspecified amount. */ pub fn connection(&self, serial: u32, version: u32) -> (super::connection::Connection) { let connection = self .0 .backend_weak() .new_object("ei_connection".to_string(), version); let args = &[ wire::Arg::Uint32(serial.into()), wire::Arg::NewId(connection.id().into()), wire::Arg::Uint32(version.into()), ]; self.0.request(2, args); self.0.backend_weak().remove_id(self.0.id()); (super::connection::Connection(connection)) } } pub use crate::eiproto_enum::handshake::ContextType; #[non_exhaustive] #[derive(Debug)] pub enum Request { /** Notifies the EIS implementation that this client supports the given version of the ei_handshake interface. The version number must be less than or equal to the version in the handshake_version event sent by the EIS implementation when the connection was established. Immediately after sending this request, the client must assume the negotiated version number for the ei_handshake interface and the EIS implementation may send events and process requests matching that version. This request must be sent exactly once and it must be the first request the client sends. */ HandshakeVersion { /** the interface version */ version: u32, }, /** Notify the EIS implementation that configuration is complete. In the future (and possibly after requiring user interaction), the EIS implementation responds by sending the ei_handshake.connection event. */ Finish, /** Notify the EIS implementation of the type of this context. The context types defines whether the client will send events to or receive events from the EIS implementation. Depending on the context type, certain requests must not be used and some events must not be sent by the EIS implementation. This request is optional, the default client type is context_type.receiver. This request must not be sent more than once and must be sent before ei_handshake.finish. */ ContextType { /** the client context type */ context_type: ContextType, }, /** Notify the EIS implementation of the client name. The name is a human-presentable UTF-8 string and should represent the client name as accurately as possible. This name may be presented to the user for identification of this client (e.g. to confirm the client has permissions to connect). There is no requirement for the EIS implementation to use this name. For example, where the client is managed through an XDG Desktop Portal an EIS implementation would typically use client identification information sent by the portal instead. This request is optional, the default client name is implementation-defined. This request must not be sent more than once and must be sent before ei_handshake.finish. */ Name { /** the client name */ name: String, }, /** Notify the EIS implementation that the client supports the given named interface with the given maximum version number. Future objects created by the EIS implementation will use the respective interface version (or any lesser version). This request must be sent for the "ei_connection" interface, failing to do so will result in the EIS implementation disconnecting the client on ei_handshake.finish. This request must not be sent for the "ei_handshake" interface, use the ei_handshake.handshake_version request instead. Note that an EIS implementation may consider some interfaces to be required and immediately ei_connection.disconnect a client not supporting those interfaces. This request must not be sent more than once per interface and must be sent before ei_handshake.finish. */ InterfaceVersion { /** the interface name */ name: String, /** the interface version */ version: u32, }, } impl Request { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("handshake_version"), 1 => Some("finish"), 2 => Some("context_type"), 3 => Some("name"), 4 => Some("interface_version"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => { let version = _bytes.read_arg()?; Ok(Self::HandshakeVersion { version }) } 1 => Ok(Self::Finish), 2 => { let context_type = _bytes.read_arg()?; Ok(Self::ContextType { context_type }) } 3 => { let name = _bytes.read_arg()?; Ok(Self::Name { name }) } 4 => { let name = _bytes.read_arg()?; let version = _bytes.read_arg()?; Ok(Self::InterfaceVersion { name, version }) } opcode => Err(wire::ParseError::InvalidOpcode("handshake", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::HandshakeVersion { version } => { args.push(version.as_arg()); } Self::Finish => {} Self::ContextType { context_type } => { args.push(context_type.as_arg()); } Self::Name { name } => { args.push(name.as_arg()); } Self::InterfaceVersion { name, version } => { args.push(name.as_arg()); args.push(version.as_arg()); } _ => unreachable!(), } args } } } pub use handshake::Handshake; /** The core connection object. This is the top-level object for any communication with the EIS implementation. Note that for a client to receive this object, it must announce support for this interface in ei_handshake.interface_version. */ pub mod connection { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Connection(pub(crate) crate::Object); impl Connection { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Connection {} impl wire::Interface for Connection { const NAME: &'static str = "ei_connection"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = false; type Incoming = Request; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::eis::Interface for Connection {} impl Connection { /** This event may be sent by the EIS implementation immediately before the client is disconnected. The last_serial argument is set to the last serial number used in a request by the client or zero if the client has not yet issued a request. Where a client is disconnected by EIS on purpose, for example after a user interaction, the reason is disconnect_reason.disconnected (i.e. zero) and the explanation is NULL. Where a client is disconnected due to some invalid request or other protocol error, the reason is one of disconnect_reason (i.e. nonzero) and explanation may contain a string explaining why. This string is intended to help debugging only and is not guaranteed to stay constant. The ei_connection object will be destroyed by the EIS implementation immediately after this event has been sent, a client must not attempt to use it after that point. There is no guarantee this event is sent - the connection may be closed without a disconnection event. */ pub fn disconnected( &self, last_serial: u32, reason: DisconnectReason, explanation: &str, ) -> () { let args = &[ wire::Arg::Uint32(last_serial.into()), wire::Arg::Uint32(reason.into()), wire::Arg::String(explanation.into()), ]; self.0.request(0, args); self.0.backend_weak().remove_id(self.0.id()); () } /** Notification that a new seat has been added. A seat is a set of input devices that logically belong together. This event is only sent if the client announced support for the "ei_seat" interface in ei_handshake.interface_version. The interface version is equal or less to the client-supported version in ei_handshake.interface_version for the "ei_seat" interface. */ pub fn seat(&self, version: u32) -> (super::seat::Seat) { let seat = self .0 .backend_weak() .new_object("ei_seat".to_string(), version); let args = &[ wire::Arg::NewId(seat.id().into()), wire::Arg::Uint32(version.into()), ]; self.0.request(1, args); (super::seat::Seat(seat)) } /** Notification that an object ID used in an earlier request was invalid and does not exist. This event is sent by the EIS implementation when an object that does not exist as seen by the EIS implementation. The protocol is asynchronous and this may occur e.g. when the EIS implementation destroys an object at the same time as the client requests functionality from that object. For example, an EIS implementation may send ei_device.destroyed and destroy the device's resources (and protocol object) at the same time as the client attempts to ei_device.start_emulating on that object. It is the client's responsibilty to unwind any state changes done to the object since the last successful message. */ pub fn invalid_object(&self, last_serial: u32, invalid_id: u64) -> () { let args = &[ wire::Arg::Uint32(last_serial.into()), wire::Arg::Uint64(invalid_id.into()), ]; self.0.request(2, args); () } /** The ping event asks the client to emit the 'done' event on the provided ei_callback object. Since requests are handled in-order and events are delivered in-order, this can be used as a synchronization point to ensure all previous requests and the resulting events have been handled. The object returned by this request must be destroyed by the ei client implementation after the callback is fired and as such the client must not attempt to use it after that point. The callback_data in the resulting ei_pingpong.done request is ignored by the EIS implementation. Note that for a EIS implementation to use this request the client must announce support for this interface in ei_handshake.interface_version. It is a protocol violation to send this event to a client without the "ei_pingpong" interface. */ pub fn ping(&self, version: u32) -> (super::pingpong::Pingpong) { let ping = self .0 .backend_weak() .new_object("ei_pingpong".to_string(), version); let args = &[ wire::Arg::NewId(ping.id().into()), wire::Arg::Uint32(version.into()), ]; self.0.request(3, args); (super::pingpong::Pingpong(ping)) } } pub use crate::eiproto_enum::connection::DisconnectReason; #[non_exhaustive] #[derive(Debug)] pub enum Request { /** The sync request asks the EIS implementation to emit the 'done' event on the returned ei_callback object. Since requests are handled in-order and events are delivered in-order, this can be used as a synchronization point to ensure all previous requests and the resulting events have been handled. The object returned by this request will be destroyed by the EIS implementation after the callback is fired and as such the client must not attempt to use it after that point. The callback_data in the ei_callback.done event is always zero. Note that for a client to use this request it must announce support for the "ei_callback" interface in ei_handshake.interface_version. It is a protocol violation to request sync without having announced the "ei_callback" interface and the EIS implementation must disconnect the client. */ Sync { /** callback object for the sync request */ callback: super::callback::Callback, }, /** A request to the EIS implementation that this client should be disconnected. This is a courtesy request to allow the EIS implementation to distinquish between a client disconnecting on purpose and one disconnecting through the socket becoming invalid. Immediately after sending this request, the client may destroy the ei_connection object and it should close the socket. The EIS implementation will treat the connection as already disconnected on receipt and does not send the ei_connection.disconnect event in response to this request. */ Disconnect, } impl Request { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("sync"), 1 => Some("disconnect"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => { let callback = _bytes.read_arg()?; let version = _bytes.read_arg()?; Ok(Self::Sync { callback: _bytes.backend().new_peer_interface(callback, version)?, }) } 1 => Ok(Self::Disconnect), opcode => Err(wire::ParseError::InvalidOpcode("connection", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Sync { callback } => { args.push(callback.as_arg()); } Self::Disconnect => {} _ => unreachable!(), } args } } } pub use connection::Connection; /** Interface for ensuring a roundtrip to the EIS implementation. Clients can handle the 'done' event to get notified when the related request that created the ei_callback object is done. Note that for a client to receive objects of this type, it must announce support for this interface in ei_handshake.interface_version. */ pub mod callback { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Callback(pub(crate) crate::Object); impl Callback { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Callback {} impl wire::Interface for Callback { const NAME: &'static str = "ei_callback"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = false; type Incoming = Request; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::eis::Interface for Callback {} impl Callback { /** Notify the client when the related request is done. Immediately after this event the ei_callback object is destroyed by the EIS implementation and as such the client must not attempt to use it after that point. */ pub fn done(&self, callback_data: u64) -> () { let args = &[wire::Arg::Uint64(callback_data.into())]; self.0.request(0, args); self.0.backend_weak().remove_id(self.0.id()); () } } #[non_exhaustive] #[derive(Debug)] pub enum Request {} impl Request { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { opcode => Err(wire::ParseError::InvalidOpcode("callback", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { _ => unreachable!(), } args } } } pub use callback::Callback; /** Interface for ensuring a roundtrip to the client implementation. This interface is identical to ei_callback but is intended for the EIS implementation to enforce a roundtrip to the client. Note that for a client to receive objects of this type, it must announce support for this interface in ei_handshake.interface_version. */ pub mod pingpong { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Pingpong(pub(crate) crate::Object); impl Pingpong { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Pingpong {} impl wire::Interface for Pingpong { const NAME: &'static str = "ei_pingpong"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = false; type Incoming = Request; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::eis::Interface for Pingpong {} impl Pingpong {} #[non_exhaustive] #[derive(Debug)] pub enum Request { /** Notify the EIS implementation when the related event is done. Immediately after this request the ei_pingpong object is destroyed by the client and as such must not be used any further. */ Done { /** request-specific data for the callback */ callback_data: u64, }, } impl Request { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("done"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => { let callback_data = _bytes.read_arg()?; Ok(Self::Done { callback_data }) } opcode => Err(wire::ParseError::InvalidOpcode("pingpong", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Done { callback_data } => { args.push(callback_data.as_arg()); } _ => unreachable!(), } args } } } pub use pingpong::Pingpong; /** An ei_seat represents a set of input devices that logically belong together. In most cases only one seat is present and all input devices on that seat share the same pointer and keyboard focus. A seat has potential capabilities, a client is expected to bind to those capabilities. The EIS implementation then creates logical input devices based on the capabilities the client is interested in. Immediately after creation of the ei_seat object, the EIS implementation sends a burst of events with information about this seat. This burst of events is terminated by the ei_seat.done event. Note that for a client to receive objects of this type, it must announce support for this interface in ei_handshake.interface_version. */ pub mod seat { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Seat(pub(crate) crate::Object); impl Seat { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Seat {} impl wire::Interface for Seat { const NAME: &'static str = "ei_seat"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = false; type Incoming = Request; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::eis::Interface for Seat {} impl Seat { /** This seat has been removed and a client should release all associated resources. This ei_seat object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ pub fn destroyed(&self, serial: u32) -> () { let args = &[wire::Arg::Uint32(serial.into())]; self.0.request(0, args); self.0.backend_weak().remove_id(self.0.id()); () } /** The name of this seat, if any. This event is optional and sent once immediately after object creation. It is a protocol violation to send this event after the ei_seat.done event. */ pub fn name(&self, name: &str) -> () { let args = &[wire::Arg::String(name.into())]; self.0.request(1, args); () } /** A notification that this seat supports devices with the given interface. The interface is mapped to a bitmask by the EIS implementation. A client may then binary OR these bitmasks in ei_seat.bind. In response, the EIS implementation may then create device based on those bound capabilities. For example, an EIS implementation may map "ei_pointer" to 0x1, "ei_keyboard" to 0x4 and "ei_touchscreen" to 0x8. A client may then ei_seat.bind(0xc) to bind to keyboard and touchscreen but not pointer. Note that as shown in this example the set of masks may be sparse. The value of the mask is contant for the lifetime of the seat but may differ between seats. Note that seat capabilities only represent a mask of possible capabilities on devices in this seat. A capability that is not available on the seat cannot ever be available on any device in this seat. For example, a seat that only has the pointer and keyboard capabilities can never have a device with the touchscreen capability. It is up to the EIS implementation to decide how many (if any) devices with any given capability exist in this seat. Only interfaces that the client announced during ei_handshake.interface_version can be a seat capability. This event is sent multiple times - once per supported interface. The set of capabilities is constant for the lifetime of the seat. It is a protocol violation to send this event after the ei_seat.done event. */ pub fn capability(&self, mask: u64, interface: &str) -> () { let args = &[ wire::Arg::Uint64(mask.into()), wire::Arg::String(interface.into()), ]; self.0.request(2, args); () } /** Notification that the initial burst of events is complete and the client can set up this seat now. It is a protocol violation to send this event more than once. */ pub fn done(&self) -> () { let args = &[]; self.0.request(3, args); () } /** Notification that a new device has been added. This event is only sent if the client announced support for the "ei_device" interface in ei_handshake.interface_version. The interface version is equal or less to the client-supported version in ei_handshake.interface_version for the "ei_device" interface. */ pub fn device(&self, version: u32) -> (super::device::Device) { let device = self .0 .backend_weak() .new_object("ei_device".to_string(), version); let args = &[ wire::Arg::NewId(device.id().into()), wire::Arg::Uint32(version.into()), ]; self.0.request(4, args); (super::device::Device(device)) } } #[non_exhaustive] #[derive(Debug)] pub enum Request { /** Notification that the client is no longer interested in this seat. The EIS implementation will release any resources related to this seat and send the ei_seat.destroyed event once complete. Note that releasing a seat does not guarantee another seat becomes available. In other words, in most single-seat cases, releasing the seat means the connection becomes effectively inert. */ Release, /** Bind to the bitmask of capabilities given. The bitmask is zero or more of the masks representing an interface as provided in the ei_seat.capability event. See the ei_seat.capability event documentation for examples. Binding masks that are not supported in the ei_device's interface version is a client bug and may result in disconnection. A client may send this request multiple times to adjust the capabilities it is interested in. If previously-bound capabilities are dropped by the client, the EIS implementation may ei_device.remove devices that have these capabilities. */ Bind { /** bitmask of the capabilities */ capabilities: u64, }, } impl Request { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("release"), 1 => Some("bind"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => Ok(Self::Release), 1 => { let capabilities = _bytes.read_arg()?; Ok(Self::Bind { capabilities }) } opcode => Err(wire::ParseError::InvalidOpcode("seat", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Release => {} Self::Bind { capabilities } => { args.push(capabilities.as_arg()); } _ => unreachable!(), } args } } } pub use seat::Seat; /** An ei_device represents a single logical input devices. Like physical input devices an ei_device may have multiple capabilities and may e.g. function as pointer and keyboard. Depending on the ei_handshake.context_type, an ei_device can emulate events via client requests or receive events. It is a protocol violation to emulate certain events on a receiver device, or for the EIS implementation to send certain events to the device. See the individual request/event documentation for details. Note that for a client to receive objects of this type, it must announce support for this interface in ei_handshake.interface_version. */ pub mod device { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Device(pub(crate) crate::Object); impl Device { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Device {} impl wire::Interface for Device { const NAME: &'static str = "ei_device"; const VERSION: u32 = 2; const CLIENT_SIDE: bool = false; type Incoming = Request; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::eis::Interface for Device {} impl Device { /** This device has been removed and a client should release all associated resources. This ei_device object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ pub fn destroyed(&self, serial: u32) -> () { let args = &[wire::Arg::Uint32(serial.into())]; self.0.request(0, args); self.0.backend_weak().remove_id(self.0.id()); () } /** The name of this device, if any. This event is optional and sent once immediately after object creation. It is a protocol violation to send this event after the ei_device.done event. */ pub fn name(&self, name: &str) -> () { let args = &[wire::Arg::String(name.into())]; self.0.request(1, args); () } /** The device type, one of virtual or physical. Devices of type ei_device.device_type.physical are supported only clients of type ei_handshake.context_type.receiver. This event is sent once immediately after object creation. It is a protocol violation to send this event after the ei_device.done event. */ pub fn device_type(&self, device_type: DeviceType) -> () { let args = &[wire::Arg::Uint32(device_type.into())]; self.0.request(2, args); () } /** The device dimensions in mm. This event is optional and sent once immediately after object creation. This event is only sent for devices of ei_device.device_type.physical. It is a protocol violation to send this event after the ei_device.done event. */ pub fn dimensions(&self, width: u32, height: u32) -> () { let args = &[ wire::Arg::Uint32(width.into()), wire::Arg::Uint32(height.into()), ]; self.0.request(3, args); () } /** Notifies the client of one region. The number of regions is constant for a device and all regions are announced immediately after object creation. A region is rectangular and defined by an x/y offset and a width and a height. A region defines the area on an EIS desktop layout that is accessible by this device - this region may not be the full area of the desktop. Input events may only be sent for points within the regions. The use of regions is private to the EIS compositor and coordinates may not match the size of the actual desktop. For example, a compositor may set a 1920x1080 region to represent a 4K monitor and transparently map input events into the respective true pixels. Absolute devices may have different regions, it is up to the libei client to send events through the correct device to target the right pixel. For example, a dual-head setup my have two absolute devices, the first with a zero offset region spanning the left screen, the second with a nonzero offset spanning the right screen. The physical scale denotes a constant factor that needs to be applied to any relative movement on this region for that movement to match the same *physical* movement on another region. It is an EIS implementation bug to advertise the absolute pointer capability on a device_type.virtual device without advertising an ei_region for this device. This event is optional and sent immediately after object creation. Where a device has multiple regions, this event is sent once for each region. It is a protocol violation to send this event after the ei_device.done event. */ pub fn region( &self, offset_x: u32, offset_y: u32, width: u32, hight: u32, scale: f32, ) -> () { let args = &[ wire::Arg::Uint32(offset_x.into()), wire::Arg::Uint32(offset_y.into()), wire::Arg::Uint32(width.into()), wire::Arg::Uint32(hight.into()), wire::Arg::Float(scale.into()), ]; self.0.request(4, args); () } /** Notification that a new device has a sub-interface. This event may be sent for the - "ei_pointer" interface if the device has the ei_device.capabilities.pointer capability - "ei_pointer_absolute" interface if the device has the ei_device.capabilities.pointer_absolute capability - "ei_scroll" interface if the device has the ei_device.capabilities.scroll capability - "ei_button" interface if the device has the ei_device.capabilities.button capability - "ei_keyboard" interface if the device has the ei_device.capabilities.keyboard capability - "ei_touchscreen" interface if the device has the ei_device.capabilities.touchscreen capability The interface version is equal or less to the client-supported version in ei_handshake.interface_version for the respective interface. This event is optional and sent immediately after object creation and at most once per interface. It is a protocol violation to send this event after the ei_device.done event. */ pub fn interface( &self, version: u32, ) -> (InterfaceName) { let object = self .0 .backend_weak() .new_object(InterfaceName::NAME.to_string(), version); let args = &[ wire::Arg::NewId(object.id().into()), wire::Arg::String(InterfaceName::NAME), wire::Arg::Uint32(version.into()), ]; self.0.request(5, args); (object.downcast_unchecked()) } /** Notification that the initial burst of events is complete and the client can set up this device now. It is a protocol violation to send this event more than once per device. */ pub fn done(&self) -> () { let args = &[]; self.0.request(6, args); () } /** Notification that the device has been resumed by the EIS implementation and (depending on the ei_handshake.context_type) the client may request ei_device.start_emulating or the EIS implementation may ei_device.start_emulating events. It is a client bug to request emulation of events on a device that is not resumed. The EIS implementation may silently discard such events. A newly advertised device is in the ei_device.paused state. */ pub fn resumed(&self, serial: u32) -> () { let args = &[wire::Arg::Uint32(serial.into())]; self.0.request(7, args); () } /** Notification that the device has been paused by the EIS implementation and no futher events will be accepted on this device until it is resumed again. For devices of ei_device_setup.context_type sender, the client thus does not need to request ei_device.stop_emulating and may request ei_device.start_emulating after a subsequent ei_device.resumed. For devices of ei_device_setup.context_type receiver and where the EIS implementation did not send a ei_device.stop_emulating prior to this event, the device may send a ei_device.start_emulating event after a subsequent ei_device.resumed event. Pausing a device resets the logical state of the device to neutral. This includes: - any buttons or keys logically down are released - any modifiers logically down are released - any touches logically down are released It is a client bug to request emulation of events on a device that is not resumed. The EIS implementation may silently discard such events. A newly advertised device is in the ei_device.paused state. */ pub fn paused(&self, serial: u32) -> () { let args = &[wire::Arg::Uint32(serial.into())]; self.0.request(8, args); () } /** See the ei_device.start_emulating request for details. It is a protocol violation to send this event for a client of an ei_handshake.context_type other than receiver. */ pub fn start_emulating(&self, serial: u32, sequence: u32) -> () { let args = &[ wire::Arg::Uint32(serial.into()), wire::Arg::Uint32(sequence.into()), ]; self.0.request(9, args); () } /** See the ei_device.stop_emulating request for details. It is a protocol violation to send this event for a client of an ei_handshake.context_type other than receiver. */ pub fn stop_emulating(&self, serial: u32) -> () { let args = &[wire::Arg::Uint32(serial.into())]; self.0.request(10, args); () } /** See the ei_device.frame request for details. It is a protocol violation to send this event for a client of an ei_handshake.context_type other than receiver. */ pub fn frame(&self, serial: u32, timestamp: u64) -> () { let args = &[ wire::Arg::Uint32(serial.into()), wire::Arg::Uint64(timestamp.into()), ]; self.0.request(11, args); () } /** Notifies the client that the region specified in the next ei_device.region event is to be assigned the given mapping_id. This ID can be used by the client to identify an external resource that has a relationship with this region. For example the client may receive a data stream with the video data that this region represents. By attaching the same identifier to the data stream and this region the EIS implementation can inform the client that the video data stream and the region represent paired data. This event is optional and sent immediately after object creation but before the corresponding ei_device.region event. Where a device has multiple regions, this event may be sent zero or one time for each region. It is a protocol violation to send this event after the ei_device.done event or to send this event without a corresponding following ei_device.region event. */ pub fn region_mapping_id(&self, mapping_id: &str) -> () { let args = &[wire::Arg::String(mapping_id.into())]; self.0.request(12, args); () } } pub use crate::eiproto_enum::device::DeviceType; #[non_exhaustive] #[derive(Debug)] pub enum Request { /** Notification that the client is no longer interested in this device. Note that releasing a device does not guarantee another device becomes available. The EIS implementation will release any resources related to this device and send the ei_device.destroyed event once complete. */ Release, /** Notify the EIS implementation that the given device is about to start sending events. This should be seen more as a transactional boundary than a time-based boundary. The primary use-cases for this are to allow for setup on the EIS implementation side and/or UI updates to indicate that a device is sending events now and for out-of-band information to sync with a given event sequence. There is no actual requirement that events start immediately once emulation starts and there is no requirement that a client calls ei_device.stop_emulating after the most recent events. For example, in a remote desktop use-case the client would call ei_device.start_emulating once the remote desktop session starts (rather than when the device sends events) and ei_device.stop_emulating once the remote desktop session stops. The sequence number identifies this transaction between start/stop emulating. It must go up by at least 1 on each call to ei_device.start_emulating. Wraparound must be handled by the EIS implementation but callers must ensure that detection of wraparound is possible. It is a protocol violation to request ei_device.start_emulating after ei_device.start_emulating without an intermediate stop_emulating. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than sender. */ StartEmulating { /** the last serial sent by the EIS implementation */ last_serial: u32, /** sequence number to identify this emulation sequence */ sequence: u32, }, /** Notify the EIS implementation that the given device is no longer sending events. See ei_device.start_emulating for details. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than sender. */ StopEmulating { /** the last serial sent by the EIS implementation */ last_serial: u32, }, /** Generate a frame event to group the current set of events into a logical hardware event. This function must be called after one or more events on any of ei_pointer, ei_pointer_absolute, ei_scroll, ei_button, ei_keyboard or ei_touchscreen has been requested by the EIS implementation. The EIS implementation should not process changes to the device state until the ei_device.frame event. For example, pressing and releasing a key within the same frame is a logical noop. The given timestamp applies to all events in the current frame. The timestamp must be in microseconds of CLOCK_MONOTONIC. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than sender. */ Frame { /** the last serial sent by the EIS implementation */ last_serial: u32, /** timestamp in microseconds */ timestamp: u64, }, } impl Request { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("release"), 1 => Some("start_emulating"), 2 => Some("stop_emulating"), 3 => Some("frame"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => Ok(Self::Release), 1 => { let last_serial = _bytes.read_arg()?; let sequence = _bytes.read_arg()?; Ok(Self::StartEmulating { last_serial, sequence, }) } 2 => { let last_serial = _bytes.read_arg()?; Ok(Self::StopEmulating { last_serial }) } 3 => { let last_serial = _bytes.read_arg()?; let timestamp = _bytes.read_arg()?; Ok(Self::Frame { last_serial, timestamp, }) } opcode => Err(wire::ParseError::InvalidOpcode("device", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Release => {} Self::StartEmulating { last_serial, sequence, } => { args.push(last_serial.as_arg()); args.push(sequence.as_arg()); } Self::StopEmulating { last_serial } => { args.push(last_serial.as_arg()); } Self::Frame { last_serial, timestamp, } => { args.push(last_serial.as_arg()); args.push(timestamp.as_arg()); } _ => unreachable!(), } args } } } pub use device::Device; /** Interface for pointer motion requests and events. This interface is available on devices with the ei_device.capability pointer. This interface is only provided once per device and where a client requests ei_pointer.release the interface does not get re-initialized. An EIS implementation may adjust the behavior of the device (including removing the device) if the interface is releasd. Note that for a client to receive objects of this type, it must announce support for this interface in ei_handshake.interface_version. */ pub mod pointer { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Pointer(pub(crate) crate::Object); impl Pointer { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Pointer {} impl wire::Interface for Pointer { const NAME: &'static str = "ei_pointer"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = false; type Incoming = Request; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::eis::Interface for Pointer {} impl Pointer { /** This object has been removed and a client should release all associated resources. This object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ pub fn destroyed(&self, serial: u32) -> () { let args = &[wire::Arg::Uint32(serial.into())]; self.0.request(0, args); self.0.backend_weak().remove_id(self.0.id()); () } /** See the ei_pointer.motion_relative request for details. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than receiver. */ pub fn motion_relative(&self, x: f32, y: f32) -> () { let args = &[wire::Arg::Float(x.into()), wire::Arg::Float(y.into())]; self.0.request(1, args); () } } #[non_exhaustive] #[derive(Debug)] pub enum Request { /** Notification that the client is no longer interested in this pointer. The EIS implementation will release any resources related to this pointer and send the ei_pointer.destroyed event once complete. */ Release, /** Generate a relative motion event on this pointer. It is a client bug to send this request more than once within the same ei_device.frame. It is a client bug to send this request on a device without the ei_device.capabilities.pointer capability. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than sender. */ MotionRelative { /** the x movement in logical pixels */ x: f32, /** the y movement in logical pixels */ y: f32, }, } impl Request { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("release"), 1 => Some("motion_relative"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => Ok(Self::Release), 1 => { let x = _bytes.read_arg()?; let y = _bytes.read_arg()?; Ok(Self::MotionRelative { x, y }) } opcode => Err(wire::ParseError::InvalidOpcode("pointer", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Release => {} Self::MotionRelative { x, y } => { args.push(x.as_arg()); args.push(y.as_arg()); } _ => unreachable!(), } args } } } pub use pointer::Pointer; /** Interface for absolute pointer requests and events. This interface is available on devices with the ei_device.capability pointer_absolute. This interface is only provided once per device and where a client requests ei_pointer_absolute.release the interface does not get re-initialized. An EIS implementation may adjust the behavior of the device (including removing the device) if the interface is releasd. Note that for a client to receive objects of this type, it must announce support for this interface in ei_handshake.interface_version. */ pub mod pointer_absolute { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct PointerAbsolute(pub(crate) crate::Object); impl PointerAbsolute { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for PointerAbsolute {} impl wire::Interface for PointerAbsolute { const NAME: &'static str = "ei_pointer_absolute"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = false; type Incoming = Request; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::eis::Interface for PointerAbsolute {} impl PointerAbsolute { /** This object has been removed and a client should release all associated resources. This object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ pub fn destroyed(&self, serial: u32) -> () { let args = &[wire::Arg::Uint32(serial.into())]; self.0.request(0, args); self.0.backend_weak().remove_id(self.0.id()); () } /** See the ei_pointer_absolute.motion_absolute request for details. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than receiver. */ pub fn motion_absolute(&self, x: f32, y: f32) -> () { let args = &[wire::Arg::Float(x.into()), wire::Arg::Float(y.into())]; self.0.request(1, args); () } } #[non_exhaustive] #[derive(Debug)] pub enum Request { /** Notification that the client is no longer interested in this object. The EIS implementation will release any resources related to this object and send the ei_pointer_absolute.destroyed event once complete. */ Release, /** Generate an absolute motion event on this pointer. The x/y coordinates must be within the device's regions or the event is silently discarded. It is a client bug to send this request more than once within the same ei_device.frame. It is a client bug to send this request on a device without the ei_device.capabilities.pointer_absolute capability. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than sender. */ MotionAbsolute { /** the x position in logical pixels */ x: f32, /** the y position in logical pixels */ y: f32, }, } impl Request { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("release"), 1 => Some("motion_absolute"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => Ok(Self::Release), 1 => { let x = _bytes.read_arg()?; let y = _bytes.read_arg()?; Ok(Self::MotionAbsolute { x, y }) } opcode => Err(wire::ParseError::InvalidOpcode("pointer_absolute", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Release => {} Self::MotionAbsolute { x, y } => { args.push(x.as_arg()); args.push(y.as_arg()); } _ => unreachable!(), } args } } } pub use pointer_absolute::PointerAbsolute; /** Interface for scroll requests and events. This interface is available on devices with the ei_device.capability scroll. This interface is only provided once per device and where a client requests ei_scroll.release the interface does not get re-initialized. An EIS implementation may adjust the behavior of the device (including removing the device) if the interface is releasd. Note that for a client to receive objects of this type, it must announce support for this interface in ei_handshake.interface_version. */ pub mod scroll { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Scroll(pub(crate) crate::Object); impl Scroll { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Scroll {} impl wire::Interface for Scroll { const NAME: &'static str = "ei_scroll"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = false; type Incoming = Request; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::eis::Interface for Scroll {} impl Scroll { /** This object has been removed and a client should release all associated resources. This object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ pub fn destroyed(&self, serial: u32) -> () { let args = &[wire::Arg::Uint32(serial.into())]; self.0.request(0, args); self.0.backend_weak().remove_id(self.0.id()); () } /** See the ei_scroll.scroll request for details. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than receiver. */ pub fn scroll(&self, x: f32, y: f32) -> () { let args = &[wire::Arg::Float(x.into()), wire::Arg::Float(y.into())]; self.0.request(1, args); () } /** See the ei_scroll.scroll_discrete request for details. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than receiver. */ pub fn scroll_discrete(&self, x: i32, y: i32) -> () { let args = &[wire::Arg::Int32(x.into()), wire::Arg::Int32(y.into())]; self.0.request(2, args); () } /** See the ei_scroll.scroll_stop request for details. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than receiver. */ pub fn scroll_stop(&self, x: u32, y: u32, is_cancel: u32) -> () { let args = &[ wire::Arg::Uint32(x.into()), wire::Arg::Uint32(y.into()), wire::Arg::Uint32(is_cancel.into()), ]; self.0.request(3, args); () } } #[non_exhaustive] #[derive(Debug)] pub enum Request { /** Notification that the client is no longer interested in this object. The EIS implementation will release any resources related to this object and send the ei_scroll.destroyed event once complete. */ Release, /** Generate a a smooth (pixel-precise) scroll event on this pointer. Clients must not send ei_scroll.scroll_discrete events for the same event, the EIS implementation is responsible for emulation of discrete scroll events. It is a client bug to send this request more than once within the same ei_device.frame. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than sender. */ Scroll { /** the x movement in logical pixels */ x: f32, /** the y movement in logical pixels */ y: f32, }, /** Generate a a discrete (e.g. wheel) scroll event on this pointer. Clients must not send ei_scroll.scroll events for the same event, the EIS implementation is responsible for emulation of smooth scroll events. A discrete scroll event is based logical scroll units (equivalent to one mouse wheel click). The value for one scroll unit is 120, a fraction or multiple thereof represents a fraction or multiple of a wheel click. It is a client bug to send this request more than once within the same ei_device.frame. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than sender. */ ScrollDiscrete { /** the x movement in fractions or multiples of 120 */ x: i32, /** the y movement in fractions or multiples of 120 */ y: i32, }, /** Generate a a scroll stop or cancel event on this pointer. A scroll stop event notifies the EIS implementation that the interaction causing a scroll motion previously triggered with ei_scroll.scroll or ei_scroll.scroll_discrete has stopped. For example, if all fingers are lifted off a touchpad, two-finger scrolling has logically stopped. The EIS implementation may use this information to e.g. start kinetic scrolling previously based on the previous finger speed. If is_cancel is nonzero, the event represents a cancellation of the current interaction. This indicates that the interaction has stopped to the point where further (server-emulated) scroll events from this device are wrong. It is a client bug to send this request more than once within the same ei_device.frame. It is a client bug to send this request for an axis that had a a nonzero value in either ei_scroll.scroll or ei_scroll.scroll_discrete in the current frame. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than sender. */ ScrollStop { /** nonzero if this axis stopped scrolling */ x: u32, /** nonzero if this axis stopped scrolling */ y: u32, /** nonzero to indicate this is a cancel event */ is_cancel: u32, }, } impl Request { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("release"), 1 => Some("scroll"), 2 => Some("scroll_discrete"), 3 => Some("scroll_stop"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => Ok(Self::Release), 1 => { let x = _bytes.read_arg()?; let y = _bytes.read_arg()?; Ok(Self::Scroll { x, y }) } 2 => { let x = _bytes.read_arg()?; let y = _bytes.read_arg()?; Ok(Self::ScrollDiscrete { x, y }) } 3 => { let x = _bytes.read_arg()?; let y = _bytes.read_arg()?; let is_cancel = _bytes.read_arg()?; Ok(Self::ScrollStop { x, y, is_cancel }) } opcode => Err(wire::ParseError::InvalidOpcode("scroll", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Release => {} Self::Scroll { x, y } => { args.push(x.as_arg()); args.push(y.as_arg()); } Self::ScrollDiscrete { x, y } => { args.push(x.as_arg()); args.push(y.as_arg()); } Self::ScrollStop { x, y, is_cancel } => { args.push(x.as_arg()); args.push(y.as_arg()); args.push(is_cancel.as_arg()); } _ => unreachable!(), } args } } } pub use scroll::Scroll; /** Interface for button requests and events. This interface is available on devices with the ei_device.capability button. This interface is only provided once per device and where a client requests ei_button.release the interface does not get re-initialized. An EIS implementation may adjust the behavior of the device (including removing the device) if the interface is releasd. Note that for a client to receive objects of this type, it must announce support for this interface in ei_handshake.interface_version. */ pub mod button { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Button(pub(crate) crate::Object); impl Button { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Button {} impl wire::Interface for Button { const NAME: &'static str = "ei_button"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = false; type Incoming = Request; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::eis::Interface for Button {} impl Button { /** This pointer has been removed and a client should release all associated resources. This ei_scroll object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ pub fn destroyed(&self, serial: u32) -> () { let args = &[wire::Arg::Uint32(serial.into())]; self.0.request(0, args); self.0.backend_weak().remove_id(self.0.id()); () } /** See the ei_scroll.button request for details. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than receiver. It is an EIS implementation bug to send more than one button request for the same button within the same ei_device.frame. */ pub fn button(&self, button: u32, state: ButtonState) -> () { let args = &[ wire::Arg::Uint32(button.into()), wire::Arg::Uint32(state.into()), ]; self.0.request(1, args); () } } pub use crate::eiproto_enum::button::ButtonState; #[non_exhaustive] #[derive(Debug)] pub enum Request { /** Notification that the client is no longer interested in this object. The EIS implementation will release any resources related to this object and send the ei_button.destroyed event once complete. */ Release, /** Generate a button event on this pointer. The button codes must match the defines in linux/input-event-codes.h. It is a client bug to send more than one button request for the same button within the same ei_device.frame. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than sender. */ Button { /** button code */ button: u32, /** */ state: ButtonState, }, } impl Request { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("release"), 1 => Some("button"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => Ok(Self::Release), 1 => { let button = _bytes.read_arg()?; let state = _bytes.read_arg()?; Ok(Self::Button { button, state }) } opcode => Err(wire::ParseError::InvalidOpcode("button", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Release => {} Self::Button { button, state } => { args.push(button.as_arg()); args.push(state.as_arg()); } _ => unreachable!(), } args } } } pub use button::Button; /** Interface for keyboard requests and events. This interface is available on devices with the ei_device.capability keyboard. This interface is only provided once per device and where a client requests ei_keyboard.release the interface does not get re-initialized. An EIS implementation may adjust the behavior of the device (including removing the device) if the interface is releasd. Note that for a client to receive objects of this type, it must announce support for this interface in ei_handshake.interface_version. */ pub mod keyboard { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Keyboard(pub(crate) crate::Object); impl Keyboard { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Keyboard {} impl wire::Interface for Keyboard { const NAME: &'static str = "ei_keyboard"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = false; type Incoming = Request; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::eis::Interface for Keyboard {} impl Keyboard { /** This keyboard has been removed and a client should release all associated resources. This ei_keyboard object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ pub fn destroyed(&self, serial: u32) -> () { let args = &[wire::Arg::Uint32(serial.into())]; self.0.request(0, args); self.0.backend_weak().remove_id(self.0.id()); () } /** Notification that this device has a keymap. Future key events must be interpreted by the client according to this keymap. For clients of ei_handshake.context_type sender it is the client's responsibility to send the correct ei_keyboard.key keycodes to generate the expected keysym in the EIS implementation. The keymap is constant for the lifetime of the device. This event provides a file descriptor to the client which can be memory-mapped in read-only mode to provide a keyboard mapping description. The fd must be mapped with MAP_PRIVATE by the recipient, as MAP_SHARED may fail. This event is sent immediately after the ei_keyboard object is created and before the ei_device.done event. It is a protocol violation to send this event after the ei_device.done event. */ pub fn keymap( &self, keymap_type: KeymapType, size: u32, keymap: std::os::unix::io::BorrowedFd, ) -> () { let args = &[ wire::Arg::Uint32(keymap_type.into()), wire::Arg::Uint32(size.into()), wire::Arg::Fd(keymap.into()), ]; self.0.request(1, args); () } /** See the ei_keyboard.key request for details. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than receiver. It is a protocol violation to send a key down event in the same frame as a key up event for the same key in the same frame. */ pub fn key(&self, key: u32, state: KeyState) -> () { let args = &[ wire::Arg::Uint32(key.into()), wire::Arg::Uint32(state.into()), ]; self.0.request(2, args); () } /** Notification that the EIS implementation has changed modifier states on this device. Future ei_keyboard.key requests must take the new modifier state into account. A client must assume that all modifiers are lifted when it receives an ei_device.paused event. The EIS implementation must send this event after ei_device.resumed to notify the client of any nonzero modifier state. This event does not reqire an ei_device.frame and should be processed immediately by the client. This event is only sent for devices with an ei_keyboard.keymap. */ pub fn modifiers( &self, serial: u32, depressed: u32, locked: u32, latched: u32, group: u32, ) -> () { let args = &[ wire::Arg::Uint32(serial.into()), wire::Arg::Uint32(depressed.into()), wire::Arg::Uint32(locked.into()), wire::Arg::Uint32(latched.into()), wire::Arg::Uint32(group.into()), ]; self.0.request(3, args); () } } pub use crate::eiproto_enum::keyboard::KeyState; pub use crate::eiproto_enum::keyboard::KeymapType; #[non_exhaustive] #[derive(Debug)] pub enum Request { /** Notification that the client is no longer interested in this keyboard. The EIS implementation will release any resources related to this keyboard and send the ei_keyboard.destroyed event once complete. */ Release, /** Generate a key event on this keyboard. If the device has an ei_keyboard.keymap, the key code corresponds to that keymap. The key codes must match the defines in linux/input-event-codes.h. It is a client bug to send more than one key request for the same key within the same ei_device.frame. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than sender. */ Key { /** the key code */ key: u32, /** logical state of the key */ state: KeyState, }, } impl Request { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("release"), 1 => Some("key"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => Ok(Self::Release), 1 => { let key = _bytes.read_arg()?; let state = _bytes.read_arg()?; Ok(Self::Key { key, state }) } opcode => Err(wire::ParseError::InvalidOpcode("keyboard", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Release => {} Self::Key { key, state } => { args.push(key.as_arg()); args.push(state.as_arg()); } _ => unreachable!(), } args } } } pub use keyboard::Keyboard; /** Interface for touchscreen requests and events. This interface is available on devices with the ei_device.capability touchscreen. This interface is only provided once per device and where a client requests ei_touchscreen.release the interface does not get re-initialized. An EIS implementation may adjust the behavior of the device (including removing the device) if the interface is releasd. Note that for a client to receive objects of this type, it must announce support for this interface in ei_handshake.interface_version. */ pub mod touchscreen { use crate::wire; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Touchscreen(pub(crate) crate::Object); impl Touchscreen { pub fn version(&self) -> u32 { self.0.version() } } impl crate::private::Sealed for Touchscreen {} impl wire::Interface for Touchscreen { const NAME: &'static str = "ei_touchscreen"; const VERSION: u32 = 1; const CLIENT_SIDE: bool = false; type Incoming = Request; fn new_unchecked(object: crate::Object) -> Self { Self(object) } fn as_arg(&self) -> wire::Arg<'_> { self.0.as_arg() } } impl crate::eis::Interface for Touchscreen {} impl Touchscreen { /** This touch has been removed and a client should release all associated resources. This ei_touchscreen object will be destroyed by the EIS implementation immmediately after after this event is sent and as such the client must not attempt to use it after that point. */ pub fn destroyed(&self, serial: u32) -> () { let args = &[wire::Arg::Uint32(serial.into())]; self.0.request(0, args); self.0.backend_weak().remove_id(self.0.id()); () } /** See the ei_touchscreen.down request for details. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than receiver. It is a protocol violation to send a touch down in the same frame as a touch motion or touch up. */ pub fn down(&self, touchid: u32, x: f32, y: f32) -> () { let args = &[ wire::Arg::Uint32(touchid.into()), wire::Arg::Float(x.into()), wire::Arg::Float(y.into()), ]; self.0.request(1, args); () } /** See the ei_touchscreen.motion request for details. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than receiver. It is a protocol violation to send a touch motion in the same frame as a touch down or touch up. */ pub fn motion(&self, touchid: u32, x: f32, y: f32) -> () { let args = &[ wire::Arg::Uint32(touchid.into()), wire::Arg::Float(x.into()), wire::Arg::Float(y.into()), ]; self.0.request(2, args); () } /** See the ei_touchscreen.up request for details. It is a protocol violation to send this request for a client of an ei_handshake.context_type other than receiver. It is a protocol violation to send a touch up in the same frame as a touch motion or touch down. */ pub fn up(&self, touchid: u32) -> () { let args = &[wire::Arg::Uint32(touchid.into())]; self.0.request(3, args); () } } #[non_exhaustive] #[derive(Debug)] pub enum Request { /** Notification that the client is no longer interested in this touch. The EIS implementation will release any resources related to this touch and send the ei_touch.destroyed event once complete. */ Release, /** Notifies the EIS implementation about a new touch logically down at the given coordinates. The touchid is a unique id for this touch. Touchids may be re-used after ei_touchscreen.up. The x/y coordinates must be within the device's regions or the event and future ei_touchscreen.motion events with the same touchid are silently discarded. It is a protocol violation to send a touch down in the same frame as a touch motion or touch up. */ Down { /** a unique touch id to identify this touch */ touchid: u32, /** touch x coordinate in logical pixels */ x: f32, /** touch y coordinate in logical pixels */ y: f32, }, /** Notifies the EIS implementation about an existing touch changing position to the given coordinates. The touchid is the unique id for this touch previously sent with ei_touchscreen.down. The x/y coordinates must be within the device's regions or the event is silently discarded. It is a protocol violation to send a touch motion in the same frame as a touch down or touch up. */ Motion { /** a unique touch id to identify this touch */ touchid: u32, /** touch x coordinate in logical pixels */ x: f32, /** touch y coordinate in logical pixels */ y: f32, }, /** Notifies the EIS implementation about an existing touch being logically up. The touchid is the unique id for this touch previously sent with ei_touchscreen.down. The touchid may be re-used after this request. It is a protocol violation to send a touch up in the same frame as a touch motion or touch down. */ Up { /** a unique touch id to identify this touch */ touchid: u32, }, } impl Request { pub(super) fn op_name(operand: u32) -> Option<&'static str> { match operand { 0 => Some("release"), 1 => Some("down"), 2 => Some("motion"), 3 => Some("up"), _ => None, } } pub(super) fn parse( operand: u32, _bytes: &mut wire::ByteStream, ) -> Result { match operand { 0 => Ok(Self::Release), 1 => { let touchid = _bytes.read_arg()?; let x = _bytes.read_arg()?; let y = _bytes.read_arg()?; Ok(Self::Down { touchid, x, y }) } 2 => { let touchid = _bytes.read_arg()?; let x = _bytes.read_arg()?; let y = _bytes.read_arg()?; Ok(Self::Motion { touchid, x, y }) } 3 => { let touchid = _bytes.read_arg()?; Ok(Self::Up { touchid }) } opcode => Err(wire::ParseError::InvalidOpcode("touchscreen", opcode)), } } #[allow( unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns )] pub(super) fn args(&self) -> Vec> { use crate::{wire::OwnedArg, Interface}; let mut args = Vec::new(); match self { Self::Release => {} Self::Down { touchid, x, y } => { args.push(touchid.as_arg()); args.push(x.as_arg()); args.push(y.as_arg()); } Self::Motion { touchid, x, y } => { args.push(touchid.as_arg()); args.push(x.as_arg()); args.push(y.as_arg()); } Self::Up { touchid } => { args.push(touchid.as_arg()); } _ => unreachable!(), } args } } } pub use touchscreen::Touchscreen; #[non_exhaustive] #[derive(Debug)] pub enum Request { Handshake(handshake::Handshake, handshake::Request), Connection(connection::Connection, connection::Request), Callback(callback::Callback, callback::Request), Pingpong(pingpong::Pingpong, pingpong::Request), Seat(seat::Seat, seat::Request), Device(device::Device, device::Request), Pointer(pointer::Pointer, pointer::Request), PointerAbsolute(pointer_absolute::PointerAbsolute, pointer_absolute::Request), Scroll(scroll::Scroll, scroll::Request), Button(button::Button, button::Request), Keyboard(keyboard::Keyboard, keyboard::Request), Touchscreen(touchscreen::Touchscreen, touchscreen::Request), } impl Request { pub(crate) fn op_name(interface: &str, operand: u32) -> Option<&'static str> { match interface { "ei_handshake" => handshake::Request::op_name(operand), "ei_connection" => connection::Request::op_name(operand), "ei_callback" => callback::Request::op_name(operand), "ei_pingpong" => pingpong::Request::op_name(operand), "ei_seat" => seat::Request::op_name(operand), "ei_device" => device::Request::op_name(operand), "ei_pointer" => pointer::Request::op_name(operand), "ei_pointer_absolute" => pointer_absolute::Request::op_name(operand), "ei_scroll" => scroll::Request::op_name(operand), "ei_button" => button::Request::op_name(operand), "ei_keyboard" => keyboard::Request::op_name(operand), "ei_touchscreen" => touchscreen::Request::op_name(operand), _ => None, } } pub(crate) fn parse( object: crate::Object, operand: u32, bytes: &mut wire::ByteStream, ) -> Result { match object.interface() { "ei_handshake" => Ok(Self::Handshake( object.downcast_unchecked(), handshake::Request::parse(operand, bytes)?, )), "ei_connection" => Ok(Self::Connection( object.downcast_unchecked(), connection::Request::parse(operand, bytes)?, )), "ei_callback" => Ok(Self::Callback( object.downcast_unchecked(), callback::Request::parse(operand, bytes)?, )), "ei_pingpong" => Ok(Self::Pingpong( object.downcast_unchecked(), pingpong::Request::parse(operand, bytes)?, )), "ei_seat" => Ok(Self::Seat( object.downcast_unchecked(), seat::Request::parse(operand, bytes)?, )), "ei_device" => Ok(Self::Device( object.downcast_unchecked(), device::Request::parse(operand, bytes)?, )), "ei_pointer" => Ok(Self::Pointer( object.downcast_unchecked(), pointer::Request::parse(operand, bytes)?, )), "ei_pointer_absolute" => Ok(Self::PointerAbsolute( object.downcast_unchecked(), pointer_absolute::Request::parse(operand, bytes)?, )), "ei_scroll" => Ok(Self::Scroll( object.downcast_unchecked(), scroll::Request::parse(operand, bytes)?, )), "ei_button" => Ok(Self::Button( object.downcast_unchecked(), button::Request::parse(operand, bytes)?, )), "ei_keyboard" => Ok(Self::Keyboard( object.downcast_unchecked(), keyboard::Request::parse(operand, bytes)?, )), "ei_touchscreen" => Ok(Self::Touchscreen( object.downcast_unchecked(), touchscreen::Request::parse(operand, bytes)?, )), intr => Err(wire::ParseError::InvalidInterface(intr.to_owned())), } } } impl wire::MessageEnum for Request { fn args(&self) -> Vec> { match self { Self::Handshake(_, x) => x.args(), Self::Connection(_, x) => x.args(), Self::Callback(_, x) => x.args(), Self::Pingpong(_, x) => x.args(), Self::Seat(_, x) => x.args(), Self::Device(_, x) => x.args(), Self::Pointer(_, x) => x.args(), Self::PointerAbsolute(_, x) => x.args(), Self::Scroll(_, x) => x.args(), Self::Button(_, x) => x.args(), Self::Keyboard(_, x) => x.args(), Self::Touchscreen(_, x) => x.args(), } } } reis-0.2.0/src/eiproto_enum.rs000064400000000000000000000231571046102023000144370ustar 00000000000000#![allow( unused_imports, unused_parens, clippy::useless_conversion, clippy::double_parens, clippy::match_single_binding, clippy::unused_unit )] // GENERATED FILE pub(crate) mod handshake { use crate::wire; /** This enum denotes context types for the libei context. A context type of receiver is a libei context receiving events from the EIS implementation. A context type of sender is a libei context sending events to the EIS implementation. */ #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum ContextType { /** this client receives events from the EIS implementation */ Receiver = 1, /** this client sends events to the EIS implementation */ Sender = 2, } impl From for u32 { fn from(value: ContextType) -> u32 { value as u32 } } impl wire::OwnedArg for ContextType { fn parse(buf: &mut wire::ByteStream) -> Result { match u32::parse(buf)? { 1 => Ok(Self::Receiver), 2 => Ok(Self::Sender), variant => Err(wire::ParseError::InvalidVariant("ContextType", variant)), } } fn as_arg(&self) -> wire::Arg<'_> { wire::Arg::Uint32(*self as u32) } fn enum_name(&self) -> Option<(&'static str, &'static str)> { Some(( "context_type", match self { Self::Receiver => "receiver", Self::Sender => "sender", }, )) } } } pub(crate) mod connection { use crate::wire; /** A reason why a client was disconnected. This enum is intended to provide information to the client on whether it was disconnected as part of normal operations or as result of an error on either the client or EIS implementation side. A nonzero value describes an error, with the generic value "error" (1) reserved as fallback. This enum may be extended in the future, clients must be able to handle values that are not in their supported version of this enum. */ #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum DisconnectReason { /** client was purposely disconnected */ Disconnected = 0, /** an error caused the disconnection */ Error = 1, /** sender/receiver client sent request for receiver/sender mode */ Mode = 2, /** client committed a protocol violation */ Protocol = 3, /** client sent an invalid value */ Value = 4, /** error on the transport layer */ Transport = 5, } impl From for u32 { fn from(value: DisconnectReason) -> u32 { value as u32 } } impl wire::OwnedArg for DisconnectReason { fn parse(buf: &mut wire::ByteStream) -> Result { match u32::parse(buf)? { 0 => Ok(Self::Disconnected), 1 => Ok(Self::Error), 2 => Ok(Self::Mode), 3 => Ok(Self::Protocol), 4 => Ok(Self::Value), 5 => Ok(Self::Transport), variant => Err(wire::ParseError::InvalidVariant( "DisconnectReason", variant, )), } } fn as_arg(&self) -> wire::Arg<'_> { wire::Arg::Uint32(*self as u32) } fn enum_name(&self) -> Option<(&'static str, &'static str)> { Some(( "disconnect_reason", match self { Self::Disconnected => "disconnected", Self::Error => "error", Self::Mode => "mode", Self::Protocol => "protocol", Self::Value => "value", Self::Transport => "transport", }, )) } } } pub(crate) mod callback { use crate::wire; } pub(crate) mod pingpong { use crate::wire; } pub(crate) mod seat { use crate::wire; } pub(crate) mod device { use crate::wire; /** If the device type is `ei_device.device_type.virtual`, the device is a virtual device representing input as applied on the EIS implementation's screen. A relative virtual device generates input events in logical pixels, an absolute virtual device generates input events in logical pixels on one of the device's regions. Virtual devices do not have a `ei_device.dimension` but it may have an `ei_device.region.` If the device type is `ei_device.device_type.physical`, the device is a representation of a physical device as if connected to the EIS implementation's host computer. A relative physical device generates input events in mm, an absolute physical device generates input events in mm within the device's specified physical size. Physical devices do not have regions and no `ei_device.region` events are sent for such devices. */ #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum DeviceType { /** a virtual device */ Virtual = 1, /** representation of a physical device */ Physical = 2, } impl From for u32 { fn from(value: DeviceType) -> u32 { value as u32 } } impl wire::OwnedArg for DeviceType { fn parse(buf: &mut wire::ByteStream) -> Result { match u32::parse(buf)? { 1 => Ok(Self::Virtual), 2 => Ok(Self::Physical), variant => Err(wire::ParseError::InvalidVariant("DeviceType", variant)), } } fn as_arg(&self) -> wire::Arg<'_> { wire::Arg::Uint32(*self as u32) } fn enum_name(&self) -> Option<(&'static str, &'static str)> { Some(( "device_type", match self { Self::Virtual => "virtual", Self::Physical => "physical", }, )) } } } pub(crate) mod pointer { use crate::wire; } pub(crate) mod pointer_absolute { use crate::wire; } pub(crate) mod scroll { use crate::wire; } pub(crate) mod button { use crate::wire; /** The logical state of a button. */ #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum ButtonState { /** the button is logically up */ Released = 0, /** the button is logically down */ Press = 1, } impl From for u32 { fn from(value: ButtonState) -> u32 { value as u32 } } impl wire::OwnedArg for ButtonState { fn parse(buf: &mut wire::ByteStream) -> Result { match u32::parse(buf)? { 0 => Ok(Self::Released), 1 => Ok(Self::Press), variant => Err(wire::ParseError::InvalidVariant("ButtonState", variant)), } } fn as_arg(&self) -> wire::Arg<'_> { wire::Arg::Uint32(*self as u32) } fn enum_name(&self) -> Option<(&'static str, &'static str)> { Some(( "button_state", match self { Self::Released => "released", Self::Press => "press", }, )) } } } pub(crate) mod keyboard { use crate::wire; /** The logical state of a key. */ #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum KeyState { /** the key is logically up */ Released = 0, /** the key is logically down */ Press = 1, } impl From for u32 { fn from(value: KeyState) -> u32 { value as u32 } } impl wire::OwnedArg for KeyState { fn parse(buf: &mut wire::ByteStream) -> Result { match u32::parse(buf)? { 0 => Ok(Self::Released), 1 => Ok(Self::Press), variant => Err(wire::ParseError::InvalidVariant("KeyState", variant)), } } fn as_arg(&self) -> wire::Arg<'_> { wire::Arg::Uint32(*self as u32) } fn enum_name(&self) -> Option<(&'static str, &'static str)> { Some(( "key_state", match self { Self::Released => "released", Self::Press => "press", }, )) } } /** The keymap type describes how the keymap in the `ei_keyboard.keymap` event should be parsed. */ #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum KeymapType { /** a libxkbcommon-compatible XKB keymap */ Xkb = 1, } impl From for u32 { fn from(value: KeymapType) -> u32 { value as u32 } } impl wire::OwnedArg for KeymapType { fn parse(buf: &mut wire::ByteStream) -> Result { match u32::parse(buf)? { 1 => Ok(Self::Xkb), variant => Err(wire::ParseError::InvalidVariant("KeymapType", variant)), } } fn as_arg(&self) -> wire::Arg<'_> { wire::Arg::Uint32(*self as u32) } fn enum_name(&self) -> Option<(&'static str, &'static str)> { Some(( "keymap_type", match self { Self::Xkb => "xkb", }, )) } } } pub(crate) mod touchscreen { use crate::wire; } reis-0.2.0/src/eiproto_enum.rs.jinja000064400000000000000000000031321046102023000155200ustar 00000000000000{# ei-scanner jinja template, for `` #} #![allow(unused_imports, unused_parens, clippy::useless_conversion, clippy::double_parens, clippy::match_single_binding, clippy::unused_unit)] // GENERATED FILE {% for interface in interfaces %} pub(crate) mod {{interface.plainname}} { use crate::wire; {% for enum in interface.enums %} /** {{enum.description.text|ei_escape_names}} */ #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum {{enum.camel_name}} { {% for entry in enum.entries %} /** {{entry.summary}} */ {{entry.name|camel}} = {{entry.value}}, {% endfor %} } impl From<{{enum.camel_name}}> for u32 { fn from(value: {{enum.camel_name}}) -> u32 { value as u32 } } impl wire::OwnedArg for {{enum.camel_name}} { fn parse(buf: &mut wire::ByteStream) -> Result { match u32::parse(buf)? { {% for entry in enum.entries %} {{entry.value}} => Ok(Self::{{entry.name|camel}}), {% endfor %} variant => Err(wire::ParseError::InvalidVariant("{{enum.camel_name}}", variant)), } } fn as_arg(&self) -> wire::Arg<'_> { wire::Arg::Uint32(*self as u32) } fn enum_name(&self) -> Option<(&'static str, &'static str)> { Some(("{{enum.name}}", match self { {% for entry in enum.entries %} Self::{{entry.name|camel}} => "{{entry.name}}", {% endfor %} } )) } } {% endfor %} } {% endfor %} reis-0.2.0/src/eis.rs000064400000000000000000000062261046102023000125100ustar 00000000000000//! Server-side EI protocol. //! //! Use [Listener] to create a socket, listening for clients creating a new //! [Context]. use std::{ env, io, os::unix::{ io::{AsFd, AsRawFd, BorrowedFd, RawFd}, net::{UnixListener, UnixStream}, }, path::{Path, PathBuf}, }; use crate::{util, wire::Backend, PendingRequestResult}; // Re-export generate bindings pub use crate::eiproto_eis::*; pub struct Listener { listener: util::UnlinkOnDrop, _lock: Option, } impl Listener { // TODO Use a lock here pub fn bind(path: &Path) -> io::Result { Self::bind_inner(PathBuf::from(path), None) } // XXX result type? // Error if XDG_RUNTIME_DIR not set? pub fn bind_auto() -> io::Result> { let xdg_dir = if let Some(var) = env::var_os("XDG_RUNTIME_DIR") { PathBuf::from(var) } else { return Ok(None); }; #[allow(clippy::never_loop)] // On 1.65 not recognzing continue in let-else for i in 1..33 { let lock_path = xdg_dir.join(format!("eis-{i}.lock")); let Some(lock_file) = util::LockFile::new(lock_path)? else { // Already locked, continue to next number continue; }; let sock_path = xdg_dir.join(format!("eis-{i}")); return Self::bind_inner(sock_path, Some(lock_file)).map(Some); } Ok(None) } fn bind_inner(path: PathBuf, lock: Option) -> io::Result { let listener = UnixListener::bind(&path)?; listener.set_nonblocking(true)?; let listener = util::UnlinkOnDrop::new(listener, path); Ok(Self { listener, _lock: lock, }) } pub fn accept(&self) -> io::Result> { match self.listener.accept() { Ok((socket, _)) => Ok(Some(Context::new(socket)?)), Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(None), Err(e) => Err(e), } } } impl AsFd for Listener { fn as_fd(&self) -> BorrowedFd { self.listener.as_fd() } } impl AsRawFd for Listener { fn as_raw_fd(&self) -> RawFd { self.listener.as_raw_fd() } } #[derive(Clone, Debug)] pub struct Context(pub(crate) Backend); impl AsFd for Context { fn as_fd(&self) -> BorrowedFd { self.0.as_fd() } } impl AsRawFd for Context { fn as_raw_fd(&self) -> RawFd { self.0.as_fd().as_raw_fd() } } impl Context { pub(crate) fn new(socket: UnixStream) -> io::Result { Ok(Self(Backend::new(socket, false)?)) } /// Read any pending data on socket into buffer pub fn read(&self) -> io::Result { self.0.read() } // XXX iterator pub fn pending_request(&self) -> Option> { self.0.pending(Request::parse) } pub fn handshake(&self) -> handshake::Handshake { self.0.object_for_id(0).unwrap().downcast_unchecked() } pub fn flush(&self) -> rustix::io::Result<()> { self.0.flush() } } #[doc(hidden)] pub trait Interface: crate::wire::Interface {} reis-0.2.0/src/event.rs000064400000000000000000001032551046102023000130510ustar 00000000000000// libei has user_data for seat, device, etc. Do we need that? // Want clarification in ei docs about events before/after done in some places? // XXX device_for_interface may be empty if something is sent before done. Can any event be sent // then? // TODO: track last serial? Including destroyed event. // - look at how libei handles it. // This uses exhastive matching, so it will have to be updated when generated API is updated for // any new events. // WIP disconnected event? EOF? #![allow(clippy::derive_partial_eq_without_eq)] use crate::{ei, util, Interface, Object, ParseError, PendingRequestResult}; use std::{ collections::{HashMap, VecDeque}, fmt, io, os::unix::io::OwnedFd, sync::Arc, }; // struct ReceiverStream(EiEventStream, ()); #[derive(Debug)] pub enum Error { DeviceEventBeforeDone, DeviceSetupEventAfterDone, SeatSetupEventAfterDone, SeatEventBeforeDone, NoDeviceType, UnexpectedHandshakeEvent, } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::DeviceEventBeforeDone => write!(f, "device event before done"), Self::DeviceSetupEventAfterDone => write!(f, "device setup event after done"), Self::SeatSetupEventAfterDone => write!(f, "seat setup event after done"), Self::SeatEventBeforeDone => write!(f, "seat event before done"), Self::NoDeviceType => write!(f, "no device"), Self::UnexpectedHandshakeEvent => write!(f, "unexpected handshake event"), } } } impl std::error::Error for Error {} pub struct EiEventConverter { pending_seats: HashMap, seats: HashMap, pending_devices: HashMap, devices: HashMap, device_for_interface: HashMap, events: VecDeque, pending_events: VecDeque, serial: u32, } impl EiEventConverter { pub fn new(serial: u32) -> Self { Self { pending_seats: HashMap::new(), seats: HashMap::new(), pending_devices: HashMap::new(), devices: HashMap::new(), device_for_interface: HashMap::new(), events: VecDeque::new(), pending_events: VecDeque::new(), serial, } } // Based on behavior of `queue_event` in libei fn queue_event(&mut self, mut event: EiEvent) { if event.time_mut().is_some() { self.pending_events.push_back(event); } else if let EiEvent::Frame(Frame { time, .. }) = &event { if self.pending_events.is_empty() { return; } for mut pending_event in self.pending_events.drain(..) { *pending_event.time_mut().unwrap() = *time; self.events.push_back(pending_event); } self.events.push_back(event); } else { // TODO: If a device event, queue a frame if anything is pending self.events.push_back(event); } } pub fn handle_event(&mut self, event: ei::Event) -> Result<(), Error> { match event { ei::Event::Handshake(_handshake, _event) => { return Err(Error::UnexpectedHandshakeEvent); } ei::Event::Connection(_connection, event) => match event { ei::connection::Event::Seat { seat } => { self.pending_seats.insert( seat.clone(), SeatInner { seat, name: None, capabilities: HashMap::new(), }, ); } ei::connection::Event::Ping { ping } => { ping.done(0); if let Some(backend) = ping.0.backend() { // XXX Error? let _ = backend.flush(); } } ei::connection::Event::Disconnected { last_serial, reason, explanation, } => { self.queue_event(EiEvent::Disconnected(Disconnected { last_serial, reason, explanation, })); } ei::connection::Event::InvalidObject { last_serial: _, invalid_id: _, } => { // TODO } }, ei::Event::Callback(_callback, event) => match event { ei::callback::Event::Done { callback_data: _ } => { // TODO } }, ei::Event::Pingpong(_ping_pong, event) => match event {}, ei::Event::Seat(seat, event) => match event { ei::seat::Event::Name { name } => { let seat = self .pending_seats .get_mut(&seat) .ok_or(Error::SeatSetupEventAfterDone)?; seat.name = Some(name); } ei::seat::Event::Capability { mask, interface } => { let seat = self .pending_seats .get_mut(&seat) .ok_or(Error::SeatSetupEventAfterDone)?; seat.capabilities.insert(interface, mask); } ei::seat::Event::Done => { let seat = self .pending_seats .remove(&seat) .ok_or(Error::SeatSetupEventAfterDone)?; let seat = Seat(Arc::new(seat)); self.seats.insert(seat.0.seat.clone(), seat.clone()); self.queue_event(EiEvent::SeatAdded(SeatAdded { seat })); } ei::seat::Event::Device { device } => { let seat = self .seats .get_mut(&seat) .ok_or(Error::SeatEventBeforeDone)?; self.pending_devices.insert( device.clone(), DeviceInner { device, seat: seat.clone(), name: None, device_type: None, interfaces: HashMap::new(), dimensions: None, regions: Vec::new(), next_region_mapping_id: None, keymap: None, }, ); } ei::seat::Event::Destroyed { serial } => { self.serial = serial; self.pending_seats.remove(&seat); if let Some(seat) = self.seats.remove(&seat) { self.queue_event(EiEvent::SeatRemoved(SeatRemoved { seat })); } } }, ei::Event::Device(device, event) => match event { ei::device::Event::Name { name } => { let device = self .pending_devices .get_mut(&device) .ok_or(Error::DeviceSetupEventAfterDone)?; device.name = Some(name); } ei::device::Event::DeviceType { device_type } => { let device = self .pending_devices .get_mut(&device) .ok_or(Error::DeviceSetupEventAfterDone)?; device.device_type = Some(device_type); } ei::device::Event::Interface { object } => { let device = self .pending_devices .get_mut(&device) .ok_or(Error::DeviceSetupEventAfterDone)?; device .interfaces .insert(object.interface().to_string(), object); } ei::device::Event::Dimensions { width, height } => { let device = self .pending_devices .get_mut(&device) .ok_or(Error::DeviceSetupEventAfterDone)?; device.dimensions = Some((width, height)); } ei::device::Event::Region { offset_x, offset_y, width, hight, scale, } => { let device = self .pending_devices .get_mut(&device) .ok_or(Error::DeviceSetupEventAfterDone)?; device.regions.push(Region { x: offset_x, y: offset_y, width, height: hight, scale, mapping_id: device.next_region_mapping_id.clone(), }); } ei::device::Event::Done => { let device = self .pending_devices .remove(&device) .ok_or(Error::DeviceSetupEventAfterDone)?; if device.device_type.is_none() { return Err(Error::NoDeviceType); } let device = Device(Arc::new(device)); self.devices.insert(device.0.device.clone(), device.clone()); for i in device.0.interfaces.values() { self.device_for_interface.insert(i.clone(), device.clone()); } self.queue_event(EiEvent::DeviceAdded(DeviceAdded { device })); } ei::device::Event::Resumed { serial } => { self.serial = serial; let device = self .devices .get(&device) .ok_or(Error::DeviceEventBeforeDone)?; self.queue_event(EiEvent::DeviceResumed(DeviceResumed { device: device.clone(), serial, })); } ei::device::Event::Paused { serial } => { self.serial = serial; let device = self .devices .get(&device) .ok_or(Error::DeviceEventBeforeDone)?; self.queue_event(EiEvent::DevicePaused(DevicePaused { device: device.clone(), serial, })); } ei::device::Event::StartEmulating { serial, sequence } => { self.serial = serial; let device = self .devices .get(&device) .ok_or(Error::DeviceEventBeforeDone)?; self.queue_event(EiEvent::DeviceStartEmulating(DeviceStartEmulating { device: device.clone(), serial, sequence, })); } ei::device::Event::StopEmulating { serial } => { self.serial = serial; let device = self .devices .get(&device) .ok_or(Error::DeviceEventBeforeDone)?; self.queue_event(EiEvent::DeviceStopEmulating(DeviceStopEmulating { device: device.clone(), serial, })); } ei::device::Event::RegionMappingId { mapping_id } => { let device = self .pending_devices .get_mut(&device) .ok_or(Error::DeviceEventBeforeDone)?; device.next_region_mapping_id = Some(mapping_id); } ei::device::Event::Frame { serial, timestamp } => { self.serial = serial; let device = self .devices .get(&device) .ok_or(Error::DeviceEventBeforeDone)?; self.queue_event(EiEvent::Frame(Frame { device: device.clone(), serial, time: timestamp, })); } ei::device::Event::Destroyed { serial } => { self.serial = serial; self.pending_devices.remove(&device); if let Some(device) = self.devices.remove(&device) { self.queue_event(EiEvent::DeviceRemoved(DeviceRemoved { device })); } } }, ei::Event::Keyboard(keyboard, event) => match event { ei::keyboard::Event::Keymap { keymap_type, size, keymap, } => { let device = self .pending_devices .values_mut() .find(|i| i.interfaces.values().any(|j| j == &keyboard.0)) .ok_or(Error::DeviceSetupEventAfterDone)?; device.keymap = Some(Keymap { type_: keymap_type, size, fd: keymap, }); } ei::keyboard::Event::Key { key, state } => { let device = self .device_for_interface .get(&keyboard.0) .ok_or(Error::DeviceEventBeforeDone)?; self.queue_event(EiEvent::KeyboardKey(KeyboardKey { device: device.clone(), time: 0, key, state, })); } ei::keyboard::Event::Modifiers { serial, depressed, locked, latched, group, } => { self.serial = serial; let device = self .device_for_interface .get(&keyboard.0) .ok_or(Error::DeviceEventBeforeDone)?; self.queue_event(EiEvent::KeyboardModifiers(KeyboardModifiers { device: device.clone(), serial, depressed, locked, latched, group, })); } ei::keyboard::Event::Destroyed { serial } => { self.serial = serial; // TODO does interface need to be removed from `Device`? self.device_for_interface.remove(&keyboard.0); } }, ei::Event::Pointer(pointer, event) => { let device = self .device_for_interface .get(&pointer.0) .ok_or(Error::DeviceEventBeforeDone)?; match event { ei::pointer::Event::MotionRelative { x, y } => { self.queue_event(EiEvent::PointerMotion(PointerMotion { device: device.clone(), time: 0, dx: x, dy: y, })); } ei::pointer::Event::Destroyed { serial } => { self.serial = serial; // TODO does interface need to be removed from `Device`? self.device_for_interface.remove(&pointer.0); } } } ei::Event::PointerAbsolute(pointer_absolute, event) => { let device = self .device_for_interface .get(&pointer_absolute.0) .ok_or(Error::DeviceEventBeforeDone)?; match event { ei::pointer_absolute::Event::MotionAbsolute { x, y } => { self.queue_event(EiEvent::PointerMotionAbsolute(PointerMotionAbsolute { device: device.clone(), time: 0, dx_absolute: x, dy_absolute: y, })); } ei::pointer_absolute::Event::Destroyed { serial } => { self.serial = serial; // TODO does interface need to be removed from `Device`? self.device_for_interface.remove(&pointer_absolute.0); } } } ei::Event::Scroll(scroll, event) => { let device = self .device_for_interface .get(&scroll.0) .ok_or(Error::DeviceEventBeforeDone)?; match event { ei::scroll::Event::Scroll { x, y } => { self.queue_event(EiEvent::ScrollDelta(ScrollDelta { device: device.clone(), time: 0, dx: x, dy: y, })); } ei::scroll::Event::ScrollDiscrete { x, y } => { self.queue_event(EiEvent::ScrollDiscrete(ScrollDiscrete { device: device.clone(), time: 0, discrete_dx: x, discrete_dy: y, })); } ei::scroll::Event::ScrollStop { x, y, is_cancel } => { if is_cancel != 0 { self.queue_event(EiEvent::ScrollCancel(ScrollCancel { device: device.clone(), time: 0, x: x != 0, y: y != 0, })); } else { self.queue_event(EiEvent::ScrollStop(ScrollStop { device: device.clone(), time: 0, x: x != 0, y: y != 0, })); } } ei::scroll::Event::Destroyed { serial } => { self.serial = serial; // TODO does interface need to be removed from `Device`? self.device_for_interface.remove(&scroll.0); } } } ei::Event::Button(button, event) => { let device = self .device_for_interface .get(&button.0) .ok_or(Error::DeviceEventBeforeDone)?; match event { ei::button::Event::Button { button, state } => { self.queue_event(EiEvent::Button(Button { device: device.clone(), time: 0, button, state, })); } ei::button::Event::Destroyed { serial } => { self.serial = serial; // TODO does interface need to be removed from `Device`? self.device_for_interface.remove(&button.0); } } } ei::Event::Touchscreen(touchscreen, event) => { let device = self .device_for_interface .get(&touchscreen.0) .ok_or(Error::DeviceEventBeforeDone)?; match event { ei::touchscreen::Event::Down { touchid, x, y } => { self.queue_event(EiEvent::TouchDown(TouchDown { device: device.clone(), time: 0, touch_id: touchid, x, y, })); } ei::touchscreen::Event::Motion { touchid, x, y } => { self.queue_event(EiEvent::TouchMotion(TouchMotion { device: device.clone(), time: 0, touch_id: touchid, x, y, })); } ei::touchscreen::Event::Up { touchid } => { self.queue_event(EiEvent::TouchUp(TouchUp { device: device.clone(), time: 0, touch_id: touchid, })); } ei::touchscreen::Event::Destroyed { serial } => { self.serial = serial; // TODO does interface need to be removed from `Device`? self.device_for_interface.remove(&touchscreen.0); } } } } Ok(()) } pub fn next_event(&mut self) -> Option { self.events.pop_front() } pub fn serial(&self) -> u32 { self.serial } } #[derive(Debug)] pub struct Region { pub x: u32, pub y: u32, pub width: u32, pub height: u32, pub scale: f32, pub mapping_id: Option, } #[derive(Debug)] pub struct Keymap { pub fd: OwnedFd, pub size: u32, pub type_: ei::keyboard::KeymapType, } // bitflags? #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[repr(u64)] pub enum DeviceCapability { Pointer, PointerAbsolute, Keyboard, Touch, Scroll, Button, } impl DeviceCapability { pub(crate) fn name(self) -> &'static str { match self { DeviceCapability::Pointer => ei::Pointer::NAME, DeviceCapability::PointerAbsolute => ei::PointerAbsolute::NAME, DeviceCapability::Keyboard => ei::Keyboard::NAME, DeviceCapability::Touch => ei::Touchscreen::NAME, DeviceCapability::Scroll => ei::Scroll::NAME, DeviceCapability::Button => ei::Button::NAME, } } } struct SeatInner { seat: ei::Seat, name: Option, capabilities: HashMap, } #[derive(Clone)] pub struct Seat(Arc); impl fmt::Debug for Seat { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(name) = self.name() { write!(f, "Seat(\"{}\")", name) } else { write!(f, "Seat(None)") } } } impl PartialEq for Seat { fn eq(&self, rhs: &Seat) -> bool { Arc::ptr_eq(&self.0, &rhs.0) } } impl Eq for Seat {} impl std::hash::Hash for Seat { fn hash(&self, state: &mut H) { self.0.seat.0.id().hash(state); } } impl Seat { pub fn name(&self) -> Option<&str> { self.0.name.as_deref() } // TODO has_capability pub fn bind_capabilities(&self, capabilities: &[DeviceCapability]) { let mut caps = 0; for i in capabilities { if let Some(value) = self.0.capabilities.get(i.name()) { caps |= value; } } self.0.seat.bind(caps); } // TODO: mirror C API more? // fn unbind_capabilities() {} } struct DeviceInner { device: ei::Device, seat: Seat, name: Option, device_type: Option, interfaces: HashMap, dimensions: Option<(u32, u32)>, regions: Vec, // Only used before `done` next_region_mapping_id: Option, // Only defined device with `ei_keyboard` interface keymap: Option, } #[derive(Clone)] pub struct Device(Arc); impl fmt::Debug for Device { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(name) = self.name() { write!(f, "Device(\"{}\")", name) } else { write!(f, "Device(None)") } } } impl Device { pub fn seat(&self) -> &Seat { &self.0.seat } pub fn device(&self) -> &ei::Device { &self.0.device } pub fn name(&self) -> Option<&str> { self.0.name.as_deref() } pub fn device_type(&self) -> ei::device::DeviceType { self.0.device_type.unwrap() } pub fn dimensions(&self) -> Option<(u32, u32)> { self.0.dimensions } pub fn regions(&self) -> &[Region] { &self.0.regions } pub fn keymap(&self) -> Option<&Keymap> { self.0.keymap.as_ref() } pub fn interface(&self) -> Option { self.0.interfaces.get(T::NAME)?.clone().downcast() } } impl PartialEq for Device { fn eq(&self, rhs: &Device) -> bool { Arc::ptr_eq(&self.0, &rhs.0) } } impl Eq for Device {} impl std::hash::Hash for Device { fn hash(&self, state: &mut H) { self.0.device.0.id().hash(state); } } #[derive(Clone, Debug, PartialEq)] pub enum EiEvent { // Connected, Disconnected(Disconnected), SeatAdded(SeatAdded), SeatRemoved(SeatRemoved), DeviceAdded(DeviceAdded), DeviceRemoved(DeviceRemoved), DevicePaused(DevicePaused), DeviceResumed(DeviceResumed), KeyboardModifiers(KeyboardModifiers), // Only for reciever context Frame(Frame), DeviceStartEmulating(DeviceStartEmulating), DeviceStopEmulating(DeviceStopEmulating), PointerMotion(PointerMotion), PointerMotionAbsolute(PointerMotionAbsolute), Button(Button), ScrollDelta(ScrollDelta), ScrollStop(ScrollStop), ScrollCancel(ScrollCancel), ScrollDiscrete(ScrollDiscrete), KeyboardKey(KeyboardKey), TouchDown(TouchDown), TouchUp(TouchUp), TouchMotion(TouchMotion), } impl EiEvent { // Events that are grouped by frames need their times set when the // frame event occurs. fn time_mut(&mut self) -> Option<&mut u64> { match self { Self::PointerMotion(evt) => Some(&mut evt.time), Self::PointerMotionAbsolute(evt) => Some(&mut evt.time), Self::Button(evt) => Some(&mut evt.time), Self::ScrollDelta(evt) => Some(&mut evt.time), Self::ScrollStop(evt) => Some(&mut evt.time), Self::ScrollCancel(evt) => Some(&mut evt.time), Self::ScrollDiscrete(evt) => Some(&mut evt.time), Self::KeyboardKey(evt) => Some(&mut evt.time), Self::TouchDown(evt) => Some(&mut evt.time), Self::TouchUp(evt) => Some(&mut evt.time), Self::TouchMotion(evt) => Some(&mut evt.time), _ => None, } } } #[derive(Clone, Debug, PartialEq)] pub struct Disconnected { pub last_serial: u32, pub reason: ei::connection::DisconnectReason, pub explanation: String, } #[derive(Clone, Debug, PartialEq)] pub struct SeatAdded { pub seat: Seat, } #[derive(Clone, Debug, PartialEq)] pub struct SeatRemoved { pub seat: Seat, } #[derive(Clone, Debug, PartialEq)] pub struct DeviceAdded { pub device: Device, } #[derive(Clone, Debug, PartialEq)] pub struct DeviceRemoved { pub device: Device, } #[derive(Clone, Debug, PartialEq)] pub struct DevicePaused { pub device: Device, pub serial: u32, } #[derive(Clone, Debug, PartialEq)] pub struct DeviceResumed { pub device: Device, pub serial: u32, } #[derive(Clone, Debug, PartialEq)] pub struct KeyboardModifiers { pub device: Device, pub serial: u32, pub depressed: u32, pub latched: u32, pub locked: u32, pub group: u32, } #[derive(Clone, Debug, PartialEq)] pub struct Frame { pub device: Device, pub serial: u32, pub time: u64, } #[derive(Clone, Debug, PartialEq)] pub struct DeviceStartEmulating { pub device: Device, pub serial: u32, pub sequence: u32, } #[derive(Clone, Debug, PartialEq)] pub struct DeviceStopEmulating { pub device: Device, pub serial: u32, } #[derive(Clone, Debug, PartialEq)] pub struct PointerMotion { pub device: Device, pub time: u64, pub dx: f32, pub dy: f32, } #[derive(Clone, Debug, PartialEq)] pub struct PointerMotionAbsolute { pub device: Device, pub time: u64, pub dx_absolute: f32, pub dy_absolute: f32, } #[derive(Clone, Debug, PartialEq)] pub struct Button { pub device: Device, pub time: u64, pub button: u32, pub state: ei::button::ButtonState, } #[derive(Clone, Debug, PartialEq)] pub struct ScrollDelta { pub device: Device, pub time: u64, pub dx: f32, pub dy: f32, } #[derive(Clone, Debug, PartialEq)] pub struct ScrollStop { pub device: Device, pub time: u64, pub x: bool, pub y: bool, } #[derive(Clone, Debug, PartialEq)] pub struct ScrollCancel { pub device: Device, pub time: u64, pub x: bool, pub y: bool, } #[derive(Clone, Debug, PartialEq)] pub struct ScrollDiscrete { pub device: Device, pub time: u64, pub discrete_dx: i32, pub discrete_dy: i32, } #[derive(Clone, Debug, PartialEq)] pub struct KeyboardKey { pub device: Device, pub time: u64, pub key: u32, pub state: ei::keyboard::KeyState, } #[derive(Clone, Debug, PartialEq)] pub struct TouchDown { pub device: Device, pub time: u64, pub touch_id: u32, pub x: f32, pub y: f32, } #[derive(Clone, Debug, PartialEq)] pub struct TouchMotion { pub device: Device, pub time: u64, pub touch_id: u32, pub x: f32, pub y: f32, } #[derive(Clone, Debug, PartialEq)] pub struct TouchUp { pub device: Device, pub time: u64, pub touch_id: u32, } pub trait SeatEvent { fn seat(&self) -> &Seat; } pub trait DeviceEvent: SeatEvent { fn device(&self) -> &Device; } pub trait EventTime: DeviceEvent { fn time(&self) -> u64; } impl SeatEvent for T { fn seat(&self) -> &Seat { &self.device().0.seat } } impl SeatEvent for SeatAdded { fn seat(&self) -> &Seat { &self.seat } } impl SeatEvent for SeatRemoved { fn seat(&self) -> &Seat { &self.seat } } macro_rules! impl_device_trait { ($ty:ty) => { impl DeviceEvent for $ty { fn device(&self) -> &Device { &self.device } } }; ($ty:ty; time) => { impl_device_trait!($ty); impl EventTime for $ty { fn time(&self) -> u64 { self.time } } }; } impl_device_trait!(DeviceAdded); impl_device_trait!(DeviceRemoved); impl_device_trait!(DevicePaused); impl_device_trait!(DeviceResumed); impl_device_trait!(KeyboardModifiers); impl_device_trait!(Frame; time); impl_device_trait!(DeviceStartEmulating); impl_device_trait!(DeviceStopEmulating); impl_device_trait!(PointerMotion; time); impl_device_trait!(PointerMotionAbsolute; time); impl_device_trait!(Button; time); impl_device_trait!(ScrollDelta; time); impl_device_trait!(ScrollStop; time); impl_device_trait!(ScrollCancel; time); impl_device_trait!(ScrollDiscrete; time); impl_device_trait!(KeyboardKey; time); impl_device_trait!(TouchDown; time); impl_device_trait!(TouchUp; time); impl_device_trait!(TouchMotion; time); #[derive(Debug)] pub enum EiConvertEventIteratorError { Io(io::Error), Parse(ParseError), Event(crate::event::Error), } pub struct EiConvertEventIterator { context: ei::Context, converter: EiEventConverter, } impl EiConvertEventIterator { pub fn new(context: ei::Context, serial: u32) -> Self { Self { context, converter: EiEventConverter::new(serial), } } } impl Iterator for EiConvertEventIterator { type Item = Result; fn next(&mut self) -> Option { loop { if let Some(event) = self.converter.next_event() { return Some(Ok(event)); } if let Err(err) = util::poll_readable(&self.context) { return Some(Err(EiConvertEventIteratorError::Io(err))); } match self.context.read() { Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => return None, Err(err) => return Some(Err(EiConvertEventIteratorError::Io(err))), Ok(_) => {} }; while let Some(result) = self.context.pending_event() { let request = match result { PendingRequestResult::Request(request) => request, PendingRequestResult::ParseError(parse_error) => { return Some(Err(EiConvertEventIteratorError::Parse(parse_error))) } PendingRequestResult::InvalidObject(_invalid_object) => { // Log? continue; } }; if let Err(err) = self.converter.handle_event(request) { return Some(Err(EiConvertEventIteratorError::Event(err))); } } } } } reis-0.2.0/src/handshake.rs000064400000000000000000000170031046102023000136510ustar 00000000000000// Generic `EiHandshaker` can be used in async or sync code use crate::{ei, eis, util, ParseError, PendingRequestResult}; use std::{collections::HashMap, error, fmt, io, mem}; pub struct HandshakeResp { pub connection: ei::Connection, pub serial: u32, pub negotiated_interfaces: HashMap, } #[derive(Debug)] pub enum HandshakeError { Io(io::Error), Parse(ParseError), InvalidObject(u64), NonHandshakeEvent, MissingInterface, DuplicateEvent, NoContextType, } impl fmt::Display for HandshakeError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::Io(err) => write!(f, "IO error: {}", err), Self::Parse(err) => write!(f, "parse error: {}", err), Self::InvalidObject(id) => write!(f, "invalid object {} during handshake", id), Self::NonHandshakeEvent => write!(f, "non-handshake event during handshake"), Self::MissingInterface => write!(f, "missing required interface"), Self::DuplicateEvent => write!(f, "duplicate event during handshake"), Self::NoContextType => write!(f, "no `context_type` sent in handshake"), } } } impl error::Error for HandshakeError {} impl From for HandshakeError { fn from(err: io::Error) -> Self { Self::Io(err) } } pub struct EiHandshaker<'a> { name: &'a str, context_type: ei::handshake::ContextType, interfaces: &'a HashMap<&'a str, u32>, negotiated_interfaces: HashMap, } impl<'a> EiHandshaker<'a> { pub fn new( name: &'a str, context_type: ei::handshake::ContextType, interfaces: &'a HashMap<&'a str, u32>, ) -> Self { Self { name, context_type, interfaces, negotiated_interfaces: HashMap::new(), } } pub fn handle_event( &mut self, event: ei::Event, ) -> Result, HandshakeError> { let ei::Event::Handshake(handshake, event) = event else { return Err(HandshakeError::NonHandshakeEvent); }; match event { ei::handshake::Event::HandshakeVersion { version: _ } => { handshake.handshake_version(1); handshake.name(self.name); handshake.context_type(self.context_type); for (interface, version) in self.interfaces.iter() { handshake.interface_version(interface, *version); } handshake.finish(); if let Some(backend) = handshake.0.backend() { // TODO Handle result let _ = backend.flush(); } Ok(None) } ei::handshake::Event::InterfaceVersion { name, version } => { self.negotiated_interfaces.insert(name, version); Ok(None) } ei::handshake::Event::Connection { connection, serial } => Ok(Some(HandshakeResp { connection, serial, negotiated_interfaces: mem::take(&mut self.negotiated_interfaces), })), } } } pub(crate) fn request_result(result: PendingRequestResult) -> Result { match result { PendingRequestResult::Request(request) => Ok(request), PendingRequestResult::ParseError(parse_error) => Err(HandshakeError::Parse(parse_error)), PendingRequestResult::InvalidObject(invalid_object) => { Err(HandshakeError::InvalidObject(invalid_object)) } } } pub fn ei_handshake_blocking( context: &ei::Context, name: &str, context_type: ei::handshake::ContextType, interfaces: &HashMap<&str, u32>, ) -> Result { let mut handshaker = EiHandshaker::new(name, context_type, interfaces); loop { util::poll_readable(context)?; context.read()?; while let Some(result) = context.pending_event() { let request = request_result(result)?; if let Some(resp) = handshaker.handle_event(request)? { return Ok(resp); } } } } pub struct EisHandshakeResp { pub connection: eis::Connection, pub name: Option, pub context_type: eis::handshake::ContextType, pub negotiated_interfaces: HashMap, } pub struct EisHandshaker<'a> { interfaces: &'a HashMap<&'a str, u32>, name: Option, context_type: Option, negotiated_interfaces: HashMap, initial_serial: u32, } impl<'a> EisHandshaker<'a> { pub fn new( context: &eis::Context, interfaces: &'a HashMap<&'a str, u32>, initial_serial: u32, ) -> Self { let handshake = context.handshake(); handshake.handshake_version(1); // XXX error handling? let _ = context.flush(); Self { interfaces, initial_serial, name: None, context_type: None, negotiated_interfaces: HashMap::new(), } } pub fn handle_request( &mut self, request: eis::Request, ) -> Result, HandshakeError> { let eis::Request::Handshake(handshake, request) = request else { return Err(HandshakeError::NonHandshakeEvent); }; match request { eis::handshake::Request::HandshakeVersion { version: _ } => {} eis::handshake::Request::Name { name } => { if self.name.is_some() { return Err(HandshakeError::DuplicateEvent); } self.name = Some(name); } eis::handshake::Request::ContextType { context_type } => { if self.context_type.is_some() { return Err(HandshakeError::DuplicateEvent); } self.context_type = Some(context_type); } eis::handshake::Request::InterfaceVersion { name, version } => { if let Some((interface, server_version)) = self.interfaces.get_key_value(name.as_str()) { self.negotiated_interfaces .insert(interface.to_string(), version.min(*server_version)); } } eis::handshake::Request::Finish => { for (interface, version) in self.negotiated_interfaces.iter() { handshake.interface_version(interface, *version); } if !self.negotiated_interfaces.contains_key("ei_connection") || !self.negotiated_interfaces.contains_key("ei_pingpong") || !self.negotiated_interfaces.contains_key("ei_callback") { return Err(HandshakeError::MissingInterface); } let connection = handshake.connection(self.initial_serial, 1); let Some(context_type) = self.context_type else { return Err(HandshakeError::NoContextType); }; return Ok(Some(EisHandshakeResp { connection, name: self.name.clone(), context_type, negotiated_interfaces: mem::take(&mut self.negotiated_interfaces), })); } } Ok(None) } } // Does handshake always succeed? When does it prompt, if needed? // Look at libei, improve any documentation. reis-0.2.0/src/lib.rs000064400000000000000000000017011046102023000124670ustar 00000000000000#![forbid(unsafe_code)] // TODO error type? // TODO split up // Implement handshake use std::{env, path::PathBuf}; pub use wire::PendingRequestResult; // XXX types? names? pub mod ei; mod eiproto_ei; mod eiproto_eis; mod eiproto_enum; pub mod eis; pub mod event; // XXX reorganize? pub mod handshake; // XXX ^ mod object; #[doc(hidden)] // TODO pub mod request; pub use object::Object; mod util; mod wire; // TODO make (a version of) this public and documented? #[doc(hidden)] pub use wire::Interface; pub use wire::ParseError; #[cfg(feature = "calloop")] #[doc(hidden)] // TODO pub mod calloop; #[cfg(feature = "tokio")] pub mod tokio; // TODO versioning? mod private { pub trait Sealed {} } // XXX // Want to fallback to higher number if exists, on server? // create on server, not client. pub fn default_socket_path() -> Option { let mut path = PathBuf::from(env::var_os("XDG_RUNTIME_DIR")?); path.push("eis-0"); Some(path) } reis-0.2.0/src/object.rs000064400000000000000000000040611046102023000131710ustar 00000000000000use std::{fmt, hash, sync::Arc}; use crate::{ wire::{Arg, Backend, BackendWeak}, Interface, }; #[derive(Clone)] pub struct Object(Arc); struct ObjectInner { backend: BackendWeak, client_side: bool, id: u64, interface: String, version: u32, } impl PartialEq for Object { fn eq(&self, rhs: &Self) -> bool { Arc::ptr_eq(&self.0, &rhs.0) } } impl Eq for Object {} impl hash::Hash for Object { fn hash(&self, hasher: &mut H) { self.0.id.hash(hasher) } } impl fmt::Debug for Object { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Object(_, {})", self.id()) } } impl Object { // Should only be called by `Backend` // Other references are then cloned from the version stored there pub(crate) fn for_new_id( backend: BackendWeak, id: u64, client_side: bool, interface: String, version: u32, ) -> Self { Self(Arc::new(ObjectInner { backend, id, client_side, interface, version, })) } pub fn backend(&self) -> Option { self.0.backend.upgrade() } pub(crate) fn backend_weak(&self) -> &BackendWeak { &self.0.backend } pub fn id(&self) -> u64 { self.0.id } pub fn interface(&self) -> &str { &self.0.interface } pub fn version(&self) -> u32 { self.0.version } pub fn request(&self, opcode: u32, args: &[Arg]) { if let Some(backend) = self.backend() { backend.request(self.0.id, opcode, args); } } pub(crate) fn downcast_unchecked(self) -> T { T::new_unchecked(self) } pub(crate) fn as_arg(&self) -> Arg<'_> { Arg::Id(self.0.id) } pub fn downcast(self) -> Option { if (self.0.client_side, self.interface()) == (T::CLIENT_SIDE, T::NAME) { Some(self.downcast_unchecked()) } else { None } } } reis-0.2.0/src/request.rs000064400000000000000000000522221046102023000134150ustar 00000000000000#![allow(clippy::derive_partial_eq_without_eq)] // TODO: rename/reorganize things; doc comments on public types/methods use crate::{eis, Object, ParseError}; use std::{ collections::{HashMap, VecDeque}, fmt, io, sync::Arc, }; pub use crate::event::DeviceCapability; #[derive(Debug)] pub enum Error { UnexpectedHandshakeEvent, UnrecognizedSeat, UnrecognizedDevice, InvalidCallbackVersion, Parse(ParseError), Io(io::Error), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::UnexpectedHandshakeEvent => write!(f, "unexpected handshake event"), Self::UnrecognizedSeat => write!(f, "unrecognized seat"), Self::UnrecognizedDevice => write!(f, "unrecognized device"), Self::InvalidCallbackVersion => write!(f, "invalid callback version"), Self::Io(err) => write!(f, "IO error: {}", err), Self::Parse(err) => write!(f, "parse error: {}", err), } } } impl std::error::Error for Error {} // need way to add seat/device? pub struct EisRequestConverter { connection: eis::Connection, requests: VecDeque, pending_requests: VecDeque, seats: HashMap, devices: HashMap, device_for_interface: HashMap, last_serial: u32, } impl EisRequestConverter { pub fn new(connection: &eis::Connection, initial_serial: u32) -> Self { Self { connection: connection.clone(), last_serial: initial_serial, requests: VecDeque::new(), pending_requests: VecDeque::new(), seats: HashMap::new(), devices: HashMap::new(), device_for_interface: HashMap::new(), } } pub fn last_serial(&self) -> u32 { self.last_serial } pub fn next_serial(&mut self) -> u32 { self.last_serial += 1; self.last_serial } } impl EisRequestConverter { // Based on behavior of `eis_queue_request` in libeis fn queue_request(&mut self, mut request: EisRequest) { if request.time_mut().is_some() { self.pending_requests.push_back(request); } else if let EisRequest::Frame(Frame { time, .. }) = &request { if self.pending_requests.is_empty() { return; } for mut pending_request in self.pending_requests.drain(..) { *pending_request.time_mut().unwrap() = *time; self.requests.push_back(pending_request); } self.requests.push_back(request); } else { // TODO: If a device request, queue a frame if anything is pending self.requests.push_back(request); } } pub fn next_request(&mut self) -> Option { self.requests.pop_front() } pub fn add_seat(&mut self, name: Option<&str>, capabilities: &[DeviceCapability]) -> Seat { let seat = self.connection.seat(1); if let Some(name) = name { seat.name(name); } for capability in capabilities { // TODO only send negotiated interfaces seat.capability(2 << *capability as u64, capability.name()); } seat.done(); let seat = Seat(Arc::new(SeatInner { seat, name: name.map(|x| x.to_owned()), })); self.seats.insert(seat.0.seat.clone(), seat.clone()); seat } // builder pattern? pub fn add_device( &mut self, seat: &Seat, name: Option<&str>, device_type: eis::device::DeviceType, capabilities: &[DeviceCapability], ) -> Device { let device = seat.0.seat.device(1); if let Some(name) = name { device.name(name); } device.device_type(device_type); // TODO // dimensions // regions; region_mapping_id // TODO add interfaces for capabilities // - `eis_device_configure_capability`; only if seat has capability let mut interfaces = HashMap::new(); for capability in capabilities { let object = match capability { DeviceCapability::Pointer => device.interface::(1).0, DeviceCapability::PointerAbsolute => device.interface::(1).0, DeviceCapability::Keyboard => device.interface::(1).0, DeviceCapability::Touch => device.interface::(1).0, DeviceCapability::Scroll => device.interface::(1).0, DeviceCapability::Button => device.interface::(1).0, }; interfaces.insert(object.interface().to_string(), object); } device.done(); let device = Device(Arc::new(DeviceInner { device, seat: seat.clone(), name: name.map(|x| x.to_string()), interfaces, })); for interface in device.0.interfaces.values() { self.device_for_interface .insert(interface.clone(), device.clone()); } self.devices.insert(device.0.device.clone(), device.clone()); device } pub fn handle_request(&mut self, request: eis::Request) -> Result<(), Error> { match request { eis::Request::Handshake(_handshake, _request) => { return Err(Error::UnexpectedHandshakeEvent); } eis::Request::Connection(_connection, request) => match request { eis::connection::Request::Sync { callback } => { if callback.version() != 1 { return Err(Error::InvalidCallbackVersion); } callback.done(0); if let Some(backend) = callback.0.backend() { // XXX Error? let _ = backend.flush(); } } eis::connection::Request::Disconnect => { self.queue_request(EisRequest::Disconnect); } }, eis::Request::Callback(_callback, request) => match request {}, eis::Request::Pingpong(_ping_pong, request) => match request { eis::pingpong::Request::Done { callback_data: _ } => { // TODO } }, eis::Request::Seat(seat, request) => match request { eis::seat::Request::Release => { // XXX let serial = self.next_serial(); seat.destroyed(serial); } eis::seat::Request::Bind { capabilities } => { let seat = self.seats.get(&seat).ok_or(Error::UnrecognizedSeat)?; self.queue_request(EisRequest::Bind(Bind { seat: seat.clone(), capabilities, })); } }, eis::Request::Device(device, request) => { let device = self.devices.get(&device).ok_or(Error::UnrecognizedDevice)?; match request { eis::device::Request::Release => {} eis::device::Request::StartEmulating { last_serial, sequence, } => { self.queue_request(EisRequest::DeviceStartEmulating( DeviceStartEmulating { device: device.clone(), last_serial, sequence, }, )); } eis::device::Request::StopEmulating { last_serial } => { self.queue_request(EisRequest::DeviceStopEmulating(DeviceStopEmulating { device: device.clone(), last_serial, })); } eis::device::Request::Frame { last_serial, timestamp, } => { self.queue_request(EisRequest::Frame(Frame { device: device.clone(), last_serial, time: timestamp, })); } } } eis::Request::Keyboard(keyboard, request) => { let device = self .device_for_interface .get(&keyboard.0) .ok_or(Error::UnrecognizedDevice)?; match request { eis::keyboard::Request::Release => {} eis::keyboard::Request::Key { key, state } => { self.queue_request(EisRequest::KeyboardKey(KeyboardKey { device: device.clone(), key, state, time: 0, })); } } } eis::Request::Pointer(pointer, request) => { let device = self .device_for_interface .get(&pointer.0) .ok_or(Error::UnrecognizedDevice)?; match request { eis::pointer::Request::Release => {} eis::pointer::Request::MotionRelative { x, y } => { self.queue_request(EisRequest::PointerMotion(PointerMotion { device: device.clone(), dx: x, dy: y, time: 0, })); } } } eis::Request::PointerAbsolute(pointer_absolute, request) => { let device = self .device_for_interface .get(&pointer_absolute.0) .ok_or(Error::UnrecognizedDevice)?; match request { eis::pointer_absolute::Request::Release => {} eis::pointer_absolute::Request::MotionAbsolute { x, y } => { self.queue_request(EisRequest::PointerMotionAbsolute( PointerMotionAbsolute { device: device.clone(), dx_absolute: x, dy_absolute: y, time: 0, }, )); } } } eis::Request::Scroll(scroll, request) => { let device = self .device_for_interface .get(&scroll.0) .ok_or(Error::UnrecognizedDevice)?; match request { eis::scroll::Request::Release => {} eis::scroll::Request::Scroll { x, y } => { self.queue_request(EisRequest::ScrollDelta(ScrollDelta { device: device.clone(), dx: x, dy: y, time: 0, })); } eis::scroll::Request::ScrollDiscrete { x, y } => { self.queue_request(EisRequest::ScrollDiscrete(ScrollDiscrete { device: device.clone(), discrete_dx: x, discrete_dy: y, time: 0, })); } eis::scroll::Request::ScrollStop { x, y, is_cancel } => { if is_cancel != 0 { self.queue_request(EisRequest::ScrollCancel(ScrollCancel { device: device.clone(), time: 0, x: x != 0, y: y != 0, })); } else { self.queue_request(EisRequest::ScrollStop(ScrollStop { device: device.clone(), time: 0, x: x != 0, y: y != 0, })); } } } } eis::Request::Button(button, request) => { let device = self .device_for_interface .get(&button.0) .ok_or(Error::UnrecognizedDevice)?; match request { eis::button::Request::Release => {} eis::button::Request::Button { button, state } => { self.queue_request(EisRequest::Button(Button { device: device.clone(), button, state, time: 0, })); } } } eis::Request::Touchscreen(touchscreen, request) => { let device = self .device_for_interface .get(&touchscreen.0) .ok_or(Error::UnrecognizedDevice)?; match request { eis::touchscreen::Request::Release => {} eis::touchscreen::Request::Down { touchid, x, y } => { self.queue_request(EisRequest::TouchDown(TouchDown { device: device.clone(), touch_id: touchid, x, y, time: 0, })); } eis::touchscreen::Request::Motion { touchid, x, y } => { self.queue_request(EisRequest::TouchMotion(TouchMotion { device: device.clone(), touch_id: touchid, x, y, time: 0, })); } eis::touchscreen::Request::Up { touchid } => { self.queue_request(EisRequest::TouchUp(TouchUp { device: device.clone(), touch_id: touchid, time: 0, })); } } } } Ok(()) } } struct SeatInner { seat: eis::Seat, name: Option, //capabilities: HashMap, } #[derive(Clone)] pub struct Seat(Arc); impl Seat { pub fn eis_seat(&self) -> &eis::Seat { &self.0.seat } } impl fmt::Debug for Seat { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(name) = &self.0.name { write!(f, "Seat(\"{}\")", name) } else { write!(f, "Seat(None)") } } } impl PartialEq for Seat { fn eq(&self, rhs: &Seat) -> bool { Arc::ptr_eq(&self.0, &rhs.0) } } impl Eq for Seat {} impl std::hash::Hash for Seat { fn hash(&self, state: &mut H) { self.0.seat.0.id().hash(state); } } struct DeviceInner { device: eis::Device, seat: Seat, name: Option, interfaces: HashMap, } #[derive(Clone)] pub struct Device(Arc); impl fmt::Debug for Device { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(name) = self.name() { write!(f, "Device(\"{}\")", name) } else { write!(f, "Device(None)") } } } impl Device { pub fn seat(&self) -> &Seat { &self.0.seat } pub fn device(&self) -> &eis::Device { &self.0.device } pub fn name(&self) -> Option<&str> { self.0.name.as_deref() } } impl PartialEq for Device { fn eq(&self, rhs: &Device) -> bool { Arc::ptr_eq(&self.0, &rhs.0) } } impl Eq for Device {} impl std::hash::Hash for Device { fn hash(&self, state: &mut H) { self.0.device.0.id().hash(state); } } #[derive(Clone, Debug, PartialEq)] pub enum EisRequest { // TODO connect, disconnect, device closed Disconnect, Bind(Bind), // Only for sender context Frame(Frame), DeviceStartEmulating(DeviceStartEmulating), DeviceStopEmulating(DeviceStopEmulating), PointerMotion(PointerMotion), PointerMotionAbsolute(PointerMotionAbsolute), Button(Button), ScrollDelta(ScrollDelta), ScrollStop(ScrollStop), ScrollCancel(ScrollCancel), ScrollDiscrete(ScrollDiscrete), KeyboardKey(KeyboardKey), TouchDown(TouchDown), TouchUp(TouchUp), TouchMotion(TouchMotion), } impl EisRequest { // Requests that are grouped by frames need their times set when the // frame request occurs. fn time_mut(&mut self) -> Option<&mut u64> { match self { Self::PointerMotion(evt) => Some(&mut evt.time), Self::PointerMotionAbsolute(evt) => Some(&mut evt.time), Self::Button(evt) => Some(&mut evt.time), Self::ScrollDelta(evt) => Some(&mut evt.time), Self::ScrollStop(evt) => Some(&mut evt.time), Self::ScrollCancel(evt) => Some(&mut evt.time), Self::ScrollDiscrete(evt) => Some(&mut evt.time), Self::KeyboardKey(evt) => Some(&mut evt.time), Self::TouchDown(evt) => Some(&mut evt.time), Self::TouchUp(evt) => Some(&mut evt.time), Self::TouchMotion(evt) => Some(&mut evt.time), _ => None, } } } #[derive(Clone, Debug, PartialEq)] pub struct Bind { pub seat: Seat, pub capabilities: u64, } #[derive(Clone, Debug, PartialEq)] pub struct Frame { pub device: Device, pub last_serial: u32, pub time: u64, } #[derive(Clone, Debug, PartialEq)] pub struct DeviceStartEmulating { pub device: Device, pub last_serial: u32, pub sequence: u32, } #[derive(Clone, Debug, PartialEq)] pub struct DeviceStopEmulating { pub device: Device, pub last_serial: u32, } #[derive(Clone, Debug, PartialEq)] pub struct PointerMotion { pub device: Device, pub time: u64, pub dx: f32, pub dy: f32, } #[derive(Clone, Debug, PartialEq)] pub struct PointerMotionAbsolute { pub device: Device, pub time: u64, pub dx_absolute: f32, pub dy_absolute: f32, } #[derive(Clone, Debug, PartialEq)] pub struct Button { pub device: Device, pub time: u64, pub button: u32, pub state: eis::button::ButtonState, } #[derive(Clone, Debug, PartialEq)] pub struct ScrollDelta { pub device: Device, pub time: u64, pub dx: f32, pub dy: f32, } #[derive(Clone, Debug, PartialEq)] pub struct ScrollStop { pub device: Device, pub time: u64, pub x: bool, pub y: bool, } #[derive(Clone, Debug, PartialEq)] pub struct ScrollCancel { pub device: Device, pub time: u64, pub x: bool, pub y: bool, } #[derive(Clone, Debug, PartialEq)] pub struct ScrollDiscrete { pub device: Device, pub time: u64, pub discrete_dx: i32, pub discrete_dy: i32, } #[derive(Clone, Debug, PartialEq)] pub struct KeyboardKey { pub device: Device, pub time: u64, pub key: u32, pub state: eis::keyboard::KeyState, } #[derive(Clone, Debug, PartialEq)] pub struct TouchDown { pub device: Device, pub time: u64, pub touch_id: u32, pub x: f32, pub y: f32, } #[derive(Clone, Debug, PartialEq)] pub struct TouchMotion { pub device: Device, pub time: u64, pub touch_id: u32, pub x: f32, pub y: f32, } #[derive(Clone, Debug, PartialEq)] pub struct TouchUp { pub device: Device, pub time: u64, pub touch_id: u32, } pub trait SeatEvent { fn seat(&self) -> &Seat; } pub trait DeviceEvent: SeatEvent { fn device(&self) -> &Device; } pub trait EventTime: DeviceEvent { fn time(&self) -> u64; } impl SeatEvent for T { fn seat(&self) -> &Seat { &self.device().0.seat } } macro_rules! impl_device_trait { ($ty:ty) => { impl DeviceEvent for $ty { fn device(&self) -> &Device { &self.device } } }; ($ty:ty; time) => { impl_device_trait!($ty); impl EventTime for $ty { fn time(&self) -> u64 { self.time } } }; } impl_device_trait!(Frame; time); impl_device_trait!(DeviceStartEmulating); impl_device_trait!(DeviceStopEmulating); impl_device_trait!(PointerMotion; time); impl_device_trait!(PointerMotionAbsolute; time); impl_device_trait!(Button; time); impl_device_trait!(ScrollDelta; time); impl_device_trait!(ScrollStop; time); impl_device_trait!(ScrollCancel; time); impl_device_trait!(ScrollDiscrete; time); impl_device_trait!(KeyboardKey; time); impl_device_trait!(TouchDown; time); impl_device_trait!(TouchUp; time); impl_device_trait!(TouchMotion; time); reis-0.2.0/src/tokio.rs000064400000000000000000000111121046102023000130430ustar 00000000000000// TODO: Handle writable fd too? use futures::stream::{Stream, StreamExt}; use std::{ collections::HashMap, io, pin::Pin, task::{Context, Poll}, }; use tokio::io::unix::AsyncFd; pub use crate::handshake::{HandshakeError, HandshakeResp}; use crate::{ei, handshake::EiHandshaker, ParseError, PendingRequestResult}; // XXX make this ei::EventStream? pub struct EiEventStream(AsyncFd); impl EiEventStream { pub fn new(context: ei::Context) -> io::Result { AsyncFd::with_interest(context, tokio::io::Interest::READABLE).map(Self) } } fn poll_pending_event( context: &mut ei::Context, ) -> Option>>>> { Some(Poll::Ready(Some(Ok(context.pending_event()?)))) } impl Stream for EiEventStream { type Item = io::Result>; // XXX fn poll_next( mut self: Pin<&mut Self>, context: &mut Context<'_>, ) -> Poll::Item>> { // If we already have a pending event, return that if let Some(res) = poll_pending_event(self.0.get_mut()) { return res; } if let Poll::Ready(guard) = Pin::new(&self.0).poll_read_ready(context) { let mut guard = match guard { Ok(guard) => guard, Err(err) => { return Poll::Ready(Some(Err(err))); } }; match guard.get_inner().read() { Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => Poll::Ready(None), Err(err) => Poll::Ready(Some(Err(err))), Ok(_) => { // `Backend::read()` reads until `WouldBlock`, EOF, or error guard.clear_ready(); poll_pending_event(self.0.get_mut()).unwrap_or(Poll::Pending) } } } else { Poll::Pending } } } #[derive(Debug)] pub enum EiConvertEventStreamError { Io(io::Error), Parse(ParseError), // TODO better error type here? Event(crate::event::Error), } // TODO rename EiProtoEventStream pub struct EiConvertEventStream { inner: EiEventStream, converter: crate::event::EiEventConverter, } impl EiConvertEventStream { pub fn new(inner: EiEventStream, serial: u32) -> Self { Self { inner, converter: crate::event::EiEventConverter::new(serial), } } } impl Stream for EiConvertEventStream { type Item = Result; fn poll_next( mut self: Pin<&mut Self>, context: &mut Context<'_>, ) -> Poll::Item>> { if let Some(event) = self.converter.next_event() { return Poll::Ready(Some(Ok(event))); } while let Poll::Ready(res) = Pin::new(&mut self.inner).poll_next(context) { match res { Some(Ok(res)) => match res { PendingRequestResult::Request(event) => { if let Err(err) = self.converter.handle_event(event) { return Poll::Ready(Some(Err(EiConvertEventStreamError::Event(err)))); } if let Some(event) = self.converter.next_event() { return Poll::Ready(Some(Ok(event))); } } PendingRequestResult::ParseError(err) => { return Poll::Ready(Some(Err(EiConvertEventStreamError::Parse(err)))); } // TODO log? PendingRequestResult::InvalidObject(_object_id) => {} }, Some(Err(err)) => { return Poll::Ready(Some(Err(EiConvertEventStreamError::Io(err)))); } None => { return Poll::Ready(None); } } } Poll::Pending } } pub async fn ei_handshake( events: &mut EiEventStream, name: &str, context_type: ei::handshake::ContextType, interfaces: &HashMap<&str, u32>, ) -> Result { let mut handshaker = EiHandshaker::new(name, context_type, interfaces); while let Some(result) = events.next().await { let request = crate::handshake::request_result(result?)?; if let Some(resp) = handshaker.handle_event(request)? { return Ok(resp); } } Err(io::Error::new( io::ErrorKind::UnexpectedEof, "unexpected EOF reading ei socket", ) .into()) } reis-0.2.0/src/util.rs000064400000000000000000000062711046102023000127050ustar 00000000000000use rustix::{ fs::FlockOperation, io::{retry_on_intr, IoSlice, IoSliceMut}, net, }; use std::{ collections::VecDeque, fs, io, ops, os::unix::{ fs::OpenOptionsExt, io::{AsFd, BorrowedFd, OwnedFd}, net::UnixStream, }, path::PathBuf, }; // Panics if iterator isn't as long as `N` pub fn array_from_iterator_unchecked, const N: usize>( mut iter: I, ) -> [T; N] { let mut arr = [T::default(); N]; for i in arr.iter_mut() { *i = iter.next().unwrap(); } arr } pub fn send_with_fds( socket: &UnixStream, //buf: &VecDeque, buf: &[IoSlice], fds: &[BorrowedFd], ) -> rustix::io::Result { let mut cmsg_space = vec![0; rustix::cmsg_space!(ScmRights(fds.len()))]; let mut cmsg_buffer = net::SendAncillaryBuffer::new(&mut cmsg_space); cmsg_buffer.push(net::SendAncillaryMessage::ScmRights(fds)); retry_on_intr(|| net::sendmsg(socket, buf, &mut cmsg_buffer, net::SendFlags::NOSIGNAL)) } pub fn recv_with_fds( socket: &UnixStream, buf: &mut [u8], fds: &mut VecDeque, ) -> rustix::io::Result { const MAX_FDS: usize = 32; let mut cmsg_space = vec![0; rustix::cmsg_space!(ScmRights(MAX_FDS))]; let mut cmsg_buffer = net::RecvAncillaryBuffer::new(&mut cmsg_space); let response = retry_on_intr(|| { net::recvmsg( socket, &mut [IoSliceMut::new(buf)], &mut cmsg_buffer, net::RecvFlags::CMSG_CLOEXEC, ) })?; if response.bytes != 0 { fds.extend( cmsg_buffer .drain() .filter_map(|msg| match msg { net::RecvAncillaryMessage::ScmRights(fds) => Some(fds), _ => None, }) .flatten(), ); } Ok(response.bytes) } pub struct UnlinkOnDrop { inner: T, path: PathBuf, } impl UnlinkOnDrop { pub fn new(inner: T, path: PathBuf) -> Self { Self { inner, path } } } impl Drop for UnlinkOnDrop { fn drop(&mut self) { let _ = fs::remove_file(&self.path); } } impl ops::Deref for UnlinkOnDrop { type Target = T; fn deref(&self) -> &T { &self.inner } } impl ops::DerefMut for UnlinkOnDrop { fn deref_mut(&mut self) -> &mut T { &mut self.inner } } // Should match way locking in libeis is handled pub struct LockFile(UnlinkOnDrop); impl LockFile { pub fn new(path: PathBuf) -> io::Result> { let inner = fs::File::options() .create(true) .read(true) .write(true) .mode(0o660) .open(&path)?; let inner = UnlinkOnDrop::new(inner, path); let locked = rustix::fs::flock(&inner.inner, FlockOperation::NonBlockingLockExclusive).is_ok(); Ok(Some(inner).filter(|_| locked).map(Self)) } } pub fn poll_readable(fd: &T) -> io::Result<()> { rustix::io::retry_on_intr(|| { rustix::event::poll( &mut [rustix::event::PollFd::new(fd, rustix::event::PollFlags::IN)], 0, ) })?; Ok(()) } reis-0.2.0/src/wire/arg.rs000064400000000000000000000110041046102023000134350ustar 00000000000000// Types representing arguments to requests/events // Used in parsing and serialization. use std::{ fmt, iter::Extend, os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd}, }; use super::{ByteStream, ParseError}; #[allow(dead_code)] #[derive(Debug)] pub enum Arg<'a> { Uint32(u32), Int32(i32), Uint64(u64), Int64(i64), Float(f32), Fd(BorrowedFd<'a>), String(&'a str), NewId(u64), Id(u64), } impl<'a> fmt::Display for Arg<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Uint32(value) => write!(f, "{value}"), Self::Int32(value) => write!(f, "{value}"), Self::Uint64(value) => write!(f, "{value}"), Self::Int64(value) => write!(f, "{value}"), Self::Float(value) => write!(f, "{value}"), Self::Fd(value) => write!(f, "fd {}", value.as_raw_fd()), Self::String(value) => write!(f, "{value:?}"), Self::NewId(value) => write!(f, "new_id {value:x}"), Self::Id(value) => write!(f, "id {value:x}"), } } } impl<'a> Arg<'a> { pub fn write(&self, buf: &mut T, fds: &mut U) where T: Extend, U: Extend, { match self { Arg::Uint32(value) => buf.extend(value.to_ne_bytes()), Arg::Int32(value) => buf.extend(value.to_ne_bytes()), Arg::Uint64(value) => buf.extend(value.to_ne_bytes()), Arg::Int64(value) => buf.extend(value.to_ne_bytes()), Arg::Float(value) => buf.extend(value.to_ne_bytes()), // XXX unwrap? Arg::Fd(value) => fds.extend([value.try_clone_to_owned().unwrap()]), Arg::String(value) => { // Write 32-bit length, including NUL let len = value.len() as u32 + 1; buf.extend(len.to_ne_bytes()); // Write contents of string, as UTF-8 buf.extend(value.as_bytes().iter().copied()); // Add NUL terminator buf.extend([b'\0']); // Pad to multiple of 32 bits if len % 4 != 0 { buf.extend((0..4 - (len % 4)).map(|_| b'\0')); } } Arg::NewId(value) => buf.extend(value.to_ne_bytes()), Arg::Id(value) => buf.extend(value.to_ne_bytes()), } } } pub(crate) trait OwnedArg: Sized { fn parse(buf: &mut ByteStream) -> Result; fn as_arg(&self) -> Arg<'_>; // For enum types, this returns the name of the enum and variant fn enum_name(&self) -> Option<(&'static str, &'static str)> { None } } impl OwnedArg for u32 { fn parse(buf: &mut ByteStream) -> Result { Ok(Self::from_ne_bytes(buf.read()?)) } fn as_arg(&self) -> Arg<'_> { Arg::Uint32(*self) } } impl OwnedArg for i32 { fn parse(buf: &mut ByteStream) -> Result { Ok(Self::from_ne_bytes(buf.read()?)) } fn as_arg(&self) -> Arg<'_> { Arg::Int32(*self) } } impl OwnedArg for u64 { fn parse(buf: &mut ByteStream) -> Result { Ok(Self::from_ne_bytes(buf.read()?)) } fn as_arg(&self) -> Arg<'_> { Arg::Uint64(*self) } } impl OwnedArg for i64 { fn parse(buf: &mut ByteStream) -> Result { Ok(Self::from_ne_bytes(buf.read()?)) } fn as_arg(&self) -> Arg<'_> { Arg::Int64(*self) } } impl OwnedArg for f32 { fn parse(buf: &mut ByteStream) -> Result { Ok(Self::from_ne_bytes(buf.read()?)) } fn as_arg(&self) -> Arg<'_> { Arg::Float(*self) } } impl OwnedArg for OwnedFd { fn parse(buf: &mut ByteStream) -> Result { buf.read_fd() } fn as_arg(&self) -> Arg<'_> { Arg::Fd(self.as_fd()) } } impl OwnedArg for String { fn parse(buf: &mut ByteStream) -> Result { let mut len = u32::parse(buf)?; if len == 0 { return Ok(String::new()); } let bytes = buf.read_n(len as usize - 1)?; // Exclude NUL let string = String::from_utf8(bytes.collect())?; buf.read_n(1)?.next(); // NUL while len % 4 != 0 { // Padding len += 1; buf.read::<1>()?; } Ok(string) } fn as_arg(&self) -> Arg<'_> { Arg::String(self) } } #[cfg(test)] mod tests { // TODO add serialization/deserialization tests } reis-0.2.0/src/wire/backend.rs000064400000000000000000000235551046102023000142710ustar 00000000000000/// Backend /// /// Handles socket reads/writes, byte/fd buffering, and calls into /// serialization code to send/receive discrete typed message. /// /// Also implements debug printing to stderr when `REIS_DEBUG` is set. use rustix::io::{Errno, IoSlice}; use std::{ collections::{HashMap, VecDeque}, env, io, os::unix::{ io::{AsFd, BorrowedFd, OwnedFd}, net::UnixStream, }, sync::{Arc, Mutex, Weak}, }; use crate::{ ei, eis, util, wire::{self, Arg, ByteStream, Header, ParseError}, Object, }; #[derive(Debug, Default)] struct Buffer { buf: VecDeque, fds: VecDeque, } impl Buffer { fn flush_write(&mut self, socket: &UnixStream) -> rustix::io::Result<()> { // TODO avoid allocation while !self.buf.is_empty() { let (slice1, slice2) = self.buf.as_slices(); let iov = &[IoSlice::new(slice1), IoSlice::new(slice2)]; let fds: Vec<_> = self.fds.iter().map(|x| x.as_fd()).collect(); let written = util::send_with_fds(socket, iov, &fds)?; self.buf.drain(0..written).for_each(|_| {}); self.fds.clear(); } Ok(()) } } #[derive(Debug)] struct BackendState { next_id: u64, next_peer_id: u64, objects: HashMap, } #[derive(Debug)] struct BackendInner { socket: UnixStream, client: bool, state: Mutex, read: Mutex, write: Mutex, debug: bool, } // Used for both ei and eis #[derive(Clone, Debug)] pub struct Backend(Arc); #[derive(Clone, Debug)] pub(crate) struct BackendWeak(Weak); impl BackendWeak { pub fn upgrade(&self) -> Option { self.0.upgrade().map(Backend) } pub fn new_object(&self, interface: String, version: u32) -> Object { if let Some(backend) = self.upgrade() { backend.new_object(interface, version) } else { // If the backend is destroyed, object will be inert and id doesn't matter Object::for_new_id(self.clone(), u64::MAX, false, interface, version) } } pub fn remove_id(&self, id: u64) { if let Some(backend) = self.upgrade() { backend.remove_id(id); } } } impl AsFd for Backend { fn as_fd(&self) -> BorrowedFd { self.0.socket.as_fd() } } #[derive(Debug)] pub enum PendingRequestResult { Request(T), ParseError(ParseError), InvalidObject(u64), } impl Backend { pub fn new(socket: UnixStream, client: bool) -> io::Result { socket.set_nonblocking(true)?; let next_id = if client { 1 } else { 0xff00000000000000 }; let next_peer_id = if client { 0xff00000000000000 } else { 1 }; let backend = Self(Arc::new(BackendInner { socket, client, state: Mutex::new(BackendState { next_id, next_peer_id, objects: HashMap::new(), }), read: Mutex::new(Buffer::default()), write: Mutex::new(Buffer::default()), debug: is_reis_debug(), })); let handshake = Object::for_new_id( backend.downgrade(), 0, client, "ei_handshake".to_string(), 1, ); backend.0.state.lock().unwrap().objects.insert(0, handshake); Ok(backend) } pub(crate) fn downgrade(&self) -> BackendWeak { BackendWeak(Arc::downgrade(&self.0)) } /// Read any pending data on socket into buffer /// /// Returns `UnexpectedEof` if end-of-file is reached. pub fn read(&self) -> io::Result { let mut read = self.0.read.lock().unwrap(); // TODO read into read.buf with iov? let mut buf = [0; 2048]; let mut total_count = 0; loop { match util::recv_with_fds(&self.0.socket, &mut buf, &mut read.fds) { Ok(0) if total_count == 0 => { return Err(io::Error::new( io::ErrorKind::UnexpectedEof, "unexpected EOF reading ei socket", )); } Ok(0) => { return Ok(total_count); } Ok(count) => { read.buf.extend(&buf[0..count]); total_count += count; } #[allow(unreachable_patterns)] // `WOULDBLOCK` and `AGAIN` typically equal Err(Errno::WOULDBLOCK | Errno::AGAIN) => { return Ok(total_count); } Err(err) => return Err(err.into()), }; } } pub(crate) fn pending( &self, parse: fn(Object, u32, &mut ByteStream) -> Result, ) -> Option> { let mut read = self.0.read.lock().unwrap(); if read.buf.len() >= 16 { let header_bytes = util::array_from_iterator_unchecked(read.buf.iter().copied()); let header = Header::parse(header_bytes); if read.buf.len() < header.length as usize { return None; } if header.length < 16 { return Some(PendingRequestResult::ParseError(ParseError::HeaderLength( header.length, ))); } if let Some(object) = self.object_for_id(header.object_id) { let read = &mut *read; read.buf.drain(..16); // Remove header let mut bytes = ByteStream { backend: self, bytes: read.buf.drain(..header.length as usize - 16), fds: &mut read.fds, }; let request = parse(object, header.opcode, &mut bytes); if bytes.bytes.len() != 0 { return Some(PendingRequestResult::ParseError(ParseError::MessageLength( header.length + bytes.bytes.len() as u32, header.length, ))); } Some(match request { Ok(request) => { if self.0.debug { self.print_msg(header.object_id, header.opcode, &request.args(), true); } PendingRequestResult::Request(request) } Err(err) => PendingRequestResult::ParseError(err), }) } else { read.buf.drain(0..header.length as usize); Some(PendingRequestResult::InvalidObject(header.object_id)) } } else { None } } pub fn new_object(&self, interface: String, version: u32) -> Object { let mut state = self.0.state.lock().unwrap(); let id = state.next_id; state.next_id += 1; let object = Object::for_new_id(self.downgrade(), id, self.0.client, interface, version); state.objects.insert(id, object.clone()); object } pub(crate) fn new_peer_object( &self, id: u64, interface: String, version: u32, ) -> Result { let mut state = self.0.state.lock().unwrap(); if id < state.next_peer_id || (!self.0.client && id >= 0xff00000000000000) { return Err(ParseError::InvalidId(id)); } state.next_peer_id = id + 1; let object = crate::Object::for_new_id(self.downgrade(), id, self.0.client, interface, version); state.objects.insert(id, object.clone()); Ok(object) } pub(crate) fn new_peer_interface( &self, id: u64, version: u32, ) -> Result { Ok(self .new_peer_object(id, T::NAME.to_string(), version)? .downcast_unchecked()) } pub fn remove_id(&self, id: u64) { self.0.state.lock().unwrap().objects.remove(&id); } pub fn object_for_id(&self, id: u64) -> Option { self.0.state.lock().unwrap().objects.get(&id).cloned() } fn print_msg(&self, object_id: u64, opcode: u32, args: &[Arg], incoming: bool) { let object = self.object_for_id(object_id); let interface = object.as_ref().map_or("UNKNOWN", |x| x.interface()); let op_name = if self.0.client != incoming { eis::Request::op_name(interface, opcode) } else { ei::Event::op_name(interface, opcode) } .unwrap_or("UNKNOWN"); if incoming { eprint!(" -> "); } eprint!("{interface}@{object_id:x}.{op_name}("); let mut first = true; for arg in args { if !first { eprint!(", "); } first = false; eprint!("{}", arg); } eprintln!(")"); } pub fn request(&self, object_id: u64, opcode: u32, args: &[Arg]) { if self.0.debug { self.print_msg(object_id, opcode, args, false); } let mut write = self.0.write.lock().unwrap(); let start_len = write.buf.len(); // Leave space for header write.buf.extend([0; 16]); // Write arguments for arg in args { let write = &mut *write; arg.write(&mut write.buf, &mut write.fds); } // Write header now we know the length let header = Header { object_id, length: (write.buf.len() - start_len) as u32, opcode, }; for (i, b) in header.as_bytes().enumerate() { write.buf[start_len + i] = b; } } pub fn flush(&self) -> rustix::io::Result<()> { self.0.write.lock().unwrap().flush_write(&self.0.socket) } } fn is_reis_debug() -> bool { env::var_os("REIS_DEBUG").map_or(false, |value| !value.is_empty()) } reis-0.2.0/src/wire/mod.rs000064400000000000000000000074471046102023000134630ustar 00000000000000/// EI wire protcol. /// /// This is the lowest level component of reis. It provides serialization /// and deserialization of the protocol, and uses Rustix to handle socket IO. use std::{ collections::{self, VecDeque}, fmt, iter, os::unix::io::OwnedFd, string::FromUtf8Error, }; use crate::Object; mod arg; pub(crate) use arg::{Arg, OwnedArg}; mod backend; pub use backend::PendingRequestResult; pub(crate) use backend::{Backend, BackendWeak}; #[derive(Debug)] pub(crate) struct Header { pub object_id: u64, pub length: u32, pub opcode: u32, } impl Header { pub fn parse(bytes: [u8; 16]) -> Self { Self { object_id: u64::from_ne_bytes(bytes[0..8].try_into().unwrap()), length: u32::from_ne_bytes(bytes[8..12].try_into().unwrap()), opcode: u32::from_ne_bytes(bytes[12..16].try_into().unwrap()), } } pub fn as_bytes(&self) -> impl Iterator { self.object_id .to_ne_bytes() .into_iter() .chain(self.length.to_ne_bytes()) .chain(self.opcode.to_ne_bytes()) } } pub trait Interface: crate::private::Sealed { const NAME: &'static str; const VERSION: u32; const CLIENT_SIDE: bool; type Incoming; fn new_unchecked(object: Object) -> Self; fn as_arg(&self) -> Arg<'_>; } pub(crate) trait MessageEnum { fn args(&self) -> Vec>; } pub(crate) struct ByteStream<'a> { pub backend: &'a Backend, pub bytes: std::collections::vec_deque::Drain<'a, u8>, pub fds: &'a mut VecDeque, } impl<'a> ByteStream<'a> { pub fn backend(&self) -> &Backend { self.backend } // TODO: Using impl Iterator ran into lifetime issues fn read_n<'b>( &'b mut self, n: usize, ) -> Result>, ParseError> { if self.bytes.len() >= n { Ok(self.bytes.by_ref().take(n)) } else { Err(ParseError::EndOfMessage) } } fn read(&mut self) -> Result<[u8; N], ParseError> { Ok(crate::util::array_from_iterator_unchecked(self.read_n(N)?)) } fn read_fd(&mut self) -> Result { self.fds.pop_front().ok_or(ParseError::NoFd) } pub fn read_arg(&mut self) -> Result { T::parse(self) } } #[derive(Debug)] pub enum ParseError { EndOfMessage, Utf8(FromUtf8Error), InvalidId(u64), NoFd, InvalidOpcode(&'static str, u32), InvalidVariant(&'static str, u32), InvalidInterface(String), HeaderLength(u32), MessageLength(u32, u32), } impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::EndOfMessage => write!(f, "found end of message while parsing argument"), Self::Utf8(e) => write!(f, "invalid UTF-8 string in message: {}", e), Self::InvalidId(id) => write!(f, "new object id '{}' invalid", id), Self::NoFd => write!(f, "expected fd"), Self::InvalidOpcode(intr, op) => { write!(f, "opcode '{}' invallid for interface '{}'", op, intr) } Self::InvalidVariant(enum_, var) => { write!(f, "variant '{}' invallid for enum '{}'", var, enum_) } Self::InvalidInterface(intr) => write!(f, "unknown interface '{}'", intr), Self::HeaderLength(len) => write!(f, "header length {} < 16", len), Self::MessageLength(a, b) => { write!(f, "message length didn't match header ({} != {})", a, b) } } } } impl From for ParseError { fn from(err: FromUtf8Error) -> Self { Self::Utf8(err) } } impl std::error::Error for ParseError {} reis-0.2.0/update-protocol-bindings.sh000075500000000000000000000011461046102023000160420ustar 00000000000000#!/bin/bash set -e if [ "$#" -ne 1 ]; then >&2 echo "Usage: update-protocol-bindings.sh [path/to/libei]" exit 1 fi "$1/proto/ei-scanner" \ --component=eis \ --output=src/eiproto_eis.rs \ --jinja-extra-data='{"eis": true}' \ "$1/proto/protocol.xml" \ src/eiproto.rs.jinja "$1/proto/ei-scanner" \ --component=ei \ --output=src/eiproto_ei.rs \ "$1/proto/protocol.xml" \ src/eiproto.rs.jinja "$1/proto/ei-scanner" \ --component=ei \ --output=src/eiproto_enum.rs \ "$1/proto/protocol.xml" \ src/eiproto_enum.rs.jinja rustfmt src/eiproto_eis.rs rustfmt src/eiproto_ei.rs rustfmt src/eiproto_enum.rs