Compare commits

...

15 Commits

Author SHA1 Message Date
Ferdinand Schober
db96717044 chore: Release lan-mouse version 0.7.3 2024-03-22 12:46:57 +01:00
Ferdinand Schober
be8124a190 fix dns resolving 2024-03-22 12:40:05 +01:00
Ferdinand Schober
dcee2933a2 Create FUNDING.yml 2024-03-22 10:09:00 +01:00
Ferdinand Schober
8aaff9fb58 move to windows from win-api (#99) 2024-03-21 23:04:20 +01:00
Ferdinand Schober
742b1585d7 rename producer, consumer to emulation and capture (#98)
input emulation / input capture is clearer than event consumer and producer
2024-03-21 20:26:57 +01:00
Ferdinand Schober
78c9de45c7 add an arm64 build (#45)
closes #45
2024-03-21 17:14:33 +01:00
Ferdinand Schober
a491c0e9e3 refactor producer and consumer (#97) 2024-03-21 16:55:54 +01:00
Ferdinand Schober
af02cccc2a exit instead of panicing when con to backend lost 2024-03-21 15:35:56 +01:00
Ferdinand Schober
4a6399f866 Update README.md - now available on crates.io 2024-03-21 13:37:01 +01:00
Ferdinand Schober
66bce9083e chore: Release lan-mouse version 0.7.2 2024-03-21 13:06:28 +01:00
Ferdinand Schober
102b64f2b4 chore: Release lan-mouse version 0.7.1 2024-03-21 13:04:22 +01:00
Ferdinand Schober
4b499742ad update dependencies 2024-03-21 13:02:21 +01:00
Ferdinand Schober
a86d74b52c update to reis 0.2 2024-03-21 12:50:47 +01:00
Ferdinand Schober
c25a15e2d8 CI: fix download-artifact job 2024-03-20 15:28:05 +01:00
Ferdinand Schober
8b82325bdb update flake.lock, Cargo.lock 2024-03-20 14:48:29 +01:00
39 changed files with 629 additions and 610 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
github: [feschber]

View File

@@ -90,16 +90,32 @@ jobs:
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: lan-mouse-macos
name: lan-mouse-macos-intel
path: lan-mouse-macos-intel
macos-aarch64-release-build:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: install dependencies
run: brew install gtk4 libadwaita
- name: Release Build
run: |
cargo build --release
cp target/release/lan-mouse lan-mouse-macos-aarch64
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: lan-mouse-macos-aarch64
path: lan-mouse-macos-aarch64
pre-release:
name: "Pre Release"
needs: [windows-release-build, linux-release-build, macos-release-build]
runs-on: "ubuntu-latest"
steps:
- name: Download build artifacts
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
- name: Create Release
uses: "marvinpinto/action-automatic-releases@latest"
with:
@@ -109,5 +125,6 @@ jobs:
title: "Development Build"
files: |
lan-mouse-linux/lan-mouse
lan-mouse-macos/lan-mouse-macos-intel
lan-mouse-macos-intel/lan-mouse-macos-intel
lan-mouse-macos-aarch64/lan-mouse-macos-aarch64
lan-mouse-windows/lan-mouse-windows.zip

View File

@@ -110,3 +110,23 @@ jobs:
with:
name: lan-mouse-macos
path: target/debug/lan-mouse
build-macos-aarch64:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: install dependencies
run: brew install gtk4 libadwaita
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- name: Check Formatting
run: cargo fmt --check
- name: Clippy
run: cargo clippy --all-features --all-targets -- --deny warnings
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: lan-mouse-macos-aarch64
path: target/debug/lan-mouse

View File

@@ -86,16 +86,32 @@ jobs:
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: lan-mouse-macos
name: lan-mouse-macos-intel
path: lan-mouse-macos-intel
macos-aarch64-release-build:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: install dependencies
run: brew install gtk4 libadwaita
- name: Release Build
run: |
cargo build --release
cp target/release/lan-mouse lan-mouse-macos-aarch64
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: lan-mouse-macos-aarch64
path: lan-mouse-macos-aarch64
tagged-release:
name: "Tagged Release"
needs: [windows-release-build, linux-release-build, macos-release-build]
runs-on: "ubuntu-latest"
steps:
- name: Download build artifacts
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
- name: "Create Release"
uses: "marvinpinto/action-automatic-releases@latest"
with:
@@ -103,5 +119,6 @@ jobs:
prerelease: false
files: |
lan-mouse-linux/lan-mouse
lan-mouse-macos/lan-mouse-macos-intel
lan-mouse-macos-intel/lan-mouse-macos-intel
lan-mouse-macos-aarch64/lan-mouse-macos-aarch64
lan-mouse-windows/lan-mouse-windows.zip

295
Cargo.lock generated
View File

@@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
@@ -275,9 +275,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.4.2"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "block-buffer"
@@ -312,23 +312,22 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cairo-rs"
version = "0.18.5"
version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2"
checksum = "2650f66005301bd33cc486dec076e1293c4cecf768bc7ba9bf5d2b1be339b99c"
dependencies = [
"bitflags 2.4.2",
"bitflags 2.5.0",
"cairo-sys-rs",
"glib",
"libc",
"once_cell",
"thiserror",
]
[[package]]
name = "cairo-sys-rs"
version = "0.18.2"
version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51"
checksum = "fd3bb3119664efbd78b5e6c93957447944f16bdbced84c17a9f41c7829b81e64"
dependencies = [
"glib-sys",
"libc",
@@ -565,16 +564,26 @@ dependencies = [
]
[[package]]
name = "env_logger"
version = "0.10.2"
name = "env_filter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
dependencies = [
"humantime",
"is-terminal",
"log",
"regex",
"termcolor",
]
[[package]]
name = "env_logger"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"humantime",
"log",
]
[[package]]
@@ -797,22 +806,21 @@ dependencies = [
[[package]]
name = "gdk-pixbuf"
version = "0.18.5"
version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec"
checksum = "f6a23f8a0b5090494fd04924662d463f8386cc678dd3915015a838c1a3679b92"
dependencies = [
"gdk-pixbuf-sys",
"gio",
"glib",
"libc",
"once_cell",
]
[[package]]
name = "gdk-pixbuf-sys"
version = "0.18.0"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7"
checksum = "3dcbd04c1b2c4834cc008b4828bc917d062483b88d26effde6342e5622028f96"
dependencies = [
"gio-sys",
"glib-sys",
@@ -823,9 +831,9 @@ dependencies = [
[[package]]
name = "gdk4"
version = "0.7.3"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edb019ad581f8ecf8ea8e4baa6df7c483a95b5a59be3140be6a9c3b0c632af6"
checksum = "9100b25604183f2fd97f55ef087fae96ab4934d7215118a35303e422688e6e4b"
dependencies = [
"cairo-rs",
"gdk-pixbuf",
@@ -838,9 +846,9 @@ dependencies = [
[[package]]
name = "gdk4-sys"
version = "0.7.2"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbab43f332a3cf1df9974da690b5bb0e26720ed09a228178ce52175372dcfef0"
checksum = "d0b76874c40bb8d1c7d03a7231e23ac75fa577a456cd53af32ec17ec8f121626"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
@@ -882,9 +890,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "gio"
version = "0.18.4"
version = "0.19.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73"
checksum = "c64947d08d7fbb03bf8ad1f25a8ac6cf4329bc772c9b7e5abe7bf9493c81194f"
dependencies = [
"futures-channel",
"futures-core",
@@ -893,7 +901,6 @@ dependencies = [
"gio-sys",
"glib",
"libc",
"once_cell",
"pin-project-lite",
"smallvec",
"thiserror",
@@ -901,24 +908,24 @@ dependencies = [
[[package]]
name = "gio-sys"
version = "0.18.1"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2"
checksum = "bcf8e1d9219bb294636753d307b030c1e8a032062cba74f493c431a5c8b81ce4"
dependencies = [
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
"winapi",
"windows-sys 0.52.0",
]
[[package]]
name = "glib"
version = "0.18.5"
version = "0.19.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5"
checksum = "01e191cc1af1f35b9699213107068cd3fe05d9816275ac118dc785a0dd8faebf"
dependencies = [
"bitflags 2.4.2",
"bitflags 2.5.0",
"futures-channel",
"futures-core",
"futures-executor",
@@ -930,26 +937,27 @@ dependencies = [
"gobject-sys",
"libc",
"memchr",
"once_cell",
"smallvec",
"thiserror",
]
[[package]]
name = "glib-build-tools"
version = "0.18.0"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3431c56f463443cba9bc3600248bc6d680cb614c2ee1cdd39dab5415bd12ac5c"
checksum = "108f374fff60efd14b0d70d8916e7213aed18d7dd071ba3e9334ed2dac1dc86a"
dependencies = [
"gio",
]
[[package]]
name = "glib-macros"
version = "0.18.5"
version = "0.19.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc"
checksum = "9972bb91643d589c889654693a4f1d07697fdcb5d104b5c44fb68649ba1bf68d"
dependencies = [
"heck 0.4.1",
"proc-macro-crate 2.0.0",
"proc-macro-error",
"heck 0.5.0",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.53",
@@ -957,9 +965,9 @@ dependencies = [
[[package]]
name = "glib-sys"
version = "0.18.1"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898"
checksum = "630f097773d7c7a0bb3258df4e8157b47dc98bbfa0e60ad9ab56174813feced4"
dependencies = [
"libc",
"system-deps",
@@ -967,9 +975,9 @@ dependencies = [
[[package]]
name = "gobject-sys"
version = "0.18.0"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44"
checksum = "c85e2b1080b9418dd0c58b498da3a5c826030343e0ef07bde6a955d28de54979"
dependencies = [
"glib-sys",
"libc",
@@ -978,9 +986,9 @@ dependencies = [
[[package]]
name = "graphene-rs"
version = "0.18.1"
version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b2228cda1505613a7a956cca69076892cfbda84fc2b7a62b94a41a272c0c401"
checksum = "99e4d388e96c5f29e2b2f67045d229ddf826d0a8d6d282f94ed3b34452222c91"
dependencies = [
"glib",
"graphene-sys",
@@ -989,9 +997,9 @@ dependencies = [
[[package]]
name = "graphene-sys"
version = "0.18.1"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc4144cee8fc8788f2a9b73dc5f1d4e1189d1f95305c4cb7bd9c1af1cfa31f59"
checksum = "236ed66cc9b18d8adf233716f75de803d0bf6fc806f60d14d948974a12e240d0"
dependencies = [
"glib-sys",
"libc",
@@ -1001,9 +1009,9 @@ dependencies = [
[[package]]
name = "gsk4"
version = "0.7.3"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d958e351d2f210309b32d081c832d7de0aca0b077aa10d88336c6379bd01f7e"
checksum = "c65036fc8f99579e8cb37b12487969b707ab23ec8ab953682ff347cbd15d396e"
dependencies = [
"cairo-rs",
"gdk4",
@@ -1016,9 +1024,9 @@ dependencies = [
[[package]]
name = "gsk4-sys"
version = "0.7.3"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12bd9e3effea989f020e8f1ff3fa3b8c63ba93d43b899c11a118868853a56d55"
checksum = "bd24c814379f9c3199dc53e52253ee8d0f657eae389ab282c330505289d24738"
dependencies = [
"cairo-sys-rs",
"gdk4-sys",
@@ -1032,9 +1040,9 @@ dependencies = [
[[package]]
name = "gtk4"
version = "0.7.3"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aeb51aa3e9728575a053e1f43543cd9992ac2477e1b186ad824fd4adfb70842"
checksum = "aa82753b8c26277e4af1446c70e35b19aad4fb794a7b143859e7eeb9a4025d83"
dependencies = [
"cairo-rs",
"field-offset",
@@ -1053,12 +1061,12 @@ dependencies = [
[[package]]
name = "gtk4-macros"
version = "0.7.2"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d57ec49cf9b657f69a05bca8027cff0a8dfd0c49e812be026fc7311f2163832f"
checksum = "40300bf071d2fcd4c94eacc09e84ec6fe73129d2ceb635cf7e55b026b5443567"
dependencies = [
"anyhow",
"proc-macro-crate 1.3.1",
"proc-macro-crate",
"proc-macro-error",
"proc-macro2",
"quote",
@@ -1067,9 +1075,9 @@ dependencies = [
[[package]]
name = "gtk4-sys"
version = "0.7.3"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54d8c4aa23638ce9faa2caf7e2a27d4a1295af2155c8e8d28c4d4eeca7a65eb8"
checksum = "0db1b104138f087ccdc81d2c332de5dd049b89de3d384437cc1093b17cd2da18"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
@@ -1188,17 +1196,6 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
[[package]]
name = "is-terminal"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
dependencies = [
"hermit-abi",
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "itoa"
version = "1.0.10"
@@ -1231,7 +1228,7 @@ dependencies = [
[[package]]
name = "lan-mouse"
version = "0.6.0"
version = "0.7.3"
dependencies = [
"anyhow",
"ashpd",
@@ -1261,15 +1258,15 @@ dependencies = [
"wayland-protocols",
"wayland-protocols-misc",
"wayland-protocols-wlr",
"winapi",
"windows",
"x11",
]
[[package]]
name = "libadwaita"
version = "0.5.3"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fe7e70c06507ed10a16cda707f358fbe60fe0dc237498f78c686ade92fd979c"
checksum = "91b4990248b9e1ec5e72094a2ccaea70ec3809f88f6fd52192f2af306b87c5d9"
dependencies = [
"gdk-pixbuf",
"gdk4",
@@ -1283,9 +1280,9 @@ dependencies = [
[[package]]
name = "libadwaita-sys"
version = "0.5.3"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e10aaa38de1d53374f90deeb4535209adc40cc5dba37f9704724169bceec69a"
checksum = "23a748e4e92be1265cd9e93d569c0b5dfc7814107985aa6743d670ab281ea1a8"
dependencies = [
"gdk4-sys",
"gio-sys",
@@ -1407,7 +1404,7 @@ version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
"bitflags 2.4.2",
"bitflags 2.5.0",
"cfg-if",
"cfg_aliases",
"libc",
@@ -1457,22 +1454,21 @@ dependencies = [
[[package]]
name = "pango"
version = "0.18.3"
version = "0.19.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4"
checksum = "b1264d13deb823cc652f26cfe59afb1ec4b9db2a5bd27c41b738c879cc1bfaa1"
dependencies = [
"gio",
"glib",
"libc",
"once_cell",
"pango-sys",
]
[[package]]
name = "pango-sys"
version = "0.18.0"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5"
checksum = "f52ef6a881c19fbfe3b1484df5cad411acaaba29dbec843941c3110d19f340ea"
dependencies = [
"glib-sys",
"gobject-sys",
@@ -1564,25 +1560,6 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit 0.19.15",
]
[[package]]
name = "proc-macro-crate"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8"
dependencies = [
"toml_edit 0.20.7",
]
[[package]]
name = "proc-macro-crate"
version = "3.1.0"
@@ -1719,8 +1696,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "reis"
version = "0.1.0"
source = "git+https://github.com/ids1024/reis#8737b0ae5a66c7c36a4b1686ebb4b68aabee4367"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "635de3608f72e8d067f8972f9401619ac7a3f34a0a17340fa0e3f9db57e067a3"
dependencies = [
"futures",
"rustix",
@@ -1754,11 +1732,11 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.31"
version = "0.38.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
dependencies = [
"bitflags 2.4.2",
"bitflags 2.5.0",
"errno",
"libc",
"linux-raw-sys",
@@ -1871,9 +1849,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.13.1"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "socket2"
@@ -1921,12 +1899,12 @@ dependencies = [
[[package]]
name = "system-deps"
version = "6.2.1"
version = "6.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8e9199467bcbc77c6a13cc6e32a6af21721ab8c96aa0261856c4fda5a4433f0"
checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
dependencies = [
"cfg-expr",
"heck 0.4.1",
"heck 0.5.0",
"pkg-config",
"toml",
"version-compare",
@@ -1950,15 +1928,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.58"
@@ -2033,7 +2002,7 @@ dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.22.8",
"toml_edit 0.22.9",
]
[[package]]
@@ -2045,28 +2014,6 @@ dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap",
"toml_datetime",
"winnow 0.5.40",
]
[[package]]
name = "toml_edit"
version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81"
dependencies = [
"indexmap",
"toml_datetime",
"winnow 0.5.40",
]
[[package]]
name = "toml_edit"
version = "0.21.1"
@@ -2080,9 +2027,9 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.22.8"
version = "0.22.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c12219811e0c1ba077867254e5ad62ee2c9c190b0d957110750ac0cda1ae96cd"
checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4"
dependencies = [
"indexmap",
"serde",
@@ -2232,9 +2179,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "version-compare"
version = "0.1.1"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
[[package]]
name = "version_check"
@@ -2268,7 +2215,7 @@ version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f"
dependencies = [
"bitflags 2.4.2",
"bitflags 2.5.0",
"rustix",
"wayland-backend",
"wayland-scanner",
@@ -2280,7 +2227,7 @@ version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4"
dependencies = [
"bitflags 2.4.2",
"bitflags 2.5.0",
"wayland-backend",
"wayland-client",
"wayland-scanner",
@@ -2292,7 +2239,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa5933740b200188c9b4c38601b8212e8c154d7de0d2cb171944e137a77de1e"
dependencies = [
"bitflags 2.4.2",
"bitflags 2.5.0",
"wayland-backend",
"wayland-client",
"wayland-protocols",
@@ -2305,7 +2252,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6"
dependencies = [
"bitflags 2.4.2",
"bitflags 2.5.0",
"wayland-backend",
"wayland-client",
"wayland-protocols",
@@ -2356,21 +2303,41 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi",
]
[[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"
version = "0.54.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49"
dependencies = [
"windows-core",
"windows-targets 0.52.4",
]
[[package]]
name = "windows-core"
version = "0.54.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65"
dependencies = [
"windows-result",
"windows-targets 0.52.4",
]
[[package]]
name = "windows-result"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64"
dependencies = [
"windows-targets 0.52.4",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
@@ -2591,7 +2558,7 @@ version = "4.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e0e3852c93dcdb49c9462afe67a2a468f7bd464150d866e861eaf06208633e0"
dependencies = [
"proc-macro-crate 3.1.0",
"proc-macro-crate",
"proc-macro2",
"quote",
"regex",
@@ -2630,7 +2597,7 @@ version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7a4b236063316163b69039f77ce3117accb41a09567fd24c168e43491e521bc"
dependencies = [
"proc-macro-crate 3.1.0",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 1.0.109",

View File

@@ -1,7 +1,7 @@
[package]
name = "lan-mouse"
description = "Software KVM Switch / mouse & keyboard sharing software for Local Area Networks"
version = "0.7.0"
version = "0.7.3"
edition = "2021"
license = "GPL-3.0-or-later"
repository = "https://github.com/ferdinandschober/lan-mouse"
@@ -20,15 +20,15 @@ toml = "0.8"
serde = { version = "1.0", features = ["derive"] }
anyhow = "1.0.71"
log = "0.4.20"
env_logger = "0.10.0"
env_logger = "0.11.3"
serde_json = "1.0.107"
tokio = {version = "1.32.0", features = ["io-util", "macros", "net", "rt", "sync", "signal"] }
async-trait = "0.1.73"
futures-core = "0.3.28"
futures = "0.3.28"
clap = { version="4.4.11", features = ["derive"] }
gtk = { package = "gtk4", version = "0.7.2", features = ["v4_2"], optional = true }
adw = { package = "libadwaita", version = "0.5.2", features = ["v1_1"], optional = true }
gtk = { package = "gtk4", version = "0.8.1", features = ["v4_2"], optional = true }
adw = { package = "libadwaita", version = "0.6.0", features = ["v1_1"], optional = true }
async-channel = { version = "2.1.1", optional = true }
keycode = "0.4.0"
once_cell = "1.19.0"
@@ -43,16 +43,16 @@ wayland-protocols-wlr = { version="0.2.0", features=["client"], optional = true
wayland-protocols-misc = { version="0.2.0", features=["client"], optional = true }
x11 = { version = "2.21.0", features = ["xlib", "xtest"], optional = true }
ashpd = { version = "0.8", default-features = false, features = ["tokio"], optional = true }
reis = { git = "https://github.com/ids1024/reis", features = [ "tokio" ], optional = true }
reis = { version = "0.2", features = [ "tokio" ], optional = true }
[target.'cfg(target_os="macos")'.dependencies]
core-graphics = { version = "0.23", features = ["highsierra"] }
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.9", features = ["winuser"] }
windows = { version = "0.54.0", features = [ "Win32_UI_Input_KeyboardAndMouse" ] }
[build-dependencies]
glib-build-tools = "0.18.0"
glib-build-tools = "0.19.0"
[features]
default = ["wayland", "x11", "xdg_desktop_portal", "libei", "gtk"]

View File

@@ -58,6 +58,10 @@ input capture (to send events *to* other clients) on different operating systems
> Otherwise input capture will not work.
## Installation
### Install with cargo
```sh
cargo install lan-mouse
```
### Download from Releases
The easiest way to install Lan Mouse is to download precompiled release binaries from the [releases section](https://github.com/feschber/lan-mouse/releases).

12
flake.lock generated
View File

@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1706913249,
"narHash": "sha256-x3M7iV++CsvRXI1fpyFPduGELUckZEhSv0XWnUopAG8=",
"lastModified": 1710806803,
"narHash": "sha256-qrxvLS888pNJFwJdK+hf1wpRCSQcqA6W5+Ox202NDa0=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "e92b6015881907e698782c77641aa49298330223",
"rev": "b06025f1533a1e07b6db3e75151caa155d1c7eb3",
"type": "github"
},
"original": {
@@ -48,11 +48,11 @@
]
},
"locked": {
"lastModified": 1707099356,
"narHash": "sha256-ph483MDKLi9I/gndYOieVP41es633DOOmPjEI50x5KU=",
"lastModified": 1710987136,
"narHash": "sha256-Q8GRdlAIKZ8tJUXrbcRO1pA33AdoPfTUirsSnmGQnOU=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "61dfa5a8129f7edbe9150253c68f673f87b16fb1",
"rev": "97596b54ac34ad8184ca1eef44b1ec2e5c2b5f9e",
"type": "github"
},
"original": {

View File

@@ -27,10 +27,6 @@ rustPlatform.buildRustPackage {
cargoLock.lockFile = ../Cargo.lock;
cargoLock.outputHashes = {
"reis-0.1.0" = "sha256-QhsRIkzW3wgOlcHpkx3axjS8Vfed00Uf36av9ossPwQ=";
};
# Set Environment Variables
RUST_BACKTRACE = "full";

View File

@@ -1,2 +0,0 @@
pub mod consumer;
pub mod producer;

View File

@@ -1,20 +0,0 @@
#[cfg(windows)]
pub mod windows;
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
pub mod x11;
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
pub mod wlroots;
#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))]
pub mod xdg_desktop_portal;
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
pub mod libei;
#[cfg(target_os = "macos")]
pub mod macos;
/// fallback consumer
pub mod dummy;

View File

@@ -1,17 +0,0 @@
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
pub mod libei;
#[cfg(target_os = "macos")]
pub mod macos;
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
pub mod wayland;
#[cfg(windows)]
pub mod windows;
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
pub mod x11;
/// fallback event producer
pub mod dummy;

78
src/capture.rs Normal file
View File

@@ -0,0 +1,78 @@
use std::io;
use futures_core::Stream;
use crate::{
client::{ClientEvent, ClientHandle},
event::Event,
};
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
pub mod libei;
#[cfg(target_os = "macos")]
pub mod macos;
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
pub mod wayland;
#[cfg(windows)]
pub mod windows;
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
pub mod x11;
/// fallback input capture (does not produce events)
pub mod dummy;
pub async fn create() -> Box<dyn InputCapture> {
#[cfg(target_os = "macos")]
match macos::MacOSInputCapture::new() {
Ok(p) => return Box::new(p),
Err(e) => log::info!("macos input capture not available: {e}"),
}
#[cfg(windows)]
match windows::WindowsInputCapture::new() {
Ok(p) => return Box::new(p),
Err(e) => log::info!("windows input capture not available: {e}"),
}
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
match libei::LibeiInputCapture::new().await {
Ok(p) => {
log::info!("using libei input capture");
return Box::new(p);
}
Err(e) => log::info!("libei input capture not available: {e}"),
}
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
match wayland::WaylandInputCapture::new() {
Ok(p) => {
log::info!("using layer-shell input capture");
return Box::new(p);
}
Err(e) => log::info!("layer_shell input capture not available: {e}"),
}
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
match x11::X11InputCapture::new() {
Ok(p) => {
log::info!("using x11 input capture");
return Box::new(p);
}
Err(e) => log::info!("x11 input capture not available: {e}"),
}
log::error!("falling back to dummy input capture");
Box::new(dummy::DummyInputCapture::new())
}
pub trait InputCapture: Stream<Item = io::Result<(ClientHandle, Event)>> + Unpin {
/// notify input capture of configuration changes
fn notify(&mut self, event: ClientEvent) -> io::Result<()>;
/// release mouse
fn release(&mut self) -> io::Result<()>;
}

View File

@@ -4,26 +4,26 @@ use std::task::{Context, Poll};
use futures_core::Stream;
use crate::capture::InputCapture;
use crate::event::Event;
use crate::producer::EventProducer;
use crate::client::{ClientEvent, ClientHandle};
pub struct DummyProducer {}
pub struct DummyInputCapture {}
impl DummyProducer {
impl DummyInputCapture {
pub fn new() -> Self {
Self {}
}
}
impl Default for DummyProducer {
impl Default for DummyInputCapture {
fn default() -> Self {
Self::new()
}
}
impl EventProducer for DummyProducer {
impl InputCapture for DummyInputCapture {
fn notify(&mut self, _event: ClientEvent) -> io::Result<()> {
Ok(())
}
@@ -33,7 +33,7 @@ impl EventProducer for DummyProducer {
}
}
impl Stream for DummyProducer {
impl Stream for DummyInputCapture {
type Item = io::Result<(ClientHandle, Event)>;
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {

View File

@@ -31,9 +31,9 @@ use futures_core::Stream;
use once_cell::sync::Lazy;
use crate::{
capture::InputCapture as LanMouseInputCapture,
client::{ClientEvent, ClientHandle, Position},
event::{Event, KeyboardEvent, PointerEvent},
producer::EventProducer,
};
#[derive(Debug)]
@@ -43,7 +43,7 @@ enum ProducerEvent {
}
#[allow(dead_code)]
pub struct LibeiProducer<'a> {
pub struct LibeiInputCapture<'a> {
input_capture: Pin<Box<InputCapture<'a>>>,
libei_task: JoinHandle<Result<()>>,
event_rx: tokio::sync::mpsc::Receiver<(u32, Event)>,
@@ -123,7 +123,7 @@ async fn update_barriers(
Ok(id_map)
}
impl<'a> Drop for LibeiProducer<'a> {
impl<'a> Drop for LibeiInputCapture<'a> {
fn drop(&mut self) {
self.libei_task.abort();
}
@@ -212,7 +212,7 @@ async fn wait_for_active_client(
Ok(())
}
impl<'a> LibeiProducer<'a> {
impl<'a> LibeiInputCapture<'a> {
pub async fn new() -> Result<Self> {
let input_capture = Box::pin(InputCapture::new().await?);
let input_capture_ptr = input_capture.as_ref().get_ref() as *const InputCapture<'static>;
@@ -522,7 +522,7 @@ async fn handle_ei_event(
}
}
impl<'a> EventProducer for LibeiProducer<'a> {
impl<'a> LanMouseInputCapture for LibeiInputCapture<'a> {
fn notify(&mut self, event: ClientEvent) -> io::Result<()> {
let notify_tx = self.notify_tx.clone();
tokio::task::spawn_local(async move {
@@ -543,7 +543,7 @@ impl<'a> EventProducer for LibeiProducer<'a> {
}
}
impl<'a> Stream for LibeiProducer<'a> {
impl<'a> Stream for LibeiInputCapture<'a> {
type Item = io::Result<(ClientHandle, Event)>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {

View File

@@ -1,20 +1,20 @@
use crate::capture::InputCapture;
use crate::client::{ClientEvent, ClientHandle};
use crate::event::Event;
use crate::producer::EventProducer;
use anyhow::{anyhow, Result};
use futures_core::Stream;
use std::task::{Context, Poll};
use std::{io, pin::Pin};
pub struct MacOSProducer;
pub struct MacOSInputCapture;
impl MacOSProducer {
impl MacOSInputCapture {
pub fn new() -> Result<Self> {
Err(anyhow!("not yet implemented"))
}
}
impl Stream for MacOSProducer {
impl Stream for MacOSInputCapture {
type Item = io::Result<(ClientHandle, Event)>;
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
@@ -22,7 +22,7 @@ impl Stream for MacOSProducer {
}
}
impl EventProducer for MacOSProducer {
impl InputCapture for MacOSInputCapture {
fn notify(&mut self, _event: ClientEvent) -> io::Result<()> {
Ok(())
}

View File

@@ -1,6 +1,6 @@
use crate::{
capture::InputCapture,
client::{ClientEvent, ClientHandle, Position},
producer::EventProducer,
};
use anyhow::{anyhow, Result};
@@ -124,7 +124,7 @@ impl AsRawFd for Inner {
}
}
pub struct WaylandEventProducer(AsyncFd<Inner>);
pub struct WaylandInputCapture(AsyncFd<Inner>);
struct Window {
buffer: wl_buffer::WlBuffer,
@@ -256,7 +256,7 @@ fn draw(f: &mut File, (width, height): (u32, u32)) {
}
}
impl WaylandEventProducer {
impl WaylandInputCapture {
pub fn new() -> Result<Self> {
let conn = match Connection::connect_to_env() {
Ok(c) => c,
@@ -390,7 +390,7 @@ impl WaylandEventProducer {
let inner = AsyncFd::new(Inner { queue, state })?;
Ok(WaylandEventProducer(inner))
Ok(WaylandInputCapture(inner))
}
fn add_client(&mut self, handle: ClientHandle, pos: Position) {
@@ -587,7 +587,7 @@ impl Inner {
}
}
impl EventProducer for WaylandEventProducer {
impl InputCapture for WaylandInputCapture {
fn notify(&mut self, client_event: ClientEvent) -> io::Result<()> {
match client_event {
ClientEvent::Create(handle, pos) => {
@@ -609,7 +609,7 @@ impl EventProducer for WaylandEventProducer {
}
}
impl Stream for WaylandEventProducer {
impl Stream for WaylandInputCapture {
type Item = io::Result<(ClientHandle, Event)>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {

View File

@@ -4,14 +4,14 @@ use futures::Stream;
use std::{io, pin::Pin};
use crate::{
capture::InputCapture,
client::{ClientEvent, ClientHandle},
event::Event,
producer::EventProducer,
};
pub struct WindowsProducer {}
pub struct WindowsInputCapture {}
impl EventProducer for WindowsProducer {
impl InputCapture for WindowsInputCapture {
fn notify(&mut self, _event: ClientEvent) -> io::Result<()> {
Ok(())
}
@@ -21,13 +21,13 @@ impl EventProducer for WindowsProducer {
}
}
impl WindowsProducer {
impl WindowsInputCapture {
pub(crate) fn new() -> Result<Self> {
Err(anyhow!("not implemented"))
}
}
impl Stream for WindowsProducer {
impl Stream for WindowsInputCapture {
type Item = io::Result<(ClientHandle, Event)>;
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
Poll::Pending

View File

@@ -4,20 +4,20 @@ use std::task::Poll;
use futures_core::Stream;
use crate::capture::InputCapture;
use crate::event::Event;
use crate::producer::EventProducer;
use crate::client::{ClientEvent, ClientHandle};
pub struct X11Producer {}
pub struct X11InputCapture {}
impl X11Producer {
impl X11InputCapture {
pub fn new() -> Result<Self> {
Err(anyhow!("not implemented"))
}
}
impl EventProducer for X11Producer {
impl InputCapture for X11InputCapture {
fn notify(&mut self, _event: ClientEvent) -> io::Result<()> {
Ok(())
}
@@ -27,7 +27,7 @@ impl EventProducer for X11Producer {
}
}
impl Stream for X11Producer {
impl Stream for X11InputCapture {
type Item = io::Result<(ClientHandle, Event)>;
fn poll_next(

View File

@@ -67,7 +67,7 @@ pub struct Client {
/// fix ips, determined by the user
pub fix_ips: Vec<IpAddr>,
/// unique handle to refer to the client.
/// This way any event consumer / producer backend does not
/// This way any emulation / capture backend does not
/// need to know anything about a client other than its handle.
pub handle: ClientHandle,
/// all ip addresses associated with a particular client

View File

@@ -1,78 +0,0 @@
use async_trait::async_trait;
use std::future;
use crate::{
backend::consumer,
client::{ClientEvent, ClientHandle},
event::Event,
};
use anyhow::Result;
#[async_trait]
pub trait EventConsumer: Send {
async fn consume(&mut self, event: Event, client_handle: ClientHandle);
async fn notify(&mut self, client_event: ClientEvent);
/// this function is waited on continuously and can be used to handle events
async fn dispatch(&mut self) -> Result<()> {
let _: () = future::pending().await;
Ok(())
}
async fn destroy(&mut self);
}
pub async fn create() -> Box<dyn EventConsumer> {
#[cfg(windows)]
match consumer::windows::WindowsConsumer::new() {
Ok(c) => return Box::new(c),
Err(e) => log::warn!("windows event consumer unavailable: {e}"),
}
#[cfg(target_os = "macos")]
match consumer::macos::MacOSConsumer::new() {
Ok(c) => {
log::info!("using macos event consumer");
return Box::new(c);
}
Err(e) => log::error!("macos consumer not available: {e}"),
}
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
match consumer::wlroots::WlrootsConsumer::new() {
Ok(c) => {
log::info!("using wlroots event consumer");
return Box::new(c);
}
Err(e) => log::info!("wayland backend not available: {e}"),
}
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
match consumer::libei::LibeiConsumer::new().await {
Ok(c) => {
log::info!("using libei event consumer");
return Box::new(c);
}
Err(e) => log::info!("libei not available: {e}"),
}
#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))]
match consumer::xdg_desktop_portal::DesktopPortalConsumer::new().await {
Ok(c) => {
log::info!("using xdg-remote-desktop-portal event consumer");
return Box::new(c);
}
Err(e) => log::info!("remote desktop portal not available: {e}"),
}
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
match consumer::x11::X11Consumer::new() {
Ok(c) => {
log::info!("using x11 event consumer");
return Box::new(c);
}
Err(e) => log::info!("x11 consumer not available: {e}"),
}
log::error!("falling back to dummy event consumer");
Box::new(consumer::dummy::DummyConsumer::new())
}

98
src/emulate.rs Normal file
View File

@@ -0,0 +1,98 @@
use async_trait::async_trait;
use std::future;
use crate::{
client::{ClientEvent, ClientHandle},
event::Event,
};
use anyhow::Result;
#[cfg(windows)]
pub mod windows;
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
pub mod x11;
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
pub mod wlroots;
#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))]
pub mod xdg_desktop_portal;
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
pub mod libei;
#[cfg(target_os = "macos")]
pub mod macos;
/// fallback input emulation (logs events)
pub mod dummy;
#[async_trait]
pub trait InputEmulation: Send {
async fn consume(&mut self, event: Event, client_handle: ClientHandle);
async fn notify(&mut self, client_event: ClientEvent);
/// this function is waited on continuously and can be used to handle events
async fn dispatch(&mut self) -> Result<()> {
let _: () = future::pending().await;
Ok(())
}
async fn destroy(&mut self);
}
pub async fn create() -> Box<dyn InputEmulation> {
#[cfg(windows)]
match windows::WindowsEmulation::new() {
Ok(c) => return Box::new(c),
Err(e) => log::warn!("windows input emulation unavailable: {e}"),
}
#[cfg(target_os = "macos")]
match macos::MacOSEmulation::new() {
Ok(c) => {
log::info!("using macos input emulation");
return Box::new(c);
}
Err(e) => log::error!("macos input emulatino not available: {e}"),
}
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
match wlroots::WlrootsEmulation::new() {
Ok(c) => {
log::info!("using wlroots input emulation");
return Box::new(c);
}
Err(e) => log::info!("wayland backend not available: {e}"),
}
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
match libei::LibeiEmulation::new().await {
Ok(c) => {
log::info!("using libei input emulation");
return Box::new(c);
}
Err(e) => log::info!("libei not available: {e}"),
}
#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))]
match xdg_desktop_portal::DesktopPortalEmulation::new().await {
Ok(c) => {
log::info!("using xdg-remote-desktop-portal input emulation");
return Box::new(c);
}
Err(e) => log::info!("remote desktop portal not available: {e}"),
}
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
match x11::X11Emulation::new() {
Ok(c) => {
log::info!("using x11 input emulation");
return Box::new(c);
}
Err(e) => log::info!("x11 input emulation not available: {e}"),
}
log::error!("falling back to dummy input emulation");
Box::new(dummy::DummyEmulation::new())
}

View File

@@ -1,21 +1,21 @@
use crate::{
client::{ClientEvent, ClientHandle},
consumer::EventConsumer,
emulate::InputEmulation,
event::Event,
};
use async_trait::async_trait;
#[derive(Default)]
pub struct DummyConsumer;
pub struct DummyEmulation;
impl DummyConsumer {
impl DummyEmulation {
pub fn new() -> Self {
Self {}
}
}
#[async_trait]
impl EventConsumer for DummyConsumer {
impl InputEmulation for DummyEmulation {
async fn consume(&mut self, event: Event, client_handle: ClientHandle) {
log::info!("received event: ({client_handle}) {event}");
}

View File

@@ -23,11 +23,11 @@ use reis::{
use crate::{
client::{ClientEvent, ClientHandle},
consumer::EventConsumer,
emulate::InputEmulation,
event::Event,
};
pub struct LibeiConsumer {
pub struct LibeiEmulation {
handshake: bool,
context: ei::Context,
events: EiEventStream,
@@ -76,7 +76,7 @@ async fn get_ei_fd() -> Result<OwnedFd, ashpd::Error> {
proxy.connect_to_eis(&session).await
}
impl LibeiConsumer {
impl LibeiEmulation {
pub async fn new() -> Result<Self> {
// fd is owned by the message, so we need to dup it
let eifd = get_ei_fd().await?;
@@ -107,7 +107,7 @@ impl LibeiConsumer {
}
#[async_trait]
impl EventConsumer for LibeiConsumer {
impl InputEmulation for LibeiEmulation {
async fn consume(&mut self, event: Event, _client_handle: ClientHandle) {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)

View File

@@ -1,5 +1,5 @@
use crate::client::{ClientEvent, ClientHandle};
use crate::consumer::EventConsumer;
use crate::emulate::InputEmulation;
use crate::event::{Event, KeyboardEvent, PointerEvent};
use anyhow::{anyhow, Result};
use async_trait::async_trait;
@@ -16,7 +16,7 @@ use tokio::task::AbortHandle;
const DEFAULT_REPEAT_DELAY: Duration = Duration::from_millis(500);
const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis(32);
pub struct MacOSConsumer {
pub struct MacOSEmulation {
pub event_source: CGEventSource,
repeat_task: Option<AbortHandle>,
button_state: ButtonState,
@@ -50,9 +50,9 @@ impl IndexMut<CGMouseButton> for ButtonState {
}
}
unsafe impl Send for MacOSConsumer {}
unsafe impl Send for MacOSEmulation {}
impl MacOSConsumer {
impl MacOSEmulation {
pub fn new() -> Result<Self> {
let event_source = match CGEventSource::new(CGEventSourceStateID::CombinedSessionState) {
Ok(e) => e,
@@ -108,7 +108,7 @@ fn key_event(event_source: CGEventSource, key: u16, state: u8) {
}
#[async_trait]
impl EventConsumer for MacOSConsumer {
impl InputEmulation for MacOSEmulation {
async fn consume(&mut self, event: Event, _client_handle: ClientHandle) {
match event {
Event::Pointer(pointer_event) => match pointer_event {

View File

@@ -1,21 +1,19 @@
use crate::{
consumer::EventConsumer,
emulate::InputEmulation,
event::{KeyboardEvent, PointerEvent},
scancode,
};
use anyhow::Result;
use async_trait::async_trait;
use std::ops::BitOrAssign;
use std::time::Duration;
use tokio::task::AbortHandle;
use winapi::um::winuser::{SendInput, KEYEVENTF_EXTENDEDKEY};
use winapi::{
self,
um::winuser::{
INPUT, INPUT_KEYBOARD, INPUT_MOUSE, KEYBDINPUT, KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE,
LPINPUT, MOUSEEVENTF_HWHEEL, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP,
MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_MOVE, MOUSEEVENTF_RIGHTDOWN,
MOUSEEVENTF_RIGHTUP, MOUSEEVENTF_WHEEL, MOUSEINPUT,
},
use windows::Win32::UI::Input::KeyboardAndMouse::{SendInput, INPUT_0, KEYEVENTF_EXTENDEDKEY};
use windows::Win32::UI::Input::KeyboardAndMouse::{
INPUT, INPUT_KEYBOARD, INPUT_MOUSE, KEYBDINPUT, KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE,
MOUSEEVENTF_HWHEEL, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEDOWN,
MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_MOVE, MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP,
MOUSEEVENTF_WHEEL, MOUSEINPUT,
};
use crate::{
@@ -26,18 +24,18 @@ use crate::{
const DEFAULT_REPEAT_DELAY: Duration = Duration::from_millis(500);
const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis(32);
pub struct WindowsConsumer {
pub struct WindowsEmulation {
repeat_task: Option<AbortHandle>,
}
impl WindowsConsumer {
impl WindowsEmulation {
pub fn new() -> Result<Self> {
Ok(Self { repeat_task: None })
}
}
#[async_trait]
impl EventConsumer for WindowsConsumer {
impl InputEmulation for WindowsEmulation {
async fn consume(&mut self, event: Event, _: ClientHandle) {
match event {
Event::Pointer(pointer_event) => match pointer_event {
@@ -87,7 +85,7 @@ impl EventConsumer for WindowsConsumer {
async fn destroy(&mut self) {}
}
impl WindowsConsumer {
impl WindowsEmulation {
async fn spawn_repeat_task(&mut self, key: u32) {
// there can only be one repeating key and it's
// always the last to be pressed
@@ -108,21 +106,30 @@ impl WindowsConsumer {
}
}
fn send_mouse_input(mi: MOUSEINPUT) {
fn send_input_safe(input: INPUT) {
unsafe {
let mut input = INPUT {
type_: INPUT_MOUSE,
u: std::mem::transmute(mi),
};
SendInput(
1_u32,
&mut input as LPINPUT,
std::mem::size_of::<INPUT>() as i32,
);
loop {
/* retval = number of successfully submitted events */
if SendInput(&[input], std::mem::size_of::<INPUT>() as i32) > 0 {
break;
}
}
}
}
fn send_mouse_input(mi: MOUSEINPUT) {
send_input_safe(INPUT {
r#type: INPUT_MOUSE,
Anonymous: INPUT_0 { mi },
});
}
fn send_keyboard_input(ki: KEYBDINPUT) {
send_input_safe(INPUT {
r#type: INPUT_KEYBOARD,
Anonymous: INPUT_0 { ki },
});
}
fn rel_mouse(dx: i32, dy: i32) {
let mi = MOUSEINPUT {
dx,
@@ -186,33 +193,23 @@ fn key_event(key: u32, state: u8) {
};
let extended = scancode > 0xff;
let scancode = scancode & 0xff;
let mut flags = KEYEVENTF_SCANCODE;
if extended {
flags.bitor_assign(KEYEVENTF_EXTENDEDKEY);
}
if state == 0 {
flags.bitor_assign(KEYEVENTF_KEYUP);
}
let ki = KEYBDINPUT {
wVk: 0,
wVk: Default::default(),
wScan: scancode,
dwFlags: KEYEVENTF_SCANCODE
| if extended { KEYEVENTF_EXTENDEDKEY } else { 0 }
| match state {
0 => KEYEVENTF_KEYUP,
1 => 0u32,
_ => return,
},
dwFlags: flags,
time: 0,
dwExtraInfo: 0,
};
send_keyboard_input(ki);
}
fn send_keyboard_input(ki: KEYBDINPUT) {
unsafe {
let mut input = INPUT {
type_: INPUT_KEYBOARD,
u: std::mem::zeroed(),
};
*input.u.ki_mut() = ki;
SendInput(1_u32, &mut input, std::mem::size_of::<INPUT>() as i32);
}
}
fn linux_keycode_to_windows_scancode(linux_keycode: u32) -> Option<u16> {
let linux_scancode = match scancode::Linux::try_from(linux_keycode) {
Ok(s) => s,

View File

@@ -1,5 +1,5 @@
use crate::client::{ClientEvent, ClientHandle};
use crate::consumer::EventConsumer;
use crate::emulate::InputEmulation;
use async_trait::async_trait;
use std::collections::HashMap;
use std::io;
@@ -40,13 +40,13 @@ struct State {
}
// App State, implements Dispatch event handlers
pub(crate) struct WlrootsConsumer {
pub(crate) struct WlrootsEmulation {
last_flush_failed: bool,
state: State,
queue: EventQueue<State>,
}
impl WlrootsConsumer {
impl WlrootsEmulation {
pub fn new() -> Result<Self> {
let conn = Connection::connect_to_env()?;
let (globals, queue) = registry_queue_init::<State>(&conn)?;
@@ -62,7 +62,7 @@ impl WlrootsConsumer {
let input_for_client: HashMap<ClientHandle, VirtualInput> = HashMap::new();
let mut consumer = WlrootsConsumer {
let mut emulate = WlrootsEmulation {
last_flush_failed: false,
state: State {
keymap: None,
@@ -74,16 +74,13 @@ impl WlrootsConsumer {
},
queue,
};
while consumer.state.keymap.is_none() {
consumer
.queue
.blocking_dispatch(&mut consumer.state)
.unwrap();
while emulate.state.keymap.is_none() {
emulate.queue.blocking_dispatch(&mut emulate.state).unwrap();
}
// let fd = unsafe { &File::from_raw_fd(consumer.state.keymap.unwrap().1.as_raw_fd()) };
// let fd = unsafe { &File::from_raw_fd(emulate.state.keymap.unwrap().1.as_raw_fd()) };
// let mmap = unsafe { MmapOptions::new().map_copy(fd).unwrap() };
// log::debug!("{:?}", &mmap[..100]);
Ok(consumer)
Ok(emulate)
}
}
@@ -106,7 +103,7 @@ impl State {
}
#[async_trait]
impl EventConsumer for WlrootsConsumer {
impl InputEmulation for WlrootsEmulation {
async fn consume(&mut self, event: Event, client_handle: ClientHandle) {
if let Some(virtual_input) = self.state.input_for_client.get(&client_handle) {
if self.last_flush_failed {

View File

@@ -8,19 +8,19 @@ use x11::{
use crate::{
client::ClientHandle,
consumer::EventConsumer,
emulate::InputEmulation,
event::{
Event, KeyboardEvent, PointerEvent, BTN_BACK, BTN_FORWARD, BTN_LEFT, BTN_MIDDLE, BTN_RIGHT,
},
};
pub struct X11Consumer {
pub struct X11Emulation {
display: *mut xlib::Display,
}
unsafe impl Send for X11Consumer {}
unsafe impl Send for X11Emulation {}
impl X11Consumer {
impl X11Emulation {
pub fn new() -> Result<Self> {
let display = unsafe {
match xlib::XOpenDisplay(ptr::null()) {
@@ -91,7 +91,7 @@ impl X11Consumer {
}
}
impl Drop for X11Consumer {
impl Drop for X11Emulation {
fn drop(&mut self) {
unsafe {
XCloseDisplay(self.display);
@@ -100,7 +100,7 @@ impl Drop for X11Consumer {
}
#[async_trait]
impl EventConsumer for X11Consumer {
impl InputEmulation for X11Emulation {
async fn consume(&mut self, event: Event, _: ClientHandle) {
match event {
Event::Pointer(pointer_event) => match pointer_event {

View File

@@ -10,20 +10,20 @@ use async_trait::async_trait;
use crate::{
client::ClientEvent,
consumer::EventConsumer,
emulate::InputEmulation,
event::{
Event::{Keyboard, Pointer},
KeyboardEvent, PointerEvent,
},
};
pub struct DesktopPortalConsumer<'a> {
pub struct DesktopPortalEmulation<'a> {
proxy: RemoteDesktop<'a>,
session: Session<'a>,
}
impl<'a> DesktopPortalConsumer<'a> {
pub async fn new() -> Result<DesktopPortalConsumer<'a>> {
impl<'a> DesktopPortalEmulation<'a> {
pub async fn new() -> Result<DesktopPortalEmulation<'a>> {
log::debug!("connecting to org.freedesktop.portal.RemoteDesktop portal ...");
let proxy = RemoteDesktop::new().await?;
@@ -59,7 +59,7 @@ impl<'a> DesktopPortalConsumer<'a> {
}
#[async_trait]
impl<'a> EventConsumer for DesktopPortalConsumer<'a> {
impl<'a> InputEmulation for DesktopPortalEmulation<'a> {
async fn consume(&mut self, event: crate::event::Event, _client: crate::client::ClientHandle) {
match event {
Pointer(p) => {

View File

@@ -129,7 +129,7 @@ fn build_ui(app: &Application) {
window.imp().stream.borrow_mut().replace(tx);
glib::spawn_future_local(clone!(@weak window => async move {
loop {
let notify = receiver.recv().await.unwrap();
let notify = receiver.recv().await.unwrap_or_else(|_| process::exit(1));
match notify {
FrontendNotify::NotifyClientActivate(handle, active) => {
window.activate_client(handle, active);

View File

@@ -4,9 +4,9 @@ use adw::subclass::prelude::*;
use adw::{prelude::*, ActionRow, ComboRow};
use glib::{subclass::InitializingObject, Binding};
use gtk::glib::clone;
use gtk::glib::once_cell::sync::Lazy;
use gtk::glib::subclass::Signal;
use gtk::{glib, Button, CompositeTemplate, Switch};
use std::sync::OnceLock;
#[derive(CompositeTemplate, Default)]
#[template(resource = "/de/feschber/LanMouse/client_row.ui")]
@@ -55,15 +55,15 @@ impl ObjectImpl for ClientRow {
}
fn signals() -> &'static [glib::subclass::Signal] {
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
SIGNALS.get_or_init(|| {
vec![
Signal::builder("request-update")
.param_types([bool::static_type()])
.build(),
Signal::builder("request-delete").build(),
]
});
SIGNALS.as_ref()
})
}
}

View File

@@ -4,9 +4,8 @@ pub mod dns;
pub mod event;
pub mod server;
pub mod consumer;
pub mod producer;
pub mod capture;
pub mod emulate;
pub mod backend;
pub mod frontend;
pub mod scancode;

View File

@@ -1,61 +0,0 @@
use std::io;
use futures_core::Stream;
use crate::backend::producer;
use crate::{
client::{ClientEvent, ClientHandle},
event::Event,
};
pub async fn create() -> Box<dyn EventProducer> {
#[cfg(target_os = "macos")]
match producer::macos::MacOSProducer::new() {
Ok(p) => return Box::new(p),
Err(e) => log::info!("macos event producer not available: {e}"),
}
#[cfg(windows)]
match producer::windows::WindowsProducer::new() {
Ok(p) => return Box::new(p),
Err(e) => log::info!("windows event producer not available: {e}"),
}
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
match producer::libei::LibeiProducer::new().await {
Ok(p) => {
log::info!("using libei event producer");
return Box::new(p);
}
Err(e) => log::info!("libei event producer not available: {e}"),
}
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
match producer::wayland::WaylandEventProducer::new() {
Ok(p) => {
log::info!("using layer-shell event producer");
return Box::new(p);
}
Err(e) => log::info!("layer_shell event producer not available: {e}"),
}
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
match producer::x11::X11Producer::new() {
Ok(p) => {
log::info!("using x11 event producer");
return Box::new(p);
}
Err(e) => log::info!("x11 event producer not available: {e}"),
}
log::error!("falling back to dummy event producer");
Box::new(producer::dummy::DummyProducer::new())
}
pub trait EventProducer: Stream<Item = io::Result<(ClientHandle, Event)>> + Unpin {
/// notify event producer of configuration changes
fn notify(&mut self, event: ClientEvent) -> io::Result<()>;
/// release mouse
fn release(&mut self) -> io::Result<()>;
}

View File

@@ -5,22 +5,22 @@ use std::{
};
use tokio::signal;
use crate::{capture, emulate};
use crate::{
client::{ClientHandle, ClientManager},
config::Config,
dns,
frontend::{FrontendEvent, FrontendListener},
server::producer_task::ProducerEvent,
server::capture_task::CaptureEvent,
};
use crate::{consumer, producer};
use self::{consumer_task::ConsumerEvent, resolver_task::DnsRequest};
use self::{emulation_task::EmulationEvent, resolver_task::DnsRequest};
mod consumer_task;
mod capture_task;
mod emulation_task;
mod frontend_task;
mod network_task;
mod ping_task;
mod producer_task;
mod resolver_task;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -78,7 +78,7 @@ impl Server {
return anyhow::Ok(());
}
};
let (consumer, producer) = tokio::join!(consumer::create(), producer::create());
let (emulate, capture) = tokio::join!(emulate::create(), capture::create());
let (timer_tx, timer_rx) = tokio::sync::mpsc::channel(1);
let (frontend_notify_tx, frontend_notify_rx) = tokio::sync::mpsc::channel(1);
@@ -87,22 +87,22 @@ impl Server {
let (mut udp_task, sender_tx, receiver_rx, port_tx) =
network_task::new(self.clone(), frontend_notify_tx).await?;
// event producer
let (mut producer_task, producer_channel) = producer_task::new(
producer,
// input capture
let (mut capture_task, capture_channel) = capture_task::new(
capture,
self.clone(),
sender_tx.clone(),
timer_tx.clone(),
self.release_bind.clone(),
);
// event consumer
let (mut consumer_task, consumer_channel) = consumer_task::new(
consumer,
// input emulation
let (mut emulation_task, emulate_channel) = emulation_task::new(
emulate,
self.clone(),
receiver_rx,
sender_tx.clone(),
producer_channel.clone(),
capture_channel.clone(),
timer_tx,
);
@@ -115,8 +115,8 @@ impl Server {
frontend,
frontend_notify_rx,
self.clone(),
producer_channel.clone(),
consumer_channel.clone(),
capture_channel.clone(),
emulate_channel.clone(),
resolve_tx.clone(),
port_tx,
);
@@ -125,8 +125,8 @@ impl Server {
let mut ping_task = ping_task::new(
self.clone(),
sender_tx.clone(),
consumer_channel.clone(),
producer_channel.clone(),
emulate_channel.clone(),
capture_channel.clone(),
timer_rx,
);
@@ -156,14 +156,14 @@ impl Server {
_ = signal::ctrl_c() => {
log::info!("terminating service");
}
e = &mut producer_task => {
e = &mut capture_task => {
if let Ok(Err(e)) = e {
log::error!("error in event producer: {e}");
log::error!("error in input capture task: {e}");
}
}
e = &mut consumer_task => {
e = &mut emulation_task => {
if let Ok(Err(e)) = e {
log::error!("error in event consumer: {e}");
log::error!("error in input emulation task: {e}");
}
}
e = &mut frontend_task => {
@@ -176,18 +176,18 @@ impl Server {
_ = &mut ping_task => { }
}
let _ = consumer_channel.send(ConsumerEvent::Terminate).await;
let _ = producer_channel.send(ProducerEvent::Terminate).await;
let _ = emulate_channel.send(EmulationEvent::Terminate).await;
let _ = capture_channel.send(CaptureEvent::Terminate).await;
let _ = frontend_tx.send(FrontendEvent::Shutdown()).await;
if !producer_task.is_finished() {
if let Err(e) = producer_task.await {
log::error!("error in event producer: {e}");
if !capture_task.is_finished() {
if let Err(e) = capture_task.await {
log::error!("error in input capture task: {e}");
}
}
if !consumer_task.is_finished() {
if let Err(e) = consumer_task.await {
log::error!("error in event consumer: {e}");
if !emulation_task.is_finished() {
if let Err(e) = emulation_task.await {
log::error!("error in input emulation task: {e}");
}
}

View File

@@ -5,9 +5,9 @@ use std::{collections::HashSet, net::SocketAddr};
use tokio::{sync::mpsc::Sender, task::JoinHandle};
use crate::{
capture::InputCapture,
client::{ClientEvent, ClientHandle},
event::{Event, KeyboardEvent},
producer::EventProducer,
scancode,
server::State,
};
@@ -15,45 +15,45 @@ use crate::{
use super::Server;
#[derive(Clone, Copy, Debug)]
pub enum ProducerEvent {
/// producer must release the mouse
pub enum CaptureEvent {
/// capture must release the mouse
Release,
/// producer is notified of a change in client states
/// capture is notified of a change in client states
ClientEvent(ClientEvent),
/// termination signal
Terminate,
}
pub fn new(
mut producer: Box<dyn EventProducer>,
mut capture: Box<dyn InputCapture>,
server: Server,
sender_tx: Sender<(Event, SocketAddr)>,
timer_tx: Sender<()>,
release_bind: Vec<scancode::Linux>,
) -> (JoinHandle<Result<()>>, Sender<ProducerEvent>) {
) -> (JoinHandle<Result<()>>, Sender<CaptureEvent>) {
let (tx, mut rx) = tokio::sync::mpsc::channel(32);
let task = tokio::task::spawn_local(async move {
let mut pressed_keys = HashSet::new();
loop {
tokio::select! {
event = producer.next() => {
event = capture.next() => {
match event {
Some(Ok(event)) => handle_producer_event(&server, &mut producer, &sender_tx, &timer_tx, event, &mut pressed_keys, &release_bind).await?,
Some(Err(e)) => return Err(anyhow!("event producer: {e:?}")),
None => return Err(anyhow!("event producer closed")),
Some(Ok(event)) => handle_capture_event(&server, &mut capture, &sender_tx, &timer_tx, event, &mut pressed_keys, &release_bind).await?,
Some(Err(e)) => return Err(anyhow!("input capture: {e:?}")),
None => return Err(anyhow!("input capture terminated")),
}
}
e = rx.recv() => {
log::debug!("producer notify rx: {e:?}");
log::debug!("input capture notify rx: {e:?}");
match e {
Some(e) => match e {
ProducerEvent::Release => {
producer.release()?;
CaptureEvent::Release => {
capture.release()?;
server.state.replace(State::Receiving);
}
ProducerEvent::ClientEvent(e) => producer.notify(e)?,
ProducerEvent::Terminate => break,
CaptureEvent::ClientEvent(e) => capture.notify(e)?,
CaptureEvent::Terminate => break,
},
None => break,
}
@@ -75,9 +75,9 @@ fn update_pressed_keys(pressed_keys: &mut HashSet<scancode::Linux>, key: u32, st
}
}
async fn handle_producer_event(
async fn handle_capture_event(
server: &Server,
producer: &mut Box<dyn EventProducer>,
capture: &mut Box<dyn InputCapture>,
sender_tx: &Sender<(Event, SocketAddr)>,
timer_tx: &Sender<()>,
event: (ClientHandle, Event),
@@ -93,7 +93,7 @@ async fn handle_producer_event(
if release_bind.iter().all(|k| pressed_keys.contains(k)) {
pressed_keys.clear();
log::info!("releasing pointer");
producer.release()?;
capture.release()?;
server.state.replace(State::Receiving);
log::trace!("STATE ===> Receiving");
// send an event to release all the modifiers
@@ -112,7 +112,7 @@ async fn handle_producer_event(
None => {
// should not happen
log::warn!("unknown client!");
producer.release()?;
capture.release()?;
server.state.replace(State::Receiving);
log::trace!("STATE ===> Receiving");
return Ok(());

View File

@@ -8,53 +8,53 @@ use tokio::{
use crate::{
client::{ClientEvent, ClientHandle},
consumer::EventConsumer,
emulate::InputEmulation,
event::{Event, KeyboardEvent},
scancode,
server::State,
};
use super::{ProducerEvent, Server};
use super::{CaptureEvent, Server};
#[derive(Clone, Debug)]
pub enum ConsumerEvent {
/// consumer is notified of a change in client states
pub enum EmulationEvent {
/// input emulation is notified of a change in client states
ClientEvent(ClientEvent),
/// consumer must release keys for client
/// input emulation must release keys for client
ReleaseKeys(ClientHandle),
/// termination signal
Terminate,
}
pub fn new(
mut consumer: Box<dyn EventConsumer>,
mut emulate: Box<dyn InputEmulation>,
server: Server,
mut udp_rx: Receiver<Result<(Event, SocketAddr)>>,
sender_tx: Sender<(Event, SocketAddr)>,
producer_tx: Sender<ProducerEvent>,
capture_tx: Sender<CaptureEvent>,
timer_tx: Sender<()>,
) -> (JoinHandle<Result<()>>, Sender<ConsumerEvent>) {
) -> (JoinHandle<Result<()>>, Sender<EmulationEvent>) {
let (tx, mut rx) = tokio::sync::mpsc::channel(32);
let consumer_task = tokio::task::spawn_local(async move {
let emulate_task = tokio::task::spawn_local(async move {
let mut last_ignored = None;
loop {
tokio::select! {
udp_event = udp_rx.recv() => {
let udp_event = udp_event.ok_or(anyhow!("receiver closed"))??;
handle_udp_rx(&server, &producer_tx, &mut consumer, &sender_tx, &mut last_ignored, udp_event, &timer_tx).await;
handle_udp_rx(&server, &capture_tx, &mut emulate, &sender_tx, &mut last_ignored, udp_event, &timer_tx).await;
}
consumer_event = rx.recv() => {
match consumer_event {
emulate_event = rx.recv() => {
match emulate_event {
Some(e) => match e {
ConsumerEvent::ClientEvent(e) => consumer.notify(e).await,
ConsumerEvent::ReleaseKeys(c) => release_keys(&server, &mut consumer, c).await,
ConsumerEvent::Terminate => break,
EmulationEvent::ClientEvent(e) => emulate.notify(e).await,
EmulationEvent::ReleaseKeys(c) => release_keys(&server, &mut emulate, c).await,
EmulationEvent::Terminate => break,
},
None => break,
}
}
res = consumer.dispatch() => {
res = emulate.dispatch() => {
res?;
}
}
@@ -68,20 +68,20 @@ pub fn new(
.map(|s| s.client.handle)
.collect::<Vec<_>>();
for client in clients {
release_keys(&server, &mut consumer, client).await;
release_keys(&server, &mut emulate, client).await;
}
// destroy consumer
consumer.destroy().await;
// destroy emulator
emulate.destroy().await;
anyhow::Ok(())
});
(consumer_task, tx)
(emulate_task, tx)
}
async fn handle_udp_rx(
server: &Server,
producer_notify_tx: &Sender<ProducerEvent>,
consumer: &mut Box<dyn EventConsumer>,
capture_tx: &Sender<CaptureEvent>,
emulate: &mut Box<dyn InputEmulation>,
sender_tx: &Sender<(Event, SocketAddr)>,
last_ignored: &mut Option<SocketAddr>,
event: (Event, SocketAddr),
@@ -127,7 +127,7 @@ async fn handle_udp_rx(
let _ = sender_tx.send((Event::Pong(), addr)).await;
}
(Event::Disconnect(), _) => {
release_keys(server, consumer, handle).await;
release_keys(server, emulate, handle).await;
}
(event, addr) => {
// tell clients that we are ready to receive events
@@ -143,7 +143,7 @@ async fn handle_udp_rx(
} else {
// upon receiving any event, we go back to receiving mode
server.state.replace(State::Receiving);
let _ = producer_notify_tx.send(ProducerEvent::Release).await;
let _ = capture_tx.send(CaptureEvent::Release).await;
log::trace!("STATE ===> Receiving");
}
}
@@ -176,8 +176,8 @@ async fn handle_udp_rx(
// workaround buggy rdp backend.
if !ignore_event {
// consume event
consumer.consume(event, handle).await;
log::trace!("{event:?} => consumer");
emulate.consume(event, handle).await;
log::trace!("{event:?} => emulate");
}
}
State::AwaitingLeave => {
@@ -194,7 +194,7 @@ async fn handle_udp_rx(
// event should still be possible
if let Event::Enter() = event {
server.state.replace(State::Receiving);
let _ = producer_notify_tx.send(ProducerEvent::Release).await;
let _ = capture_tx.send(CaptureEvent::Release).await;
log::trace!("STATE ===> Receiving");
}
}
@@ -205,7 +205,7 @@ async fn handle_udp_rx(
async fn release_keys(
server: &Server,
consumer: &mut Box<dyn EventConsumer>,
emulate: &mut Box<dyn InputEmulation>,
client: ClientHandle,
) {
let keys = server
@@ -222,7 +222,7 @@ async fn release_keys(
key,
state: 0,
});
consumer.consume(event, client).await;
emulate.consume(event, client).await;
if let Ok(key) = scancode::Linux::try_from(key) {
log::warn!("releasing stuck key: {key:?}");
}
@@ -234,7 +234,7 @@ async fn release_keys(
mods_locked: 0,
group: 0,
};
consumer
emulate
.consume(Event::Keyboard(modifiers_event), client)
.await;
}

View File

@@ -22,15 +22,15 @@ use crate::{
};
use super::{
consumer_task::ConsumerEvent, producer_task::ProducerEvent, resolver_task::DnsRequest, Server,
capture_task::CaptureEvent, emulation_task::EmulationEvent, resolver_task::DnsRequest, Server,
};
pub(crate) fn new(
mut frontend: FrontendListener,
mut notify_rx: Receiver<FrontendNotify>,
server: Server,
producer_notify: Sender<ProducerEvent>,
consumer_notify: Sender<ConsumerEvent>,
capture_notify: Sender<CaptureEvent>,
emulate_notify: Sender<EmulationEvent>,
resolve_ch: Sender<DnsRequest>,
port_tx: Sender<u16>,
) -> (JoinHandle<Result<()>>, Sender<FrontendEvent>) {
@@ -51,7 +51,7 @@ pub(crate) fn new(
}
event = event_rx.recv() => {
let frontend_event = event.ok_or(anyhow!("frontend channel closed"))?;
if handle_frontend_event(&server, &producer_notify, &consumer_notify, &resolve_ch, &mut frontend, &port_tx, frontend_event).await {
if handle_frontend_event(&server, &capture_notify, &emulate_notify, &resolve_ch, &mut frontend, &port_tx, frontend_event).await {
break;
}
}
@@ -98,8 +98,8 @@ async fn handle_frontend_stream(
async fn handle_frontend_event(
server: &Server,
producer_tx: &Sender<ProducerEvent>,
consumer_tx: &Sender<ConsumerEvent>,
capture_tx: &Sender<CaptureEvent>,
emulate_tx: &Sender<EmulationEvent>,
resolve_tx: &Sender<DnsRequest>,
frontend: &mut FrontendListener,
port_tx: &Sender<u16>,
@@ -120,7 +120,7 @@ async fn handle_frontend_event(
Some(FrontendNotify::NotifyClientCreate(client))
}
FrontendEvent::ActivateClient(handle, active) => {
activate_client(server, producer_tx, consumer_tx, handle, active).await;
activate_client(server, capture_tx, emulate_tx, handle, active).await;
Some(FrontendNotify::NotifyClientActivate(handle, active))
}
FrontendEvent::ChangePort(port) => {
@@ -128,7 +128,7 @@ async fn handle_frontend_event(
None
}
FrontendEvent::DelClient(handle) => {
remove_client(server, producer_tx, consumer_tx, frontend, handle).await;
remove_client(server, capture_tx, emulate_tx, frontend, handle).await;
Some(FrontendNotify::NotifyClientDelete(handle))
}
FrontendEvent::Enumerate() => {
@@ -147,8 +147,8 @@ async fn handle_frontend_event(
FrontendEvent::UpdateClient(handle, hostname, port, pos) => {
update_client(
server,
producer_tx,
consumer_tx,
capture_tx,
emulate_tx,
resolve_tx,
(handle, hostname, port, pos),
)
@@ -204,8 +204,8 @@ pub async fn add_client(
pub async fn activate_client(
server: &Server,
producer_notify_tx: &Sender<ProducerEvent>,
consumer_notify_tx: &Sender<ConsumerEvent>,
capture_notify_tx: &Sender<CaptureEvent>,
emulate_notify_tx: &Sender<EmulationEvent>,
client: ClientHandle,
active: bool,
) {
@@ -217,26 +217,28 @@ pub async fn activate_client(
None => return,
};
if active {
let _ = producer_notify_tx
.send(ProducerEvent::ClientEvent(ClientEvent::Create(client, pos)))
let _ = capture_notify_tx
.send(CaptureEvent::ClientEvent(ClientEvent::Create(client, pos)))
.await;
let _ = consumer_notify_tx
.send(ConsumerEvent::ClientEvent(ClientEvent::Create(client, pos)))
let _ = emulate_notify_tx
.send(EmulationEvent::ClientEvent(ClientEvent::Create(
client, pos,
)))
.await;
} else {
let _ = producer_notify_tx
.send(ProducerEvent::ClientEvent(ClientEvent::Destroy(client)))
let _ = capture_notify_tx
.send(CaptureEvent::ClientEvent(ClientEvent::Destroy(client)))
.await;
let _ = consumer_notify_tx
.send(ConsumerEvent::ClientEvent(ClientEvent::Destroy(client)))
let _ = emulate_notify_tx
.send(EmulationEvent::ClientEvent(ClientEvent::Destroy(client)))
.await;
}
}
pub async fn remove_client(
server: &Server,
producer_notify_tx: &Sender<ProducerEvent>,
consumer_notify_tx: &Sender<ConsumerEvent>,
capture_notify_tx: &Sender<CaptureEvent>,
emulate_notify_tx: &Sender<EmulationEvent>,
frontend: &mut FrontendListener,
client: ClientHandle,
) -> Option<ClientHandle> {
@@ -250,11 +252,11 @@ pub async fn remove_client(
};
if active {
let _ = producer_notify_tx
.send(ProducerEvent::ClientEvent(ClientEvent::Destroy(client)))
let _ = capture_notify_tx
.send(CaptureEvent::ClientEvent(ClientEvent::Destroy(client)))
.await;
let _ = consumer_notify_tx
.send(ConsumerEvent::ClientEvent(ClientEvent::Destroy(client)))
let _ = emulate_notify_tx
.send(EmulationEvent::ClientEvent(ClientEvent::Destroy(client)))
.await;
}
@@ -268,8 +270,8 @@ pub async fn remove_client(
async fn update_client(
server: &Server,
producer_notify_tx: &Sender<ProducerEvent>,
consumer_notify_tx: &Sender<ConsumerEvent>,
capture_notify_tx: &Sender<CaptureEvent>,
emulate_notify_tx: &Sender<EmulationEvent>,
resolve_tx: &Sender<DnsRequest>,
client_update: (ClientHandle, Option<String>, u16, Position),
) {
@@ -311,25 +313,30 @@ async fn update_client(
)
};
// update state in event consumer & producer
if changed && active {
// resolve dns if something changed
if changed {
// resolve dns
if let Some(hostname) = hostname {
let _ = resolve_tx.send(DnsRequest { hostname, handle }).await;
}
}
// update state in event input emulator & input capture
if changed && active {
// update state
let _ = producer_notify_tx
.send(ProducerEvent::ClientEvent(ClientEvent::Destroy(handle)))
let _ = capture_notify_tx
.send(CaptureEvent::ClientEvent(ClientEvent::Destroy(handle)))
.await;
let _ = consumer_notify_tx
.send(ConsumerEvent::ClientEvent(ClientEvent::Destroy(handle)))
let _ = emulate_notify_tx
.send(EmulationEvent::ClientEvent(ClientEvent::Destroy(handle)))
.await;
let _ = producer_notify_tx
.send(ProducerEvent::ClientEvent(ClientEvent::Create(handle, pos)))
let _ = capture_notify_tx
.send(CaptureEvent::ClientEvent(ClientEvent::Create(handle, pos)))
.await;
let _ = consumer_notify_tx
.send(ConsumerEvent::ClientEvent(ClientEvent::Create(handle, pos)))
let _ = emulate_notify_tx
.send(EmulationEvent::ClientEvent(ClientEvent::Create(
handle, pos,
)))
.await;
}
}

View File

@@ -84,7 +84,6 @@ fn send_event(sock: &UdpSocket, e: Event, addr: SocketAddr) -> Result<usize> {
log::trace!("{:20} ------>->->-> {addr}", e.to_string());
let data: Vec<u8> = (&e).into();
// When udp blocks, we dont want to block the event loop.
// Dropping events is better than potentially crashing the event
// producer.
// Dropping events is better than potentially crashing the input capture.
Ok(sock.try_send_to(&data, addr)?)
}

View File

@@ -7,15 +7,15 @@ use tokio::{
use crate::{client::ClientHandle, event::Event};
use super::{consumer_task::ConsumerEvent, producer_task::ProducerEvent, Server, State};
use super::{capture_task::CaptureEvent, emulation_task::EmulationEvent, Server, State};
const MAX_RESPONSE_TIME: Duration = Duration::from_millis(500);
pub fn new(
server: Server,
sender_ch: Sender<(Event, SocketAddr)>,
consumer_notify: Sender<ConsumerEvent>,
producer_notify: Sender<ProducerEvent>,
emulate_notify: Sender<EmulationEvent>,
capture_notify: Sender<CaptureEvent>,
mut timer_rx: Receiver<()>,
) -> JoinHandle<()> {
// timer task
@@ -114,14 +114,14 @@ pub fn new(
if receiving {
for c in unresponsive_clients {
log::warn!("device not responding, releasing keys!");
let _ = consumer_notify.send(ConsumerEvent::ReleaseKeys(c)).await;
let _ = emulate_notify.send(EmulationEvent::ReleaseKeys(c)).await;
}
} else {
// release pointer if the active client has not responded
if !unresponsive_clients.is_empty() {
log::warn!("client not responding, releasing pointer!");
server.state.replace(State::Receiving);
let _ = producer_notify.send(ProducerEvent::Release).await;
let _ = capture_notify.send(CaptureEvent::Release).await;
}
}
}