Support event consumer on KDE! (portal backend) (#31)

* Support event consumer on KDE! (portal backend)

Support for KDE event emulation using the remote-desktop xdg-desktop-portal

* fix scrolling (TODO: smooth / kinetic scrolling)

* windows: fix compilation errors

* Update README.md
This commit is contained in:
Ferdinand Schober
2023-10-13 13:57:33 +02:00
committed by GitHub
parent 4cdc5ea49c
commit be0fe9f2d9
11 changed files with 807 additions and 151 deletions

550
Cargo.lock generated
View File

@@ -32,6 +32,109 @@ version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "ashpd"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3affe251686bd936a0afb74b9693e8bf2f193d51da1b9a45d3f1303a9bd2cc7"
dependencies = [
"enumflags2",
"futures-channel",
"futures-util",
"once_cell",
"rand",
"serde",
"serde_repr",
"tokio",
"url",
"zbus",
]
[[package]]
name = "async-broadcast"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b"
dependencies = [
"event-listener",
"futures-core",
]
[[package]]
name = "async-channel"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
dependencies = [
"concurrent-queue",
"event-listener",
"futures-core",
]
[[package]]
name = "async-io"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
dependencies = [
"async-lock",
"autocfg",
"cfg-if",
"concurrent-queue",
"futures-lite",
"log",
"parking",
"polling",
"rustix 0.37.24",
"slab",
"socket2 0.4.9",
"waker-fn",
]
[[package]]
name = "async-lock"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
dependencies = [
"event-listener",
]
[[package]]
name = "async-process"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9"
dependencies = [
"async-io",
"async-lock",
"autocfg",
"blocking",
"cfg-if",
"event-listener",
"futures-lite",
"rustix 0.37.24",
"signal-hook",
"windows-sys",
]
[[package]]
name = "async-recursion"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.37",
]
[[package]]
name = "async-task"
version = "4.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9441c6b2fe128a7c2bf680a44c34d0df31ce09e5b7e401fcca3faa483dbc921"
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.73" version = "0.1.73"
@@ -43,6 +146,12 @@ dependencies = [
"syn 2.0.37", "syn 2.0.37",
] ]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@@ -76,6 +185,37 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[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.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a"
dependencies = [
"async-channel",
"async-lock",
"async-task",
"fastrand 2.0.0",
"futures-io",
"futures-lite",
"piper",
"tracing",
]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.5.0" version = "1.5.0"
@@ -132,12 +272,70 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "concurrent-queue"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "cpufeatures"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
dependencies = [
"libc",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[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]] [[package]]
name = "data-encoding" name = "data-encoding"
version = "2.4.0" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[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]] [[package]]
name = "dlib" name = "dlib"
version = "0.5.2" version = "0.5.2"
@@ -165,6 +363,27 @@ dependencies = [
"syn 2.0.37", "syn 2.0.37",
] ]
[[package]]
name = "enumflags2"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939"
dependencies = [
"enumflags2_derive",
"serde",
]
[[package]]
name = "enumflags2_derive"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.37",
]
[[package]] [[package]]
name = "env_logger" name = "env_logger"
version = "0.10.0" version = "0.10.0"
@@ -205,6 +424,21 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "event-listener"
version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "fastrand"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
dependencies = [
"instant",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.0.0" version = "2.0.0"
@@ -262,6 +496,21 @@ version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
[[package]]
name = "futures-lite"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
dependencies = [
"fastrand 1.9.0",
"futures-core",
"futures-io",
"memchr",
"parking",
"pin-project-lite",
"waker-fn",
]
[[package]] [[package]]
name = "futures-macro" name = "futures-macro"
version = "0.3.28" version = "0.3.28"
@@ -273,6 +522,12 @@ dependencies = [
"syn 2.0.37", "syn 2.0.37",
] ]
[[package]]
name = "futures-sink"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.28" version = "0.3.28"
@@ -287,6 +542,7 @@ checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-macro", "futures-macro",
"futures-sink",
"futures-task", "futures-task",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
@@ -351,6 +607,16 @@ dependencies = [
"system-deps", "system-deps",
] ]
[[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]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.10" version = "0.2.10"
@@ -590,6 +856,12 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "hostname" name = "hostname"
version = "0.3.1" version = "0.3.1"
@@ -627,6 +899,15 @@ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "io-lifetimes" name = "io-lifetimes"
version = "1.0.11" version = "1.0.11"
@@ -644,7 +925,7 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f"
dependencies = [ dependencies = [
"socket2", "socket2 0.5.4",
"widestring", "widestring",
"windows-sys", "windows-sys",
"winreg", "winreg",
@@ -663,7 +944,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi",
"rustix", "rustix 0.38.13",
"windows-sys", "windows-sys",
] ]
@@ -678,6 +959,8 @@ name = "lan-mouse"
version = "0.3.3" version = "0.3.3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"ashpd",
"async-trait",
"env_logger", "env_logger",
"glib-build-tools", "glib-build-tools",
"gtk4", "gtk4",
@@ -754,6 +1037,12 @@ version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.4.7" version = "0.4.7"
@@ -882,6 +1171,16 @@ version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[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]] [[package]]
name = "pango" name = "pango"
version = "0.18.0" version = "0.18.0"
@@ -907,6 +1206,12 @@ dependencies = [
"system-deps", "system-deps",
] ]
[[package]]
name = "parking"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e52c774a4c39359c1d1c52e43f73dd91a75a614652c825408eec30c95a9b2067"
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.1" version = "0.12.1"
@@ -948,12 +1253,39 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 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 2.0.0",
"futures-io",
]
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.27" version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]]
name = "polling"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
dependencies = [
"autocfg",
"bitflags 1.3.2",
"cfg-if",
"concurrent-queue",
"libc",
"log",
"pin-project-lite",
"windows-sys",
]
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.17" version = "0.2.17"
@@ -1120,6 +1452,20 @@ dependencies = [
"semver", "semver",
] ]
[[package]]
name = "rustix"
version = "0.37.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4279d76516df406a8bd37e7dff53fd37d1a093f997a3c34a5c21658c126db06d"
dependencies = [
"bitflags 1.3.2",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys 0.3.8",
"windows-sys",
]
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.13" version = "0.38.13"
@@ -1129,7 +1475,7 @@ dependencies = [
"bitflags 2.4.0", "bitflags 2.4.0",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys 0.4.7",
"windows-sys", "windows-sys",
] ]
@@ -1188,6 +1534,17 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_repr"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.37",
]
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "0.6.3" version = "0.6.3"
@@ -1197,6 +1554,36 @@ dependencies = [
"serde", "serde",
] ]
[[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]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"
@@ -1212,6 +1599,16 @@ version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]]
name = "socket2"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
dependencies = [
"libc",
"winapi",
]
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.5.4" version = "0.5.4"
@@ -1222,6 +1619,12 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@@ -1270,9 +1673,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"fastrand", "fastrand 2.0.0",
"redox_syscall", "redox_syscall",
"rustix", "rustix 0.38.13",
"windows-sys", "windows-sys",
] ]
@@ -1332,8 +1735,10 @@ dependencies = [
"mio", "mio",
"num_cpus", "num_cpus",
"pin-project-lite", "pin-project-lite",
"socket2", "signal-hook-registry",
"socket2 0.5.4",
"tokio-macros", "tokio-macros",
"tracing",
"windows-sys", "windows-sys",
] ]
@@ -1460,6 +1865,22 @@ dependencies = [
"trust-dns-proto", "trust-dns-proto",
] ]
[[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.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d"
dependencies = [
"tempfile",
"winapi",
]
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.13" version = "0.3.13"
@@ -1490,6 +1911,7 @@ dependencies = [
"form_urlencoded", "form_urlencoded",
"idna", "idna",
"percent-encoding", "percent-encoding",
"serde",
] ]
[[package]] [[package]]
@@ -1504,6 +1926,12 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "waker-fn"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
@@ -1741,3 +2169,113 @@ dependencies = [
"libc", "libc",
"pkg-config", "pkg-config",
] ]
[[package]]
name = "xdg-home"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd"
dependencies = [
"nix",
"winapi",
]
[[package]]
name = "zbus"
version = "3.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948"
dependencies = [
"async-broadcast",
"async-process",
"async-recursion",
"async-trait",
"byteorder",
"derivative",
"enumflags2",
"event-listener",
"futures-core",
"futures-sink",
"futures-util",
"hex",
"nix",
"once_cell",
"ordered-stream",
"rand",
"serde",
"serde_repr",
"sha1",
"static_assertions",
"tokio",
"tracing",
"uds_windows",
"winapi",
"xdg-home",
"zbus_macros",
"zbus_names",
"zvariant",
]
[[package]]
name = "zbus_macros"
version = "3.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"regex",
"syn 1.0.109",
"zvariant_utils",
]
[[package]]
name = "zbus_names"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9"
dependencies = [
"serde",
"static_assertions",
"zvariant",
]
[[package]]
name = "zvariant"
version = "3.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c"
dependencies = [
"byteorder",
"enumflags2",
"libc",
"serde",
"static_assertions",
"url",
"zvariant_derive",
]
[[package]]
name = "zvariant_derive"
version = "3.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 1.0.109",
"zvariant_utils",
]
[[package]]
name = "zvariant_utils"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]

