diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 20ac8b0..7913e63 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,6 +24,7 @@ jobs: sudo apt-get install libdbus-1-dev -y sudo apt-get install libssl-dev -y sudo apt-get install pkg-config -y + sudo apt-get install libxkbcommon-dev -y - run: cargo install cargo-deb - run: cargo install cargo-generate-rpm diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 33e4760..12d8b9f 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -23,7 +23,7 @@ jobs: AW_WEBUI_DIR: ${{ github.workspace }}/src/bundle steps: - uses: actions/checkout@v4 - - run: sudo apt-get install -y libdbus-1-dev + - run: sudo apt-get install -y libdbus-1-dev libxkbcommon-dev - uses: dtolnay/rust-toolchain@stable with: components: clippy @@ -35,7 +35,7 @@ jobs: AW_WEBUI_DIR: ${{ github.workspace }}/src/bundle steps: - uses: actions/checkout@v4 - - run: sudo apt-get install -y libdbus-1-dev + - run: sudo apt-get install -y libdbus-1-dev libxkbcommon-dev - uses: dtolnay/rust-toolchain@stable with: components: clippy @@ -47,7 +47,7 @@ jobs: AW_WEBUI_DIR: ${{ github.workspace }}/src/bundle steps: - uses: actions/checkout@v4 - - run: sudo apt-get install -y libdbus-1-dev + - run: sudo apt-get install -y libdbus-1-dev libxkbcommon-dev - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - run: cargo test --all-features --workspace diff --git a/.gitignore b/.gitignore index 284aad8..f1251a5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -/target +/**/target src/bundle/logo.argb32 lcov.info diff --git a/Cargo.lock b/Cargo.lock index ee4f311..c89978f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,9 +149,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "appdirs" @@ -483,7 +483,7 @@ dependencies = [ "aw-query", "aw-transform", "chrono", - "clap 4.5.41", + "clap 4.5.45", "fern 0.6.2", "gethostname 0.4.3", "jemallocator", @@ -525,7 +525,7 @@ dependencies = [ "aw-datastore", "aw-server", "chrono", - "clap 4.5.41", + "clap 4.5.45", "fern 0.7.1", "image", "ksni", @@ -535,7 +535,7 @@ dependencies = [ "serde", "tempfile", "tokio", - "toml 0.9.2", + "toml 0.9.5", "watchers", ] @@ -595,9 +595,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bitstream-io" @@ -644,6 +644,20 @@ name = "bytemuck" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "byteorder" @@ -663,6 +677,32 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +[[package]] +name = "calloop" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.9.1", + "log", + "polling", + "rustix 0.38.42", + "slab", + "thiserror 1.0.64", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" +dependencies = [ + "calloop", + "rustix 0.38.42", + "wayland-backend", + "wayland-client", +] + [[package]] name = "cc" version = "1.1.28" @@ -734,9 +774,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.41" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" +checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" dependencies = [ "clap_builder", "clap_derive", @@ -744,9 +784,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.41" +version = "4.5.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" +checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" dependencies = [ "anstream", "anstyle", @@ -756,9 +796,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.41" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" dependencies = [ "heck", "proc-macro2", @@ -851,6 +891,33 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cosmic-client-toolkit" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-protocols?rev=8e84152#8e84152fedf350d2756a2c1c90e07313acb9cdf6" +dependencies = [ + "bitflags 2.9.1", + "cosmic-protocols", + "libc", + "smithay-client-toolkit", + "wayland-client", + "wayland-protocols", +] + +[[package]] +name = "cosmic-protocols" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-protocols?rev=8e84152#8e84152fedf350d2756a2c1c90e07313acb9cdf6" +dependencies = [ + "bitflags 2.9.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "wayland-server", +] + [[package]] name = "cpufeatures" version = "0.2.14" @@ -919,6 +986,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "cursor-icon" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" + [[package]] name = "darling" version = "0.20.10" @@ -1020,7 +1093,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "proc-macro2", "proc-macro2-diagnostics", "quote", @@ -1079,15 +1152,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "dlib" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" -dependencies = [ - "libloading", -] - [[package]] name = "downcast-rs" version = "1.2.1" @@ -1790,7 +1854,7 @@ version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "cfg-if", "libc", ] @@ -1972,23 +2036,13 @@ dependencies = [ "once_cell", ] -[[package]] -name = "libloading" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" -dependencies = [ - "cfg-if", - "windows-targets 0.52.6", -] - [[package]] name = "libredox" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "libc", ] @@ -2090,6 +2144,24 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.9.1" @@ -2199,7 +2271,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "cfg-if", "cfg_aliases", "libc", @@ -2340,7 +2412,7 @@ version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "cfg-if", "foreign-types", "libc", @@ -2632,9 +2704,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.36.2" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] @@ -2769,7 +2841,7 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", ] [[package]] @@ -3044,7 +3116,7 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "chrono", "fallible-iterator", "fallible-streaming-iterator", @@ -3110,7 +3182,7 @@ version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.4.14", @@ -3123,7 +3195,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.9.4", @@ -3218,7 +3290,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "core-foundation", "core-foundation-sys", "libc", @@ -3421,6 +3493,34 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "smithay-client-toolkit" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" +dependencies = [ + "bitflags 2.9.1", + "bytemuck", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2 0.9.7", + "pkg-config", + "rustix 0.38.42", + "thiserror 1.0.64", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkbcommon", + "xkeysym", +] + [[package]] name = "socket2" version = "0.5.7" @@ -3674,9 +3774,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.0" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43864ed400b6043a4757a25c7a64a8efde741aed79a056a2fb348a406701bb35" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", @@ -3750,9 +3850,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" dependencies = [ "indexmap", "serde", @@ -3797,9 +3897,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" dependencies = [ "winnow 0.7.12", ] @@ -4154,6 +4254,7 @@ dependencies = [ "async-trait", "aw-client-rust", "chrono", + "cosmic-client-toolkit", "dirs 6.0.0", "gethostname 1.0.2", "log", @@ -4164,7 +4265,7 @@ dependencies = [ "serde_json", "tempfile", "tokio", - "toml 0.9.2", + "toml 0.9.5", "wayland-client", "wayland-protocols", "wayland-protocols-plasma", @@ -4175,40 +4276,62 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.7" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" +checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.42", - "scoped-tls", + "rustix 1.0.7", "smallvec", "wayland-sys", ] [[package]] name = "wayland-client" -version = "0.31.7" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" +checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ - "bitflags 2.6.0", - "rustix 0.38.42", + "bitflags 2.9.1", + "rustix 1.0.7", "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.9.1", + "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.0.7", + "wayland-client", + "xcursor", +] + [[package]] name = "wayland-protocols" -version = "0.32.5" +version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" +checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-scanner", + "wayland-server", ] [[package]] @@ -4217,7 +4340,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b31cab548ee68c7eb155517f2212049dc151f7cd7910c2b66abfd31c3ee12bd" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -4230,32 +4353,44 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "782e12f6cd923c3c316130d56205ebab53f55d6666b7faddfad36cecaeeb4022" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-protocols", "wayland-scanner", + "wayland-server", ] [[package]] name = "wayland-scanner" -version = "0.31.5" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" +checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" dependencies = [ "proc-macro2", "quick-xml", "quote", ] +[[package]] +name = "wayland-server" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbd4f3aba6c9fba70445ad2a484c0ef0356c1a9459b1e8e435bedc1971a6222" +dependencies = [ + "bitflags 2.9.1", + "downcast-rs", + "rustix 1.0.7", + "wayland-backend", + "wayland-scanner", +] + [[package]] name = "wayland-sys" -version = "0.31.5" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" +checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" dependencies = [ - "dlib", - "log", "pkg-config", ] @@ -4524,7 +4659,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", ] [[package]] @@ -4544,6 +4679,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" +[[package]] +name = "xcursor" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" + [[package]] name = "xdg-home" version = "1.3.0" @@ -4554,6 +4695,26 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "xkbcommon" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" +dependencies = [ + "libc", + "memmap2 0.8.0", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" +dependencies = [ + "bytemuck", +] + [[package]] name = "xml-rs" version = "0.8.22" diff --git a/README.md b/README.md index 101515b..891cbee 100644 --- a/README.md +++ b/README.md @@ -49,18 +49,21 @@ as soon as the environment has the necessary interfaces. | --------------- | -------------------- | ------------------- | | X11 | :green_circle: | :green_circle: | | Sway, Hyprland | :green_circle: [^1] | :green_circle: [^2] | -| Wayland + KDE | :yellow_circle: [^3] | :green_circle: | -| Wayland + Gnome | :yellow_circle: [^4] | :green_circle: | +| COSMIC | :green_circle: [^3] | :green_circle: | +| Wayland + KDE | :yellow_circle: [^4] | :green_circle: [^5] | +| Wayland + Gnome | :yellow_circle: [^6] | :green_circle: | > [!IMPORTANT] > Gnome watcher in Wayland requires [this extension](https://extensions.gnome.org/extension/5592/focused-window-d-bus/) to be installed. > Also, if you have problems with tray icons in Gnome, you may try [this extension](https://extensions.gnome.org/extension/615/appindicator-support/) for the bundle (StatusNotifierItem specification). [^1]: A few other DEs besides Sway may implement [wlr foreign toplevel protocol](https://wayland.app/protocols/wlr-foreign-toplevel-management-unstable-v1), -[^2]: [KWin idle](https://wayland.app/protocols/kde-idle) and [Idle notify](https://wayland.app/protocols/ext-idle-notify-v1) protocols are supported. -[^3]: KWin doesn't implement any toplevel protocol yet, KWin script is utilized instead (builtin, no actions required). +[^2]: [Idle notify](https://wayland.app/protocols/ext-idle-notify-v1) protocols are supported by most environments. +[^3]: COSMIC has its own [toplevel protocol](https://wayland.app/protocols/cosmic-toplevel-info-unstable-v1). +[^4]: KWin doesn't implement any toplevel protocol yet, KWin script is utilized instead (builtin, no actions required). KDE partially supports XWayland, but inconsistently, hence X11 is not utilized for it. -[^4]: Gnome doesn't implement any toplevel protocol yet, so [this extension](https://extensions.gnome.org/extension/5592/focused-window-d-bus/) should be installed. +[^5]: [KWin idle](https://wayland.app/protocols/kde-idle) protocol is supported for older KDE versions. +[^6]: Gnome doesn't implement any toplevel protocol yet, so [this extension](https://extensions.gnome.org/extension/5592/focused-window-d-bus/) should be installed. ## Configuration diff --git a/watchers/Cargo.toml b/watchers/Cargo.toml index fce64cd..e1b6147 100644 --- a/watchers/Cargo.toml +++ b/watchers/Cargo.toml @@ -16,9 +16,10 @@ tempfile = "3.13.0" [dependencies] aw-client-rust = { git = "https://github.com/ActivityWatch/aw-server-rust", rev = "656f3c9" } wayland-client = "0.31.7" -wayland-protocols = { version = "0.32.5", features = ["staging", "client" ]} +wayland-protocols = { version = "0.32.6", features = ["staging", "client" ]} wayland-protocols-plasma = { version = "0.3.5", features = ["client"] } wayland-protocols-wlr = { version = "0.3.5", features = ["client"] } +cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "8e84152" } x11rb = { version = "0.13.1", features = ["screensaver"] } zbus = {version = "5.1.0", optional = true} chrono = "0.4.38" diff --git a/watchers/src/watchers.rs b/watchers/src/watchers.rs index f81624a..2012126 100644 --- a/watchers/src/watchers.rs +++ b/watchers/src/watchers.rs @@ -8,6 +8,7 @@ pub mod idle; #[cfg(feature = "kwin_window")] mod kwin_window; mod wl_connection; +mod wl_cosmic_toplevel_management; mod wl_ext_idle_notify; mod wl_foreign_toplevel_management; mod wl_kwin_idle; @@ -104,6 +105,12 @@ async fn filter_first_supported( client, "Wayland window (wlr-foreign-toplevel-management-unstable-v1)" )); + watch!( + create_watcher::( + client, + "Cosmic Wayland window (cosmic-toplevel-info-unstable-v1)" + ) + ); // XWayland gives _NET_WM_NAME on some windows in KDE, but not on others #[cfg(feature = "kwin_window")] watch!(create_watcher::( diff --git a/watchers/src/watchers/wl_cosmic_toplevel_management.rs b/watchers/src/watchers/wl_cosmic_toplevel_management.rs new file mode 100644 index 0000000..ad92bc7 --- /dev/null +++ b/watchers/src/watchers/wl_cosmic_toplevel_management.rs @@ -0,0 +1,187 @@ +use super::Watcher; +use crate::report_client::ReportClient; +use anyhow::{anyhow, Context}; +use async_trait::async_trait; +use cctk::{ + delegate_toplevel_info, + sctk::registry::{ProvidesRegistryState, RegistryState}, + toplevel_info::{ToplevelInfoHandler, ToplevelInfoState}, + wayland_client::{ + globals::registry_queue_init, protocol::wl_registry, Connection, Dispatch, Proxy, + QueueHandle, + }, + wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1, +}; +use std::{sync::Arc, thread}; +use tokio::sync::mpsc; + +struct WindowData { + app_id: String, + title: String, +} + +struct ToplevelState { + registry_state: RegistryState, + toplevel_info_state: ToplevelInfoState, + active_toplevel_identifier: Option, + // We hold the sender to communicate back to the main task. + sender: mpsc::Sender, +} + +fn wayland_thread(sender: mpsc::Sender) -> anyhow::Result<()> { + let conn = Connection::connect_to_env()?; + let (globals, mut event_queue) = registry_queue_init(&conn)?; + let qh = event_queue.handle(); + + let registry_state = RegistryState::new(&globals); + let toplevel_info_state = ToplevelInfoState::try_new(®istry_state, &qh) + .ok_or_else(|| anyhow!("Required COSMIC toplevel protocols not found"))?; + + let mut state = ToplevelState { + registry_state, + toplevel_info_state, + active_toplevel_identifier: None, + sender, + }; + + log::debug!("Performing initial roundtrip"); + event_queue.roundtrip(&mut state)?; + log::debug!("Initial roundtrip completed"); + + loop { + match event_queue.roundtrip(&mut state) { + Ok(_) => { + // Small sleep to prevent busy waiting + std::thread::sleep(std::time::Duration::from_millis(10)); + } + Err(e) => { + log::error!("Error in Wayland event loop: {e:?}"); + break; + } + } + } + + Ok(()) +} + +impl ToplevelInfoHandler for ToplevelState { + fn toplevel_info_state(&mut self) -> &mut ToplevelInfoState { + &mut self.toplevel_info_state + } + + fn new_toplevel( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + toplevel: &ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, + ) { + self.update_toplevel(_conn, _qh, toplevel); + } + + fn update_toplevel( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + toplevel: &ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, + ) { + if let Some(info) = self.toplevel_info_state.info(toplevel) { + if info.state.contains(&cctk::cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::State::Activated) { + // If the active window has changed, send an update. + if self.active_toplevel_identifier.as_ref() != Some(&info.identifier) { + log::debug!("Active window changed to: {} - {}", info.app_id, info.title); + self.active_toplevel_identifier = Some(info.identifier.clone()); + let active_window = WindowData { + app_id: info.app_id.clone(), + title: info.title.clone(), + }; + // This send can fail if the receiver is dropped, which means the app is shutting down. + if self.sender.blocking_send(active_window).is_err() { + log::info!("Wayland thread shutting down: receiver closed."); + } + } + } + } + } + + fn toplevel_closed( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _toplevel: &ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, + ) { + } +} + +delegate_toplevel_info!(ToplevelState); + +impl ProvidesRegistryState for ToplevelState { + fn registry(&mut self) -> &mut RegistryState { + &mut self.registry_state + } + cctk::sctk::registry_handlers!(); +} + +cctk::sctk::delegate_registry!(ToplevelState); + +impl Dispatch for ToplevelState { + fn event( + _: &mut Self, + _: &wl_registry::WlRegistry, + _: ::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + } +} + +pub struct WindowWatcher { + receiver: mpsc::Receiver, + last_active_window: Option, +} + +#[async_trait] +impl Watcher for WindowWatcher { + async fn new(_: &Arc) -> anyhow::Result { + // Create a channel to communicate between the new thread and the async runtime. + let (sender, receiver) = mpsc::channel(32); + + // Spawn a dedicated OS thread for all blocking Wayland communication. + thread::spawn(move || { + if let Err(e) = wayland_thread(sender) { + log::error!("Wayland thread failed: {e:?}"); + } + }); + + Ok(Self { + receiver, + last_active_window: None, + }) + } + + async fn run_iteration(&mut self, client: &Arc) -> anyhow::Result<()> { + // Check for a new message from the Wayland thread without blocking. + // We process all pending messages to get the most recent one. + while let Ok(new_window) = self.receiver.try_recv() { + self.last_active_window = Some(new_window); + } + + if let Some(active_window) = &self.last_active_window { + match tokio::time::timeout( + std::time::Duration::from_millis(800), + client.send_active_window(&active_window.app_id, &active_window.title), + ) + .await + { + Ok(result) => { + result.with_context(|| "Failed to send heartbeat for active window")?; + } + Err(_) => { + log::warn!("Client send_active_window timed out after 800ms"); + } + } + } + + Ok(()) + } +}