diff --git a/crates/Cargo.lock b/crates/Cargo.lock index 8a8062e30..c50ec44e6 100644 --- a/crates/Cargo.lock +++ b/crates/Cargo.lock @@ -351,7 +351,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -526,7 +526,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -561,7 +561,30 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[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", ] [[package]] @@ -616,7 +639,7 @@ dependencies = [ "derive_utils", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -723,7 +746,7 @@ dependencies = [ "regex", "rustc-hash 2.1.1", "shlex", - "syn", + "syn 2.0.117", ] [[package]] @@ -859,7 +882,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -880,6 +903,31 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.11.0", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + [[package]] name = "calloop" version = "0.13.0" @@ -967,6 +1015,16 @@ dependencies = [ "nom 7.1.3", ] +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -1116,7 +1174,7 @@ checksum = "9b5dc851b7ea80cc4f69aa28632385ffd3e0f88014d53db3f2e5a781896ca783" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1487,7 +1545,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn", + "syn 2.0.117", "unicode-xid", ] @@ -1499,7 +1557,7 @@ checksum = "362f47930db19fe7735f527e6595e4900316b893ebf6d48ad3d31be928d57dd6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1592,7 +1650,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1728,7 +1786,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1771,7 +1829,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2033,7 +2091,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2126,7 +2184,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2180,6 +2238,64 @@ dependencies = [ "libc", ] +[[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 = "genawaiter" version = "0.99.1" @@ -2286,6 +2402,38 @@ dependencies = [ "weezl", ] +[[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 = "gl_generator" version = "0.14.0" @@ -2297,6 +2445,53 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.11.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.117", +] + +[[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 = "glob" version = "0.3.3" @@ -2381,12 +2576,75 @@ dependencies = [ "gl_generator", ] +[[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 = "grid" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b40ca9252762c466af32d0b1002e91e4e1bc5398f77455e55474deb466355ff5" +[[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.117", +] + [[package]] name = "half" version = "2.7.1" @@ -2449,6 +2707,12 @@ dependencies = [ "hashbrown 0.14.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" @@ -2684,7 +2948,7 @@ dependencies = [ "i-slint-renderer-software", "imgref", "lyon_path", - "muda", + "muda 0.18.0", "objc2 0.6.4", "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", @@ -2808,7 +3072,7 @@ checksum = "7931356d5238281e6be994ec15241fb2d9e66359776d53b4bd6b2fdd9c257c6e" dependencies = [ "quote", "serde_json", - "syn", + "syn 2.0.117", ] [[package]] @@ -3181,7 +3445,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3308,7 +3572,7 @@ checksum = "3c22e04db9c58f5136eb1757f3d5c49a7b187f49e52185228cbd2f5acdfcc08c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3354,7 +3618,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn", + "syn 2.0.117", ] [[package]] @@ -3379,7 +3643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3476,6 +3740,30 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" +[[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 0.7.4", + "once_cell", +] + [[package]] name = "libc" version = "0.2.183" @@ -3560,6 +3848,25 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libxdo" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" +dependencies = [ + "libxdo-sys", +] + +[[package]] +name = "libxdo-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" +dependencies = [ + "libc", + "x11", +] + [[package]] name = "linebender_resource_handle" version = "0.1.1" @@ -3643,7 +3950,7 @@ checksum = "458ace39169e4b83c4f77ae3d42d5d1d11c422feef590219a97c973d3b524557" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3830,6 +4137,27 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "muda" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a2e3dff89cd322c66647942668faee0a2b1f88ea6cbb4d374b4a8d7e92528c" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "libxdo", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "once_cell", + "png 0.18.1", + "thiserror 2.0.18", + "windows-sys 0.61.2", +] + [[package]] name = "natord" version = "1.0.9" @@ -3978,7 +4306,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4038,10 +4366,10 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4555,6 +4883,31 @@ dependencies = [ "ttf-parser", ] +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + [[package]] name = "parking" version = "2.2.1" @@ -4670,7 +5023,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4848,7 +5201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.117", ] [[package]] @@ -4860,13 +5213,57 @@ dependencies = [ "num-integer", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit", + "toml_edit 0.25.4+spec-1.1.0", +] + +[[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", ] [[package]] @@ -4894,7 +5291,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" dependencies = [ "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4917,7 +5314,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -5307,6 +5704,8 @@ dependencies = [ "image", "ksni", "log", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", "open", "qbz-app", "qbz-audio", @@ -5329,6 +5728,7 @@ dependencies = [ "slint", "slint-build", "tokio", + "tray-icon", "urlencoding", "uuid", ] @@ -6249,7 +6649,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -6273,7 +6673,16 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", ] [[package]] @@ -6401,12 +6810,12 @@ dependencies = [ "bindgen", "cc", "flate2", - "heck", + "heck 0.5.0", "pkg-config", "regex", "serde_json", "tar", - "toml", + "toml 0.9.12+spec-1.1.0", ] [[package]] @@ -6467,7 +6876,7 @@ dependencies = [ "fontique", "i-slint-compiler", "spin_on", - "toml_edit", + "toml_edit 0.25.4+spec-1.1.0", ] [[package]] @@ -6594,10 +7003,10 @@ version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -6723,10 +7132,10 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -6951,6 +7360,16 @@ dependencies = [ "symphonia-metadata", ] +[[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.117" @@ -6979,7 +7398,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -6994,6 +7413,19 @@ dependencies = [ "web-sys", ] +[[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 = "taffy" version = "0.9.2" @@ -7017,6 +7449,12 @@ dependencies = [ "xattr", ] +[[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.26.0" @@ -7062,7 +7500,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -7073,7 +7511,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -7249,7 +7687,7 @@ checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -7303,6 +7741,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + [[package]] name = "toml" version = "0.9.12+spec-1.1.0" @@ -7311,13 +7761,22 @@ checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ "indexmap", "serde_core", - "serde_spanned", + "serde_spanned 1.1.1", "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", "winnow 0.7.15", ] +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" @@ -7336,6 +7795,30 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.25.4+spec-1.1.0" @@ -7429,7 +7912,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -7451,6 +7934,27 @@ dependencies = [ "strength_reduce", ] +[[package]] +name = "tray-icon" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e47e6d063cfe4ad2e416fcbb310be3a37c5fd85c745b62cb562bfa4a003df674" +dependencies = [ + "crossbeam-channel", + "dirs 6.0.0", + "libappindicator", + "muda 0.19.2", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.2", + "once_cell", + "png 0.18.1", + "thiserror 2.0.18", + "windows-sys 0.61.2", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -7713,6 +8217,12 @@ 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" @@ -7739,7 +8249,7 @@ checksum = "482a37f9777480673d3a99c80ac2edc69f92382230440946855fa27ebc47cba6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -7831,7 +8341,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -8228,7 +8738,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -8239,7 +8749,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -8677,6 +9187,15 @@ dependencies = [ "xkbcommon-dl", ] +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "0.7.15" @@ -8711,7 +9230,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" dependencies = [ "anyhow", - "heck", + "heck 0.5.0", "wit-parser", ] @@ -8722,10 +9241,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", - "heck", + "heck 0.5.0", "indexmap", "prettyplease", - "syn", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -8741,7 +9260,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -8802,6 +9321,16 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "x11-clipboard" version = "0.9.3" @@ -8954,7 +9483,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -9051,7 +9580,7 @@ checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "zbus-lockstep", "zbus_xml", "zvariant 5.11.0", @@ -9063,10 +9592,10 @@ version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "zvariant_utils 2.1.0", ] @@ -9076,10 +9605,10 @@ version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "zbus_names 4.3.2", "zvariant 5.11.0", "zvariant_utils 3.3.1", @@ -9142,7 +9671,7 @@ checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -9162,7 +9691,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -9183,7 +9712,7 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -9217,7 +9746,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -9284,10 +9813,10 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "zvariant_utils 2.1.0", ] @@ -9297,10 +9826,10 @@ version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "zvariant_utils 3.3.1", ] @@ -9312,7 +9841,7 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -9324,6 +9853,6 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn", + "syn 2.0.117", "winnow 1.0.3", ] diff --git a/crates/qbz-slint/Cargo.toml b/crates/qbz-slint/Cargo.toml index cc37bd37c..d85746b2c 100644 --- a/crates/qbz-slint/Cargo.toml +++ b/crates/qbz-slint/Cargo.toml @@ -67,5 +67,24 @@ qbz-credentials = { path = "../qbz-credentials" } [target.'cfg(target_os = "linux")'.dependencies] ksni = { version = "0.3", features = ["blocking"] } +# macOS (NSStatusItem) + Windows (Shell_NotifyIcon) tray via the tao team's +# tray-icon crate. Re-exports muda as tray_icon::menu. Windows is built but +# not shipped/tested. +[target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies] +tray-icon = "0.24" + +# macOS Dock-icon activation-policy toggle. Pinned to tray-icon's objc2 0.6 / +# app-kit 0.3 series (NOT winit's 0.5) so MainThreadMarker/NSApplication unify +# with tray-icon. setActivationPolicy is gated behind the NSRunningApplication +# feature. +[target.'cfg(target_os = "macos")'.dependencies] +objc2 = "0.6" +objc2-app-kit = { version = "0.3", default-features = false, features = [ + "std", + "NSApplication", + "NSResponder", + "NSRunningApplication", +] } + [build-dependencies] slint-build = "1.16" diff --git a/crates/qbz-slint/src/main.rs b/crates/qbz-slint/src/main.rs index 17728240b..e22b68cef 100644 --- a/crates/qbz-slint/src/main.rs +++ b/crates/qbz-slint/src/main.rs @@ -6637,11 +6637,16 @@ fn main() -> Result<(), Box> { // with quit_on_last_window_closed = false (so a tray-hide keeps the app // alive) — without this, the native close would leave a headless process. window.window().on_close_requested(move || { - if tray_settings::get().close_to_tray && tray::handle().is_some() { + let settings = tray_settings::get(); + if settings.close_to_tray && tray::handle().is_some() { // Slint performs the hide (destroys the surface) for HideWindow; // we only sync the shown flag so the next tray toggle shows it. log::info!("[qbz-slint] close-to-tray (WM close): hiding to tray"); tray::set_window_shown(false); + // macOS: drop the Dock icon if the user opted in (no-op elsewhere). + if settings.mac_hide_dock { + tray::set_mac_dock_hidden(true); + } slint::CloseRequestResponse::HideWindow } else { log::info!("[qbz-slint] WM close requested: quitting"); diff --git a/crates/qbz-slint/src/tray/mac_win.rs b/crates/qbz-slint/src/tray/mac_win.rs new file mode 100644 index 000000000..dff67ab67 --- /dev/null +++ b/crates/qbz-slint/src/tray/mac_win.rs @@ -0,0 +1,183 @@ +//! macOS / Windows system tray via the `tray-icon` crate. +//! +//! macOS has no "system tray" — `tray-icon` gives us an `NSStatusItem` in the +//! menu bar. Unlike the Linux ksni path, `tray_icon::TrayIcon` is `!Send` +//! (`Rc>`) and on macOS MUST be built on the main thread with the +//! `NSApplication` already running, so the whole thing lives in a +//! `thread_local!` and is created via `slint::invoke_from_event_loop` (main +//! thread). Events arrive on global crossbeam channels which we drain from a +//! repeating `slint::Timer` (also main-thread). +//! +//! Faithful to the Tauri macOS tray: same 5-item menu, left-click toggles the +//! window, static "QBZ - Music Player" tooltip (no live track reflection on +//! macOS), icon theme switchable. close-to-tray itself is handled in the +//! cross-platform `on_close_requested` path (Slint hide/show). This module +//! adds the macOS Dock-icon activation-policy toggle on top. + +use std::cell::RefCell; + +use image::GenericImageView; +use tray_icon::menu::{Menu, MenuEvent, MenuItem, PredefinedMenuItem}; +use tray_icon::{Icon, MouseButton, MouseButtonState, TrayIcon, TrayIconBuilder, TrayIconEvent}; + +use super::Runtime; +use crate::AppWindow; + +// 44px assets (= 22pt @2x menu bar). Filename trap (shared with Linux): +// `tray-dark-*` holds the WHITE glyph, `tray-light-*` holds the BLACK glyph. +const ICON_COLOR: &[u8] = include_bytes!("../../icons/tray-color-44.png"); +const ICON_WHITE: &[u8] = include_bytes!("../../icons/tray-dark-44.png"); +const ICON_BLACK: &[u8] = include_bytes!("../../icons/tray-light-44.png"); + +thread_local! { + // Kept alive for the icon's lifetime; dropping the TrayIcon removes it. + static TRAY: RefCell> = const { RefCell::new(None) }; + // The event-pump timer; dropping it stops polling. + static PUMP_TIMER: RefCell> = const { RefCell::new(None) }; +} + +/// Resolve the icon bytes + whether to render it as a macOS template image +/// (template = adapts to the light/dark menu bar automatically). +/// - "color" → full vinyl, not a template +/// - "mono-light" → white glyph (`tray-dark`), not a template +/// - "mono-dark" → black glyph (`tray-light`), not a template +/// - "auto"/other → black glyph as a template, so macOS adapts it +fn icon_for(theme: &str) -> (&'static [u8], bool) { + match theme { + "color" => (ICON_COLOR, false), + "mono-light" => (ICON_WHITE, false), + "mono-dark" => (ICON_BLACK, false), + _ => (ICON_BLACK, true), + } +} + +fn decode(bytes: &[u8]) -> Option { + let img = image::load_from_memory(bytes).ok()?; + let (w, h) = img.dimensions(); + let rgba = img.into_rgba8().into_vec(); + Icon::from_rgba(rgba, w, h).ok() +} + +/// Build the menu-bar item + menu and start the event pump. MUST run on the +/// main thread (call via `slint::invoke_from_event_loop`). +pub fn create( + runtime: Runtime, + weak: slint::Weak, + handle: tokio::runtime::Handle, + theme_override: &str, +) { + let (bytes, is_template) = icon_for(theme_override); + let Some(icon) = decode(bytes) else { + log::error!("[tray] failed to decode menu-bar icon"); + return; + }; + + let menu = Menu::new(); + let play = MenuItem::with_id("play_pause", "Play/Pause", true, None); + let next = MenuItem::with_id("next", "Next Track", true, None); + let prev = MenuItem::with_id("previous", "Previous Track", true, None); + let sep1 = PredefinedMenuItem::separator(); + let show = MenuItem::with_id("show_hide", "Show/Hide Window", true, None); + let sep2 = PredefinedMenuItem::separator(); + let quit = MenuItem::with_id("quit", "Quit QBZ", true, None); + if let Err(e) = + menu.append_items(&[&play, &next, &prev, &sep1, &show, &sep2, &quit]) + { + log::error!("[tray] failed to build menu: {e}"); + return; + } + + let tray = match TrayIconBuilder::new() + .with_icon(icon) + .with_tooltip("QBZ - Music Player") + .with_menu(Box::new(menu)) + .with_menu_on_left_click(false) + .build() + { + Ok(t) => t, + Err(e) => { + log::error!("[tray] menu-bar item build failed: {e}"); + return; + } + }; + #[cfg(target_os = "macos")] + tray.set_icon_as_template(is_template); + #[cfg(not(target_os = "macos"))] + let _ = is_template; + + TRAY.with(|t| *t.borrow_mut() = Some(tray)); + + // Drain tray + menu events on the main thread. + let timer = slint::Timer::default(); + timer.start( + slint::TimerMode::Repeated, + std::time::Duration::from_millis(120), + move || pump(&runtime, &weak, &handle), + ); + PUMP_TIMER.with(|t| *t.borrow_mut() = Some(timer)); + + log::info!("[tray] menu-bar item initialized (theme={theme_override})"); +} + +fn pump(runtime: &Runtime, weak: &slint::Weak, handle: &tokio::runtime::Handle) { + while let Ok(ev) = MenuEvent::receiver().try_recv() { + match ev.id.0.as_str() { + "play_pause" => { + super::dispatch_play_pause(runtime.clone(), weak.clone(), handle.clone()) + } + "next" => super::dispatch_next(runtime.clone(), weak.clone(), handle.clone()), + "previous" => super::dispatch_previous(runtime.clone(), weak.clone(), handle.clone()), + "show_hide" => super::toggle_window(weak), + "quit" => super::quit(), + other => log::debug!("[tray] unhandled menu id '{other}'"), + } + } + while let Ok(ev) = TrayIconEvent::receiver().try_recv() { + if let TrayIconEvent::Click { + button: MouseButton::Left, + button_state: MouseButtonState::Up, + .. + } = ev + { + super::toggle_window(weak); + } + } +} + +/// Re-theme the live menu-bar icon (called on the main thread). +pub fn set_icon_theme(theme: &str) { + let (bytes, is_template) = icon_for(theme); + let Some(icon) = decode(bytes) else { return }; + TRAY.with(|t| { + if let Some(tray) = t.borrow().as_ref() { + let _ = tray.set_icon(Some(icon)); + #[cfg(target_os = "macos")] + tray.set_icon_as_template(is_template); + #[cfg(not(target_os = "macos"))] + let _ = is_template; + } + }); +} + +/// Switch the macOS activation policy: `.accessory` hides the Dock icon +/// (menu-bar-only), `.regular` keeps it (Spotify default). Must run on the +/// main thread. No-op on Windows. +#[cfg(target_os = "macos")] +pub fn set_dock_icon_hidden(hidden: bool) { + use objc2::MainThreadMarker; + use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy}; + + let Some(mtm) = MainThreadMarker::new() else { + return; + }; + let app = NSApplication::sharedApplication(mtm); + let policy = if hidden { + NSApplicationActivationPolicy::Accessory + } else { + NSApplicationActivationPolicy::Regular + }; + app.setActivationPolicy(policy); +} + +#[cfg(not(target_os = "macos"))] +pub fn set_dock_icon_hidden(_hidden: bool) {} diff --git a/crates/qbz-slint/src/tray/mod.rs b/crates/qbz-slint/src/tray/mod.rs index 721817c76..c6befb1ef 100644 --- a/crates/qbz-slint/src/tray/mod.rs +++ b/crates/qbz-slint/src/tray/mod.rs @@ -25,6 +25,9 @@ use qbz_app::shell::AppRuntime; #[cfg(target_os = "linux")] mod linux; +#[cfg(any(target_os = "macos", target_os = "windows"))] +mod mac_win; + /// Shared runtime handle type used across the Slint app. pub(crate) type Runtime = Arc>; @@ -76,7 +79,14 @@ impl TrayHandle { h.set_icon_theme(theme); return; } - #[cfg(not(target_os = "linux"))] + #[cfg(any(target_os = "macos", target_os = "windows"))] + { + // The TrayIcon is !Send and lives on the main thread — re-theme it + // there. + let _ = slint::invoke_from_event_loop(move || mac_win::set_icon_theme(&theme)); + return; + } + #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))] let _ = theme; } } @@ -130,11 +140,21 @@ pub fn init( .expect("spawn tray init thread"); } - #[cfg(not(target_os = "linux"))] + #[cfg(any(target_os = "macos", target_os = "windows"))] + { + // tray-icon's TrayIcon is !Send and (on macOS) must be built on the + // main thread with NSApplication already running — create it on the + // Slint event loop. + let _ = slint::invoke_from_event_loop(move || { + mac_win::create(runtime, weak, handle, &theme_override); + let _ = TRAY.set(TrayHandle::default()); + }); + } + + #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))] { - // macOS / Windows tray lands with the CustomApplicationHandler slice. let _ = (runtime, weak, handle, theme_override); - log::info!("[tray] no tray backend on this platform yet"); + log::info!("[tray] no tray backend on this platform"); } } @@ -170,6 +190,9 @@ pub(crate) fn show_window(weak: &slint::Weak) { w.window().with_winit_window(|win| { win.focus_window(); }); + // Restore the Dock icon when coming back from the menu bar. + #[cfg(target_os = "macos")] + mac_win::set_dock_icon_hidden(false); }); } @@ -181,6 +204,11 @@ pub(crate) fn hide_window(weak: &slint::Weak) { if let Err(e) = w.hide() { log::error!("[tray] window hide failed: {e}"); } + // Spotify-style opt-in: drop the Dock icon while closed to the menu bar. + #[cfg(target_os = "macos")] + if crate::tray_settings::get().mac_hide_dock { + mac_win::set_dock_icon_hidden(true); + } }); } @@ -191,6 +219,16 @@ pub(crate) fn set_window_shown(shown: bool) { WINDOW_SHOWN.store(shown, Ordering::Relaxed); } +/// Apply the macOS Dock-icon activation policy (`.accessory` hides the Dock +/// icon, `.regular` keeps it). No-op off macOS. Must be called on the main +/// thread (it is, from the close handlers / window hide-show). +pub(crate) fn set_mac_dock_hidden(hidden: bool) { + #[cfg(target_os = "macos")] + mac_win::set_dock_icon_hidden(hidden); + #[cfg(not(target_os = "macos"))] + let _ = hidden; +} + /// Quit the whole app from a tray action (any thread). pub(crate) fn quit() { log::info!("[tray] quit requested"); @@ -270,7 +308,9 @@ pub(crate) fn dispatch_previous( /// Step the local volume by `ticks` notches of 5% (positive = up). Mirrors the /// Tauri `tray:volume_delta` handler. Local-only for now (remote-renderer -/// volume forwarding is a later refinement). +/// volume forwarding is a later refinement). Linux-only: scroll-to-volume is a +/// StatusNotifierItem feature the macOS/Windows tray doesn't expose. +#[cfg(target_os = "linux")] pub(crate) fn dispatch_volume_delta( runtime: Runtime, weak: slint::Weak,