View File

@@ -23,7 +23,8 @@ log = "0.4.20"
env_logger = "0.10.0" env_logger = "0.10.0"
libc = "0.2.148" libc = "0.2.148"
serde_json = "1.0.107" serde_json = "1.0.107"
tokio = {version = "1.32.0", features = ["io-util", "macros", "net", "rt", "sync" ] } tokio = {version = "1.32.0", features = ["io-util", "macros", "net", "rt", "sync", "signal"] }
async-trait = "0.1.73"
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
wayland-client = { version="0.30.2", optional = true } wayland-client = { version="0.30.2", optional = true }
@@ -34,6 +35,7 @@ wayland-protocols-plasma = { version="0.1.0", features=["client"], optional = tr
x11 = { version = "2.21.0", features = ["xlib", "xtest"], optional = true } x11 = { version = "2.21.0", features = ["xlib", "xtest"], optional = true }
gtk = { package = "gtk4", version = "0.7.2", features = ["v4_6"], optional = true } gtk = { package = "gtk4", version = "0.7.2", features = ["v4_6"], optional = true }
adw = { package = "libadwaita", version = "0.5.2", features = ["v1_1"], optional = true } adw = { package = "libadwaita", version = "0.5.2", features = ["v1_1"], optional = true }
ashpd = { version = "0.6.2", default-features = false, features = ["tokio"], optional = true }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.9", features = ["winuser"] } winapi = { version = "0.3.9", features = ["winuser"] }
@@ -56,6 +58,6 @@ wayland = [
"dep:wayland-protocols-misc", "dep:wayland-protocols-misc",
"dep:wayland-protocols-plasma" ] "dep:wayland-protocols-plasma" ]
x11 = [ "dep:x11" ] x11 = [ "dep:x11" ]
xdg_desktop_portal = [] xdg_desktop_portal = ["dep:ashpd"]
libei = [] libei = []
gtk = ["dep:gtk", "dep:adw"] gtk = ["dep:gtk", "dep:adw"]

