From a8ffa4bd837e154e9411b17c93ee652f04aa83e0 Mon Sep 17 00:00:00 2001 From: AZ Computer Guru Date: Mon, 29 Dec 2025 17:51:22 -0700 Subject: [PATCH] Add native viewer with low-level keyboard hooks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New viewer crate for Windows native remote desktop viewing - Implements WH_KEYBOARD_LL hook for Win key, Alt+Tab capture - WebSocket client for server communication - softbuffer rendering for frame display - Zstd decompression for compressed frames - Mouse and keyboard input forwarding 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- Cargo.lock | 1911 +++++++++++++++++++++++++-------------- Cargo.toml | 1 + viewer/Cargo.toml | 66 ++ viewer/build.rs | 9 + viewer/src/input.rs | 173 ++++ viewer/src/main.rs | 167 ++++ viewer/src/proto.rs | 4 + viewer/src/render.rs | 507 +++++++++++ viewer/src/transport.rs | 100 ++ 9 files changed, 2254 insertions(+), 684 deletions(-) create mode 100644 viewer/Cargo.toml create mode 100644 viewer/build.rs create mode 100644 viewer/src/input.rs create mode 100644 viewer/src/main.rs create mode 100644 viewer/src/proto.rs create mode 100644 viewer/src/render.rs create mode 100644 viewer/src/transport.rs diff --git a/Cargo.lock b/Cargo.lock index a4c9337..45ffb2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,41 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ab_glyph" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" + [[package]] name = "adler2" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -23,6 +52,33 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "android-activity" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +dependencies = [ + "android-properties", + "bitflags 2.10.0", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror 1.0.69", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -32,6 +88,56 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + [[package]] name = "anyhow" version = "1.0.100" @@ -50,6 +156,24 @@ dependencies = [ "password-hash", ] +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + [[package]] name = "async-compression" version = "0.4.36" @@ -71,30 +195,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", -] - -[[package]] -name = "atk" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" -dependencies = [ - "atk-sys", - "glib", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", + "syn", ] [[package]] @@ -185,7 +286,7 @@ checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -242,15 +343,6 @@ dependencies = [ "objc2 0.5.2", ] -[[package]] -name = "block2" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" -dependencies = [ - "objc2 0.6.3", -] - [[package]] name = "bumpalo" version = "3.19.1" @@ -262,6 +354,20 @@ name = "bytemuck" version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "byteorder" @@ -269,12 +375,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - [[package]] name = "bytes" version = "1.11.0" @@ -282,28 +382,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] -name = "cairo-rs" -version = "0.18.5" +name = "calloop" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ "bitflags 2.10.0", - "cairo-sys-rs", - "glib", - "libc", - "once_cell", + "log", + "polling", + "rustix 0.38.44", + "slab", "thiserror 1.0.69", ] [[package]] -name = "cairo-sys-rs" -version = "0.18.2" +name = "calloop-wayland-source" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ - "glib-sys", - "libc", - "system-deps", + "calloop", + "rustix 0.38.44", + "wayland-backend", + "wayland-client", ] [[package]] @@ -319,14 +420,10 @@ dependencies = [ ] [[package]] -name = "cfg-expr" -version = "0.15.8" +name = "cesu8" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" -dependencies = [ - "smallvec", - "target-lexicon", -] +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfg-if" @@ -334,6 +431,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.42" @@ -348,6 +451,62 @@ dependencies = [ "windows-link", ] +[[package]] +name = "clap" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "compression-codecs" version = "0.4.35" @@ -396,6 +555,30 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -429,15 +612,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -463,6 +637,18 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctor-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f791803201ab277ace03903de1594460708d2d54df6053f2d9e82f592b19e3b" + +[[package]] +name = "cursor-icon" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" + [[package]] name = "data-encoding" version = "2.9.0" @@ -502,25 +688,10 @@ dependencies = [ ] [[package]] -name = "dirs" -version = "6.0.0" +name = "dispatch" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.61.2", -] +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" [[package]] name = "dispatch2" @@ -540,7 +711,16 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", ] [[package]] @@ -549,12 +729,58 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "dpi" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +[[package]] +name = "drm" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" +dependencies = [ + "bitflags 2.10.0", + "bytemuck", + "drm-ffi", + "drm-fourcc", + "libc", + "rustix 0.38.44", +] + +[[package]] +name = "drm-ffi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e41459d99a9b529845f6d2c909eb9adf3b6d2f82635ae40be8de0601726e8b" +dependencies = [ + "drm-sys", + "rustix 0.38.44", +] + +[[package]] +name = "drm-fourcc" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" + +[[package]] +name = "drm-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bafb66c8dbc944d69e15cfcc661df7e703beffbaec8bd63151368b06c5f9858c" +dependencies = [ + "libc", + "linux-raw-sys 0.6.5", +] + [[package]] name = "either" version = "1.15.0" @@ -608,25 +834,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "field-offset" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" -dependencies = [ - "memoffset", - "rustc_version", -] - [[package]] name = "find-msvc-tools" version = "0.1.5" @@ -672,7 +879,28 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -681,6 +909,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -742,7 +976,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -774,64 +1008,6 @@ dependencies = [ "slab", ] -[[package]] -name = "gdk" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" -dependencies = [ - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", -] - -[[package]] -name = "gdk-pixbuf" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" -dependencies = [ - "gdk-pixbuf-sys", - "gio", - "glib", - "libc", - "once_cell", -] - -[[package]] -name = "gdk-pixbuf-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gdk-sys" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -842,6 +1018,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix 1.1.2", + "windows-link", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -867,148 +1053,6 @@ dependencies = [ "wasip2", ] -[[package]] -name = "gio" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "gio-sys", - "glib", - "libc", - "once_cell", - "pin-project-lite", - "smallvec", - "thiserror 1.0.69", -] - -[[package]] -name = "gio-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", -] - -[[package]] -name = "glib" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" -dependencies = [ - "bitflags 2.10.0", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "memchr", - "once_cell", - "smallvec", - "thiserror 1.0.69", -] - -[[package]] -name = "glib-macros" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" -dependencies = [ - "heck 0.4.1", - "proc-macro-crate 2.0.2", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.111", -] - -[[package]] -name = "glib-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" -dependencies = [ - "libc", - "system-deps", -] - -[[package]] -name = "gobject-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gtk" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" -dependencies = [ - "atk", - "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk-sys", - "gtk3-macros", - "libc", - "pango", - "pkg-config", -] - -[[package]] -name = "gtk-sys" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" -dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", -] - -[[package]] -name = "gtk3-macros" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "guruconnect-agent" version = "0.1.0" @@ -1018,8 +1062,6 @@ dependencies = [ "chrono", "futures-util", "hostname", - "image", - "muda", "prost", "prost-build", "prost-types", @@ -1029,15 +1071,12 @@ dependencies = [ "thiserror 1.0.69", "tokio", "tokio-tungstenite", - "toml 0.8.2", + "toml", "tracing", "tracing-subscriber", - "tray-icon", - "urlencoding", "uuid", "windows", "windows-service", - "winres", "zstd", ] @@ -1062,7 +1101,7 @@ dependencies = [ "sqlx", "thiserror 1.0.69", "tokio", - "toml 0.8.2", + "toml", "tower", "tower-http", "tracing", @@ -1070,6 +1109,33 @@ dependencies = [ "uuid", ] +[[package]] +name = "guruconnect-viewer" +version = "0.1.0" +dependencies = [ + "anyhow", + "bytes", + "clap", + "futures-util", + "prost", + "prost-build", + "prost-types", + "raw-window-handle", + "serde", + "serde_json", + "softbuffer", + "thiserror 1.0.69", + "tokio", + "tokio-tungstenite", + "tracing", + "tracing-subscriber", + "url", + "uuid", + "windows", + "winit", + "zstd", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -1096,18 +1162,18 @@ dependencies = [ "hashbrown 0.15.5", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "hex" version = "0.4.3" @@ -1366,19 +1432,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "image" -version = "0.25.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" -dependencies = [ - "bytemuck", - "byteorder-lite", - "moxcms", - "num-traits", - "png 0.18.0", -] - [[package]] name = "indexmap" version = "2.12.1" @@ -1389,6 +1442,12 @@ dependencies = [ "hashbrown 0.16.1", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itertools" version = "0.14.0" @@ -1404,6 +1463,28 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.34" @@ -1439,17 +1520,6 @@ dependencies = [ "simple_asn1", ] -[[package]] -name = "keyboard-types" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" -dependencies = [ - "bitflags 2.10.0", - "serde", - "unicode-segmentation", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1459,30 +1529,6 @@ dependencies = [ "spin", ] -[[package]] -name = "libappindicator" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" -dependencies = [ - "glib", - "gtk", - "gtk-sys", - "libappindicator-sys", - "log", -] - -[[package]] -name = "libappindicator-sys" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" -dependencies = [ - "gtk-sys", - "libloading", - "once_cell", -] - [[package]] name = "libc" version = "0.2.178" @@ -1491,12 +1537,12 @@ checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libloading" -version = "0.7.4" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "winapi", + "windows-link", ] [[package]] @@ -1527,23 +1573,16 @@ dependencies = [ ] [[package]] -name = "libxdo" -version = "0.6.0" +name = "linux-raw-sys" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" -dependencies = [ - "libxdo-sys", -] +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] -name = "libxdo-sys" -version = "0.11.0" +name = "linux-raw-sys" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" -dependencies = [ - "libc", - "x11", -] +checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" [[package]] name = "linux-raw-sys" @@ -1604,12 +1643,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] -name = "memoffset" -version = "0.9.1" +name = "memmap2" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" dependencies = [ - "autocfg", + "libc", ] [[package]] @@ -1649,36 +1688,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "moxcms" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" -dependencies = [ - "num-traits", - "pxfm", -] - -[[package]] -name = "muda" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdae9c00e61cc0579bcac625e8ad22104c60548a025bfc972dc83868a28e1484" -dependencies = [ - "crossbeam-channel", - "dpi", - "gtk", - "keyboard-types", - "libxdo", - "objc2 0.5.2", - "objc2-app-kit 0.2.2", - "objc2-foundation 0.2.2", - "once_cell", - "png 0.17.16", - "thiserror 1.0.69", - "windows-sys 0.59.0", -] - [[package]] name = "multimap" version = "0.10.1" @@ -1702,6 +1711,36 @@ dependencies = [ "tempfile", ] +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.10.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1773,6 +1812,28 @@ dependencies = [ "libm", ] +[[package]] +name = "num_enum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "objc-sys" version = "0.3.5" @@ -1805,25 +1866,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.10.0", - "block2 0.5.1", + "block2", "libc", "objc2 0.5.2", "objc2-core-data", "objc2-core-image", "objc2-foundation 0.2.2", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", ] [[package]] -name = "objc2-app-kit" -version = "0.3.2" +name = "objc2-cloud-kit" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ "bitflags 2.10.0", - "objc2 0.6.3", - "objc2-core-foundation", - "objc2-foundation 0.3.2", + "block2", + "objc2 0.5.2", + "objc2-core-location", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -1833,7 +1906,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.10.0", - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -1856,7 +1929,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ "bitflags 2.10.0", + "dispatch2", + "objc2 0.6.3", "objc2-core-foundation", + "objc2-io-surface", ] [[package]] @@ -1865,12 +1941,24 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal", ] +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2 0.5.2", + "objc2-contacts", + "objc2-foundation 0.2.2", +] + [[package]] name = "objc2-encode" version = "4.1.0" @@ -1884,7 +1972,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.10.0", - "block2 0.5.1", + "block2", + "dispatch", "libc", "objc2 0.5.2", ] @@ -1896,11 +1985,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.10.0", - "block2 0.6.2", "objc2 0.6.3", "objc2-core-foundation", ] +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.10.0", + "objc2 0.6.3", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation 0.2.2", +] + [[package]] name = "objc2-metal" version = "0.2.2" @@ -1908,7 +2019,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.10.0", - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -1920,18 +2031,91 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.10.0", - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal", ] +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.10.0", + "objc2 0.6.3", + "objc2-core-foundation", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.10.0", + "block2", + "objc2 0.5.2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation 0.2.2", + "objc2-link-presentation", + "objc2-quartz-core 0.2.2", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.10.0", + "block2", + "objc2 0.5.2", + "objc2-core-location", + "objc2-foundation 0.2.2", +] + [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "openssl" version = "0.10.75" @@ -1940,7 +2124,7 @@ checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ "bitflags 2.10.0", "cfg-if", - "foreign-types", + "foreign-types 0.3.2", "libc", "once_cell", "openssl-macros", @@ -1955,7 +2139,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -1977,34 +2161,21 @@ dependencies = [ ] [[package]] -name = "option-ext" -version = "0.2.0" +name = "orbclient" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "pango" -version = "0.18.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +checksum = "247ad146e19b9437f8604c21f8652423595cf710ad108af40e77d3ae6e96b827" dependencies = [ - "gio", - "glib", - "libc", - "once_cell", - "pango-sys", + "libredox", ] [[package]] -name = "pango-sys" -version = "0.18.0" +name = "owned_ttf_parser" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", + "ttf-parser", ] [[package]] @@ -2082,6 +2253,26 @@ dependencies = [ "indexmap", ] +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -2122,29 +2313,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] -name = "png" -version = "0.17.16" +name = "polling" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "png" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" -dependencies = [ - "bitflags 2.10.0", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.1.2", + "windows-sys 0.61.2", ] [[package]] @@ -2178,51 +2357,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.111", + "syn", ] [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" -dependencies = [ - "toml_datetime", - "toml_edit 0.20.2", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", + "toml_edit 0.23.10+spec-1.0.0", ] [[package]] @@ -2250,7 +2394,7 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ - "heck 0.5.0", + "heck", "itertools", "log", "multimap", @@ -2260,7 +2404,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.111", + "syn", "tempfile", ] @@ -2274,7 +2418,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -2287,12 +2431,12 @@ dependencies = [ ] [[package]] -name = "pxfm" -version = "0.1.27" +name = "quick-xml" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ - "num-traits", + "memchr", ] [[package]] @@ -2340,6 +2484,21 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -2358,17 +2517,6 @@ dependencies = [ "bitflags 2.10.0", ] -[[package]] -name = "redox_users" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" -dependencies = [ - "getrandom 0.2.16", - "libredox", - "thiserror 2.0.17", -] - [[package]] name = "regex" version = "1.12.2" @@ -2433,12 +2581,16 @@ dependencies = [ ] [[package]] -name = "rustc_version" -version = "0.4.1" +name = "rustix" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "semver", + "bitflags 2.10.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.52.0", ] [[package]] @@ -2450,7 +2602,7 @@ dependencies = [ "bitflags 2.10.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.11.0", "windows-sys 0.61.2", ] @@ -2466,6 +2618,15 @@ version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.28" @@ -2475,12 +2636,31 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sctk-adwaita" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -2504,12 +2684,6 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - [[package]] name = "serde" version = "1.0.228" @@ -2537,7 +2711,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -2674,6 +2848,40 @@ dependencies = [ "serde", ] +[[package]] +name = "smithay-client-toolkit" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" +dependencies = [ + "bitflags 2.10.0", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix 0.38.44", + "thiserror 1.0.69", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + [[package]] name = "socket2" version = "0.6.1" @@ -2684,6 +2892,38 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "softbuffer" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" +dependencies = [ + "as-raw-xcb-connection", + "bytemuck", + "drm", + "fastrand", + "js-sys", + "memmap2", + "ndk", + "objc2 0.6.3", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", + "raw-window-handle", + "redox_syscall 0.5.18", + "rustix 1.1.2", + "tiny-xlib", + "tracing", + "wasm-bindgen", + "wayland-backend", + "wayland-client", + "wayland-sys", + "web-sys", + "windows-sys 0.61.2", + "x11rb", +] + [[package]] name = "spin" version = "0.9.8" @@ -2762,7 +3002,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.111", + "syn", ] [[package]] @@ -2773,7 +3013,7 @@ checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" dependencies = [ "dotenvy", "either", - "heck 0.5.0", + "heck", "hex", "once_cell", "proc-macro2", @@ -2785,7 +3025,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.111", + "syn", "tokio", "url", ] @@ -2905,6 +3145,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + [[package]] name = "stringprep" version = "0.1.5" @@ -2916,22 +3162,18 @@ dependencies = [ "unicode-properties", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.111" @@ -2957,28 +3199,9 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] -[[package]] -name = "system-deps" -version = "6.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" -dependencies = [ - "cfg-expr", - "heck 0.5.0", - "pkg-config", - "toml 0.8.2", - "version-compare", -] - -[[package]] -name = "target-lexicon" -version = "0.12.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" - [[package]] name = "tempfile" version = "3.23.0" @@ -2988,7 +3211,7 @@ dependencies = [ "fastrand", "getrandom 0.3.4", "once_cell", - "rustix", + "rustix 1.1.2", "windows-sys 0.61.2", ] @@ -3018,7 +3241,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -3029,7 +3252,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -3072,6 +3295,44 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tiny-xlib" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e" +dependencies = [ + "as-raw-xcb-connection", + "ctor-lite", + "libloading", + "pkg-config", + "tracing", +] + [[package]] name = "tinystr" version = "0.8.2" @@ -3122,7 +3383,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -3175,58 +3436,75 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.11" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "toml" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", - "toml_datetime", - "toml_edit 0.20.2", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] [[package]] -name = "toml_edit" -version = "0.19.15" +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ - "indexmap", - "toml_datetime", - "winnow", + "serde_core", ] [[package]] name = "toml_edit" -version = "0.20.2" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", "serde_spanned", - "toml_datetime", + "toml_datetime 0.6.11", + "toml_write", "winnow", ] +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "tower" version = "0.5.2" @@ -3302,7 +3580,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -3345,25 +3623,10 @@ dependencies = [ ] [[package]] -name = "tray-icon" -version = "0.19.3" +name = "ttf-parser" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadd75f5002e2513eaa19b2365f533090cc3e93abd38788452d9ea85cff7b48a" -dependencies = [ - "crossbeam-channel", - "dirs", - "libappindicator", - "muda", - "objc2 0.6.3", - "objc2-app-kit 0.3.2", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-foundation 0.3.2", - "once_cell", - "png 0.17.16", - "thiserror 2.0.17", - "windows-sys 0.59.0", -] +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" [[package]] name = "tungstenite" @@ -3447,12 +3710,6 @@ dependencies = [ "serde", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - [[package]] name = "utf-8" version = "0.7.6" @@ -3465,6 +3722,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.19.0" @@ -3489,18 +3752,22 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "version-compare" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" - [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -3535,6 +3802,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.106" @@ -3554,7 +3834,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.111", + "syn", "wasm-bindgen-shared", ] @@ -3567,6 +3847,135 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wayland-backend" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" +dependencies = [ + "cc", + "downcast-rs", + "rustix 1.1.2", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" +dependencies = [ + "bitflags 2.10.0", + "rustix 1.1.2", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.10.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" +dependencies = [ + "rustix 1.1.2", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" +dependencies = [ + "bitflags 2.10.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" +dependencies = [ + "bitflags 2.10.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" +dependencies = [ + "bitflags 2.10.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "whoami" version = "1.6.1" @@ -3584,27 +3993,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" [[package]] -name = "winapi" -version = "0.3.9" +name = "winapi-util" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "windows-sys 0.61.2", ] -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows" version = "0.58.0" @@ -3649,7 +4045,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -3660,7 +4056,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -3671,7 +4067,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -3682,7 +4078,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -3739,6 +4135,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -3757,15 +4162,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.60.2" @@ -3784,6 +4180,21 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -3832,6 +4243,12 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -3850,6 +4267,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -3868,6 +4291,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -3898,6 +4327,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -3916,6 +4351,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -3934,6 +4375,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -3952,6 +4399,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3971,21 +4424,64 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] -name = "winnow" -version = "0.5.40" +name = "winit" +version = "0.30.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732" dependencies = [ - "memchr", + "ahash", + "android-activity", + "atomic-waker", + "bitflags 2.10.0", + "block2", + "bytemuck", + "calloop", + "cfg_aliases", + "concurrent-queue", + "core-foundation", + "core-graphics", + "cursor-icon", + "dpi", + "js-sys", + "libc", + "memmap2", + "ndk", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation 0.2.2", + "objc2-ui-kit", + "orbclient", + "percent-encoding", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix 0.38.44", + "sctk-adwaita", + "smithay-client-toolkit", + "smol_str", + "tracing", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.52.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", ] [[package]] -name = "winres" -version = "0.1.12" +name = "winnow" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ - "toml 0.5.11", + "memchr", ] [[package]] @@ -4001,15 +4497,62 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] -name = "x11" +name = "x11-dl" version = "2.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" dependencies = [ "libc", + "once_cell", "pkg-config", ] +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading", + "once_cell", + "rustix 1.1.2", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + +[[package]] +name = "xcursor" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.10.0", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + [[package]] name = "yoke" version = "0.8.1" @@ -4029,7 +4572,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", "synstructure", ] @@ -4050,7 +4593,7 @@ checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] @@ -4070,7 +4613,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", "synstructure", ] @@ -4110,7 +4653,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9a5462e..d68553a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "agent", "server", + "viewer", ] [workspace.package] diff --git a/viewer/Cargo.toml b/viewer/Cargo.toml new file mode 100644 index 0000000..14d163a --- /dev/null +++ b/viewer/Cargo.toml @@ -0,0 +1,66 @@ +[package] +name = "guruconnect-viewer" +version = "0.1.0" +edition = "2021" +authors = ["AZ Computer Guru"] +description = "GuruConnect Native Remote Desktop Viewer" + +[dependencies] +# Async runtime +tokio = { version = "1", features = ["full", "sync", "time", "rt-multi-thread", "macros"] } + +# WebSocket +tokio-tungstenite = { version = "0.24", features = ["native-tls"] } +futures-util = "0.3" +url = "2" + +# Windowing +winit = { version = "0.30", features = ["rwh_06"] } +softbuffer = "0.4" +raw-window-handle = "0.6" + +# Compression +zstd = "0.13" + +# Protocol (protobuf) +prost = "0.13" +prost-types = "0.13" +bytes = "1" + +# Serialization +serde = { version = "1", features = ["derive"] } +serde_json = "1" + +# Logging +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } + +# Error handling +anyhow = "1" +thiserror = "1" + +# UUID +uuid = { version = "1", features = ["v4", "serde"] } + +# CLI args +clap = { version = "4", features = ["derive"] } + +[target.'cfg(windows)'.dependencies] +# Windows APIs for low-level keyboard hooks +windows = { version = "0.58", features = [ + "Win32_Foundation", + "Win32_UI_WindowsAndMessaging", + "Win32_UI_Input_KeyboardAndMouse", + "Win32_System_LibraryLoader", + "Win32_System_Threading", +]} + +[build-dependencies] +prost-build = "0.13" + +[profile.release] +lto = true +codegen-units = 1 +opt-level = "z" +strip = true +panic = "abort" diff --git a/viewer/build.rs b/viewer/build.rs new file mode 100644 index 0000000..5580f9e --- /dev/null +++ b/viewer/build.rs @@ -0,0 +1,9 @@ +use std::io::Result; + +fn main() -> Result<()> { + println!("cargo:rerun-if-changed=../proto/guruconnect.proto"); + + prost_build::compile_protos(&["../proto/guruconnect.proto"], &["../proto/"])?; + + Ok(()) +} diff --git a/viewer/src/input.rs b/viewer/src/input.rs new file mode 100644 index 0000000..c080d29 --- /dev/null +++ b/viewer/src/input.rs @@ -0,0 +1,173 @@ +//! Low-level keyboard hook for capturing all keys including Win key + +use crate::InputEvent; +#[cfg(windows)] +use crate::proto; +use anyhow::Result; +use tokio::sync::mpsc; +#[cfg(windows)] +use tracing::trace; + +#[cfg(windows)] +use windows::{ + Win32::Foundation::{LPARAM, LRESULT, WPARAM}, + Win32::UI::WindowsAndMessaging::{ + CallNextHookEx, DispatchMessageW, GetMessageW, PeekMessageW, SetWindowsHookExW, + TranslateMessage, UnhookWindowsHookEx, HHOOK, KBDLLHOOKSTRUCT, MSG, PM_REMOVE, + WH_KEYBOARD_LL, WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, WM_SYSKEYUP, + }, +}; + +#[cfg(windows)] +use std::sync::OnceLock; + +#[cfg(windows)] +static INPUT_TX: OnceLock> = OnceLock::new(); + +#[cfg(windows)] +static mut HOOK_HANDLE: HHOOK = HHOOK(std::ptr::null_mut()); + +/// Virtual key codes for special keys +#[cfg(windows)] +mod vk { + pub const VK_LWIN: u32 = 0x5B; + pub const VK_RWIN: u32 = 0x5C; + pub const VK_APPS: u32 = 0x5D; + pub const VK_LSHIFT: u32 = 0xA0; + pub const VK_RSHIFT: u32 = 0xA1; + pub const VK_LCONTROL: u32 = 0xA2; + pub const VK_RCONTROL: u32 = 0xA3; + pub const VK_LMENU: u32 = 0xA4; // Left Alt + pub const VK_RMENU: u32 = 0xA5; // Right Alt + pub const VK_TAB: u32 = 0x09; + pub const VK_ESCAPE: u32 = 0x1B; + pub const VK_SNAPSHOT: u32 = 0x2C; // Print Screen +} + +#[cfg(windows)] +pub struct KeyboardHook { + _hook: HHOOK, +} + +#[cfg(windows)] +impl KeyboardHook { + pub fn new(input_tx: mpsc::Sender) -> Result { + // Store the sender globally for the hook callback + INPUT_TX.set(input_tx).map_err(|_| anyhow::anyhow!("Input TX already set"))?; + + unsafe { + let hook = SetWindowsHookExW( + WH_KEYBOARD_LL, + Some(keyboard_hook_proc), + None, + 0, + )?; + + HOOK_HANDLE = hook; + Ok(Self { _hook: hook }) + } + } +} + +#[cfg(windows)] +impl Drop for KeyboardHook { + fn drop(&mut self) { + unsafe { + if !HOOK_HANDLE.0.is_null() { + let _ = UnhookWindowsHookEx(HOOK_HANDLE); + HOOK_HANDLE = HHOOK(std::ptr::null_mut()); + } + } + } +} + +#[cfg(windows)] +unsafe extern "system" fn keyboard_hook_proc( + code: i32, + wparam: WPARAM, + lparam: LPARAM, +) -> LRESULT { + if code >= 0 { + let kb_struct = &*(lparam.0 as *const KBDLLHOOKSTRUCT); + let vk_code = kb_struct.vkCode; + let scan_code = kb_struct.scanCode; + + let is_down = wparam.0 as u32 == WM_KEYDOWN || wparam.0 as u32 == WM_SYSKEYDOWN; + let is_up = wparam.0 as u32 == WM_KEYUP || wparam.0 as u32 == WM_SYSKEYUP; + + if is_down || is_up { + // Check if this is a key we want to intercept (Win key, Alt+Tab, etc.) + let should_intercept = matches!( + vk_code, + vk::VK_LWIN | vk::VK_RWIN | vk::VK_APPS + ); + + // Send the key event to the remote + if let Some(tx) = INPUT_TX.get() { + let event = proto::KeyEvent { + down: is_down, + key_type: proto::KeyEventType::KeyVk as i32, + vk_code, + scan_code, + unicode: String::new(), + modifiers: Some(get_current_modifiers()), + }; + + let _ = tx.try_send(InputEvent::Key(event)); + trace!("Key hook: vk={:#x} scan={} down={}", vk_code, scan_code, is_down); + } + + // For Win key, consume the event so it doesn't open Start menu locally + if should_intercept { + return LRESULT(1); + } + } + } + + CallNextHookEx(HOOK_HANDLE, code, wparam, lparam) +} + +#[cfg(windows)] +fn get_current_modifiers() -> proto::Modifiers { + use windows::Win32::UI::Input::KeyboardAndMouse::GetAsyncKeyState; + + unsafe { + proto::Modifiers { + ctrl: GetAsyncKeyState(0x11) < 0, // VK_CONTROL + alt: GetAsyncKeyState(0x12) < 0, // VK_MENU + shift: GetAsyncKeyState(0x10) < 0, // VK_SHIFT + meta: GetAsyncKeyState(0x5B) < 0 || GetAsyncKeyState(0x5C) < 0, // VK_LWIN/RWIN + caps_lock: GetAsyncKeyState(0x14) & 1 != 0, // VK_CAPITAL + num_lock: GetAsyncKeyState(0x90) & 1 != 0, // VK_NUMLOCK + } + } +} + +/// Pump Windows message queue (required for hooks to work) +#[cfg(windows)] +pub fn pump_messages() { + unsafe { + let mut msg = MSG::default(); + while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() { + let _ = TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } +} + +// Non-Windows stubs +#[cfg(not(windows))] +#[allow(dead_code)] +pub struct KeyboardHook; + +#[cfg(not(windows))] +#[allow(dead_code)] +impl KeyboardHook { + pub fn new(_input_tx: mpsc::Sender) -> Result { + Ok(Self) + } +} + +#[cfg(not(windows))] +#[allow(dead_code)] +pub fn pump_messages() {} diff --git a/viewer/src/main.rs b/viewer/src/main.rs new file mode 100644 index 0000000..de236d8 --- /dev/null +++ b/viewer/src/main.rs @@ -0,0 +1,167 @@ +//! GuruConnect Native Viewer +//! +//! Native remote desktop viewer with full keyboard capture including Win key. + +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +mod proto; +mod transport; +mod render; +mod input; + +use anyhow::Result; +use clap::Parser; +use std::sync::Arc; +use tokio::sync::{mpsc, Mutex}; +use tracing::{info, error, warn, Level}; +use tracing_subscriber::FmtSubscriber; + +/// GuruConnect Native Viewer +#[derive(Parser, Debug)] +#[command(name = "guruconnect-viewer")] +#[command(about = "Native remote desktop viewer with full keyboard capture")] +struct Args { + /// Server URL (e.g., wss://connect.azcomputerguru.com/ws/viewer) + #[arg(short, long, default_value = "wss://connect.azcomputerguru.com/ws/viewer")] + server: String, + + /// Session ID to connect to + #[arg(short = 'i', long)] + session_id: String, + + /// API key for authentication + #[arg(short, long, default_value = "dev-key")] + api_key: String, + + /// Enable verbose logging + #[arg(short, long)] + verbose: bool, +} + +#[derive(Debug, Clone)] +pub enum ViewerEvent { + Connected, + Disconnected(String), + Frame(render::FrameData), + CursorPosition(i32, i32, bool), + CursorShape(proto::CursorShape), +} + +#[derive(Debug, Clone)] +pub enum InputEvent { + Mouse(proto::MouseEvent), + Key(proto::KeyEvent), + SpecialKey(proto::SpecialKeyEvent), +} + +fn main() -> Result<()> { + let args = Args::parse(); + + // Initialize logging + let level = if args.verbose { Level::DEBUG } else { Level::INFO }; + FmtSubscriber::builder() + .with_max_level(level) + .with_target(false) + .init(); + + info!("GuruConnect Viewer starting"); + info!("Server: {}", args.server); + info!("Session: {}", args.session_id); + + // Create channels for communication between components + let (viewer_tx, viewer_rx) = mpsc::channel::(100); + let (input_tx, input_rx) = mpsc::channel::(100); + + // Run the viewer + let rt = tokio::runtime::Runtime::new()?; + rt.block_on(async { + run_viewer(args, viewer_tx, viewer_rx, input_tx, input_rx).await + }) +} + +async fn run_viewer( + args: Args, + viewer_tx: mpsc::Sender, + viewer_rx: mpsc::Receiver, + input_tx: mpsc::Sender, + mut input_rx: mpsc::Receiver, +) -> Result<()> { + // Connect to server + let ws_url = format!("{}?session_id={}", args.server, args.session_id); + info!("Connecting to {}", ws_url); + + let (ws_sender, mut ws_receiver) = transport::connect(&ws_url, &args.api_key).await?; + let ws_sender = Arc::new(Mutex::new(ws_sender)); + + info!("Connected to server"); + let _ = viewer_tx.send(ViewerEvent::Connected).await; + + // Clone sender for input forwarding + let ws_sender_input = ws_sender.clone(); + + // Spawn task to forward input events to server + let input_task = tokio::spawn(async move { + while let Some(event) = input_rx.recv().await { + let msg = match event { + InputEvent::Mouse(m) => proto::Message { + payload: Some(proto::message::Payload::MouseEvent(m)), + }, + InputEvent::Key(k) => proto::Message { + payload: Some(proto::message::Payload::KeyEvent(k)), + }, + InputEvent::SpecialKey(s) => proto::Message { + payload: Some(proto::message::Payload::SpecialKey(s)), + }, + }; + + if let Err(e) = transport::send_message(&ws_sender_input, &msg).await { + error!("Failed to send input: {}", e); + break; + } + } + }); + + // Spawn task to receive messages from server + let viewer_tx_recv = viewer_tx.clone(); + let receive_task = tokio::spawn(async move { + while let Some(msg) = ws_receiver.recv().await { + match msg.payload { + Some(proto::message::Payload::VideoFrame(frame)) => { + if let Some(proto::video_frame::Encoding::Raw(raw)) = frame.encoding { + let frame_data = render::FrameData { + width: raw.width as u32, + height: raw.height as u32, + data: raw.data, + compressed: raw.compressed, + is_keyframe: raw.is_keyframe, + }; + let _ = viewer_tx_recv.send(ViewerEvent::Frame(frame_data)).await; + } + } + Some(proto::message::Payload::CursorPosition(pos)) => { + let _ = viewer_tx_recv.send(ViewerEvent::CursorPosition( + pos.x, pos.y, pos.visible + )).await; + } + Some(proto::message::Payload::CursorShape(shape)) => { + let _ = viewer_tx_recv.send(ViewerEvent::CursorShape(shape)).await; + } + Some(proto::message::Payload::Disconnect(d)) => { + warn!("Server disconnected: {}", d.reason); + let _ = viewer_tx_recv.send(ViewerEvent::Disconnected(d.reason)).await; + break; + } + _ => {} + } + } + }); + + // Run the window (this blocks until window closes) + render::run_window(viewer_rx, input_tx).await?; + + // Cleanup + input_task.abort(); + receive_task.abort(); + + Ok(()) +} diff --git a/viewer/src/proto.rs b/viewer/src/proto.rs new file mode 100644 index 0000000..2310784 --- /dev/null +++ b/viewer/src/proto.rs @@ -0,0 +1,4 @@ +//! Protocol buffer definitions + +// Include generated protobuf code +include!(concat!(env!("OUT_DIR"), "/guruconnect.rs")); diff --git a/viewer/src/render.rs b/viewer/src/render.rs new file mode 100644 index 0000000..28a8a56 --- /dev/null +++ b/viewer/src/render.rs @@ -0,0 +1,507 @@ +//! Window rendering and frame display + +use crate::{ViewerEvent, InputEvent, proto}; +#[cfg(windows)] +use crate::input; +use anyhow::Result; +use std::num::NonZeroU32; +use std::sync::Arc; +use tokio::sync::mpsc; +use tracing::{debug, error, info, warn}; +use winit::{ + application::ApplicationHandler, + dpi::LogicalSize, + event::{ElementState, MouseButton, MouseScrollDelta, WindowEvent}, + event_loop::{ActiveEventLoop, ControlFlow, EventLoop}, + keyboard::{KeyCode, PhysicalKey}, + window::{Window, WindowId}, +}; + +/// Frame data received from server +#[derive(Debug, Clone)] +pub struct FrameData { + pub width: u32, + pub height: u32, + pub data: Vec, + pub compressed: bool, + pub is_keyframe: bool, +} + +struct ViewerApp { + window: Option>, + surface: Option, Arc>>, + frame_buffer: Vec, + frame_width: u32, + frame_height: u32, + viewer_rx: mpsc::Receiver, + input_tx: mpsc::Sender, + mouse_x: i32, + mouse_y: i32, + #[cfg(windows)] + keyboard_hook: Option, +} + +impl ViewerApp { + fn new( + viewer_rx: mpsc::Receiver, + input_tx: mpsc::Sender, + ) -> Self { + Self { + window: None, + surface: None, + frame_buffer: Vec::new(), + frame_width: 0, + frame_height: 0, + viewer_rx, + input_tx, + mouse_x: 0, + mouse_y: 0, + #[cfg(windows)] + keyboard_hook: None, + } + } + + fn process_frame(&mut self, frame: FrameData) { + let data = if frame.compressed { + // Decompress zstd + match zstd::decode_all(frame.data.as_slice()) { + Ok(decompressed) => decompressed, + Err(e) => { + error!("Failed to decompress frame: {}", e); + return; + } + } + } else { + frame.data + }; + + // Convert BGRA to ARGB (softbuffer expects 0RGB format on little-endian) + let pixel_count = (frame.width * frame.height) as usize; + if data.len() < pixel_count * 4 { + error!("Frame data too small: {} < {}", data.len(), pixel_count * 4); + return; + } + + // Resize frame buffer if needed + if self.frame_width != frame.width || self.frame_height != frame.height { + self.frame_width = frame.width; + self.frame_height = frame.height; + self.frame_buffer.resize(pixel_count, 0); + + // Resize window to match frame + if let Some(window) = &self.window { + let _ = window.request_inner_size(LogicalSize::new(frame.width, frame.height)); + } + } + + // Convert BGRA to 0RGB (ignore alpha, swap B and R) + for i in 0..pixel_count { + let offset = i * 4; + let b = data[offset] as u32; + let g = data[offset + 1] as u32; + let r = data[offset + 2] as u32; + // 0RGB format: 0x00RRGGBB + self.frame_buffer[i] = (r << 16) | (g << 8) | b; + } + + // Request redraw + if let Some(window) = &self.window { + window.request_redraw(); + } + } + + fn render(&mut self) { + let Some(surface) = &mut self.surface else { return }; + let Some(window) = &self.window else { return }; + + if self.frame_buffer.is_empty() || self.frame_width == 0 || self.frame_height == 0 { + return; + } + + let size = window.inner_size(); + if size.width == 0 || size.height == 0 { + return; + } + + // Resize surface if needed + let width = NonZeroU32::new(size.width).unwrap(); + let height = NonZeroU32::new(size.height).unwrap(); + + if let Err(e) = surface.resize(width, height) { + error!("Failed to resize surface: {}", e); + return; + } + + let mut buffer = match surface.buffer_mut() { + Ok(b) => b, + Err(e) => { + error!("Failed to get surface buffer: {}", e); + return; + } + }; + + // Simple nearest-neighbor scaling + let scale_x = self.frame_width as f32 / size.width as f32; + let scale_y = self.frame_height as f32 / size.height as f32; + + for y in 0..size.height { + for x in 0..size.width { + let src_x = ((x as f32 * scale_x) as u32).min(self.frame_width - 1); + let src_y = ((y as f32 * scale_y) as u32).min(self.frame_height - 1); + let src_idx = (src_y * self.frame_width + src_x) as usize; + let dst_idx = (y * size.width + x) as usize; + + if src_idx < self.frame_buffer.len() && dst_idx < buffer.len() { + buffer[dst_idx] = self.frame_buffer[src_idx]; + } + } + } + + if let Err(e) = buffer.present() { + error!("Failed to present buffer: {}", e); + } + } + + fn send_mouse_event(&self, event_type: proto::MouseEventType, x: i32, y: i32) { + let event = proto::MouseEvent { + x, + y, + buttons: Some(proto::MouseButtons::default()), + wheel_delta_x: 0, + wheel_delta_y: 0, + event_type: event_type as i32, + }; + + let _ = self.input_tx.try_send(InputEvent::Mouse(event)); + } + + fn send_mouse_button(&self, button: MouseButton, state: ElementState) { + let event_type = match state { + ElementState::Pressed => proto::MouseEventType::MouseDown, + ElementState::Released => proto::MouseEventType::MouseUp, + }; + + let mut buttons = proto::MouseButtons::default(); + match button { + MouseButton::Left => buttons.left = true, + MouseButton::Right => buttons.right = true, + MouseButton::Middle => buttons.middle = true, + _ => {} + } + + let event = proto::MouseEvent { + x: self.mouse_x, + y: self.mouse_y, + buttons: Some(buttons), + wheel_delta_x: 0, + wheel_delta_y: 0, + event_type: event_type as i32, + }; + + let _ = self.input_tx.try_send(InputEvent::Mouse(event)); + } + + fn send_mouse_wheel(&self, delta_x: i32, delta_y: i32) { + let event = proto::MouseEvent { + x: self.mouse_x, + y: self.mouse_y, + buttons: Some(proto::MouseButtons::default()), + wheel_delta_x: delta_x, + wheel_delta_y: delta_y, + event_type: proto::MouseEventType::MouseWheel as i32, + }; + + let _ = self.input_tx.try_send(InputEvent::Mouse(event)); + } + + fn send_key_event(&self, key: PhysicalKey, state: ElementState) { + let vk_code = match key { + PhysicalKey::Code(code) => keycode_to_vk(code), + _ => return, + }; + + let event = proto::KeyEvent { + down: state == ElementState::Pressed, + key_type: proto::KeyEventType::KeyVk as i32, + vk_code, + scan_code: 0, + unicode: String::new(), + modifiers: Some(proto::Modifiers::default()), + }; + + let _ = self.input_tx.try_send(InputEvent::Key(event)); + } + + fn screen_to_frame_coords(&self, x: f64, y: f64) -> (i32, i32) { + let Some(window) = &self.window else { + return (x as i32, y as i32); + }; + + let size = window.inner_size(); + if size.width == 0 || size.height == 0 || self.frame_width == 0 || self.frame_height == 0 { + return (x as i32, y as i32); + } + + // Scale from window coordinates to frame coordinates + let scale_x = self.frame_width as f64 / size.width as f64; + let scale_y = self.frame_height as f64 / size.height as f64; + + let frame_x = (x * scale_x) as i32; + let frame_y = (y * scale_y) as i32; + + (frame_x, frame_y) + } +} + +impl ApplicationHandler for ViewerApp { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + if self.window.is_some() { + return; + } + + let window_attrs = Window::default_attributes() + .with_title("GuruConnect Viewer") + .with_inner_size(LogicalSize::new(1280, 720)); + + let window = Arc::new(event_loop.create_window(window_attrs).unwrap()); + + // Create software rendering surface + let context = softbuffer::Context::new(window.clone()).unwrap(); + let surface = softbuffer::Surface::new(&context, window.clone()).unwrap(); + + self.window = Some(window.clone()); + self.surface = Some(surface); + + // Install keyboard hook + #[cfg(windows)] + { + let input_tx = self.input_tx.clone(); + match input::KeyboardHook::new(input_tx) { + Ok(hook) => { + info!("Keyboard hook installed"); + self.keyboard_hook = Some(hook); + } + Err(e) => { + error!("Failed to install keyboard hook: {}", e); + } + } + } + + info!("Window created"); + } + + fn window_event(&mut self, event_loop: &ActiveEventLoop, _: WindowId, event: WindowEvent) { + // Check for incoming viewer events (non-blocking) + while let Ok(viewer_event) = self.viewer_rx.try_recv() { + match viewer_event { + ViewerEvent::Frame(frame) => { + self.process_frame(frame); + } + ViewerEvent::Connected => { + info!("Connected to remote session"); + } + ViewerEvent::Disconnected(reason) => { + warn!("Disconnected: {}", reason); + event_loop.exit(); + } + ViewerEvent::CursorPosition(_x, _y, _visible) => { + // Could update cursor display here + } + ViewerEvent::CursorShape(_shape) => { + // Could update cursor shape here + } + } + } + + match event { + WindowEvent::CloseRequested => { + info!("Window close requested"); + event_loop.exit(); + } + WindowEvent::RedrawRequested => { + self.render(); + } + WindowEvent::Resized(size) => { + debug!("Window resized to {}x{}", size.width, size.height); + if let Some(window) = &self.window { + window.request_redraw(); + } + } + WindowEvent::CursorMoved { position, .. } => { + let (x, y) = self.screen_to_frame_coords(position.x, position.y); + self.mouse_x = x; + self.mouse_y = y; + self.send_mouse_event(proto::MouseEventType::MouseMove, x, y); + } + WindowEvent::MouseInput { state, button, .. } => { + self.send_mouse_button(button, state); + } + WindowEvent::MouseWheel { delta, .. } => { + let (dx, dy) = match delta { + MouseScrollDelta::LineDelta(x, y) => (x as i32 * 120, y as i32 * 120), + MouseScrollDelta::PixelDelta(pos) => (pos.x as i32, pos.y as i32), + }; + self.send_mouse_wheel(dx, dy); + } + WindowEvent::KeyboardInput { event, .. } => { + // Note: This handles keys that aren't captured by the low-level hook + // The hook handles Win key and other special keys + if !event.repeat { + self.send_key_event(event.physical_key, event.state); + } + } + _ => {} + } + } + + fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { + // Keep checking for events + event_loop.set_control_flow(ControlFlow::Poll); + + // Process Windows messages for keyboard hook + #[cfg(windows)] + input::pump_messages(); + + // Request redraw periodically to check for new frames + if let Some(window) = &self.window { + window.request_redraw(); + } + } +} + +/// Run the viewer window +pub async fn run_window( + viewer_rx: mpsc::Receiver, + input_tx: mpsc::Sender, +) -> Result<()> { + let event_loop = EventLoop::new()?; + let mut app = ViewerApp::new(viewer_rx, input_tx); + + event_loop.run_app(&mut app)?; + + Ok(()) +} + +/// Convert winit KeyCode to Windows virtual key code +fn keycode_to_vk(code: KeyCode) -> u32 { + match code { + // Letters + KeyCode::KeyA => 0x41, + KeyCode::KeyB => 0x42, + KeyCode::KeyC => 0x43, + KeyCode::KeyD => 0x44, + KeyCode::KeyE => 0x45, + KeyCode::KeyF => 0x46, + KeyCode::KeyG => 0x47, + KeyCode::KeyH => 0x48, + KeyCode::KeyI => 0x49, + KeyCode::KeyJ => 0x4A, + KeyCode::KeyK => 0x4B, + KeyCode::KeyL => 0x4C, + KeyCode::KeyM => 0x4D, + KeyCode::KeyN => 0x4E, + KeyCode::KeyO => 0x4F, + KeyCode::KeyP => 0x50, + KeyCode::KeyQ => 0x51, + KeyCode::KeyR => 0x52, + KeyCode::KeyS => 0x53, + KeyCode::KeyT => 0x54, + KeyCode::KeyU => 0x55, + KeyCode::KeyV => 0x56, + KeyCode::KeyW => 0x57, + KeyCode::KeyX => 0x58, + KeyCode::KeyY => 0x59, + KeyCode::KeyZ => 0x5A, + + // Numbers + KeyCode::Digit0 => 0x30, + KeyCode::Digit1 => 0x31, + KeyCode::Digit2 => 0x32, + KeyCode::Digit3 => 0x33, + KeyCode::Digit4 => 0x34, + KeyCode::Digit5 => 0x35, + KeyCode::Digit6 => 0x36, + KeyCode::Digit7 => 0x37, + KeyCode::Digit8 => 0x38, + KeyCode::Digit9 => 0x39, + + // Function keys + KeyCode::F1 => 0x70, + KeyCode::F2 => 0x71, + KeyCode::F3 => 0x72, + KeyCode::F4 => 0x73, + KeyCode::F5 => 0x74, + KeyCode::F6 => 0x75, + KeyCode::F7 => 0x76, + KeyCode::F8 => 0x77, + KeyCode::F9 => 0x78, + KeyCode::F10 => 0x79, + KeyCode::F11 => 0x7A, + KeyCode::F12 => 0x7B, + + // Special keys + KeyCode::Escape => 0x1B, + KeyCode::Tab => 0x09, + KeyCode::CapsLock => 0x14, + KeyCode::ShiftLeft => 0x10, + KeyCode::ShiftRight => 0x10, + KeyCode::ControlLeft => 0x11, + KeyCode::ControlRight => 0x11, + KeyCode::AltLeft => 0x12, + KeyCode::AltRight => 0x12, + KeyCode::Space => 0x20, + KeyCode::Enter => 0x0D, + KeyCode::Backspace => 0x08, + KeyCode::Delete => 0x2E, + KeyCode::Insert => 0x2D, + KeyCode::Home => 0x24, + KeyCode::End => 0x23, + KeyCode::PageUp => 0x21, + KeyCode::PageDown => 0x22, + + // Arrow keys + KeyCode::ArrowUp => 0x26, + KeyCode::ArrowDown => 0x28, + KeyCode::ArrowLeft => 0x25, + KeyCode::ArrowRight => 0x27, + + // Numpad + KeyCode::NumLock => 0x90, + KeyCode::Numpad0 => 0x60, + KeyCode::Numpad1 => 0x61, + KeyCode::Numpad2 => 0x62, + KeyCode::Numpad3 => 0x63, + KeyCode::Numpad4 => 0x64, + KeyCode::Numpad5 => 0x65, + KeyCode::Numpad6 => 0x66, + KeyCode::Numpad7 => 0x67, + KeyCode::Numpad8 => 0x68, + KeyCode::Numpad9 => 0x69, + KeyCode::NumpadAdd => 0x6B, + KeyCode::NumpadSubtract => 0x6D, + KeyCode::NumpadMultiply => 0x6A, + KeyCode::NumpadDivide => 0x6F, + KeyCode::NumpadDecimal => 0x6E, + KeyCode::NumpadEnter => 0x0D, + + // Punctuation + KeyCode::Semicolon => 0xBA, + KeyCode::Equal => 0xBB, + KeyCode::Comma => 0xBC, + KeyCode::Minus => 0xBD, + KeyCode::Period => 0xBE, + KeyCode::Slash => 0xBF, + KeyCode::Backquote => 0xC0, + KeyCode::BracketLeft => 0xDB, + KeyCode::Backslash => 0xDC, + KeyCode::BracketRight => 0xDD, + KeyCode::Quote => 0xDE, + + // Other + KeyCode::PrintScreen => 0x2C, + KeyCode::ScrollLock => 0x91, + KeyCode::Pause => 0x13, + + _ => 0, + } +} diff --git a/viewer/src/transport.rs b/viewer/src/transport.rs new file mode 100644 index 0000000..1d976bd --- /dev/null +++ b/viewer/src/transport.rs @@ -0,0 +1,100 @@ +//! WebSocket transport for viewer-server communication + +use crate::proto; +use anyhow::{anyhow, Result}; +use bytes::Bytes; +use futures_util::{SinkExt, StreamExt}; +use prost::Message as ProstMessage; +use std::sync::Arc; +use tokio::sync::Mutex; +use tokio_tungstenite::{ + connect_async, + tungstenite::protocol::Message as WsMessage, + MaybeTlsStream, WebSocketStream, +}; +use tokio::net::TcpStream; +use tracing::{debug, error, trace}; + +pub type WsSender = futures_util::stream::SplitSink< + WebSocketStream>, + WsMessage, +>; + +pub type WsReceiver = futures_util::stream::SplitStream< + WebSocketStream>, +>; + +/// Receiver wrapper that parses protobuf messages +pub struct MessageReceiver { + inner: WsReceiver, +} + +impl MessageReceiver { + pub async fn recv(&mut self) -> Option { + loop { + match self.inner.next().await { + Some(Ok(WsMessage::Binary(data))) => { + match proto::Message::decode(Bytes::from(data)) { + Ok(msg) => return Some(msg), + Err(e) => { + error!("Failed to decode message: {}", e); + continue; + } + } + } + Some(Ok(WsMessage::Close(_))) => { + debug!("WebSocket closed"); + return None; + } + Some(Ok(WsMessage::Ping(_))) => { + trace!("Received ping"); + continue; + } + Some(Ok(WsMessage::Pong(_))) => { + trace!("Received pong"); + continue; + } + Some(Ok(_)) => continue, + Some(Err(e)) => { + error!("WebSocket error: {}", e); + return None; + } + None => return None, + } + } + } +} + +/// Connect to the GuruConnect server +pub async fn connect(url: &str, api_key: &str) -> Result<(WsSender, MessageReceiver)> { + // Add API key to URL + let full_url = if url.contains('?') { + format!("{}&api_key={}", url, api_key) + } else { + format!("{}?api_key={}", url, api_key) + }; + + debug!("Connecting to {}", full_url); + + let (ws_stream, _) = connect_async(&full_url) + .await + .map_err(|e| anyhow!("Failed to connect: {}", e))?; + + let (sender, receiver) = ws_stream.split(); + + Ok((sender, MessageReceiver { inner: receiver })) +} + +/// Send a protobuf message over the WebSocket +pub async fn send_message( + sender: &Arc>, + msg: &proto::Message, +) -> Result<()> { + let mut buf = Vec::with_capacity(msg.encoded_len()); + msg.encode(&mut buf)?; + + let mut sender = sender.lock().await; + sender.send(WsMessage::Binary(buf)).await?; + + Ok(()) +}