205
README.md
View File

@@ -1,54 +1,29 @@
# Lan Mouse # Lan Mouse
- _Now with a gtk frontend_
![image](https://github.com/ferdinandschober/lan-mouse/assets/40996949/ccb33815-4357-4c8d-a5d2-8897ab626a08) <img src="https://github.com/ferdinandschober/lan-mouse/assets/40996949/ccb33815-4357-4c8d-a5d2-8897ab626a08" width=75%>
Goal of this project is to be an open-source replacement for proprietary tools like [Synergy](https://symless.com/synergy), [Share Mouse](https://www.sharemouse.com/de/). Goal of this project is to be an open-source replacement for proprietary tools like [Synergy](https://symless.com/synergy), [Share Mouse](https://www.sharemouse.com/de/).
Focus lies on performance and a clean, manageable implementation that can easily be expanded to support additional backends like e.g. Android, iOS, ... . Focus lies on performance and a clean, manageable implementation that can easily be expanded to support additional backends like e.g. Android, iOS, ... .
Of course ***blazingly fast™*** and stable, because it's written in rust. ***blazingly fast™*** and ***stable™***, because it's written in rust.
For an alternative (with slightly different goals) you may check out [Input Leap](https://github.com/input-leap). For an alternative (with slightly different goals) you may check out [Input Leap](https://github.com/input-leap).
_Now with a gtk frontend_ ## OS Support
## Configuration The following table shows support for Event receiving and event Emitting
Configuration is done through the file `config.toml`, on different operating systems:
which must be located in the current working directory when
executing lan-mouse.
### Example config | Backend | Event Receiving | Event Emitting |
A config file could look like this: |---------------------------|--------------------------|--------------------------------------|
| Wayland (wlroots) | :heavy_check_mark: | :heavy_check_mark: |
```toml | Wayland (KDE) | :heavy_check_mark: | :heavy_check_mark: |
# example configuration | Wayland (Gnome) | TODO (libei support) | TODO (wlr-layer-shell not supported) |
| X11 | (WIP) | TODO |
# optional port (defaults to 4242) | Windows | (:heavy_check_mark:) | TODO |
port = 4242 | MacOS | TODO (I dont own a Mac) | TODO (I dont own a Mac) |
# # optional frontend -> defaults to gtk if available
# # possible values are "cli" and "gtk"
# frontend = "gtk"
# define a client on the right side with host name "iridium"
[right]
# hostname
host_name = "iridium"
# optional list of (known) ip addresses
ips = ["192.168.178.156"]
# define a client on the left side with IP address 192.168.178.189
[left]
# The hostname is optional: When no hostname is specified,
# at least one ip address needs to be specified.
host_name = "thorium"
# ips for ethernet and wifi
ips = ["192.168.178.189", "192.168.178.172"]
# optional port
port = 4242
```
Where `left` can be either `left`, `right`, `top` or `bottom`.
## Build and Run ## Build and Run
@@ -81,79 +56,42 @@ an executable with just support for wayland:
cargo build --no-default-features --features wayland cargo build --no-default-features --features wayland
``` ```
## OS Support ## Configuration
To automatically load clients on startup, the file `config.toml` is parsed.
(must be in the directory where lan-mouse is executed).
The following table shows support for Event receiving and event Emitting ### Example config
on different operating systems:
| Backend | Event Receiving | Event Emitting | ```toml
|---------------------------|--------------------------|--------------------------------------| # example configuration
| Wayland (wlroots) | :heavy_check_mark: | :heavy_check_mark: |
| Wayland (KDE) | WIP | :heavy_check_mark: |
| Wayland (Gnome) | TODO (libei support) | TODO (wlr-layer-shell not supported) |
| X11 | WIP | TODO |
| Windows | needs improvements | TODO |
| MacOS | TODO (I dont own a Mac) | TODO (I dont own a Mac) |
## Wayland compositor support # optional port (defaults to 4242)
### Input Emulation (for receiving events) port = 4242
On wayland input-emulation is in an early/unstable state as of writing this. # # optional frontend -> defaults to gtk if available
# # possible values are "cli" and "gtk"
# frontend = "gtk"
Different compositors have different ways of enabling input emulation: # define a client on the right side with host name "iridium"
[right]
# hostname
host_name = "iridium"
# optional list of (known) ip addresses
ips = ["192.168.178.156"]
Most wlroots-based compositors like Hyprland and Sway support the following # define a client on the left side with IP address 192.168.178.189
unstable wayland protocols for keyboard and mouse emulation: [left]
- [virtual-keyboard-unstable-v1](https://wayland.app/protocols/virtual-keyboard-unstable-v1) # The hostname is optional: When no hostname is specified,
- [wlr-virtual-pointer-unstable-v1](https://wayland.app/protocols/wlr-virtual-pointer-unstable-v1) are used to emulate input on wlroots compositors # at least one ip address needs to be specified.
host_name = "thorium"
# ips for ethernet and wifi
ips = ["192.168.178.189", "192.168.178.172"]
# optional port
port = 4242
```
KDE also has a protocol for input emulation ([kde-fake-input](https://wayland.app/protocols/kde-fake-input)), it is however not exposed to Where `left` can be either `left`, `right`, `top` or `bottom`.
third party apps, so the recommended way of enabling input emulation in KDE is the
[freedesktop remote-desktop-portal](https://flatpak.github.io/xdg-desktop-portal/#gdbus-org.freedesktop.portal.RemoteDesktop).
Gnome uses [libei](https://gitlab.freedesktop.org/libinput/libei) for input emulation, ## Roadmap
which has the goal to become the general approach for emulating Input on wayland.
| Required Protocols (Event Receiving) | Sway | Kwin | Gnome |
|----------------------------------------|--------------------|----------------------|----------------------|
| wlr-virtual-pointer-unstable-v1 | :heavy_check_mark: | :x: | :x: |
| virtual-keyboard-unstable-v1 | :heavy_check_mark: | :x: | :x: |
| ~fake-input~ | :x: | ~:heavy_check_mark:~ | :x: |
### Input capture
To capture mouse and keyboard input, a few things are necessary:
- Displaying an immovable surface at screen edges
- Locking the mouse in place
- (optionally but highly recommended) reading unaccelerated mouse input
| Required Protocols (Event Emitting) | Sway | Kwin | Gnome |
|----------------------------------------|--------------------|----------------------|----------------------|
| pointer-constraints-unstable-v1 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| relative-pointer-unstable-v1 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| keyboard-shortcuts-inhibit-unstable-v1 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| wlr-layer-shell-unstable-v1 | :heavy_check_mark: | :heavy_check_mark: | :x: |
The [zwlr\_virtual\_pointer\_manager\_v1](wlr-virtual-pointer-unstable-v1) is required
to display surfaces on screen edges and used to display the immovable window on
both wlroots based compositors and KDE.
Gnome unfortunately does not support this protocol
and [likely won't ever support it](https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1141).
So there is currently no way of doing this in Wayland, aside from a custom Gnome-Shell
extension, which is not a very elegant solution.
This is to be looked into in the future.
~In order for layershell surfaces to be able to lock the pointer using the pointer\_constraints protocol [this patch](https://github.com/swaywm/sway/pull/7178) needs to be applied to sway.~
(this works natively on sway versions >= 1.8)
## Windows support
Currently windows can receive mouse and keyboard events,
event producing on windows is WIP.
## TODOS
- [x] Capture the actual mouse events on the server side via a wayland client and send them to the client - [x] Capture the actual mouse events on the server side via a wayland client and send them to the client
- [x] Mouse grabbing - [x] Mouse grabbing
- [x] Window with absolute position -> wlr\_layer\_shell - [x] Window with absolute position -> wlr\_layer\_shell
@@ -219,3 +157,60 @@ would be a better choice for the future and could also help for WIFI connections
Sending key and mouse event data over the local network might not be the biggest security concern but in any public network or business environment it's *QUITE* a problem to basically broadcast your keystrokes. Sending key and mouse event data over the local network might not be the biggest security concern but in any public network or business environment it's *QUITE* a problem to basically broadcast your keystrokes.
- There should be an encryption layer below the application to enable a secure link. - There should be an encryption layer below the application to enable a secure link.
- The encryption keys could be generated by the graphical frontend. - The encryption keys could be generated by the graphical frontend.
## Wayland support
### Input Emulation (for receiving events)
On wayland input-emulation is in an early/unstable state as of writing this.
For this reason a suitable backend is chosen based on the active desktop environment / compositor.
Different compositors have different ways of enabling input emulation:
#### Wlroots
Most wlroots-based compositors like Hyprland and Sway support the following
unstable wayland protocols for keyboard and mouse emulation:
- [virtual-keyboard-unstable-v1](https://wayland.app/protocols/virtual-keyboard-unstable-v1)
- [wlr-virtual-pointer-unstable-v1](https://wayland.app/protocols/wlr-virtual-pointer-unstable-v1)
#### KDE
KDE also has a protocol for input emulation ([kde-fake-input](https://wayland.app/protocols/kde-fake-input)),
it is however not exposed to third party applications.
The recommended way to emulate input on KDE is the
[freedesktop remote-desktop-portal](https://flatpak.github.io/xdg-desktop-portal/#gdbus-org.freedesktop.portal.RemoteDesktop).
#### Gnome (TODO)
Gnome uses [libei](https://gitlab.freedesktop.org/libinput/libei) for input emulation,
which has the goal to become the general approach for emulating Input on wayland.
### Input capture
To capture mouse and keyboard input, a few things are necessary:
- Displaying an immovable surface at screen edges
- Locking the mouse in place
- (optionally but highly recommended) reading unaccelerated mouse input
| Required Protocols (Event Emitting) | Sway | Kwin | Gnome |
|----------------------------------------|--------------------|----------------------|----------------------|
| pointer-constraints-unstable-v1 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| relative-pointer-unstable-v1 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| keyboard-shortcuts-inhibit-unstable-v1 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| wlr-layer-shell-unstable-v1 | :heavy_check_mark: | :heavy_check_mark: | :x: |
The [zwlr\_virtual\_pointer\_manager\_v1](wlr-virtual-pointer-unstable-v1) is required
to display surfaces on screen edges and used to display the immovable window on
both wlroots based compositors and KDE.
Gnome unfortunately does not support this protocol
and [likely won't ever support it](https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1141).
So there is currently no way of doing this in Wayland, aside from a custom Gnome-Shell
extension, which is not a very elegant solution.
This is to be looked into in the future.
~In order for layershell surfaces to be able to lock the pointer using the pointer\_constraints protocol [this patch](https://github.com/swaywm/sway/pull/7178) needs to be applied to sway.~
(this works natively on sway versions >= 1.8)

View File

@@ -1,4 +1,4 @@
use crate::consumer::EventConsumer; use crate::consumer::SyncConsumer;
pub struct LibeiConsumer {} pub struct LibeiConsumer {}
@@ -6,7 +6,7 @@ impl LibeiConsumer {
pub fn new() -> Self { Self { } } pub fn new() -> Self { Self { } }
} }
impl EventConsumer for LibeiConsumer { impl SyncConsumer for LibeiConsumer {
fn consume(&mut self, _: crate::event::Event, _: crate::client::ClientHandle) { fn consume(&mut self, _: crate::event::Event, _: crate::client::ClientHandle) {
log::error!("libei backend not yet implemented!"); log::error!("libei backend not yet implemented!");
todo!() todo!()

View File

@@ -1,4 +1,4 @@
use crate::{event::{KeyboardEvent, PointerEvent}, consumer::EventConsumer}; use crate::{event::{KeyboardEvent, PointerEvent}, consumer::SyncConsumer};
use winapi::{ use winapi::{
self, self,
um::winuser::{INPUT, INPUT_MOUSE, LPINPUT, MOUSEEVENTF_MOVE, MOUSEINPUT, um::winuser::{INPUT, INPUT_MOUSE, LPINPUT, MOUSEEVENTF_MOVE, MOUSEINPUT,
@@ -25,7 +25,7 @@ impl WindowsConsumer {
pub fn new() -> Self { Self { } } pub fn new() -> Self { Self { } }
} }
impl EventConsumer for WindowsConsumer { impl SyncConsumer for WindowsConsumer {
fn consume(&mut self, event: Event, _: ClientHandle) { fn consume(&mut self, event: Event, _: ClientHandle) {
match event { match event {
Event::Pointer(pointer_event) => match pointer_event { Event::Pointer(pointer_event) => match pointer_event {

View File

@@ -1,7 +1,7 @@
use wayland_client::WEnum; use wayland_client::WEnum;
use wayland_client::backend::WaylandError; use wayland_client::backend::WaylandError;
use crate::client::{ClientHandle, ClientEvent}; use crate::client::{ClientHandle, ClientEvent};
use crate::consumer::EventConsumer; use crate::consumer::SyncConsumer;
use std::collections::HashMap; use std::collections::HashMap;
use std::io; use std::io;
use std::os::fd::OwnedFd; use std::os::fd::OwnedFd;
@@ -140,7 +140,7 @@ impl State {
} }
} }
impl EventConsumer for WlrootsConsumer { impl SyncConsumer for WlrootsConsumer {
fn consume(&mut self, event: Event, client_handle: ClientHandle) { fn consume(&mut self, event: Event, client_handle: ClientHandle) {
if let Some(virtual_input) = self.state.input_for_client.get(&client_handle) { if let Some(virtual_input) = self.state.input_for_client.get(&client_handle) {
if self.last_flush_failed { if self.last_flush_failed {

View File

@@ -3,7 +3,7 @@ use x11::{xlib, xtest};
use crate::{ use crate::{
client::ClientHandle, client::ClientHandle,
event::Event, consumer::EventConsumer, event::Event, consumer::SyncConsumer,
}; };
pub struct X11Consumer { pub struct X11Consumer {
@@ -30,7 +30,7 @@ impl X11Consumer {
} }
} }
impl EventConsumer for X11Consumer { impl SyncConsumer for X11Consumer {
fn consume(&mut self, event: Event, _: ClientHandle) { fn consume(&mut self, event: Event, _: ClientHandle) {
match event { match event {
Event::Pointer(pointer_event) => match pointer_event { Event::Pointer(pointer_event) => match pointer_event {

View File

@@ -1,15 +1,91 @@
use crate::consumer::EventConsumer; use async_trait::async_trait;
use anyhow::Result;
use ashpd::{desktop::{remote_desktop::{RemoteDesktop, DeviceType, KeyState, Axis}, Session}, WindowIdentifier};
pub struct DesktopPortalConsumer {} use crate::consumer::AsyncConsumer;
impl DesktopPortalConsumer { pub struct DesktopPortalConsumer<'a> {
pub fn new() -> Self { Self { } } proxy: RemoteDesktop<'a>,
session: Session<'a>,
} }
impl EventConsumer for DesktopPortalConsumer { impl<'a> DesktopPortalConsumer<'a> {
fn consume(&mut self, _: crate::event::Event, _: crate::client::ClientHandle) { pub async fn new() -> Result<DesktopPortalConsumer<'a>> {
log::error!("xdg_desktop_portal backend not yet implemented!"); let proxy = RemoteDesktop::new().await?;
let session = proxy.create_session().await?;
proxy
.select_devices(&session, DeviceType::Keyboard | DeviceType::Pointer)
.await?;
let _ = proxy
.start(&session, &WindowIdentifier::default())
.await?
.response()?;
Ok(Self { proxy, session })
}
}
#[async_trait]
impl<'a> AsyncConsumer for DesktopPortalConsumer<'a> {
async fn consume(&mut self, event: crate::event::Event, _client: crate::client::ClientHandle) {
match event {
crate::event::Event::Pointer(p) => {
match p {
crate::event::PointerEvent::Motion { time: _, relative_x, relative_y } => {
if let Err(e) = self.proxy.notify_pointer_motion(&self.session, relative_x, relative_y).await {
log::warn!("{e}");
}
},
crate::event::PointerEvent::Button { time: _, button, state } => {
let state = match state {
0 => KeyState::Released,
_ => KeyState::Pressed,
};
if let Err(e) = self.proxy.notify_pointer_button(&self.session, button as i32, state).await {
log::warn!("{e}");
}
},
crate::event::PointerEvent::Axis { time: _, axis, value } => {
let axis = match axis {
0 => Axis::Vertical,
_ => Axis::Horizontal,
};
// TODO smooth scrolling
if let Err(e) = self.proxy.notify_pointer_axis_discrete(&self.session, axis, value as i32).await {
log::warn!("{e}");
}
},
crate::event::PointerEvent::Frame { } => {},
}
},
crate::event::Event::Keyboard(k) => {
match k {
crate::event::KeyboardEvent::Key { time: _, key, state } => {
let state = match state {
0 => KeyState::Released,
_ => KeyState::Pressed,
};
if let Err(e) = self.proxy.notify_keyboard_keycode(&self.session, key as i32, state).await {
log::warn!("{e}");
}
},
crate::event::KeyboardEvent::Modifiers { .. } => {
// ignore
},
}
},
_ => {},
}
} }
fn notify(&mut self, _: crate::client::ClientEvent) {} async fn notify(&mut self, _client: crate::client::ClientEvent) { }
async fn destroy(&mut self) {
log::debug!("closing remote desktop session");
if let Err(e) = self.session.close().await {
log::error!("failed to close remote desktop session: {e}");
}
}
} }

View File

@@ -1,3 +1,5 @@
use async_trait::async_trait;
#[cfg(unix)] #[cfg(unix)]
use std::env; use std::env;
@@ -13,7 +15,12 @@ enum Backend {
Libei, Libei,
} }
pub trait EventConsumer { pub enum EventConsumer {
Sync(Box<dyn SyncConsumer>),
Async(Box<dyn AsyncConsumer>),
}
pub trait SyncConsumer {
/// Event corresponding to an abstract `client_handle` /// Event corresponding to an abstract `client_handle`
fn consume(&mut self, event: Event, client_handle: ClientHandle); fn consume(&mut self, event: Event, client_handle: ClientHandle);
@@ -21,9 +28,16 @@ pub trait EventConsumer {
fn notify(&mut self, client_event: ClientEvent); fn notify(&mut self, client_event: ClientEvent);
} }
pub fn create() -> Result<Box<dyn EventConsumer>> { #[async_trait]
pub trait AsyncConsumer {
async fn consume(&mut self, event: Event, client_handle: ClientHandle);
async fn notify(&mut self, client_event: ClientEvent);
async fn destroy(&mut self);
}
pub async fn create() -> Result<EventConsumer> {
#[cfg(windows)] #[cfg(windows)]
return Ok(Box::new(consumer::windows::WindowsConsumer::new())); return Ok(EventConsumer::Sync(Box::new(consumer::windows::WindowsConsumer::new())));
#[cfg(unix)] #[cfg(unix)]
let backend = match env::var("XDG_SESSION_TYPE") { let backend = match env::var("XDG_SESSION_TYPE") {
@@ -75,25 +89,25 @@ pub fn create() -> Result<Box<dyn EventConsumer>> {
#[cfg(not(feature = "libei"))] #[cfg(not(feature = "libei"))]
panic!("feature libei not enabled"); panic!("feature libei not enabled");
#[cfg(feature = "libei")] #[cfg(feature = "libei")]
Ok(Box::new(consumer::libei::LibeiConsumer::new())) Ok(EventConsumer::Sync(Box::new(consumer::libei::LibeiConsumer::new())))
}, },
Backend::RemoteDesktopPortal => { Backend::RemoteDesktopPortal => {
#[cfg(not(feature = "xdg_desktop_portal"))] #[cfg(not(feature = "xdg_desktop_portal"))]
panic!("feature xdg_desktop_portal not enabled"); panic!("feature xdg_desktop_portal not enabled");
#[cfg(feature = "xdg_desktop_portal")] #[cfg(feature = "xdg_desktop_portal")]
Ok(Box::new(consumer::xdg_desktop_portal::DesktopPortalConsumer::new())) Ok(EventConsumer::Async(Box::new(consumer::xdg_desktop_portal::DesktopPortalConsumer::new().await?)))
}, },
Backend::Wlroots => { Backend::Wlroots => {
#[cfg(not(feature = "wayland"))] #[cfg(not(feature = "wayland"))]
panic!("feature wayland not enabled"); panic!("feature wayland not enabled");
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
Ok(Box::new(consumer::wlroots::WlrootsConsumer::new()?)) Ok(EventConsumer::Sync(Box::new(consumer::wlroots::WlrootsConsumer::new()?)))
}, },
Backend::X11 => { Backend::X11 => {
#[cfg(not(feature = "x11"))] #[cfg(not(feature = "x11"))]
panic!("feature x11 not enabled"); panic!("feature x11 not enabled");
#[cfg(feature = "x11")] #[cfg(feature = "x11")]
Ok(Box::new(consumer::x11::X11Consumer::new())) Ok(EventConsumer::Sync(Box::new(consumer::x11::X11Consumer::new())))
}, },
} }
} }

View File

@@ -1,6 +1,6 @@
use std::{error::Error, io::Result, collections::HashSet, time::{Duration, Instant}, net::IpAddr}; use std::{error::Error, io::Result, collections::HashSet, time::{Duration, Instant}, net::IpAddr};
use log; use log;
use tokio::{net::UdpSocket, io::ReadHalf, sync::mpsc::{Sender, Receiver}}; use tokio::{net::UdpSocket, io::ReadHalf, signal, sync::mpsc::{Sender, Receiver}};
#[cfg(unix)] #[cfg(unix)]
use tokio::net::UnixStream; use tokio::net::UnixStream;
@@ -26,7 +26,7 @@ pub struct Server {
client_manager: ClientManager, client_manager: ClientManager,
state: State, state: State,
frontend: FrontendListener, frontend: FrontendListener,
consumer: Box<dyn EventConsumer>, consumer: EventConsumer,
producer: Box<dyn EventProducer>, producer: Box<dyn EventProducer>,
socket: UdpSocket, socket: UdpSocket,
frontend_rx: Receiver<FrontendEvent>, frontend_rx: Receiver<FrontendEvent>,
@@ -37,7 +37,7 @@ impl Server {
pub async fn new( pub async fn new(
port: u16, port: u16,
frontend: FrontendListener, frontend: FrontendListener,
consumer: Box<dyn EventConsumer>, consumer: EventConsumer,
producer: Box<dyn EventProducer>, producer: Box<dyn EventProducer>,
) -> anyhow::Result<Self> { ) -> anyhow::Result<Self> {
@@ -102,6 +102,10 @@ impl Server {
} }
} }
} }
_ = signal::ctrl_c() => {
log::info!("terminating gracefully ...");
break;
}
} }
} }
@@ -135,9 +139,18 @@ impl Server {
} }
} }
} }
_ = signal::ctrl_c() => {
log::info!("terminating gracefully ...");
break;
}
} }
} }
// destroy consumer
if let EventConsumer::Async(c) = &mut self.consumer {
c.destroy().await;
}
Ok(()) Ok(())
} }
@@ -164,22 +177,31 @@ impl Server {
client client
} }
pub fn activate_client(&mut self, client: ClientHandle, active: bool) { pub async fn activate_client(&mut self, client: ClientHandle, active: bool) {
if let Some(state) = self.client_manager.get_mut(client) { if let Some(state) = self.client_manager.get_mut(client) {
state.active = active; state.active = active;
if state.active { if state.active {
self.producer.notify(ClientEvent::Create(client, state.client.pos)); self.producer.notify(ClientEvent::Create(client, state.client.pos));
self.consumer.notify(ClientEvent::Create(client, state.client.pos)); match &mut self.consumer {
EventConsumer::Sync(consumer) => consumer.notify(ClientEvent::Create(client, state.client.pos)),
EventConsumer::Async(consumer) => consumer.notify(ClientEvent::Create(client, state.client.pos)).await,
}
} else { } else {
self.producer.notify(ClientEvent::Destroy(client)); self.producer.notify(ClientEvent::Destroy(client));
self.consumer.notify(ClientEvent::Destroy(client)); match &mut self.consumer {
EventConsumer::Sync(consumer) => consumer.notify(ClientEvent::Destroy(client)),
EventConsumer::Async(consumer) => consumer.notify(ClientEvent::Destroy(client)).await,
}
} }
} }
} }
pub async fn remove_client(&mut self, client: ClientHandle) -> Option<ClientHandle> { pub async fn remove_client(&mut self, client: ClientHandle) -> Option<ClientHandle> {
self.producer.notify(ClientEvent::Destroy(client)); self.producer.notify(ClientEvent::Destroy(client));
self.consumer.notify(ClientEvent::Destroy(client)); match &mut self.consumer {
EventConsumer::Sync(consumer) => consumer.notify(ClientEvent::Destroy(client)),
EventConsumer::Async(consumer) => consumer.notify(ClientEvent::Destroy(client)).await,
}
if let Some(client) = self.client_manager.remove_client(client).map(|s| s.client.handle) { if let Some(client) = self.client_manager.remove_client(client).map(|s| s.client.handle) {
let notify = FrontendNotify::NotifyClientDelete(client); let notify = FrontendNotify::NotifyClientDelete(client);
log::debug!("{notify:?}"); log::debug!("{notify:?}");
@@ -208,9 +230,15 @@ impl Server {
state.client.pos = pos; state.client.pos = pos;
if state.active { if state.active {
self.producer.notify(ClientEvent::Destroy(client)); self.producer.notify(ClientEvent::Destroy(client));
self.consumer.notify(ClientEvent::Destroy(client)); match &mut self.consumer {
EventConsumer::Sync(consumer) => consumer.notify(ClientEvent::Destroy(client)),
EventConsumer::Async(consumer) => consumer.notify(ClientEvent::Destroy(client)).await,
}
self.producer.notify(ClientEvent::Create(client, pos)); self.producer.notify(ClientEvent::Create(client, pos));
self.consumer.notify(ClientEvent::Create(client, pos)); match &mut self.consumer {
EventConsumer::Sync(consumer) => consumer.notify(ClientEvent::Create(client, pos)),
EventConsumer::Async(consumer) => consumer.notify(ClientEvent::Create(client, pos)).await,
}
} }
// update port // update port
@@ -294,7 +322,10 @@ impl Server {
} }
State::Receiving => { State::Receiving => {
// consume event // consume event
self.consumer.consume(event, handle); match &mut self.consumer {
EventConsumer::Sync(consumer) => consumer.consume(event, handle),
EventConsumer::Async(consumer) => consumer.consume(event, handle).await,
}
// let the server know we are still alive once every second // let the server know we are still alive once every second
let last_replied = state.last_replied; let last_replied = state.last_replied;
@@ -421,7 +452,7 @@ impl Server {
log::debug!("frontend: {event:?}"); log::debug!("frontend: {event:?}");
match event { match event {
FrontendEvent::AddClient(hostname, port, pos) => { self.add_client(hostname, HashSet::new(), port, pos).await; }, FrontendEvent::AddClient(hostname, port, pos) => { self.add_client(hostname, HashSet::new(), port, pos).await; },
FrontendEvent::ActivateClient(client, active) => self.activate_client(client, active), FrontendEvent::ActivateClient(client, active) => self.activate_client(client, active).await,
FrontendEvent::DelClient(client) => { self.remove_client(client).await; }, FrontendEvent::DelClient(client) => { self.remove_client(client).await; },
FrontendEvent::UpdateClient(client, hostname, port, pos) => self.update_client(client, hostname, port, pos).await, FrontendEvent::UpdateClient(client, hostname, port, pos) => self.update_client(client, hostname, port, pos).await,
FrontendEvent::Enumerate() => self.enumerate().await, FrontendEvent::Enumerate() => self.enumerate().await,

View File

@@ -27,10 +27,6 @@ pub fn run() -> Result<(), Box<dyn Error>> {
// parse config file // parse config file
let config = Config::new()?; let config = Config::new()?;
// start producing and consuming events
let producer = producer::create()?;
let consumer = consumer::create()?;
let runtime = tokio::runtime::Builder::new_current_thread() let runtime = tokio::runtime::Builder::new_current_thread()
.enable_io() .enable_io()
.enable_time() .enable_time()
@@ -38,6 +34,10 @@ pub fn run() -> Result<(), Box<dyn Error>> {
// run async event loop // run async event loop
runtime.block_on(LocalSet::new().run_until(async { runtime.block_on(LocalSet::new().run_until(async {
// start producing and consuming events
let producer = producer::create()?;
let consumer = consumer::create().await?;
// create frontend communication adapter // create frontend communication adapter
let frontend_adapter = FrontendListener::new().await?; let frontend_adapter = FrontendListener::new().await?;