From 012cc3d019f18120a29477559fc75e5def2ec6f6 Mon Sep 17 00:00:00 2001 From: Jan Luca Oster <janluca.oster@proton.me> Date: Sat, 19 Oct 2024 11:54:51 +0200 Subject: [PATCH 01/18] rebase off main --- Cargo.lock | 1071 +++++++++++------ Cargo.toml | 1 + .../scalable/status/pause-large-symbolic.svg | 2 + .../scalable/status/play-large-symbolic.svg | 2 + .../status/playlist-consecutive-symbolic.svg | 2 + .../status/playlist-shuffle-symbolic.svg | 2 + .../status/seek-backward-large-symbolic.svg | 2 + .../status/seek-forward-large-symbolic.svg | 2 + .../scalable/status/stop-large-symbolic.svg | 2 + data/swayosd.gresource.xml | 8 + src/argtypes.rs | 6 + src/client/main.rs | 18 + src/global_utils.rs | 22 + src/mpris-backend/mod.rs | 164 +++ src/server/application.rs | 26 + src/server/main.rs | 3 + src/server/osd_window.rs | 12 + src/server/utils.rs | 17 + 18 files changed, 1000 insertions(+), 362 deletions(-) create mode 100644 data/icons/scalable/status/pause-large-symbolic.svg create mode 100644 data/icons/scalable/status/play-large-symbolic.svg create mode 100644 data/icons/scalable/status/playlist-consecutive-symbolic.svg create mode 100644 data/icons/scalable/status/playlist-shuffle-symbolic.svg create mode 100644 data/icons/scalable/status/seek-backward-large-symbolic.svg create mode 100644 data/icons/scalable/status/seek-forward-large-symbolic.svg create mode 100644 data/icons/scalable/status/stop-large-symbolic.svg create mode 100644 src/mpris-backend/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 5a082de..0698161 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,20 +1,20 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 +version = 3 [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" [[package]] name = "async-broadcast" -version = "0.7.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.3.1", "event-listener-strategy", "futures-core", "pin-project-lite", @@ -84,9 +84,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.4.0" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" dependencies = [ "async-lock", "cfg-if", @@ -107,7 +107,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.3.1", "event-listener-strategy", "pin-project-lite", ] @@ -125,7 +125,7 @@ dependencies = [ "async-task", "blocking", "cfg-if", - "event-listener 5.4.0", + "event-listener 5.3.1", "futures-lite", "rustix", "tracing", @@ -138,8 +138,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", - "quote", - "syn 2.0.96", + "quote 1.0.37", + "syn 2.0.79", ] [[package]] @@ -194,13 +194,37 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.85" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", - "quote", - "syn 2.0.96", + "quote 1.0.37", + "syn 2.0.79", +] + +[[package]] +name = "atk" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba16453d10c712284061a05f6510f75abeb92b56ba88dfeb48c74775020cc22" +dependencies = [ + "atk-sys", + "bitflags 1.3.2", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf0a7ca572fbd5762fd8f8cd65a581e06767bc1234913fe1f43e370cff6e90" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", ] [[package]] @@ -223,9 +247,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "blight" @@ -237,6 +261,15 @@ dependencies = [ "fs4", ] +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "blocking" version = "1.6.1" @@ -256,23 +289,31 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cairo-rs" -version = "0.20.7" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae50b5510d86cf96ac2370e66d8dc960882f3df179d6a5a1e52bd94a1416c0f7" +checksum = "ab3603c4028a5e368d09b51c8b624b9a46edcd7c3778284077a6125af73c9f0a" dependencies = [ - "bitflags 2.8.0", + "bitflags 1.3.2", "cairo-sys-rs", "glib", "libc", + "once_cell", + "thiserror", ] [[package]] name = "cairo-sys-rs" -version = "0.20.7" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f18b6bb8e43c7eb0f2aac7976afe0c61b6f5fc2ab7bc4c139537ea56c92290df" +checksum = "691d0c66b1fb4881be80a760cb8fe76ea97218312f9dfe2c9cc0f496ca279cb1" dependencies = [ "glib-sys", "libc", @@ -287,18 +328,18 @@ checksum = "d499b43edbf784dd81e16f0395f5b4350a35b477da8a074251087adefc11cb52" [[package]] name = "cc" -version = "1.2.10" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" dependencies = [ "shlex", ] [[package]] name = "cfg-expr" -version = "0.17.2" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d4ba6e40bd1184518716a6e1a781bf9160e286d219ccdb8ab2612e74cfe4789" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ "smallvec", "target-lexicon", @@ -318,12 +359,12 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "colored" -version = "2.2.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" dependencies = [ "lazy_static", - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -335,11 +376,97 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote 1.0.37", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi", +] + +[[package]] +name = "derive_is_enum_variant" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ac8859845146979953797f03cc5b282fb4396891807cdb3d04929a88418197" +dependencies = [ + "heck 0.3.3", + "quote 0.3.15", + "syn 0.11.11", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] [[package]] name = "either" @@ -353,11 +480,22 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" +[[package]] +name = "enum-kinds" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e40a16955681d469ab3da85aaa6b42ff656b3c67b52e1d8d3dd36afe97fd462" +dependencies = [ + "proc-macro2", + "quote 1.0.37", + "syn 1.0.109", +] + [[package]] name = "enumflags2" -version = "0.7.11" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" dependencies = [ "enumflags2_derive", "serde", @@ -365,13 +503,13 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.11" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", - "quote", - "syn 2.0.96", + "quote 1.0.37", + "syn 2.0.79", ] [[package]] @@ -382,12 +520,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -421,9 +559,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.4.0" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ "concurrent-queue", "parking", @@ -432,19 +570,19 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.3.1", "pin-project-lite", ] [[package]] name = "fastrand" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "field-offset" @@ -452,10 +590,37 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset", + "memoffset 0.9.1", "rustc_version", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "from_variants" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e859c8f2057687618905dbe99fc76e836e0a69738865ef90e46fc214a41bbf2" +dependencies = [ + "from_variants_impl", +] + +[[package]] +name = "from_variants_impl" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55a5e644a80e6d96b2b4910fa7993301d7b7926c045b475b62202b20a36ce69e" +dependencies = [ + "darling", + "proc-macro2", + "quote 1.0.37", + "syn 1.0.109", +] + [[package]] name = "fs4" version = "0.6.6" @@ -500,9 +665,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.6.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ "fastrand", "futures-core", @@ -518,10 +683,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", - "quote", - "syn 2.0.96", + "quote 1.0.37", + "syn 2.0.79", ] +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + [[package]] name = "futures-task" version = "0.3.31" @@ -537,6 +708,7 @@ dependencies = [ "futures-core", "futures-io", "futures-macro", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -544,23 +716,41 @@ dependencies = [ "slab", ] +[[package]] +name = "gdk" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1df5ea52cccd7e3a0897338b5564968274b52f5fd12601e0afa44f454c74d3" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + [[package]] name = "gdk-pixbuf" -version = "0.20.7" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6efc7705f7863d37b12ad6974cbb310d35d054f5108cdc1e69037742f573c4c" +checksum = "695d6bc846438c5708b07007537b9274d883373dd30858ca881d7d71b5540717" dependencies = [ + "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", "glib", "libc", + "once_cell", ] [[package]] name = "gdk-pixbuf-sys" -version = "0.20.7" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67f2587c9202bf997476bbba6aaed4f78a11538a2567df002a5f57f5331d0b5c" +checksum = "9285ec3c113c66d7d0ab5676599176f1f42f4944ca1b581852215bf5694870cb" dependencies = [ "gio-sys", "glib-sys", @@ -570,26 +760,10 @@ dependencies = [ ] [[package]] -name = "gdk4" -version = "0.9.5" +name = "gdk-sys" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0196720118f880f71fe7da971eff58cc43a89c9cf73f46076b7cb1e60889b15" -dependencies = [ - "cairo-rs", - "gdk-pixbuf", - "gdk4-sys", - "gio", - "gl", - "glib", - "libc", - "pango", -] - -[[package]] -name = "gdk4-sys" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b0e1340bd15e7a78810cf39fed9e5d85f0a8f80b1d999d384ca17dcc452b60" +checksum = "2152de9d38bc67a17b3fe49dc0823af5bf874df59ea088c5f28f31cf103de703" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -602,6 +776,16 @@ dependencies = [ "system-deps", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -615,10 +799,11 @@ dependencies = [ [[package]] name = "gio" -version = "0.20.7" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a517657589a174be9f60c667f1fec8b7ac82ed5db4ebf56cf073a3b5955d8e2e" +checksum = "a6973e92937cf98689b6a054a9e56c657ed4ff76de925e36fc331a15f0c5d30a" dependencies = [ + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", @@ -626,50 +811,32 @@ dependencies = [ "gio-sys", "glib", "libc", + "once_cell", "pin-project-lite", "smallvec", + "thiserror", ] [[package]] name = "gio-sys" -version = "0.20.8" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8446d9b475730ebef81802c1738d972db42fde1c5a36a627ebc4d665fc87db04" +checksum = "0ccf87c30a12c469b6d958950f6a9c09f2be20b7773f7e70d20b867fdf2628c3" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", - "windows-sys 0.59.0", -] - -[[package]] -name = "gl" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a94edab108827d67608095e269cf862e60d920f144a5026d3dbcfd8b877fb404" -dependencies = [ - "gl_generator", -] - -[[package]] -name = "gl_generator" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" -dependencies = [ - "khronos_api", - "log", - "xml-rs", + "winapi", ] [[package]] name = "glib" -version = "0.20.7" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f969edf089188d821a30cde713b6f9eb08b20c63fc2e584aba2892a7984a8cc0" +checksum = "d3fad45ba8d4d2cea612b432717e834f48031cd8853c8aaf43b2c79fec8d144b" dependencies = [ - "bitflags 2.8.0", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-executor", @@ -681,27 +848,31 @@ dependencies = [ "gobject-sys", "libc", "memchr", + "once_cell", "smallvec", + "thiserror", ] [[package]] name = "glib-macros" -version = "0.20.7" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715601f8f02e71baef9c1f94a657a9a77c192aea6097cf9ae7e5e177cd8cde68" +checksum = "eca5c79337338391f1ab8058d6698125034ce8ef31b72a442437fa6c8580de26" dependencies = [ - "heck", - "proc-macro-crate", + "anyhow", + "heck 0.4.1", + "proc-macro-crate 1.3.1", + "proc-macro-error", "proc-macro2", - "quote", - "syn 2.0.96", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] name = "glib-sys" -version = "0.20.7" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b360ff0f90d71de99095f79c526a5888c9c92fc9ee1b19da06c6f5e75f0c2a53" +checksum = "d80aa6ea7bba0baac79222204aa786a6293078c210abe69ef1336911d4bdc4f0" dependencies = [ "libc", "system-deps", @@ -721,9 +892,9 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.20.7" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a56235e971a63bfd75abb13ef70064e1346388723422a68580d8a6fbac6423" +checksum = "cd34c3317740a6358ec04572c1bcfd3ac0b5b6529275fae255b237b314bb8062" dependencies = [ "glib-sys", "libc", @@ -731,144 +902,108 @@ dependencies = [ ] [[package]] -name = "graphene-rs" -version = "0.20.7" +name = "gtk" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f39d3bcd2e24fd9c2874a56f277b72c03e728de9bdc95a8d4ef4c962f10ced98" +checksum = "b6c4222ab92b08d4d0bab90ddb6185b4e575ceeea8b8cdf00b938d7b6661d966" dependencies = [ + "atk", + "bitflags 1.3.2", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", "glib", - "graphene-sys", + "gtk-sys", + "gtk3-macros", "libc", + "once_cell", + "pango", + "pkg-config", ] [[package]] -name = "graphene-sys" -version = "0.20.7" +name = "gtk-layer-shell" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a68d39515bf340e879b72cecd4a25c1332557757ada6e8aba8654b4b81d23a" +checksum = "992f5fedb31835424a5280acd162bf348995f617d26969fde8d3dfd389b3ff5f" dependencies = [ + "bitflags 2.6.0", + "gdk", + "glib", "glib-sys", + "gtk", + "gtk-layer-shell-sys", "libc", - "pkg-config", - "system-deps", ] [[package]] -name = "gsk4" -version = "0.9.5" +name = "gtk-layer-shell-sys" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b9188db0a6219e708b6b6e7225718e459def664023dbddb8395ca1486d8102" +checksum = "5754bcfaadfc3529116af6ae93559b267d88647f965382153a4b8ea9372be75a" dependencies = [ - "cairo-rs", - "gdk4", - "glib", - "graphene-rs", - "gsk4-sys", + "gdk-sys", + "glib-sys", + "gtk-sys", "libc", - "pango", + "system-deps", ] [[package]] -name = "gsk4-sys" -version = "0.9.5" +name = "gtk-sys" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca10fc65d68528a548efa3d8747934adcbe7058b73695c9a7f43a25352fce14" +checksum = "4d8eb6a4b93e5a7e6980f7348d08c1cd93d31fae07cf97f20678c5ec41de3d7e" dependencies = [ + "atk-sys", "cairo-sys-rs", - "gdk4-sys", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", "glib-sys", "gobject-sys", - "graphene-sys", "libc", "pango-sys", "system-deps", ] [[package]] -name = "gtk4" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b697ff938136625f6acf75f01951220f47a45adcf0060ee55b4671cf734dac44" -dependencies = [ - "cairo-rs", - "field-offset", - "futures-channel", - "gdk-pixbuf", - "gdk4", - "gio", - "glib", - "graphene-rs", - "gsk4", - "gtk4-macros", - "gtk4-sys", - "libc", - "pango", -] - -[[package]] -name = "gtk4-layer-shell" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e1e1b1516be3d7ca089dfa6a1e688e268c74aef50c0c25fe8c46b1ba8ed1cc" -dependencies = [ - "bitflags 2.8.0", - "gdk4", - "glib", - "glib-sys", - "gtk4", - "gtk4-layer-shell-sys", - "libc", -] - -[[package]] -name = "gtk4-layer-shell-sys" -version = "0.3.0" +name = "gtk3-macros" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3057dc117db2d664a9b45f1956568701914e80cf9f2c8cef0a755af4c1c8105" +checksum = "3efb84d682c9a39c10bd9f24f5a4b9c15cc8c7edc45c19cb2ca2c4fc38b2d95e" dependencies = [ - "gdk4-sys", - "glib-sys", - "gtk4-sys", - "libc", - "system-deps", + "anyhow", + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] -name = "gtk4-macros" -version = "0.9.5" +name = "hashbrown" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed1786c4703dd196baf7e103525ce0cf579b3a63a0570fe653b7ee6bac33999" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.96", -] +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" [[package]] -name = "gtk4-sys" -version = "0.9.5" +name = "heck" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3af4b680cee5d2f786a2f91f1c77e95ecf2254522f0ca4edf3a2dce6cb35cecf" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk4-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "graphene-sys", - "gsk4-sys", - "libc", - "pango-sys", - "system-deps", + "unicode-segmentation", ] [[package]] -name = "hashbrown" -version = "0.15.2" +name = "heck" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "heck" @@ -894,11 +1029,17 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "indexmap" -version = "2.7.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown", @@ -946,20 +1087,13 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ - "once_cell", "wasm-bindgen", ] -[[package]] -name = "khronos_api" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" - [[package]] name = "kv-log-macro" version = "1.0.7" @@ -977,15 +1111,24 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" + +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "pkg-config", +] [[package]] name = "libpulse-binding" -version = "2.28.2" +version = "2.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1040a6c4c4d1e9e852000f6202df1a02a4f074320de336ab21e4fd317b538" +checksum = "ed3557a2dfc380c8f061189a01c6ae7348354e0c9886038dc6c171219c08eaff" dependencies = [ "bitflags 1.3.2", "libc", @@ -1020,15 +1163,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.15" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "log" -version = "0.4.25" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" dependencies = [ "value-bag", ] @@ -1039,6 +1182,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.9.1" @@ -1048,17 +1200,43 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mpris" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cef955a7826b1e00e901a3652e7a895abd221fb4ab61547e7d0e4c235d7feb" +dependencies = [ + "dbus", + "derive_is_enum_variant", + "enum-kinds", + "from_variants", + "thiserror", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", + "pin-utils", +] + [[package]] name = "nix" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.6.0", "cfg-if", "cfg_aliases", "libc", - "memoffset", + "memoffset 0.9.1", ] [[package]] @@ -1068,7 +1246,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.37", "syn 1.0.109", ] @@ -1099,21 +1277,23 @@ dependencies = [ [[package]] name = "pango" -version = "0.20.7" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e89bd74250a03a05cec047b43465469102af803be2bf5e5a1088f8b8455e087" +checksum = "35be456fc620e61f62dff7ff70fbd54dcbaf0a4b920c0f16de1107c47d921d48" dependencies = [ + "bitflags 1.3.2", "gio", "glib", "libc", + "once_cell", "pango-sys", ] [[package]] name = "pango-sys" -version = "0.20.7" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71787e0019b499a5eda889279e4adb455a4f3fdd6870cd5ab7f4a5aa25df6699" +checksum = "3da69f9f3850b0d8990d462f8c709561975e95f689c1cdf0fecdebde78b35195" dependencies = [ "glib-sys", "gobject-sys", @@ -1129,9 +1309,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -1158,9 +1338,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "polling" -version = "3.7.4" +version = "3.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" dependencies = [ "cfg-if", "concurrent-queue", @@ -1171,20 +1351,63 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[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 = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit", + "toml_edit 0.22.22", +] + +[[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 1.0.37", + "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 1.0.37", + "version_check", ] [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] @@ -1200,13 +1423,49 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.38" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "rustc_version" version = "0.4.1" @@ -1218,47 +1477,41 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.43" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] -[[package]] -name = "rustversion" -version = "1.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" - [[package]] name = "semver" -version = "1.0.24" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", - "quote", - "syn 2.0.96", + "quote 1.0.37", + "syn 2.0.79", ] [[package]] @@ -1268,8 +1521,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", - "quote", - "syn 2.0.96", + "quote 1.0.37", + "syn 2.0.79", ] [[package]] @@ -1281,6 +1534,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1296,7 +1560,7 @@ dependencies = [ "bitflags 1.3.2", "itertools", "proc-macro2", - "quote", + "quote 1.0.37", "syn 1.0.109", ] @@ -1330,6 +1594,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "substring" version = "1.4.5" @@ -1344,18 +1614,18 @@ name = "swayosd" version = "0.1.0" dependencies = [ "anyhow", - "async-channel 2.3.1", "async-std", "blight", "cascade", "evdev-rs", - "gtk4", - "gtk4-layer-shell", + "gtk", + "gtk-layer-shell", "input", "lazy_static", "libc", "libpulse-binding", - "nix", + "mpris", + "nix 0.26.4", "pulsectl-rs", "serde", "serde_derive", @@ -1366,6 +1636,17 @@ dependencies = [ "zbus", ] +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +dependencies = [ + "quote 0.3.15", + "synom", + "unicode-xid", +] + [[package]] name = "syn" version = "1.0.109" @@ -1373,29 +1654,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.37", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.96" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.37", "unicode-ident", ] +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +dependencies = [ + "unicode-xid", +] + [[package]] name = "system-deps" -version = "7.0.3" +version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ "cfg-expr", - "heck", + "heck 0.5.0", "pkg-config", "toml", "version-compare", @@ -1409,13 +1699,12 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.15.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", - "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", @@ -1423,22 +1712,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.69" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.69" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", - "quote", - "syn 2.0.96", + "quote 1.0.37", + "syn 2.0.79", ] [[package]] @@ -1450,7 +1739,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.22", ] [[package]] @@ -1462,6 +1751,17 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.22.22" @@ -1472,14 +1772,14 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.20", ] [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -1488,24 +1788,30 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", - "quote", - "syn 2.0.96", + "quote 1.0.37", + "syn 2.0.79", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "udev" version = "0.7.0" @@ -1523,22 +1829,34 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" dependencies = [ - "memoffset", + "memoffset 0.9.1", "tempfile", "winapi", ] [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-xid" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" [[package]] name = "value-bag" -version = "1.10.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" [[package]] name = "version-compare" @@ -1546,6 +1864,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1554,80 +1878,76 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", - "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.100" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", + "once_cell", "proc-macro2", - "quote", - "syn 2.0.96", + "quote 1.0.37", + "syn 2.0.79", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", - "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ - "quote", + "quote 1.0.37", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", - "quote", - "syn 2.0.96", + "quote 1.0.37", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", @@ -1664,6 +1984,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -1796,9 +2125,18 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.24" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] @@ -1813,17 +2151,11 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "xml-rs" -version = "0.8.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" - [[package]] name = "zbus" -version = "5.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "192a0d989036cd60a1e91a54c9851fb9ad5bd96125d41803eed79d2e2ef74bd7" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" dependencies = [ "async-broadcast", "async-executor", @@ -1836,19 +2168,21 @@ dependencies = [ "async-trait", "blocking", "enumflags2", - "event-listener 5.4.0", + "event-listener 5.3.1", "futures-core", + "futures-sink", "futures-util", "hex", - "nix", + "nix 0.29.0", "ordered-stream", + "rand", "serde", "serde_repr", + "sha1", "static_assertions", "tracing", "uds_windows", - "windows-sys 0.59.0", - "winnow", + "windows-sys 0.52.0", "xdg-home", "zbus_macros", "zbus_names", @@ -1857,69 +2191,82 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3685b5c81fce630efc3e143a4ded235b107f1b1cdf186c3f115529e5e5ae4265" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.2.0", "proc-macro2", - "quote", - "syn 2.0.96", - "zbus_names", - "zvariant", + "quote 1.0.37", + "syn 2.0.79", "zvariant_utils", ] [[package]] name = "zbus_names" -version = "4.1.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "519629a3f80976d89c575895b05677cbc45eaf9f70d62a364d819ba646409cc8" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" dependencies = [ "serde", "static_assertions", - "winnow", "zvariant", ] +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote 1.0.37", + "syn 2.0.79", +] + [[package]] name = "zvariant" -version = "5.2.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e6b9b5f1361de2d5e7d9fd1ee5f6f7fcb6060618a1f82f3472f58f2b8d4be9" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" dependencies = [ "endi", "enumflags2", "serde", "static_assertions", - "winnow", "zvariant_derive", - "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.2.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573a8dd76961957108b10f7a45bac6ab1ea3e9b7fe01aff88325dc57bb8f5c8b" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.2.0", "proc-macro2", - "quote", - "syn 2.0.96", + "quote 1.0.37", + "syn 2.0.79", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.1.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd46446ea2a1f353bfda53e35f17633afa79f4fe290a611c94645c69fe96a50" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", - "quote", - "serde", - "static_assertions", - "syn 2.0.96", - "winnow", + "quote 1.0.37", + "syn 2.0.79", ] diff --git a/Cargo.toml b/Cargo.toml index 6ce6ed1..2903f9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,3 +42,4 @@ blight = "0.7.0" anyhow = "1.0.75" thiserror = "1.0.49" async-channel = "2.3.1" +mpris = "2.0.1" diff --git a/data/icons/scalable/status/pause-large-symbolic.svg b/data/icons/scalable/status/pause-large-symbolic.svg new file mode 100644 index 0000000..d40c097 --- /dev/null +++ b/data/icons/scalable/status/pause-large-symbolic.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222"><path d="m 13.003906 1 h -3 c -0.554687 0 -1 0.449219 -1 1 v 12 c 0 0.550781 0.445313 1 1 1 h 3 c 0.550782 0 1 -0.449219 1 -1 v -12 c 0 -0.550781 -0.449218 -1 -1 -1 z m 0 0"/><path d="m 6.003906 1 h -3 c -0.554687 0 -1 0.449219 -1 1 v 12 c 0 0.550781 0.445313 1 1 1 h 3 c 0.550782 0 1 -0.449219 1 -1 v -12 c 0 -0.550781 -0.449218 -1 -1 -1 z m 0 0"/></g></svg> diff --git a/data/icons/scalable/status/play-large-symbolic.svg b/data/icons/scalable/status/play-large-symbolic.svg new file mode 100644 index 0000000..5210a7e --- /dev/null +++ b/data/icons/scalable/status/play-large-symbolic.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 2.003906 13.492188 v -11 c 0 -1.5 1.265625 -1.492188 1.265625 -1.492188 h 0.132813 c 0.246094 0 0.484375 0.054688 0.699218 0.175781 l 9.796876 5.597657 c 0.433593 0.238281 0.65625 0.730468 0.65625 1.222656 c 0 0.492187 -0.222657 0.984375 -0.65625 1.226562 l -9.796876 5.597656 c -0.214843 0.121094 -0.453124 0.175782 -0.699218 0.171876 h -0.132813 s -1.265625 0 -1.265625 -1.5 z m 0 0" fill="#222222"/></svg> diff --git a/data/icons/scalable/status/playlist-consecutive-symbolic.svg b/data/icons/scalable/status/playlist-consecutive-symbolic.svg new file mode 100644 index 0000000..6bd9903 --- /dev/null +++ b/data/icons/scalable/status/playlist-consecutive-symbolic.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 12 1 v 2 h -12 v 2 h 12 v 2 h 1 v -0.007812 c 0.265625 0.003906 0.519531 -0.101563 0.707031 -0.285157 l 2 -2 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 l -2 -2 c -0.1875 -0.183594 -0.441406 -0.289063 -0.707031 -0.285157 v -0.007812 z m 0 8 v 2 h -12 v 2 h 12 v 2 h 1 v -0.007812 c 0.265625 0.003906 0.519531 -0.101563 0.707031 -0.285157 l 2 -2 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 l -2 -2 c -0.1875 -0.183594 -0.441406 -0.289063 -0.707031 -0.285157 v -0.007812 z m 0 0" fill="#222222"/></svg> diff --git a/data/icons/scalable/status/playlist-shuffle-symbolic.svg b/data/icons/scalable/status/playlist-shuffle-symbolic.svg new file mode 100644 index 0000000..f18cf05 --- /dev/null +++ b/data/icons/scalable/status/playlist-shuffle-symbolic.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 12 1 v 2 h -3 c -0.859375 0 -1.59375 0.480469 -2.011719 1.007812 c -0.417969 0.523438 -0.648437 1.074219 -0.882812 1.542969 l -0.105469 0.210938 l -0.105469 -0.210938 c -0.191406 -0.382812 -0.386719 -0.816406 -0.671875 -1.25 c -0.414062 -0.632812 -1.207031 -1.300781 -2.222656 -1.300781 h -3 v 2 h 3 c 0.296875 0 0.316406 0.039062 0.550781 0.398438 c 0.164063 0.246093 0.34375 0.625 0.554688 1.050781 l 0.777343 1.550781 l -0.777343 1.550781 c -0.261719 0.523438 -0.476563 0.96875 -0.65625 1.199219 c -0.183594 0.226562 -0.199219 0.25 -0.449219 0.25 h -3 v 2 h 3 c 0.859375 0 1.59375 -0.480469 2.011719 -1.007812 c 0.417969 -0.523438 0.648437 -1.074219 0.882812 -1.542969 l 0.105469 -0.210938 l 0.105469 0.210938 c 0.164062 0.328125 0.335937 0.703125 0.5625 1.078125 c 0.414062 0.6875 1.222656 1.472656 2.332031 1.472656 h 3 v 2 h 1 v -0.007812 c 0.265625 0.003906 0.519531 -0.101563 0.707031 -0.285157 l 2 -2 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 l -2 -2 c -0.1875 -0.183594 -0.441406 -0.289063 -0.707031 -0.285157 v -0.007812 h -1 v 2 h -3 c -0.324219 0 -0.351562 -0.058594 -0.617188 -0.503906 c -0.148437 -0.242188 -0.304687 -0.574219 -0.488281 -0.945313 l -0.777343 -1.550781 l 0.777343 -1.550781 c 0.261719 -0.523438 0.476563 -0.96875 0.65625 -1.199219 c 0.183594 -0.226562 0.199219 -0.25 0.449219 -0.25 h 3 v 2 h 1 v -0.007812 c 0.265625 0.003906 0.519531 -0.101563 0.707031 -0.285157 l 2 -2 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 l -2 -2 c -0.1875 -0.183594 -0.441406 -0.289063 -0.707031 -0.285157 v -0.007812 z m 0 0" fill="#222222"/></svg> diff --git a/data/icons/scalable/status/seek-backward-large-symbolic.svg b/data/icons/scalable/status/seek-backward-large-symbolic.svg new file mode 100644 index 0000000..11fa0c3 --- /dev/null +++ b/data/icons/scalable/status/seek-backward-large-symbolic.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222"><path d="m 7.003906 4.003906 c -0.183594 -0.003906 -0.359375 0.046875 -0.515625 0.140625 l -5 3 c -0.648437 0.386719 -0.648437 1.328125 0 1.714844 l 5 3 c 0.15625 0.09375 0.332031 0.140625 0.515625 0.140625 v 0.003906 c 1 -0.003906 1 -1 1 -1 v -6 s 0.003906 -1 -1 -1 z m 0 0"/><path d="m 14.003906 4.003906 c -0.183594 -0.003906 -0.359375 0.046875 -0.515625 0.140625 l -5 3 c -0.648437 0.386719 -0.648437 1.328125 0 1.714844 l 5 3 c 0.15625 0.09375 0.332031 0.140625 0.515625 0.140625 v 0.003906 c 1 -0.003906 1 -1 1 -1 v -6 s 0.003906 -1 -1 -1 z m 0 0"/></g></svg> diff --git a/data/icons/scalable/status/seek-forward-large-symbolic.svg b/data/icons/scalable/status/seek-forward-large-symbolic.svg new file mode 100644 index 0000000..72b819e --- /dev/null +++ b/data/icons/scalable/status/seek-forward-large-symbolic.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222"><path d="m 2.003906 4 c 0.179688 0 0.355469 0.046875 0.511719 0.140625 l 5 3 c 0.648437 0.386719 0.648437 1.328125 0 1.71875 l -5 3 c -0.15625 0.089844 -0.332031 0.140625 -0.511719 0.140625 c -1 0 -1 -1 -1 -1 v -6 s -0.003906 -1 1 -1 z m 0 0"/><path d="m 9.003906 4 c 0.179688 0 0.355469 0.046875 0.511719 0.140625 l 5 3 c 0.648437 0.386719 0.648437 1.328125 0 1.71875 l -5 3 c -0.15625 0.089844 -0.332031 0.140625 -0.511719 0.140625 c -1 0 -1 -1 -1 -1 v -6 s -0.003906 -1 1 -1 z m 0 0"/></g></svg> diff --git a/data/icons/scalable/status/stop-large-symbolic.svg b/data/icons/scalable/status/stop-large-symbolic.svg new file mode 100644 index 0000000..92ce2c6 --- /dev/null +++ b/data/icons/scalable/status/stop-large-symbolic.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 3.503906 2 h 9 c 0.828125 0 1.5 0.671875 1.5 1.5 v 9 c 0 0.828125 -0.671875 1.5 -1.5 1.5 h -9 c -0.828125 0 -1.5 -0.671875 -1.5 -1.5 v -9 c 0 -0.828125 0.671875 -1.5 1.5 -1.5 z m 0 0" fill="#222222"/></svg> diff --git a/data/swayosd.gresource.xml b/data/swayosd.gresource.xml index ffc363c..2e5a067 100644 --- a/data/swayosd.gresource.xml +++ b/data/swayosd.gresource.xml @@ -18,5 +18,13 @@ <file>icons/scalable/status/source-volume-medium-symbolic.svg</file> <file>icons/scalable/status/source-volume-low-symbolic.svg</file> <file>icons/scalable/status/source-volume-muted-symbolic.svg</file> + + <file>icons/scalable/status/pause-large-symbolic.svg</file> + <file>icons/scalable/status/play-large-symbolic.svg</file> + <file>icons/scalable/status/seek-backward-large-symbolic.svg</file> + <file>icons/scalable/status/seek-forward-large-symbolic.svg</file> + <file>icons/scalable/status/playlist-consecutive-symbolic.svg</file> + <file>icons/scalable/status/playlist-shuffle-symbolic.svg</file> + <file>icons/scalable/status/stop-large-symbolic.svg</file> </gresource> </gresources> diff --git a/src/argtypes.rs b/src/argtypes.rs index 9e8427c..acfa466 100644 --- a/src/argtypes.rs +++ b/src/argtypes.rs @@ -8,6 +8,7 @@ pub enum ArgTypes { TopMargin = isize::MIN + 1, MaxVolume = isize::MIN + 2, CustomIcon = isize::MIN + 3, + Player = isize::MIN + 4, // Other None = 0, CapsLock = 1, @@ -23,6 +24,7 @@ pub enum ArgTypes { NumLock = 10, ScrollLock = 11, CustomMessage = 13, + Playerctl = 14, } impl fmt::Display for ArgTypes { @@ -46,6 +48,8 @@ impl fmt::Display for ArgTypes { ArgTypes::TopMargin => "TOP-MARGIN", ArgTypes::CustomMessage => "CUSTOM-MESSAGE", ArgTypes::CustomIcon => "CUSTOM-ICON", + ArgTypes::Playerctl => "PLAYERCTL", + ArgTypes::Player => "PLAYER", }; return write!(f, "{}", string); } @@ -73,6 +77,8 @@ impl str::FromStr for ArgTypes { "TOP-MARGIN" => ArgTypes::TopMargin, "CUSTOM-MESSAGE" => ArgTypes::CustomMessage, "CUSTOM-ICON" => ArgTypes::CustomIcon, + "PLAYERCTL" => ArgTypes::Playerctl, + "PLAYER" => ArgTypes::Player, other_type => return Err(other_type.to_owned()), }; Ok(result) diff --git a/src/client/main.rs b/src/client/main.rs index 0edda88..6d695a3 100644 --- a/src/client/main.rs +++ b/src/client/main.rs @@ -156,6 +156,16 @@ fn main() -> Result<(), glib::Error> { "Shows brightness osd and raises or loweres all available sources of brightness device", Some("raise|lower|(±)number"), ); + + // Control players cmdline arg + app.add_main_option( + "playerctl", + glib::Char::from(0), + OptionFlags::NONE, + OptionArg::String, + "Shows Playerctl osd and runs the playerctl command", + Some("play-pause|play|pause|stop|next|prev|shuffle"), + ); app.add_main_option( "max-volume", glib::Char::from(0), @@ -172,6 +182,14 @@ fn main() -> Result<(), glib::Error> { "For which device to increase/decrease audio", Some("Pulseaudio device name (pactl list short sinks|sources)"), ); + app.add_main_option( + "player", + glib::Char::from(0), + OptionFlags::NONE, + OptionArg::String, + "For which player to run the playerctl commands", + Some("auto|all|(playerctl -l)"), + ); app.add_main_option( "custom-message", diff --git a/src/global_utils.rs b/src/global_utils.rs index 9c0b87d..ed3413b 100644 --- a/src/global_utils.rs +++ b/src/global_utils.rs @@ -95,6 +95,18 @@ pub(crate) fn handle_application_args( } } } + "playerctl" => { + let value = child.value().str().unwrap_or(""); + match value { + "play-pause" | "play" | "pause" | "next" |"prev" | "previous" | "shuffle" | "stop" => (), + x => { + eprintln!("Unknown Playerctl command: \"{}\"!...", x); + return (HandleLocalStatus::FAILURE, actions) + } + } + + (ArgTypes::Playerctl, Some(value.to_string())) + }, "device" => { let value = match child.value().str() { Some(v) => v.to_string(), @@ -125,6 +137,16 @@ pub(crate) fn handle_application_args( }; (ArgTypes::CustomIcon, Some(value)) } + "player" => { + let value = match child.value().str() { + Some(v) => v.to_string(), + None => { + eprintln!("--player found but no name given"); + return (HandleLocalStatus::FAILURE, actions); + } + }; + (ArgTypes::Player, Some(value)) + } "top-margin" => { let value = child.value().str().unwrap_or("").trim(); match value.parse::<f32>() { diff --git a/src/mpris-backend/mod.rs b/src/mpris-backend/mod.rs new file mode 100644 index 0000000..fbef544 --- /dev/null +++ b/src/mpris-backend/mod.rs @@ -0,0 +1,164 @@ +use mpris::{ + PlayerFinder, + Player, + PlaybackStatus, +}; + +use std::error::Error; +use crate::utils::get_player; + +pub enum PlayerctlAction { + PlayPause, + Play, + Pause, + Stop, + Next, + Prev, + Shuffle, +} + +#[derive(Clone, Debug)] +pub enum PlayerctlDeviceRaw { + None, + All, + Some(String), +} + +pub enum PlayerctlDevice { + All(Vec<Player>), + Some(Player), +} + +pub struct Playerctl { + player: PlayerctlDevice, + action: PlayerctlAction, + pub icon: Option<String>, + pub label: Option<String>, +} + +impl Playerctl { + pub fn new(action: PlayerctlAction) -> Result<Playerctl, Box<dyn Error>> { + let playerfinder = PlayerFinder::new()?; + let player = get_player(); + let player = match player { + PlayerctlDeviceRaw::None => PlayerctlDevice::Some(playerfinder.find_active()?), + PlayerctlDeviceRaw::Some(name) => { + PlayerctlDevice::Some(playerfinder.find_by_name(name.as_str())?) + }, + PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), + }; + Ok(Self { + player, + action, + icon: None, + label: None, + }) + } + pub fn run(&mut self) -> Result<(), Box<dyn Error>> { + use PlayerctlAction::*; + use PlaybackStatus::*; + let run_single = |player: &Player| -> Result<&str, Box<dyn Error>> { + let out = match self.action { + PlayPause => { + match player.get_playback_status()? { + Playing => {player.pause()?; "pause-large -symbolic"}, + Paused | Stopped => {player.play()?;"play-large-symbolic"} + } + }, + Shuffle => { + let shuffle = player.get_shuffle()?; + player.set_shuffle(!shuffle)?; + if shuffle { + "playlist-consecutive-symbolic" + } else { + "playlist-shuffle-symbolic" + } + }, + Play => {player.play()?; "play-large-symbolic"}, + Pause => {player.pause()?; "pause-large-symbolic"}, + Stop => {player.stop()?; "stop-large-symbolic"}, + Next => {player.next()?; "media-seek-forward-symbolic"}, + Prev => {player.previous()?; "media-seek-backward-symbolic"}, + }; + Ok(out) + }; + let mut metadata = Err("some errro"); + let icon = match &self.player { + PlayerctlDevice::Some(player) => { + metadata = player.get_metadata().or_else(|x| Err("")); + run_single(player)? + }, + PlayerctlDevice::All(players) => { + let mut icon = Err("couldn't change any players!"); + for player in players { + let icon_new = run_single(player); + if let Ok(icon_new) = icon_new { + if icon.is_err() { + icon = Ok(icon_new); + } + }; + if let Err(_) = metadata { + metadata = player.get_metadata().or_else(|x| Err("")); + } + } + icon? + }, + }; + + self.icon = Some(icon.to_string()); + let label = if let Ok(metadata) = metadata { + let artist = metadata.artists().and_then(|x| { + if x.len() != 0 { + Some(x[0].to_string()) + } else { + None + } + }); + let title = metadata.title().and_then(|x| Some(x.to_string())); + if title.is_none() { + if artist.is_none() { + None + } else { + artist + } + } else { + if artist.is_none() { + title + } else { + Some(format!("{} - {}", title.unwrap(), artist.unwrap())) + } + } + } else { + None + }; + self.label = label; + Ok(()) + } +} + +impl PlayerctlAction { + pub fn from(action: &str) -> Result<Self, String> { + use PlayerctlAction::*; + match action { + "play-pause" => Ok(PlayPause), + "play" => Ok(Play), + "pause" => Ok(Pause), + "stop" => Ok(Stop), + "next" => Ok(Next), + "prev" | "previous" => Ok(Prev), + "shuffle" => Ok(Shuffle), + x => Err(x.to_string()) + } + } +} + +impl PlayerctlDeviceRaw { + pub fn from(player: String) -> Result<Self, ()> { + use PlayerctlDeviceRaw::*; + match player.as_str() { + "auto" | "" => Ok(None), + "all" => Ok(All), + _ => Ok(Some(player)) + } + } +} diff --git a/src/server/application.rs b/src/server/application.rs index e7c08ca..7f3724b 100644 --- a/src/server/application.rs +++ b/src/server/application.rs @@ -386,6 +386,32 @@ impl SwayOSDApplication { }; set_max_volume(volume) } + (ArgTypes::Player, name) => { + set_player(name.unwrap_or("".to_string())) + } + (ArgTypes::Playerctl, value) => { + use crate::playerctl::*; + let value = &value.unwrap_or("".to_string()); + + let action = PlayerctlAction::from(value).unwrap(); + if let Ok(mut player) = Playerctl::new(action) { + match player.run() { + Ok(_) => { + let (icon, label) = (player.icon.unwrap(), player.label.unwrap()); + for window in osd_app.windows.borrow().to_owned() { + window.changed_player(&icon, &label) + } + }, + Err(x) => { + eprintln!("couldn't run player change: \"{:?}\"!", x) + }, + } + } else { + eprintln!("Unable to get players! are any opened?") + } + + reset_player(); + } (ArgTypes::DeviceName, name) => { set_device_name(name.unwrap_or(DEVICE_NAME_DEFAULT.to_string())) } diff --git a/src/server/main.rs b/src/server/main.rs index 83ef208..79e8179 100644 --- a/src/server/main.rs +++ b/src/server/main.rs @@ -12,6 +12,9 @@ mod global_utils; #[path = "../brightness_backend/mod.rs"] mod brightness_backend; +#[path = "../mpris-backend/mod.rs"] +mod playerctl; + #[macro_use] extern crate shrinkwraprs; diff --git a/src/server/osd_window.rs b/src/server/osd_window.rs index 5635890..7993bee 100644 --- a/src/server/osd_window.rs +++ b/src/server/osd_window.rs @@ -166,6 +166,18 @@ impl SwayosdWindow { self.run_timeout(); } + pub fn changed_player(&self, icon: &str, label: &str) { + self.clear_osd(); + + let icon = self.build_icon_widget(&icon); + let label = self.build_text_widget(Some(&label)); + + self.container.add(&icon); + self.container.add(&label); + + self.run_timeout(); + } + pub fn changed_keylock(&self, key: KeysLocks, state: bool) { self.clear_osd(); diff --git a/src/server/utils.rs b/src/server/utils.rs index 93cd914..8c0ed0c 100644 --- a/src/server/utils.rs +++ b/src/server/utils.rs @@ -13,6 +13,7 @@ use pulse::volume::Volume; use pulsectl::controllers::{types::DeviceInfo, DeviceControl, SinkController, SourceController}; use crate::brightness_backend; +use crate::playerctl::PlayerctlDeviceRaw; static PRIV_MAX_VOLUME_DEFAULT: u8 = 100_u8; @@ -23,6 +24,7 @@ lazy_static! { static ref DEVICE_NAME: Mutex<Option<String>> = Mutex::new(None); pub static ref ICON_NAME_DEFAULT: &'static str = "text-x-generic"; static ref ICON_NAME: Mutex<Option<String>> = Mutex::new(None); + static ref PLAYER_NAME: Mutex<PlayerctlDeviceRaw> = Mutex::new(PlayerctlDeviceRaw::None); pub static ref TOP_MARGIN_DEFAULT: f32 = 0.85_f32; static ref TOP_MARGIN: Mutex<f32> = Mutex::new(*TOP_MARGIN_DEFAULT); pub static ref SHOW_PERCENTAGE: Mutex<bool> = Mutex::new(false); @@ -103,6 +105,21 @@ pub fn reset_icon_name() { *icon_name = None; } +pub fn set_player(name: String) { + let mut global_player = PLAYER_NAME.lock().unwrap(); + *global_player = PlayerctlDeviceRaw::from(name).unwrap_or(PlayerctlDeviceRaw::None); +} + +pub fn reset_player() { + let mut global_name = PLAYER_NAME.lock().unwrap(); + *global_name = PlayerctlDeviceRaw::None; +} + +pub fn get_player() -> PlayerctlDeviceRaw { + let player = PLAYER_NAME.lock().unwrap(); + player.clone() +} + pub fn get_key_lock_state(key: KeysLocks, led: Option<String>) -> bool { const BASE_PATH: &str = "/sys/class/leds"; match fs::read_dir(BASE_PATH) { From ba9afe281a9907de83e4546fd09664fc8647a567 Mon Sep 17 00:00:00 2001 From: Jan Luca Oster <janluca.oster@proton.me> Date: Sat, 19 Oct 2024 12:05:16 +0200 Subject: [PATCH 02/18] fix 2 unused errors --- src/mpris-backend/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mpris-backend/mod.rs b/src/mpris-backend/mod.rs index fbef544..c60025c 100644 --- a/src/mpris-backend/mod.rs +++ b/src/mpris-backend/mod.rs @@ -85,7 +85,7 @@ impl Playerctl { let mut metadata = Err("some errro"); let icon = match &self.player { PlayerctlDevice::Some(player) => { - metadata = player.get_metadata().or_else(|x| Err("")); + metadata = player.get_metadata().or_else(|_| Err("")); run_single(player)? }, PlayerctlDevice::All(players) => { @@ -98,7 +98,7 @@ impl Playerctl { } }; if let Err(_) = metadata { - metadata = player.get_metadata().or_else(|x| Err("")); + metadata = player.get_metadata().or_else(|_| Err("")); } } icon? From 9e427860b4c1d9e75ef115e358c15496532dd824 Mon Sep 17 00:00:00 2001 From: Jan Luca <janluca.oster@proton.me> Date: Wed, 11 Dec 2024 20:31:02 +0100 Subject: [PATCH 03/18] update to GTK4 --- src/server/osd_window.rs | 550 +++++++++++++++++++-------------------- 1 file changed, 275 insertions(+), 275 deletions(-) diff --git a/src/server/osd_window.rs b/src/server/osd_window.rs index 7993bee..9e6e74e 100644 --- a/src/server/osd_window.rs +++ b/src/server/osd_window.rs @@ -3,18 +3,18 @@ use std::rc::Rc; use std::time::Duration; use gtk::{ - gdk, - glib::{self, clone}, - prelude::*, + gdk, + glib::{self, clone}, + prelude::*, }; use pulsectl::controllers::types::DeviceInfo; use crate::{ - brightness_backend::BrightnessBackend, - utils::{ - get_max_volume, get_show_percentage, get_top_margin, volume_to_f64, KeysLocks, - VolumeDeviceType, - }, + brightness_backend::BrightnessBackend, + utils::{ + get_max_volume, get_show_percentage, get_top_margin, volume_to_f64, KeysLocks, + VolumeDeviceType, + }, }; use gtk_layer_shell::LayerShell; @@ -24,274 +24,274 @@ const ICON_SIZE: i32 = 32; /// A window that our application can open that contains the main project view. #[derive(Clone, Debug)] pub struct SwayosdWindow { - pub window: gtk::ApplicationWindow, - pub display: gdk::Display, - pub monitor: gdk::Monitor, - container: gtk::Box, - timeout_id: Rc<RefCell<Option<glib::SourceId>>>, + pub window: gtk::ApplicationWindow, + pub display: gdk::Display, + pub monitor: gdk::Monitor, + container: gtk::Box, + timeout_id: Rc<RefCell<Option<glib::SourceId>>>, } impl SwayosdWindow { - /// Create a new window and assign it to the given application. - pub fn new(app: >k::Application, display: &gdk::Display, monitor: &gdk::Monitor) -> Self { - let window = gtk::ApplicationWindow::new(app); - window.set_widget_name("osd"); - window.add_css_class("osd"); - - window.init_layer_shell(); - window.set_monitor(monitor); - window.set_namespace("swayosd"); - - window.set_exclusive_zone(-1); - window.set_layer(gtk_layer_shell::Layer::Overlay); - window.set_anchor(gtk_layer_shell::Edge::Top, true); - - // Set up the widgets - window.set_width_request(250); - - let container = cascade! { - gtk::Box::new(gtk::Orientation::Horizontal, 12); - ..set_widget_name("container"); - }; - - window.set_child(Some(&container)); - - // Disable mouse input - window.connect_map(|window| { - if let Some(surface) = window.surface() { - let region = gtk::cairo::Region::create(); - surface.set_input_region(®ion); - } - }); - - let update_margins = |window: >k::ApplicationWindow, monitor: &gdk::Monitor| { - // Monitor scale factor is not always correct - // Transform monitor height into coordinate system of window - let mon_height = - monitor.geometry().height() * monitor.scale_factor() / window.scale_factor(); - // Calculate new margin - let bottom = mon_height - window.allocated_height(); - let margin = (bottom as f32 * get_top_margin()).round() as i32; - window.set_margin(gtk_layer_shell::Edge::Top, margin); - }; - - // Set the window margin - update_margins(&window, monitor); - // Ensure window margin is updated when necessary - window.connect_scale_factor_notify(clone!( - #[weak] - monitor, - move |window| update_margins(window, &monitor) - )); - monitor.connect_scale_factor_notify(clone!( - #[weak] - window, - move |monitor| update_margins(&window, monitor) - )); - monitor.connect_geometry_notify(clone!( - #[weak] - window, - move |monitor| update_margins(&window, monitor) - )); - - Self { - window, - container, - display: display.clone(), - monitor: monitor.clone(), - timeout_id: Rc::new(RefCell::new(None)), - } - } - - pub fn close(&self) { - self.window.close(); - } - - pub fn changed_volume(&self, device: &DeviceInfo, device_type: &VolumeDeviceType) { - self.clear_osd(); - - let volume = volume_to_f64(&device.volume.avg()); - let icon_prefix = match device_type { - VolumeDeviceType::Sink(_) => "sink", - VolumeDeviceType::Source(_) => "source", - }; - let icon_state = &match (device.mute, volume) { - (true, _) => "muted", - (_, x) if x == 0.0 => "muted", - (false, x) if x > 0.0 && x <= 33.0 => "low", - (false, x) if x > 33.0 && x <= 66.0 => "medium", - (false, x) if x > 66.0 && x <= 100.0 => "high", - (false, x) if x > 100.0 => match device_type { - VolumeDeviceType::Sink(_) => "high", - VolumeDeviceType::Source(_) => "overamplified", - }, - (_, _) => "high", - }; - let icon_name = &format!("{}-volume-{}-symbolic", icon_prefix, icon_state); - - let max_volume: f64 = get_max_volume().into(); - - let icon = self.build_icon_widget(icon_name); - let progress = self.build_progress_widget(volume / max_volume); - let label = self.build_text_widget(Some(&format!("{}%", volume))); - - progress.set_sensitive(!device.mute); - - self.container.append(&icon); - self.container.append(&progress); - if get_show_percentage() { - self.container.append(&label); - } - - self.run_timeout(); - } - - pub fn changed_brightness(&self, brightness_backend: &mut dyn BrightnessBackend) { - self.clear_osd(); - - let icon_name = "display-brightness-symbolic"; - let icon = self.build_icon_widget(icon_name); - - let brightness = brightness_backend.get_current() as f64; - let max = brightness_backend.get_max() as f64; - let progress = self.build_progress_widget(brightness / max); - let label = self.build_text_widget(Some(&format!("{}%", (brightness / max * 100.) as i32))); - - self.container.append(&icon); - self.container.append(&progress); - if get_show_percentage() { - self.container.append(&label); - } - - self.run_timeout(); - } - - pub fn changed_player(&self, icon: &str, label: &str) { - self.clear_osd(); - - let icon = self.build_icon_widget(&icon); - let label = self.build_text_widget(Some(&label)); - - self.container.add(&icon); - self.container.add(&label); - - self.run_timeout(); - } - - pub fn changed_keylock(&self, key: KeysLocks, state: bool) { - self.clear_osd(); - - let label = self.build_text_widget(None); - - let on_off_text = match state { - true => "On", - false => "Off", - }; - - let (label_text, symbol) = match key { - KeysLocks::CapsLock => { - let symbol = "caps-lock-symbolic"; - let text = "Caps Lock ".to_string() + on_off_text; - (text, symbol) - } - KeysLocks::NumLock => { - let symbol = "num-lock-symbolic"; - let text = "Num Lock ".to_string() + on_off_text; - (text, symbol) - } - KeysLocks::ScrollLock => { - let symbol = "scroll-lock-symbolic"; - let text = "Scroll Lock ".to_string() + on_off_text; - (text, symbol) - } - }; - - label.set_text(&label_text); - let icon = self.build_icon_widget(symbol); - - icon.set_sensitive(state); - - self.container.append(&icon); - self.container.append(&label); - - self.run_timeout(); - } - - pub fn custom_message(&self, message: &str, icon_name: Option<&str>) { - self.clear_osd(); - - let label = self.build_text_widget(Some(message)); - - if let Some(icon_name) = icon_name { - let icon = self.build_icon_widget(icon_name); - self.container.append(&icon); - self.container.append(&label); - let box_spacing = self.container.spacing(); - icon.connect_realize(move |icon| { - label.set_margin_end( - icon.allocation().width() - + icon.margin_start() - + icon.margin_end() - + box_spacing, - ); - }); - } else { - self.container.append(&label); - } - - self.run_timeout(); - } - - /// Clear all container children - fn clear_osd(&self) { - let mut next = self.container.first_child(); - while let Some(widget) = next { - next = widget.next_sibling(); - self.container.remove(&widget); - } - } - - fn run_timeout(&self) { - // Hide window after timeout - if let Some(timeout_id) = self.timeout_id.take() { - timeout_id.remove() - } - let s = self.clone(); - self.timeout_id.replace(Some(glib::timeout_add_local_once( - Duration::from_millis(1000), - move || { - s.window.hide(); - s.timeout_id.replace(None); - }, - ))); - - self.window.show(); - } - - fn build_icon_widget(&self, icon_name: &str) -> gtk::Image { - let icon = gtk::gio::ThemedIcon::from_names(&[icon_name, "missing-symbolic"]); - - cascade! { - gtk::Image::from_gicon(&icon.upcast::<gtk::gio::Icon>()); - ..set_pixel_size(ICON_SIZE); - } - } - - fn build_text_widget(&self, text: Option<&str>) -> gtk::Label { - cascade! { - gtk::Label::new(text); - ..set_halign(gtk::Align::Center); - ..set_hexpand(true); - ..add_css_class("title-4"); - } - } - - fn build_progress_widget(&self, fraction: f64) -> gtk::ProgressBar { - cascade! { - gtk::ProgressBar::new(); - ..set_fraction(fraction); - ..set_valign(gtk::Align::Center); - ..set_hexpand(true); - } - } + /// Create a new window and assign it to the given application. + pub fn new(app: >k::Application, display: &gdk::Display, monitor: &gdk::Monitor) -> Self { + let window = gtk::ApplicationWindow::new(app); + window.set_widget_name("osd"); + window.add_css_class("osd"); + + window.init_layer_shell(); + window.set_monitor(monitor); + window.set_namespace("swayosd"); + + window.set_exclusive_zone(-1); + window.set_layer(gtk_layer_shell::Layer::Overlay); + window.set_anchor(gtk_layer_shell::Edge::Top, true); + + // Set up the widgets + window.set_width_request(250); + + let container = cascade! { + gtk::Box::new(gtk::Orientation::Horizontal, 12); + ..set_widget_name("container"); + }; + + window.set_child(Some(&container)); + + // Disable mouse input + window.connect_map(|window| { + if let Some(surface) = window.surface() { + let region = gtk::cairo::Region::create(); + surface.set_input_region(®ion); + } + }); + + let update_margins = |window: >k::ApplicationWindow, monitor: &gdk::Monitor| { + // Monitor scale factor is not always correct + // Transform monitor height into coordinate system of window + let mon_height = + monitor.geometry().height() * monitor.scale_factor() / window.scale_factor(); + // Calculate new margin + let bottom = mon_height - window.allocated_height(); + let margin = (bottom as f32 * get_top_margin()).round() as i32; + window.set_margin(gtk_layer_shell::Edge::Top, margin); + }; + + // Set the window margin + update_margins(&window, monitor); + // Ensure window margin is updated when necessary + window.connect_scale_factor_notify(clone!( + #[weak] + monitor, + move |window| update_margins(window, &monitor) + )); + monitor.connect_scale_factor_notify(clone!( + #[weak] + window, + move |monitor| update_margins(&window, monitor) + )); + monitor.connect_geometry_notify(clone!( + #[weak] + window, + move |monitor| update_margins(&window, monitor) + )); + + Self { + window, + container, + display: display.clone(), + monitor: monitor.clone(), + timeout_id: Rc::new(RefCell::new(None)), + } + } + + pub fn close(&self) { + self.window.close(); + } + + pub fn changed_volume(&self, device: &DeviceInfo, device_type: &VolumeDeviceType) { + self.clear_osd(); + + let volume = volume_to_f64(&device.volume.avg()); + let icon_prefix = match device_type { + VolumeDeviceType::Sink(_) => "sink", + VolumeDeviceType::Source(_) => "source", + }; + let icon_state = &match (device.mute, volume) { + (true, _) => "muted", + (_, x) if x == 0.0 => "muted", + (false, x) if x > 0.0 && x <= 33.0 => "low", + (false, x) if x > 33.0 && x <= 66.0 => "medium", + (false, x) if x > 66.0 && x <= 100.0 => "high", + (false, x) if x > 100.0 => match device_type { + VolumeDeviceType::Sink(_) => "high", + VolumeDeviceType::Source(_) => "overamplified", + }, + (_, _) => "high", + }; + let icon_name = &format!("{}-volume-{}-symbolic", icon_prefix, icon_state); + + let max_volume: f64 = get_max_volume().into(); + + let icon = self.build_icon_widget(icon_name); + let progress = self.build_progress_widget(volume / max_volume); + let label = self.build_text_widget(Some(&format!("{}%", volume))); + + progress.set_sensitive(!device.mute); + + self.container.append(&icon); + self.container.append(&progress); + if get_show_percentage() { + self.container.append(&label); + } + + self.run_timeout(); + } + + pub fn changed_brightness(&self, brightness_backend: &mut dyn BrightnessBackend) { + self.clear_osd(); + + let icon_name = "display-brightness-symbolic"; + let icon = self.build_icon_widget(icon_name); + + let brightness = brightness_backend.get_current() as f64; + let max = brightness_backend.get_max() as f64; + let progress = self.build_progress_widget(brightness / max); + let label = self.build_text_widget(Some(&format!("{}%", (brightness / max * 100.) as i32))); + + self.container.append(&icon); + self.container.append(&progress); + if get_show_percentage() { + self.container.append(&label); + } + + self.run_timeout(); + } + + pub fn changed_player(&self, icon: &str, label: &str) { + self.clear_osd(); + + let icon = self.build_icon_widget(&icon); + let label = self.build_text_widget(Some(&label)); + + self.container.append(&icon); + self.container.append(&label); + + self.run_timeout(); + } + + pub fn changed_keylock(&self, key: KeysLocks, state: bool) { + self.clear_osd(); + + let label = self.build_text_widget(None); + + let on_off_text = match state { + true => "On", + false => "Off", + }; + + let (label_text, symbol) = match key { + KeysLocks::CapsLock => { + let symbol = "caps-lock-symbolic"; + let text = "Caps Lock ".to_string() + on_off_text; + (text, symbol) + } + KeysLocks::NumLock => { + let symbol = "num-lock-symbolic"; + let text = "Num Lock ".to_string() + on_off_text; + (text, symbol) + } + KeysLocks::ScrollLock => { + let symbol = "scroll-lock-symbolic"; + let text = "Scroll Lock ".to_string() + on_off_text; + (text, symbol) + } + }; + + label.set_text(&label_text); + let icon = self.build_icon_widget(symbol); + + icon.set_sensitive(state); + + self.container.append(&icon); + self.container.append(&label); + + self.run_timeout(); + } + + pub fn custom_message(&self, message: &str, icon_name: Option<&str>) { + self.clear_osd(); + + let label = self.build_text_widget(Some(message)); + + if let Some(icon_name) = icon_name { + let icon = self.build_icon_widget(icon_name); + self.container.append(&icon); + self.container.append(&label); + let box_spacing = self.container.spacing(); + icon.connect_realize(move |icon| { + label.set_margin_end( + icon.allocation().width() + + icon.margin_start() + + icon.margin_end() + + box_spacing, + ); + }); + } else { + self.container.append(&label); + } + + self.run_timeout(); + } + + /// Clear all container children + fn clear_osd(&self) { + let mut next = self.container.first_child(); + while let Some(widget) = next { + next = widget.next_sibling(); + self.container.remove(&widget); + } + } + + fn run_timeout(&self) { + // Hide window after timeout + if let Some(timeout_id) = self.timeout_id.take() { + timeout_id.remove() + } + let s = self.clone(); + self.timeout_id.replace(Some(glib::timeout_add_local_once( + Duration::from_millis(1000), + move || { + s.window.hide(); + s.timeout_id.replace(None); + }, + ))); + + self.window.show(); + } + + fn build_icon_widget(&self, icon_name: &str) -> gtk::Image { + let icon = gtk::gio::ThemedIcon::from_names(&[icon_name, "missing-symbolic"]); + + cascade! { + gtk::Image::from_gicon(&icon.upcast::<gtk::gio::Icon>()); + ..set_pixel_size(ICON_SIZE); + } + } + + fn build_text_widget(&self, text: Option<&str>) -> gtk::Label { + cascade! { + gtk::Label::new(text); + ..set_halign(gtk::Align::Center); + ..set_hexpand(true); + ..add_css_class("title-4"); + } + } + + fn build_progress_widget(&self, fraction: f64) -> gtk::ProgressBar { + cascade! { + gtk::ProgressBar::new(); + ..set_fraction(fraction); + ..set_valign(gtk::Align::Center); + ..set_hexpand(true); + } + } } From f5d5a07fc5202f9a518717d8edf255afe48c5d88 Mon Sep 17 00:00:00 2001 From: Jan Luca <janluca.oster@proton.me> Date: Wed, 11 Dec 2024 20:34:58 +0100 Subject: [PATCH 04/18] add cargo.lock --- Cargo.lock | 558 ++++++++++++++++++++++++++--------------------------- 1 file changed, 273 insertions(+), 285 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0698161..35bd1bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,12 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "anyhow" -version = "1.0.90" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "async-broadcast" @@ -84,9 +84,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock", "cfg-if", @@ -139,7 +139,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -200,31 +200,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.79", -] - -[[package]] -name = "atk" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba16453d10c712284061a05f6510f75abeb92b56ba88dfeb48c74775020cc22" -dependencies = [ - "atk-sys", - "bitflags 1.3.2", - "glib", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf0a7ca572fbd5762fd8f8cd65a581e06767bc1234913fe1f43e370cff6e90" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", + "syn 2.0.90", ] [[package]] @@ -297,23 +273,21 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cairo-rs" -version = "0.17.10" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3603c4028a5e368d09b51c8b624b9a46edcd7c3778284077a6125af73c9f0a" +checksum = "d7fa699e1d7ae691001a811dda5ef0e3e42e1d4119b26426352989df9e94e3e6" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "cairo-sys-rs", "glib", "libc", - "once_cell", - "thiserror", ] [[package]] name = "cairo-sys-rs" -version = "0.17.10" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "691d0c66b1fb4881be80a760cb8fe76ea97218312f9dfe2c9cc0f496ca279cb1" +checksum = "428290f914b9b86089f60f5d8a9f6e440508e1bcff23b25afd51502b0a2da88f" dependencies = [ "glib-sys", "libc", @@ -328,18 +302,18 @@ checksum = "d499b43edbf784dd81e16f0395f5b4350a35b477da8a074251087adefc11cb52" [[package]] name = "cc" -version = "1.1.31" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" dependencies = [ "shlex", ] [[package]] name = "cfg-expr" -version = "0.15.8" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +checksum = "8d4ba6e40bd1184518716a6e1a781bf9160e286d219ccdb8ab2612e74cfe4789" dependencies = [ "smallvec", "target-lexicon", @@ -378,9 +352,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -509,7 +483,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -520,12 +494,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -570,9 +544,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ "event-listener 5.3.1", "pin-project-lite", @@ -580,9 +554,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "field-offset" @@ -665,9 +639,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ "fastrand", "futures-core", @@ -684,7 +658,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -716,41 +690,23 @@ dependencies = [ "slab", ] -[[package]] -name = "gdk" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1df5ea52cccd7e3a0897338b5564968274b52f5fd12601e0afa44f454c74d3" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", -] - [[package]] name = "gdk-pixbuf" -version = "0.17.10" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "695d6bc846438c5708b07007537b9274d883373dd30858ca881d7d71b5540717" +checksum = "c4c29071a9e92337d8270a85cb0510cda4ac478be26d09ad027cc1d081911b19" dependencies = [ - "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", "glib", "libc", - "once_cell", ] [[package]] name = "gdk-pixbuf-sys" -version = "0.17.10" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9285ec3c113c66d7d0ab5676599176f1f42f4944ca1b581852215bf5694870cb" +checksum = "687343b059b91df5f3fbd87b4307038fa9e647fcc0461d0d3f93e94fee20bf3d" dependencies = [ "gio-sys", "glib-sys", @@ -760,10 +716,26 @@ dependencies = [ ] [[package]] -name = "gdk-sys" -version = "0.17.0" +name = "gdk4" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2152de9d38bc67a17b3fe49dc0823af5bf874df59ea088c5f28f31cf103de703" +checksum = "75933c4a86e8a2428814d367e22c733304fdfabc87f415750fd2f55409b6ee48" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk4-sys", + "gio", + "gl", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk4-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20af0656d543aed3e57ac4120ef76d091c3c42ab1e0507a8febde7cd005640e2" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -799,11 +771,10 @@ dependencies = [ [[package]] name = "gio" -version = "0.17.10" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6973e92937cf98689b6a054a9e56c657ed4ff76de925e36fc331a15f0c5d30a" +checksum = "8826d2a9ad56ce3de1f04bea0bea0daff6f5f1c913cc834996cfea1f9401361c" dependencies = [ - "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", @@ -811,32 +782,50 @@ dependencies = [ "gio-sys", "glib", "libc", - "once_cell", "pin-project-lite", "smallvec", - "thiserror", ] [[package]] name = "gio-sys" -version = "0.17.10" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ccf87c30a12c469b6d958950f6a9c09f2be20b7773f7e70d20b867fdf2628c3" +checksum = "b965df6f3534c84816b5c1a7d9efcb5671ae790822de5abe8e299797039529bc" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", - "winapi", + "windows-sys 0.52.0", +] + +[[package]] +name = "gl" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94edab108827d67608095e269cf862e60d920f144a5026d3dbcfd8b877fb404" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", ] [[package]] name = "glib" -version = "0.17.10" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fad45ba8d4d2cea612b432717e834f48031cd8853c8aaf43b2c79fec8d144b" +checksum = "86bd3e4ee7998ab5a135d900db56930cc19ad16681adf245daff54f618b9d5e1" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "futures-channel", "futures-core", "futures-executor", @@ -848,31 +837,27 @@ dependencies = [ "gobject-sys", "libc", "memchr", - "once_cell", "smallvec", - "thiserror", ] [[package]] name = "glib-macros" -version = "0.17.10" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca5c79337338391f1ab8058d6698125034ce8ef31b72a442437fa6c8580de26" +checksum = "e7d21ca27acfc3e91da70456edde144b4ac7c36f78ee77b10189b3eb4901c156" dependencies = [ - "anyhow", - "heck 0.4.1", - "proc-macro-crate 1.3.1", - "proc-macro-error", + "heck 0.5.0", + "proc-macro-crate", "proc-macro2", "quote 1.0.37", - "syn 1.0.109", + "syn 2.0.90", ] [[package]] name = "glib-sys" -version = "0.17.10" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d80aa6ea7bba0baac79222204aa786a6293078c210abe69ef1336911d4bdc4f0" +checksum = "3d0b1827e8621fc42c0dfb228e5d57ff6a71f9699e666ece8113f979ad87c2de" dependencies = [ "libc", "system-deps", @@ -892,9 +877,9 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.17.10" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd34c3317740a6358ec04572c1bcfd3ac0b5b6529275fae255b237b314bb8062" +checksum = "a4c674d2ff8478cf0ec29d2be730ed779fef54415a2fb4b565c52def62696462" dependencies = [ "glib-sys", "libc", @@ -902,93 +887,144 @@ dependencies = [ ] [[package]] -name = "gtk" -version = "0.17.1" +name = "graphene-rs" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c4222ab92b08d4d0bab90ddb6185b4e575ceeea8b8cdf00b938d7b6661d966" +checksum = "1f53144c7fe78292705ff23935f1477d511366fb2f73c43d63b37be89076d2fe" +dependencies = [ + "glib", + "graphene-sys", + "libc", +] + +[[package]] +name = "graphene-sys" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e741797dc5081e59877a4d72c442c72d61efdd99161a0b1c1b29b6b988934b99" +dependencies = [ + "glib-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gsk4" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b36933c1e79df378aa6e606576e680358a9582ed8c16f33e94899636e6fa6df6" +dependencies = [ + "cairo-rs", + "gdk4", + "glib", + "graphene-rs", + "gsk4-sys", + "libc", + "pango", +] + +[[package]] +name = "gsk4-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0877a9d485bd9ba5262b0c9bce39e63750e525e3aebeb359d271ca1f0e111f1d" +dependencies = [ + "cairo-sys-rs", + "gdk4-sys", + "glib-sys", + "gobject-sys", + "graphene-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk4" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9376d14d7e33486c54823a42bef296e882b9f25cb4c52b52f4d1d57bbadb5b6d" dependencies = [ - "atk", - "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", - "gdk", "gdk-pixbuf", + "gdk4", "gio", "glib", - "gtk-sys", - "gtk3-macros", + "graphene-rs", + "gsk4", + "gtk4-macros", + "gtk4-sys", "libc", - "once_cell", "pango", - "pkg-config", ] [[package]] -name = "gtk-layer-shell" -version = "0.6.1" +name = "gtk4-layer-shell" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "992f5fedb31835424a5280acd162bf348995f617d26969fde8d3dfd389b3ff5f" +checksum = "f3e1e1b1516be3d7ca089dfa6a1e688e268c74aef50c0c25fe8c46b1ba8ed1cc" dependencies = [ "bitflags 2.6.0", - "gdk", + "gdk4", "glib", "glib-sys", - "gtk", - "gtk-layer-shell-sys", + "gtk4", + "gtk4-layer-shell-sys", "libc", ] [[package]] -name = "gtk-layer-shell-sys" -version = "0.6.0" +name = "gtk4-layer-shell-sys" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5754bcfaadfc3529116af6ae93559b267d88647f965382153a4b8ea9372be75a" +checksum = "e3057dc117db2d664a9b45f1956568701914e80cf9f2c8cef0a755af4c1c8105" dependencies = [ - "gdk-sys", + "gdk4-sys", "glib-sys", - "gtk-sys", + "gtk4-sys", "libc", "system-deps", ] [[package]] -name = "gtk-sys" -version = "0.17.0" +name = "gtk4-macros" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d8eb6a4b93e5a7e6980f7348d08c1cd93d31fae07cf97f20678c5ec41de3d7e" +checksum = "a7c518d5dd41c57385c7cd30af52e261820c897fc1144e558bb88c303d048ae2" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote 1.0.37", + "syn 2.0.90", +] + +[[package]] +name = "gtk4-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e653b0a9001ba9be1ffddb9373bfe9a111f688222f5aeee2841481300d91b55a" dependencies = [ - "atk-sys", "cairo-sys-rs", "gdk-pixbuf-sys", - "gdk-sys", + "gdk4-sys", "gio-sys", "glib-sys", "gobject-sys", + "graphene-sys", + "gsk4-sys", "libc", "pango-sys", "system-deps", ] -[[package]] -name = "gtk3-macros" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3efb84d682c9a39c10bd9f24f5a4b9c15cc8c7edc45c19cb2ca2c4fc38b2d95e" -dependencies = [ - "anyhow", - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote 1.0.37", - "syn 1.0.109", -] - [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -999,12 +1035,6 @@ dependencies = [ "unicode-segmentation", ] -[[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" @@ -1037,9 +1067,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown", @@ -1087,13 +1117,20 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -1111,9 +1148,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "libdbus-sys" @@ -1126,9 +1163,9 @@ dependencies = [ [[package]] name = "libpulse-binding" -version = "2.28.1" +version = "2.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3557a2dfc380c8f061189a01c6ae7348354e0c9886038dc6c171219c08eaff" +checksum = "b6b1040a6c4c4d1e9e852000f6202df1a02a4f074320de336ab21e4fd317b538" dependencies = [ "bitflags 1.3.2", "libc", @@ -1277,23 +1314,21 @@ dependencies = [ [[package]] name = "pango" -version = "0.17.10" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35be456fc620e61f62dff7ff70fbd54dcbaf0a4b920c0f16de1107c47d921d48" +checksum = "71e34e7ca2c52e3933d7e5251409a82b83725fa9d6d48fbdaacec056b3a0554a" dependencies = [ - "bitflags 1.3.2", "gio", "glib", "libc", - "once_cell", "pango-sys", ] [[package]] name = "pango-sys" -version = "0.17.10" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da69f9f3850b0d8990d462f8c709561975e95f689c1cdf0fecdebde78b35195" +checksum = "84fd65917bf12f06544ae2bbc200abf9fc0a513a5a88a0fa81013893aef2b838" dependencies = [ "glib-sys", "gobject-sys", @@ -1309,9 +1344,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -1338,9 +1373,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", @@ -1360,54 +1395,20 @@ dependencies = [ "zerocopy", ] -[[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 = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit 0.22.22", -] - -[[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 1.0.37", - "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 1.0.37", - "version_check", + "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -1477,15 +1478,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1496,22 +1497,22 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -1522,7 +1523,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -1614,12 +1615,13 @@ name = "swayosd" version = "0.1.0" dependencies = [ "anyhow", + "async-channel 2.3.1", "async-std", "blight", "cascade", "evdev-rs", - "gtk", - "gtk-layer-shell", + "gtk4", + "gtk4-layer-shell", "input", "lazy_static", "libc", @@ -1660,9 +1662,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote 1.0.37", @@ -1680,9 +1682,9 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.2.2" +version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005" dependencies = [ "cfg-expr", "heck 0.5.0", @@ -1699,9 +1701,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", @@ -1712,22 +1714,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -1739,7 +1741,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.22", + "toml_edit", ] [[package]] @@ -1751,17 +1753,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.22.22" @@ -1772,14 +1763,14 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.20", + "winnow", ] [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -1788,20 +1779,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] @@ -1836,9 +1827,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-segmentation" @@ -1854,9 +1845,9 @@ checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" [[package]] name = "value-bag" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" +checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" [[package]] name = "version-compare" @@ -1878,9 +1869,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -1889,36 +1880,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.90", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote 1.0.37", "wasm-bindgen-macro-support", @@ -1926,28 +1917,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -2123,15 +2114,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[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.6.20" @@ -2151,6 +2133,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "xml-rs" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432" + [[package]] name = "zbus" version = "4.4.0" @@ -2195,10 +2183,10 @@ version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate", "proc-macro2", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.90", "zvariant_utils", ] @@ -2231,7 +2219,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -2253,10 +2241,10 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate", "proc-macro2", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.90", "zvariant_utils", ] @@ -2268,5 +2256,5 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.90", ] From 5fbcdd1c22530331213121607a9106b1e2e23ee4 Mon Sep 17 00:00:00 2001 From: Jan Luca <janluca.oster@proton.me> Date: Thu, 9 Jan 2025 15:19:19 +0100 Subject: [PATCH 05/18] resolve merge conflict #2 --- Cargo.lock | 32 ++ Cargo.toml | 2 + src/config/user.rs | 61 +-- src/mpris-backend/mod.rs | 57 ++- src/server/application.rs | 990 +++++++++++++++++++------------------- src/server/main.rs | 228 ++++----- 6 files changed, 711 insertions(+), 659 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 35bd1bf..ddb8351 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1467,6 +1467,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "runtime-format" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09958d5b38bca768ede7928c767c89a08ba568144a7b61992aecae79b03c8c94" +dependencies = [ + "tinyvec", +] + [[package]] name = "rustc_version" version = "0.4.1" @@ -1595,6 +1604,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strfmt" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a8348af2d9fc3258c8733b8d9d8db2e56f54b2363a4b5b81585c7875ed65e65" + [[package]] name = "strsim" version = "0.10.0" @@ -1629,9 +1644,11 @@ dependencies = [ "mpris", "nix 0.26.4", "pulsectl-rs", + "runtime-format", "serde", "serde_derive", "shrinkwraprs", + "strfmt", "substring", "thiserror", "toml", @@ -1732,6 +1749,21 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "toml" version = "0.8.19" diff --git a/Cargo.toml b/Cargo.toml index 2903f9b..a4e52a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,3 +43,5 @@ anyhow = "1.0.75" thiserror = "1.0.49" async-channel = "2.3.1" mpris = "2.0.1" +runtime-format = "0.1.3" +strfmt = "0.2.4" diff --git a/src/config/user.rs b/src/config/user.rs index ab18453..e406683 100644 --- a/src/config/user.rs +++ b/src/config/user.rs @@ -9,47 +9,48 @@ use std::path::PathBuf; #[serde(deny_unknown_fields)] pub struct ClientConfig {} -#[derive(Deserialize, Default, Debug)] +#[derive(Deserialize, Default, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct ServerConfig { - pub style: Option<PathBuf>, - pub top_margin: Option<f32>, - pub max_volume: Option<u8>, - pub show_percentage: Option<bool>, + pub style: Option<PathBuf>, + pub top_margin: Option<f32>, + pub max_volume: Option<u8>, + pub show_percentage: Option<bool>, + pub playerctl_format: Option<String>, } #[derive(Deserialize, Default, Debug)] #[serde(deny_unknown_fields)] pub struct UserConfig { - #[serde(default)] - pub server: ServerConfig, - #[serde(default)] - pub client: ClientConfig, + #[serde(default)] + pub server: ServerConfig, + #[serde(default)] + pub client: ClientConfig, } fn find_user_config() -> Option<PathBuf> { - let path = user_config_dir().join("swayosd").join("config.toml"); - if path.exists() { - return Some(path); - } - - for path in system_config_dirs() { - let path = path.join("swayosd").join("config.toml"); - if path.exists() { - return Some(path); - } - } - - None + let path = user_config_dir().join("swayosd").join("config.toml"); + if path.exists() { + return Some(path); + } + + for path in system_config_dirs() { + let path = path.join("swayosd").join("config.toml"); + if path.exists() { + return Some(path); + } + } + + None } pub fn read_user_config(path: Option<&Path>) -> Result<UserConfig, Box<dyn Error>> { - let path = match path.map(Path::to_owned).or_else(find_user_config) { - Some(path) => path, - None => return Ok(Default::default()), - }; - - let config_file = std::fs::read_to_string(path)?; - let config: UserConfig = toml::from_str(&config_file)?; - Ok(config) + let path = match path.map(Path::to_owned).or_else(find_user_config) { + Some(path) => path, + None => return Ok(Default::default()), + }; + + let config_file = std::fs::read_to_string(path)?; + let config: UserConfig = toml::from_str(&config_file)?; + Ok(config) } diff --git a/src/mpris-backend/mod.rs b/src/mpris-backend/mod.rs index c60025c..0941cc8 100644 --- a/src/mpris-backend/mod.rs +++ b/src/mpris-backend/mod.rs @@ -17,6 +17,8 @@ pub enum PlayerctlAction { Shuffle, } +use super::config::user::ServerConfig; + #[derive(Clone, Debug)] pub enum PlayerctlDeviceRaw { None, @@ -34,10 +36,11 @@ pub struct Playerctl { action: PlayerctlAction, pub icon: Option<String>, pub label: Option<String>, + fmt_str: Option<String>, } impl Playerctl { - pub fn new(action: PlayerctlAction) -> Result<Playerctl, Box<dyn Error>> { + pub fn new(action: PlayerctlAction, config: &ServerConfig) -> Result<Playerctl, Box<dyn Error>> { let playerfinder = PlayerFinder::new()?; let player = get_player(); let player = match player { @@ -47,11 +50,13 @@ impl Playerctl { }, PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), }; + let fmt_str = config.playerctl_format.clone(); Ok(Self { player, action, icon: None, label: None, + fmt_str, }) } pub fn run(&mut self) -> Result<(), Box<dyn Error>> { @@ -107,33 +112,41 @@ impl Playerctl { self.icon = Some(icon.to_string()); let label = if let Ok(metadata) = metadata { - let artist = metadata.artists().and_then(|x| { - if x.len() != 0 { - Some(x[0].to_string()) - } else { - None - } - }); - let title = metadata.title().and_then(|x| Some(x.to_string())); - if title.is_none() { - if artist.is_none() { - None - } else { - artist - } - } else { - if artist.is_none() { - title - } else { - Some(format!("{} - {}", title.unwrap(), artist.unwrap())) - } - } + Some(self.fmt_string(metadata)) } else { None }; self.label = label; Ok(()) } + fn fmt_string(&self, metadata: mpris::Metadata) -> String { + use strfmt::{strfmt, Format}; + use std::collections::HashMap; + + let mut vars = HashMap::new(); + let artists = metadata.artists().unwrap_or(vec![""]); + let artists_album = metadata.album_artists().unwrap_or(vec![""]); + let artist = artists.get(0).map_or("", |v| v); + let artist_album = artists_album.get(0).map_or("", |v| v); + + let title = metadata.title().unwrap_or(""); + let album = metadata.album_name().unwrap_or(""); + let track_num = metadata.track_number().and_then(|x| Some(x.to_string())).unwrap_or(String::new()); + let disc_num = metadata.disc_number().and_then(|x| Some(x.to_string())).unwrap_or(String::new()); + let autorating = metadata.auto_rating().and_then(|x| Some(x.to_string())).unwrap_or(String::new()); + + vars.insert("artist".to_string(), artist); + vars.insert("albumArtist".to_string(), artist_album); + vars.insert("title".to_string(), title); + vars.insert("trackNumber".to_string(), &track_num); + vars.insert("discNumber".to_string(), &disc_num); + vars.insert("autoRating".to_string(), &autorating); + + self.fmt_str.clone().unwrap_or("{artist} - {title}".into()).format(&vars).unwrap_or_else(|e| { + eprintln!("error: {}. using default string", e); + "{artist} - {title}".format(&vars).unwrap() + }) + } } impl PlayerctlAction { diff --git a/src/server/application.rs b/src/server/application.rs index 7f3724b..e644a7d 100644 --- a/src/server/application.rs +++ b/src/server/application.rs @@ -5,15 +5,15 @@ use crate::osd_window::SwayosdWindow; use crate::utils::{self, *}; use async_channel::Receiver; use gtk::{ - gdk, - gio::{ - self, ApplicationFlags, BusNameWatcherFlags, BusType, DBusSignalFlags, SignalSubscriptionId, - }, - glib::{ - clone, variant::ToVariant, Char, ControlFlow::Break, MainContext, OptionArg, OptionFlags, - }, - prelude::*, - Application, + gdk, + gio::{ + self, ApplicationFlags, BusNameWatcherFlags, BusType, DBusSignalFlags, SignalSubscriptionId, + }, + glib::{ + clone, variant::ToVariant, Char, ControlFlow::Break, MainContext, OptionArg, OptionFlags, + }, + prelude::*, + Application, }; use pulsectl::controllers::{SinkController, SourceController}; use std::cell::RefCell; @@ -24,491 +24,495 @@ use super::config::user::ServerConfig; #[derive(Clone, Shrinkwrap)] pub struct SwayOSDApplication { - #[shrinkwrap(main_field)] - app: gtk::Application, - windows: Rc<RefCell<Vec<SwayosdWindow>>>, - _hold: Rc<gio::ApplicationHoldGuard>, + #[shrinkwrap(main_field)] + app: gtk::Application, + windows: Rc<RefCell<Vec<SwayosdWindow>>>, + _hold: Rc<gio::ApplicationHoldGuard>, } impl SwayOSDApplication { - pub fn new(server_config: ServerConfig, action_receiver: Receiver<(ArgTypes, String)>) -> Self { - let app = Application::new(Some(APPLICATION_NAME), ApplicationFlags::FLAGS_NONE); - let hold = Rc::new(app.hold()); - - app.add_main_option( - "config", - Char::from(0), - OptionFlags::NONE, - OptionArg::String, - "Use a custom config file instead of looking for one.", - Some("<CONFIG FILE PATH>"), - ); - - app.add_main_option( - "style", - Char::from('s' as u8), - OptionFlags::NONE, - OptionArg::String, - "Use a custom Stylesheet file instead of looking for one", - Some("<CSS FILE PATH>"), - ); - - app.add_main_option( - "top-margin", - Char::from(0), - OptionFlags::NONE, - OptionArg::String, - &format!( - "OSD margin from top edge (0.5 would be screen center). Default is {}", - *utils::TOP_MARGIN_DEFAULT - ), - Some("<from 0.0 to 1.0>"), - ); - - let osd_app = SwayOSDApplication { - app: app.clone(), - windows: Rc::new(RefCell::new(Vec::new())), - _hold: hold, - }; - - // Apply Server Config - if let Some(margin) = server_config.top_margin { - if (0_f32..1_f32).contains(&margin) { - set_top_margin(margin); - } - } - if let Some(max_volume) = server_config.max_volume { - set_default_max_volume(max_volume); - } - if let Some(show) = server_config.show_percentage { - set_show_percentage(show); - } - - // Parse args - app.connect_handle_local_options(clone!( - #[strong] - osd_app, - move |_app, args| { - let actions = match handle_application_args(args.to_variant()) { - (HandleLocalStatus::SUCCESS | HandleLocalStatus::CONTINUE, actions) => actions, - (status @ HandleLocalStatus::FAILURE, _) => return status as i32, - }; - for (arg_type, data) in actions { - match (arg_type, data) { - (ArgTypes::TopMargin, margin) => { - let margin: Option<f32> = margin - .and_then(|margin| margin.parse().ok()) - .and_then(|margin| { - (0_f32..1_f32).contains(&margin).then_some(margin) - }); - - if let Some(margin) = margin { - set_top_margin(margin) - } - } - (ArgTypes::MaxVolume, max) => { - let max: Option<u8> = max.and_then(|max| max.parse().ok()); - - if let Some(max) = max { - set_default_max_volume(max); - } - } - (arg_type, data) => Self::action_activated(&osd_app, arg_type, data), - } - } - - HandleLocalStatus::CONTINUE as i32 - } - )); - - // Listen to any Client actions - MainContext::default().spawn_local(clone!( - #[strong] - osd_app, - async move { - while let Ok((arg_type, data)) = action_receiver.recv().await { - Self::action_activated(&osd_app, arg_type, (!data.is_empty()).then_some(data)); - } - Break - } - )); - - // Listen to the LibInput Backend and activate the Application action - let (sender, receiver) = async_channel::bounded::<(u16, i32)>(1); - MainContext::default().spawn_local(clone!( - #[strong] - osd_app, - async move { - while let Ok((key_code, state)) = receiver.recv().await { - let (arg_type, data): (ArgTypes, Option<String>) = - match evdev_rs::enums::int_to_ev_key(key_code as u32) { - Some(evdev_rs::enums::EV_KEY::KEY_CAPSLOCK) => { - (ArgTypes::CapsLock, Some(state.to_string())) - } - Some(evdev_rs::enums::EV_KEY::KEY_NUMLOCK) => { - (ArgTypes::NumLock, Some(state.to_string())) - } - Some(evdev_rs::enums::EV_KEY::KEY_SCROLLLOCK) => { - (ArgTypes::ScrollLock, Some(state.to_string())) - } - _ => continue, - }; - Self::action_activated(&osd_app, arg_type, data); - } - Break - } - )); - // Start watching for the LibInput Backend - let signal_id: Arc<Mutex<Option<SignalSubscriptionId>>> = Arc::new(Mutex::new(None)); - gio::bus_watch_name( - BusType::System, - DBUS_BACKEND_NAME, - BusNameWatcherFlags::NONE, - clone!( - #[strong] - sender, - #[strong] - signal_id, - move |connection, _, _| { - println!("Connecting to the SwayOSD LibInput Backend"); - let mut mutex = match signal_id.lock() { - Ok(mut mutex) => match mutex.as_mut() { - Some(_) => return, - None => mutex, - }, - Err(error) => return println!("Mutex lock Error: {}", error), - }; - mutex.replace(connection.signal_subscribe( - Some(config::DBUS_BACKEND_NAME), - Some(config::DBUS_BACKEND_NAME), - Some("KeyPressed"), - Some(config::DBUS_PATH), - None, - DBusSignalFlags::NONE, - clone!( - #[strong] - sender, - move |_, _, _, _, _, variant| { - let key_code = variant.try_child_get::<u16>(0); - let state = variant.try_child_get::<i32>(1); - match (key_code, state) { - (Ok(Some(key_code)), Ok(Some(state))) => { - MainContext::default().spawn_local(clone!( - #[strong] - sender, - async move { - if let Err(error) = - sender.send((key_code, state)).await - { - eprintln!("Channel Send error: {}", error); - } - } - )); - } - variables => { - return eprintln!("Variables don't match: {:?}", variables) - } - }; - } - ), - )); - } - ), - clone!( - #[strong] - signal_id, - move |connection, _| { - eprintln!("SwayOSD LibInput Backend isn't available, waiting..."); - match signal_id.lock() { - Ok(mut mutex) => { - if let Some(sig_id) = mutex.take() { - connection.signal_unsubscribe(sig_id); - } - } - Err(error) => println!("Mutex lock Error: {}", error), - } - } - ), - ); - - return osd_app; - } - - pub fn start(&self) -> i32 { - let s = self.clone(); - self.app.connect_activate(move |_| { - s.initialize(); - }); - - let _ = self.app.register(gio::Cancellable::NONE); - self.app.run().into() - } - - fn action_activated(osd_app: &SwayOSDApplication, arg_type: ArgTypes, value: Option<String>) { - match (arg_type, value) { - (ArgTypes::SinkVolumeRaise, step) => { - let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Raise, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SinkVolumeLower, step) => { - let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Lower, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SinkVolumeMuteToggle, _) => { - let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SourceVolumeRaise, step) => { - let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Raise, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SourceVolumeLower, step) => { - let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Lower, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SourceVolumeMuteToggle, _) => { - let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - // TODO: Brightness - (ArgTypes::BrightnessRaise, step) => { - if let Ok(mut brightness_backend) = - change_brightness(BrightnessChangeType::Raise, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_brightness(brightness_backend.as_mut()); - } - } - } - (ArgTypes::BrightnessLower, step) => { - if let Ok(mut brightness_backend) = - change_brightness(BrightnessChangeType::Lower, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_brightness(brightness_backend.as_mut()); - } - } - } - (ArgTypes::BrightnessSet, value) => { - if let Ok(mut brightness_backend) = - change_brightness(BrightnessChangeType::Set, value) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_brightness(brightness_backend.as_mut()); - } - } - } - (ArgTypes::CapsLock, value) => { - let i32_value = value.clone().unwrap_or("-1".to_owned()); - let state = match i32_value.parse::<i32>() { - Ok(value) if value >= 0 && value <= 1 => value == 1, - _ => get_key_lock_state(KeysLocks::CapsLock, value), - }; - for window in osd_app.windows.borrow().to_owned() { - window.changed_keylock(KeysLocks::CapsLock, state) - } - } - (ArgTypes::NumLock, value) => { - let i32_value = value.clone().unwrap_or("-1".to_owned()); - let state = match i32_value.parse::<i32>() { - Ok(value) if value >= 0 && value <= 1 => value == 1, - _ => get_key_lock_state(KeysLocks::NumLock, value), - }; - for window in osd_app.windows.borrow().to_owned() { - window.changed_keylock(KeysLocks::NumLock, state) - } - } - (ArgTypes::ScrollLock, value) => { - let i32_value = value.clone().unwrap_or("-1".to_owned()); - let state = match i32_value.parse::<i32>() { - Ok(value) if value >= 0 && value <= 1 => value == 1, - _ => get_key_lock_state(KeysLocks::ScrollLock, value), - }; - for window in osd_app.windows.borrow().to_owned() { - window.changed_keylock(KeysLocks::ScrollLock, state) - } - } - (ArgTypes::MaxVolume, max) => { - let volume: u8 = match max { - Some(max) => match max.parse() { - Ok(max) => max, - _ => get_default_max_volume(), - }, - _ => get_default_max_volume(), - }; - set_max_volume(volume) - } - (ArgTypes::Player, name) => { - set_player(name.unwrap_or("".to_string())) - } - (ArgTypes::Playerctl, value) => { - use crate::playerctl::*; - let value = &value.unwrap_or("".to_string()); - - let action = PlayerctlAction::from(value).unwrap(); - if let Ok(mut player) = Playerctl::new(action) { - match player.run() { - Ok(_) => { - let (icon, label) = (player.icon.unwrap(), player.label.unwrap()); - for window in osd_app.windows.borrow().to_owned() { - window.changed_player(&icon, &label) - } - }, - Err(x) => { - eprintln!("couldn't run player change: \"{:?}\"!", x) - }, - } - } else { - eprintln!("Unable to get players! are any opened?") - } - - reset_player(); - } - (ArgTypes::DeviceName, name) => { - set_device_name(name.unwrap_or(DEVICE_NAME_DEFAULT.to_string())) - } - (ArgTypes::CustomMessage, message) => { - if let Some(message) = message { - for window in osd_app.windows.borrow().to_owned() { - window.custom_message(message.as_str(), get_icon_name().as_deref()); - } - } - reset_icon_name(); - } - (ArgTypes::CustomIcon, icon) => { - set_icon_name(icon.unwrap_or(ICON_NAME_DEFAULT.to_string())) - } - (arg_type, data) => { - eprintln!( - "Failed to parse command... Type: {:?}, Data: {:?}", - arg_type, data - ) - } - }; - } - - fn initialize(&self) { - let display: gdk::Display = match gdk::Display::default() { - Some(x) => x, - _ => return, - }; - - self.init_windows(&display); - - let _self = self; - - display.connect_opened(clone!( - #[strong] - _self, - move |d| { - _self.init_windows(d); - } - )); - - display.connect_closed(clone!( - #[strong] - _self, - move |_d, is_error| { - if is_error { - eprintln!("Display closed due to errors..."); - } - _self.close_all_windows(); - } - )); - - display.monitors().connect_items_changed(clone!( - #[strong] - _self, - move |monitors, position, removed, added| { - if removed != 0 { - _self.init_windows(&display); - } else if added != 0 { - for i in 0..added { - if let Some(mon) = monitors - .item(position + i) - .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) - { - _self.add_window(&display, &mon); - } - } - } - } - )); - } - - fn add_window(&self, display: &gdk::Display, monitor: &gdk::Monitor) { - let win = SwayosdWindow::new(&self.app, display, monitor); - self.windows.borrow_mut().push(win); - } - - fn init_windows(&self, display: &gdk::Display) { - self.close_all_windows(); - - let monitors = display.monitors(); - for i in 0..monitors.n_items() { - let monitor = match monitors - .item(i) - .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) - { - Some(x) => x, - _ => continue, - }; - self.add_window(display, &monitor); - } - } - - fn close_all_windows(&self) { - self.windows.borrow_mut().retain(|window| { - window.close(); - false - }); - } + pub fn new(server_config: ServerConfig, action_receiver: Receiver<(ArgTypes, String)>) -> Self { + let app = Application::new(Some(APPLICATION_NAME), ApplicationFlags::FLAGS_NONE); + let hold = Rc::new(app.hold()); + + app.add_main_option( + "config", + Char::from(0), + OptionFlags::NONE, + OptionArg::String, + "Use a custom config file instead of looking for one.", + Some("<CONFIG FILE PATH>"), + ); + + app.add_main_option( + "style", + Char::from('s' as u8), + OptionFlags::NONE, + OptionArg::String, + "Use a custom Stylesheet file instead of looking for one", + Some("<CSS FILE PATH>"), + ); + + app.add_main_option( + "top-margin", + Char::from(0), + OptionFlags::NONE, + OptionArg::String, + &format!( + "OSD margin from top edge (0.5 would be screen center). Default is {}", + *utils::TOP_MARGIN_DEFAULT + ), + Some("<from 0.0 to 1.0>"), + ); + + let osd_app = SwayOSDApplication { + app: app.clone(), + windows: Rc::new(RefCell::new(Vec::new())), + _hold: hold, + }; + + // Apply Server Config + if let Some(margin) = server_config.top_margin { + if (0_f32..1_f32).contains(&margin) { + set_top_margin(margin); + } + } + if let Some(max_volume) = server_config.max_volume { + set_default_max_volume(max_volume); + } + if let Some(show) = server_config.show_percentage { + set_show_percentage(show); + } + + let config_cloned = server_config.clone(); + + // Parse args + app.connect_handle_local_options(clone!( + #[strong] + osd_app, + move |_app, args| { + let actions = match handle_application_args(args.to_variant()) { + (HandleLocalStatus::SUCCESS | HandleLocalStatus::CONTINUE, actions) => actions, + (status @ HandleLocalStatus::FAILURE, _) => return status as i32, + }; + for (arg_type, data) in actions { + match (arg_type, data) { + (ArgTypes::TopMargin, margin) => { + let margin: Option<f32> = margin + .and_then(|margin| margin.parse().ok()) + .and_then(|margin| { + (0_f32..1_f32).contains(&margin).then_some(margin) + }); + + if let Some(margin) = margin { + set_top_margin(margin) + } + } + (ArgTypes::MaxVolume, max) => { + let max: Option<u8> = max.and_then(|max| max.parse().ok()); + + if let Some(max) = max { + set_default_max_volume(max); + } + } + (arg_type, data) => Self::action_activated(&osd_app, &config_cloned, arg_type, data), + } + } + + HandleLocalStatus::CONTINUE as i32 + } + )); + + // Listen to any Client actions + let config_cloned = server_config.clone(); + + MainContext::default().spawn_local(clone!( + #[strong] + osd_app, + async move { + while let Ok((arg_type, data)) = action_receiver.recv().await { + Self::action_activated(&osd_app, &config_cloned, arg_type, (!data.is_empty()).then_some(data)); + } + Break + } + )); + + // Listen to the LibInput Backend and activate the Application action + let (sender, receiver) = async_channel::bounded::<(u16, i32)>(1); + MainContext::default().spawn_local(clone!( + #[strong] + osd_app, + async move { + while let Ok((key_code, state)) = receiver.recv().await { + let (arg_type, data): (ArgTypes, Option<String>) = + match evdev_rs::enums::int_to_ev_key(key_code as u32) { + Some(evdev_rs::enums::EV_KEY::KEY_CAPSLOCK) => { + (ArgTypes::CapsLock, Some(state.to_string())) + } + Some(evdev_rs::enums::EV_KEY::KEY_NUMLOCK) => { + (ArgTypes::NumLock, Some(state.to_string())) + } + Some(evdev_rs::enums::EV_KEY::KEY_SCROLLLOCK) => { + (ArgTypes::ScrollLock, Some(state.to_string())) + } + _ => continue, + }; + Self::action_activated(&osd_app, &server_config, arg_type, data); + } + Break + } + )); + // Start watching for the LibInput Backend + let signal_id: Arc<Mutex<Option<SignalSubscriptionId>>> = Arc::new(Mutex::new(None)); + gio::bus_watch_name( + BusType::System, + DBUS_BACKEND_NAME, + BusNameWatcherFlags::NONE, + clone!( + #[strong] + sender, + #[strong] + signal_id, + move |connection, _, _| { + println!("Connecting to the SwayOSD LibInput Backend"); + let mut mutex = match signal_id.lock() { + Ok(mut mutex) => match mutex.as_mut() { + Some(_) => return, + None => mutex, + }, + Err(error) => return println!("Mutex lock Error: {}", error), + }; + mutex.replace(connection.signal_subscribe( + Some(config::DBUS_BACKEND_NAME), + Some(config::DBUS_BACKEND_NAME), + Some("KeyPressed"), + Some(config::DBUS_PATH), + None, + DBusSignalFlags::NONE, + clone!( + #[strong] + sender, + move |_, _, _, _, _, variant| { + let key_code = variant.try_child_get::<u16>(0); + let state = variant.try_child_get::<i32>(1); + match (key_code, state) { + (Ok(Some(key_code)), Ok(Some(state))) => { + MainContext::default().spawn_local(clone!( + #[strong] + sender, + async move { + if let Err(error) = + sender.send((key_code, state)).await + { + eprintln!("Channel Send error: {}", error); + } + } + )); + } + variables => { + return eprintln!("Variables don't match: {:?}", variables) + } + }; + } + ), + )); + } + ), + clone!( + #[strong] + signal_id, + move |connection, _| { + eprintln!("SwayOSD LibInput Backend isn't available, waiting..."); + match signal_id.lock() { + Ok(mut mutex) => { + if let Some(sig_id) = mutex.take() { + connection.signal_unsubscribe(sig_id); + } + } + Err(error) => println!("Mutex lock Error: {}", error), + } + } + ), + ); + + return osd_app; + } + + pub fn start(&self) -> i32 { + let s = self.clone(); + self.app.connect_activate(move |_| { + s.initialize(); + }); + + let _ = self.app.register(gio::Cancellable::NONE); + self.app.run().into() + } + + fn action_activated(osd_app: &SwayOSDApplication, server_config: &ServerConfig, arg_type: ArgTypes, value: Option<String>) { + match (arg_type, value) { + (ArgTypes::SinkVolumeRaise, step) => { + let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Raise, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SinkVolumeLower, step) => { + let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Lower, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SinkVolumeMuteToggle, _) => { + let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SourceVolumeRaise, step) => { + let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Raise, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SourceVolumeLower, step) => { + let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Lower, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SourceVolumeMuteToggle, _) => { + let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + // TODO: Brightness + (ArgTypes::BrightnessRaise, step) => { + if let Ok(mut brightness_backend) = + change_brightness(BrightnessChangeType::Raise, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_brightness(brightness_backend.as_mut()); + } + } + } + (ArgTypes::BrightnessLower, step) => { + if let Ok(mut brightness_backend) = + change_brightness(BrightnessChangeType::Lower, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_brightness(brightness_backend.as_mut()); + } + } + } + (ArgTypes::BrightnessSet, value) => { + if let Ok(mut brightness_backend) = + change_brightness(BrightnessChangeType::Set, value) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_brightness(brightness_backend.as_mut()); + } + } + } + (ArgTypes::CapsLock, value) => { + let i32_value = value.clone().unwrap_or("-1".to_owned()); + let state = match i32_value.parse::<i32>() { + Ok(value) if value >= 0 && value <= 1 => value == 1, + _ => get_key_lock_state(KeysLocks::CapsLock, value), + }; + for window in osd_app.windows.borrow().to_owned() { + window.changed_keylock(KeysLocks::CapsLock, state) + } + } + (ArgTypes::NumLock, value) => { + let i32_value = value.clone().unwrap_or("-1".to_owned()); + let state = match i32_value.parse::<i32>() { + Ok(value) if value >= 0 && value <= 1 => value == 1, + _ => get_key_lock_state(KeysLocks::NumLock, value), + }; + for window in osd_app.windows.borrow().to_owned() { + window.changed_keylock(KeysLocks::NumLock, state) + } + } + (ArgTypes::ScrollLock, value) => { + let i32_value = value.clone().unwrap_or("-1".to_owned()); + let state = match i32_value.parse::<i32>() { + Ok(value) if value >= 0 && value <= 1 => value == 1, + _ => get_key_lock_state(KeysLocks::ScrollLock, value), + }; + for window in osd_app.windows.borrow().to_owned() { + window.changed_keylock(KeysLocks::ScrollLock, state) + } + } + (ArgTypes::MaxVolume, max) => { + let volume: u8 = match max { + Some(max) => match max.parse() { + Ok(max) => max, + _ => get_default_max_volume(), + }, + _ => get_default_max_volume(), + }; + set_max_volume(volume) + } + (ArgTypes::Player, name) => { + set_player(name.unwrap_or("".to_string())) + } + (ArgTypes::Playerctl, value) => { + use crate::playerctl::*; + let value = &value.unwrap_or("".to_string()); + + let action = PlayerctlAction::from(value).unwrap(); + if let Ok(mut player) = Playerctl::new(action, server_config) { + match player.run() { + Ok(_) => { + let (icon, label) = (player.icon.unwrap(), player.label.unwrap()); + for window in osd_app.windows.borrow().to_owned() { + window.changed_player(&icon, &label) + } + }, + Err(x) => { + eprintln!("couldn't run player change: \"{:?}\"!", x) + }, + } + } else { + eprintln!("Unable to get players! are any opened?") + } + + reset_player(); + } + (ArgTypes::DeviceName, name) => { + set_device_name(name.unwrap_or(DEVICE_NAME_DEFAULT.to_string())) + } + (ArgTypes::CustomMessage, message) => { + if let Some(message) = message { + for window in osd_app.windows.borrow().to_owned() { + window.custom_message(message.as_str(), get_icon_name().as_deref()); + } + } + reset_icon_name(); + } + (ArgTypes::CustomIcon, icon) => { + set_icon_name(icon.unwrap_or(ICON_NAME_DEFAULT.to_string())) + } + (arg_type, data) => { + eprintln!( + "Failed to parse command... Type: {:?}, Data: {:?}", + arg_type, data + ) + } + }; + } + + fn initialize(&self) { + let display: gdk::Display = match gdk::Display::default() { + Some(x) => x, + _ => return, + }; + + self.init_windows(&display); + + let _self = self; + + display.connect_opened(clone!( + #[strong] + _self, + move |d| { + _self.init_windows(d); + } + )); + + display.connect_closed(clone!( + #[strong] + _self, + move |_d, is_error| { + if is_error { + eprintln!("Display closed due to errors..."); + } + _self.close_all_windows(); + } + )); + + display.monitors().connect_items_changed(clone!( + #[strong] + _self, + move |monitors, position, removed, added| { + if removed != 0 { + _self.init_windows(&display); + } else if added != 0 { + for i in 0..added { + if let Some(mon) = monitors + .item(position + i) + .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) + { + _self.add_window(&display, &mon); + } + } + } + } + )); + } + + fn add_window(&self, display: &gdk::Display, monitor: &gdk::Monitor) { + let win = SwayosdWindow::new(&self.app, display, monitor); + self.windows.borrow_mut().push(win); + } + + fn init_windows(&self, display: &gdk::Display) { + self.close_all_windows(); + + let monitors = display.monitors(); + for i in 0..monitors.n_items() { + let monitor = match monitors + .item(i) + .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) + { + Some(x) => x, + _ => continue, + }; + self.add_window(display, &monitor); + } + } + + fn close_all_windows(&self) { + self.windows.borrow_mut().retain(|window| { + window.close(); + false + }); + } } diff --git a/src/server/main.rs b/src/server/main.rs index 79e8179..be4ee26 100644 --- a/src/server/main.rs +++ b/src/server/main.rs @@ -26,138 +26,138 @@ use argtypes::ArgTypes; use async_channel::Sender; use config::{DBUS_PATH, DBUS_SERVER_NAME}; use gtk::{ - gdk::Display, - gio::{self, Resource}, - glib::Bytes, - CssProvider, IconTheme, + gdk::Display, + gio::{self, Resource}, + glib::Bytes, + CssProvider, IconTheme, }; use std::env::args_os; use std::future::pending; use std::path::PathBuf; use std::str::FromStr; use utils::{get_system_css_path, user_style_path}; -use zbus::{connection, interface}; +use zbus::{interface, ConnectionBuilder}; struct DbusServer { - sender: Sender<(ArgTypes, String)>, + sender: Sender<(ArgTypes, String)>, } #[interface(name = "org.erikreider.swayosd")] impl DbusServer { - pub async fn handle_action(&self, arg_type: String, data: String) -> bool { - let arg_type = match ArgTypes::from_str(&arg_type) { - Ok(arg_type) => arg_type, - Err(other_type) => { - eprintln!("Unknown action in Dbus handle_action: {:?}", other_type); - return false; - } - }; - if let Err(error) = self.sender.send((arg_type, data)).await { - eprintln!("Channel Send error: {}", error); - return false; - } - true - } + pub async fn handle_action(&self, arg_type: String, data: String) -> bool { + let arg_type = match ArgTypes::from_str(&arg_type) { + Ok(arg_type) => arg_type, + Err(other_type) => { + eprintln!("Unknown action in Dbus handle_action: {:?}", other_type); + return false; + } + }; + if let Err(error) = self.sender.send((arg_type, data)).await { + eprintln!("Channel Send error: {}", error); + return false; + } + true + } } impl DbusServer { - async fn new(sender: Sender<(ArgTypes, String)>) -> zbus::Result<()> { - let _connection = connection::Builder::session()? - .name(DBUS_SERVER_NAME)? - .serve_at(DBUS_PATH, DbusServer { sender })? - .build() - .await?; - pending::<()>().await; - Ok(()) - } + async fn new(sender: Sender<(ArgTypes, String)>) -> zbus::Result<()> { + let _connection = ConnectionBuilder::session()? + .name(DBUS_SERVER_NAME)? + .serve_at(DBUS_PATH, DbusServer { sender })? + .build() + .await?; + pending::<()>().await; + Ok(()) + } } const GRESOURCE_BASE_PATH: &str = "/org/erikreider/swayosd"; fn main() { - if gtk::init().is_err() { - eprintln!("failed to initialize GTK Application"); - std::process::exit(1); - } - - // Load the compiled resource bundle - let resources_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/swayosd.gresource")); - let resource_data = Bytes::from(&resources_bytes[..]); - let res = Resource::from_data(&resource_data).unwrap(); - gio::resources_register(&res); - - // Load the icon theme - let theme = IconTheme::default(); - theme.add_resource_path(&format!("{}/icons", GRESOURCE_BASE_PATH)); - - // Load the CSS themes - let display = Display::default().expect("Failed getting the default screen"); - - // Load the provided default CSS theme - let provider = CssProvider::new(); - provider.connect_parsing_error(|_provider, _section, error| { - eprintln!("Could not load default CSS stylesheet: {}", error); - }); - match get_system_css_path() { - Some(path) => { - provider.load_from_path(path.to_str().unwrap()); - gtk::style_context_add_provider_for_display( - &display, - &provider, - gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, - ); - } - None => eprintln!("Could not find the system CSS file..."), - } - - // Get config path and CSS theme path from command line - let mut config_path: Option<PathBuf> = None; - let mut custom_user_css: Option<PathBuf> = None; - let mut args = args_os().into_iter(); - while let Some(arg) = args.next() { - match arg.to_str() { - Some("--config") => { - if let Some(path) = args.next() { - config_path = Some(path.into()); - } - } - Some("-s") | Some("--style") => { - if let Some(path) = args.next() { - custom_user_css = Some(path.into()); - } - } - _ => (), - } - } - - // Parse Config - let server_config = config::user::read_user_config(config_path.as_deref()) - .expect("Failed to parse config file") - .server; - - // Load style path from config if none is given on CLI - if custom_user_css.is_none() { - custom_user_css = server_config.style.clone(); - } - - // Try loading the users CSS theme - if let Some(user_config_path) = user_style_path(custom_user_css) { - let user_provider = CssProvider::new(); - user_provider.connect_parsing_error(|_provider, _section, error| { - eprintln!("Failed loading user defined style.css: {}", error); - }); - user_provider.load_from_path(&user_config_path); - gtk::style_context_add_provider_for_display( - &display, - &user_provider, - gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, - ); - println!("Loaded user defined CSS file"); - } - - let (sender, receiver) = async_channel::bounded::<(ArgTypes, String)>(1); - // Start the DBus Server - async_std::task::spawn(DbusServer::new(sender)); - // Start the GTK Application - std::process::exit(SwayOSDApplication::new(server_config, receiver).start()); + if gtk::init().is_err() { + eprintln!("failed to initialize GTK Application"); + std::process::exit(1); + } + + // Load the compiled resource bundle + let resources_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/swayosd.gresource")); + let resource_data = Bytes::from(&resources_bytes[..]); + let res = Resource::from_data(&resource_data).unwrap(); + gio::resources_register(&res); + + // Load the icon theme + let theme = IconTheme::default(); + theme.add_resource_path(&format!("{}/icons", GRESOURCE_BASE_PATH)); + + // Load the CSS themes + let display = Display::default().expect("Failed getting the default screen"); + + // Load the provided default CSS theme + let provider = CssProvider::new(); + provider.connect_parsing_error(|_provider, _section, error| { + eprintln!("Could not load default CSS stylesheet: {}", error); + }); + match get_system_css_path() { + Some(path) => { + provider.load_from_path(path.to_str().unwrap()); + gtk::style_context_add_provider_for_display( + &display, + &provider, + gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, + ); + } + None => eprintln!("Could not find the system CSS file..."), + } + + // Get config path and CSS theme path from command line + let mut config_path: Option<PathBuf> = None; + let mut custom_user_css: Option<PathBuf> = None; + let mut args = args_os().into_iter(); + while let Some(arg) = args.next() { + match arg.to_str() { + Some("--config") => { + if let Some(path) = args.next() { + config_path = Some(path.into()); + } + } + Some("-s") | Some("--style") => { + if let Some(path) = args.next() { + custom_user_css = Some(path.into()); + } + } + _ => (), + } + } + + // Parse Config + let server_config = config::user::read_user_config(config_path.as_deref()) + .expect("Failed to parse config file") + .server; + + // Load style path from config if none is given on CLI + if custom_user_css.is_none() { + custom_user_css = server_config.style.clone(); + } + + // Try loading the users CSS theme + if let Some(user_config_path) = user_style_path(custom_user_css) { + let user_provider = CssProvider::new(); + user_provider.connect_parsing_error(|_provider, _section, error| { + eprintln!("Failed loading user defined style.css: {}", error); + }); + user_provider.load_from_path(&user_config_path); + gtk::style_context_add_provider_for_display( + &display, + &user_provider, + gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, + ); + println!("Loaded user defined CSS file"); + } + + let (sender, receiver) = async_channel::bounded::<(ArgTypes, String)>(1); + // Start the DBus Server + async_std::task::spawn(DbusServer::new(sender)); + // Start the GTK Application + std::process::exit(SwayOSDApplication::new(server_config, receiver).start()); } From 0b084e0cd5fbca4fb1c707dd4a530d439cfd2783 Mon Sep 17 00:00:00 2001 From: Jan Luca <janluca.oster@proton.me> Date: Thu, 9 Jan 2025 15:27:06 +0100 Subject: [PATCH 06/18] cargo fmt --- src/config/user.rs | 60 +-- src/global_utils.rs | 7 +- src/mpris-backend/mod.rs | 323 ++++++------ src/server/application.rs | 1004 +++++++++++++++++++------------------ src/server/main.rs | 226 ++++----- src/server/osd_window.rs | 550 ++++++++++---------- 6 files changed, 1106 insertions(+), 1064 deletions(-) diff --git a/src/config/user.rs b/src/config/user.rs index e406683..4b88bba 100644 --- a/src/config/user.rs +++ b/src/config/user.rs @@ -12,45 +12,45 @@ pub struct ClientConfig {} #[derive(Deserialize, Default, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct ServerConfig { - pub style: Option<PathBuf>, - pub top_margin: Option<f32>, - pub max_volume: Option<u8>, - pub show_percentage: Option<bool>, - pub playerctl_format: Option<String>, + pub style: Option<PathBuf>, + pub top_margin: Option<f32>, + pub max_volume: Option<u8>, + pub show_percentage: Option<bool>, + pub playerctl_format: Option<String>, } #[derive(Deserialize, Default, Debug)] #[serde(deny_unknown_fields)] pub struct UserConfig { - #[serde(default)] - pub server: ServerConfig, - #[serde(default)] - pub client: ClientConfig, + #[serde(default)] + pub server: ServerConfig, + #[serde(default)] + pub client: ClientConfig, } fn find_user_config() -> Option<PathBuf> { - let path = user_config_dir().join("swayosd").join("config.toml"); - if path.exists() { - return Some(path); - } - - for path in system_config_dirs() { - let path = path.join("swayosd").join("config.toml"); - if path.exists() { - return Some(path); - } - } - - None + let path = user_config_dir().join("swayosd").join("config.toml"); + if path.exists() { + return Some(path); + } + + for path in system_config_dirs() { + let path = path.join("swayosd").join("config.toml"); + if path.exists() { + return Some(path); + } + } + + None } pub fn read_user_config(path: Option<&Path>) -> Result<UserConfig, Box<dyn Error>> { - let path = match path.map(Path::to_owned).or_else(find_user_config) { - Some(path) => path, - None => return Ok(Default::default()), - }; - - let config_file = std::fs::read_to_string(path)?; - let config: UserConfig = toml::from_str(&config_file)?; - Ok(config) + let path = match path.map(Path::to_owned).or_else(find_user_config) { + Some(path) => path, + None => return Ok(Default::default()), + }; + + let config_file = std::fs::read_to_string(path)?; + let config: UserConfig = toml::from_str(&config_file)?; + Ok(config) } diff --git a/src/global_utils.rs b/src/global_utils.rs index ed3413b..c8a7035 100644 --- a/src/global_utils.rs +++ b/src/global_utils.rs @@ -98,15 +98,16 @@ pub(crate) fn handle_application_args( "playerctl" => { let value = child.value().str().unwrap_or(""); match value { - "play-pause" | "play" | "pause" | "next" |"prev" | "previous" | "shuffle" | "stop" => (), + "play-pause" | "play" | "pause" | "next" | "prev" | "previous" | "shuffle" + | "stop" => (), x => { eprintln!("Unknown Playerctl command: \"{}\"!...", x); - return (HandleLocalStatus::FAILURE, actions) + return (HandleLocalStatus::FAILURE, actions); } } (ArgTypes::Playerctl, Some(value.to_string())) - }, + } "device" => { let value = match child.value().str() { Some(v) => v.to_string(), diff --git a/src/mpris-backend/mod.rs b/src/mpris-backend/mod.rs index 0941cc8..6da91e8 100644 --- a/src/mpris-backend/mod.rs +++ b/src/mpris-backend/mod.rs @@ -1,177 +1,208 @@ -use mpris::{ - PlayerFinder, - Player, - PlaybackStatus, -}; +use mpris::{PlaybackStatus, Player, PlayerFinder}; -use std::error::Error; use crate::utils::get_player; +use std::error::Error; pub enum PlayerctlAction { - PlayPause, - Play, - Pause, - Stop, - Next, - Prev, - Shuffle, + PlayPause, + Play, + Pause, + Stop, + Next, + Prev, + Shuffle, } use super::config::user::ServerConfig; #[derive(Clone, Debug)] pub enum PlayerctlDeviceRaw { - None, - All, - Some(String), + None, + All, + Some(String), } pub enum PlayerctlDevice { - All(Vec<Player>), - Some(Player), + All(Vec<Player>), + Some(Player), } pub struct Playerctl { - player: PlayerctlDevice, - action: PlayerctlAction, - pub icon: Option<String>, - pub label: Option<String>, - fmt_str: Option<String>, + player: PlayerctlDevice, + action: PlayerctlAction, + pub icon: Option<String>, + pub label: Option<String>, + fmt_str: Option<String>, } impl Playerctl { - pub fn new(action: PlayerctlAction, config: &ServerConfig) -> Result<Playerctl, Box<dyn Error>> { - let playerfinder = PlayerFinder::new()?; - let player = get_player(); - let player = match player { - PlayerctlDeviceRaw::None => PlayerctlDevice::Some(playerfinder.find_active()?), - PlayerctlDeviceRaw::Some(name) => { - PlayerctlDevice::Some(playerfinder.find_by_name(name.as_str())?) - }, - PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), - }; - let fmt_str = config.playerctl_format.clone(); - Ok(Self { - player, - action, - icon: None, - label: None, - fmt_str, - }) - } - pub fn run(&mut self) -> Result<(), Box<dyn Error>> { - use PlayerctlAction::*; - use PlaybackStatus::*; - let run_single = |player: &Player| -> Result<&str, Box<dyn Error>> { - let out = match self.action { - PlayPause => { - match player.get_playback_status()? { - Playing => {player.pause()?; "pause-large -symbolic"}, - Paused | Stopped => {player.play()?;"play-large-symbolic"} - } - }, - Shuffle => { - let shuffle = player.get_shuffle()?; - player.set_shuffle(!shuffle)?; - if shuffle { - "playlist-consecutive-symbolic" - } else { - "playlist-shuffle-symbolic" - } - }, - Play => {player.play()?; "play-large-symbolic"}, - Pause => {player.pause()?; "pause-large-symbolic"}, - Stop => {player.stop()?; "stop-large-symbolic"}, - Next => {player.next()?; "media-seek-forward-symbolic"}, - Prev => {player.previous()?; "media-seek-backward-symbolic"}, - }; - Ok(out) - }; - let mut metadata = Err("some errro"); - let icon = match &self.player { - PlayerctlDevice::Some(player) => { - metadata = player.get_metadata().or_else(|_| Err("")); - run_single(player)? - }, - PlayerctlDevice::All(players) => { - let mut icon = Err("couldn't change any players!"); - for player in players { - let icon_new = run_single(player); - if let Ok(icon_new) = icon_new { - if icon.is_err() { - icon = Ok(icon_new); - } - }; - if let Err(_) = metadata { - metadata = player.get_metadata().or_else(|_| Err("")); - } - } - icon? - }, - }; + pub fn new( + action: PlayerctlAction, + config: &ServerConfig, + ) -> Result<Playerctl, Box<dyn Error>> { + let playerfinder = PlayerFinder::new()?; + let player = get_player(); + let player = match player { + PlayerctlDeviceRaw::None => PlayerctlDevice::Some(playerfinder.find_active()?), + PlayerctlDeviceRaw::Some(name) => { + PlayerctlDevice::Some(playerfinder.find_by_name(name.as_str())?) + } + PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), + }; + let fmt_str = config.playerctl_format.clone(); + Ok(Self { + player, + action, + icon: None, + label: None, + fmt_str, + }) + } + pub fn run(&mut self) -> Result<(), Box<dyn Error>> { + use PlaybackStatus::*; + use PlayerctlAction::*; + let run_single = |player: &Player| -> Result<&str, Box<dyn Error>> { + let out = match self.action { + PlayPause => match player.get_playback_status()? { + Playing => { + player.pause()?; + "pause-large -symbolic" + } + Paused | Stopped => { + player.play()?; + "play-large-symbolic" + } + }, + Shuffle => { + let shuffle = player.get_shuffle()?; + player.set_shuffle(!shuffle)?; + if shuffle { + "playlist-consecutive-symbolic" + } else { + "playlist-shuffle-symbolic" + } + } + Play => { + player.play()?; + "play-large-symbolic" + } + Pause => { + player.pause()?; + "pause-large-symbolic" + } + Stop => { + player.stop()?; + "stop-large-symbolic" + } + Next => { + player.next()?; + "media-seek-forward-symbolic" + } + Prev => { + player.previous()?; + "media-seek-backward-symbolic" + } + }; + Ok(out) + }; + let mut metadata = Err("some errro"); + let icon = match &self.player { + PlayerctlDevice::Some(player) => { + metadata = player.get_metadata().or_else(|_| Err("")); + run_single(player)? + } + PlayerctlDevice::All(players) => { + let mut icon = Err("couldn't change any players!"); + for player in players { + let icon_new = run_single(player); + if let Ok(icon_new) = icon_new { + if icon.is_err() { + icon = Ok(icon_new); + } + }; + if let Err(_) = metadata { + metadata = player.get_metadata().or_else(|_| Err("")); + } + } + icon? + } + }; - self.icon = Some(icon.to_string()); - let label = if let Ok(metadata) = metadata { - Some(self.fmt_string(metadata)) - } else { - None - }; - self.label = label; - Ok(()) - } - fn fmt_string(&self, metadata: mpris::Metadata) -> String { - use strfmt::{strfmt, Format}; - use std::collections::HashMap; + self.icon = Some(icon.to_string()); + let label = if let Ok(metadata) = metadata { + Some(self.fmt_string(metadata)) + } else { + None + }; + self.label = label; + Ok(()) + } + fn fmt_string(&self, metadata: mpris::Metadata) -> String { + use std::collections::HashMap; + use strfmt::{strfmt, Format}; - let mut vars = HashMap::new(); - let artists = metadata.artists().unwrap_or(vec![""]); - let artists_album = metadata.album_artists().unwrap_or(vec![""]); - let artist = artists.get(0).map_or("", |v| v); - let artist_album = artists_album.get(0).map_or("", |v| v); + let mut vars = HashMap::new(); + let artists = metadata.artists().unwrap_or(vec![""]); + let artists_album = metadata.album_artists().unwrap_or(vec![""]); + let artist = artists.get(0).map_or("", |v| v); + let artist_album = artists_album.get(0).map_or("", |v| v); - let title = metadata.title().unwrap_or(""); - let album = metadata.album_name().unwrap_or(""); - let track_num = metadata.track_number().and_then(|x| Some(x.to_string())).unwrap_or(String::new()); - let disc_num = metadata.disc_number().and_then(|x| Some(x.to_string())).unwrap_or(String::new()); - let autorating = metadata.auto_rating().and_then(|x| Some(x.to_string())).unwrap_or(String::new()); + let title = metadata.title().unwrap_or(""); + let album = metadata.album_name().unwrap_or(""); + let track_num = metadata + .track_number() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); + let disc_num = metadata + .disc_number() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); + let autorating = metadata + .auto_rating() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); - vars.insert("artist".to_string(), artist); - vars.insert("albumArtist".to_string(), artist_album); - vars.insert("title".to_string(), title); - vars.insert("trackNumber".to_string(), &track_num); - vars.insert("discNumber".to_string(), &disc_num); - vars.insert("autoRating".to_string(), &autorating); + vars.insert("artist".to_string(), artist); + vars.insert("albumArtist".to_string(), artist_album); + vars.insert("title".to_string(), title); + vars.insert("trackNumber".to_string(), &track_num); + vars.insert("discNumber".to_string(), &disc_num); + vars.insert("autoRating".to_string(), &autorating); - self.fmt_str.clone().unwrap_or("{artist} - {title}".into()).format(&vars).unwrap_or_else(|e| { - eprintln!("error: {}. using default string", e); - "{artist} - {title}".format(&vars).unwrap() - }) - } + self.fmt_str + .clone() + .unwrap_or("{artist} - {title}".into()) + .format(&vars) + .unwrap_or_else(|e| { + eprintln!("error: {}. using default string", e); + "{artist} - {title}".format(&vars).unwrap() + }) + } } impl PlayerctlAction { - pub fn from(action: &str) -> Result<Self, String> { - use PlayerctlAction::*; - match action { - "play-pause" => Ok(PlayPause), - "play" => Ok(Play), - "pause" => Ok(Pause), - "stop" => Ok(Stop), - "next" => Ok(Next), - "prev" | "previous" => Ok(Prev), - "shuffle" => Ok(Shuffle), - x => Err(x.to_string()) - } - } + pub fn from(action: &str) -> Result<Self, String> { + use PlayerctlAction::*; + match action { + "play-pause" => Ok(PlayPause), + "play" => Ok(Play), + "pause" => Ok(Pause), + "stop" => Ok(Stop), + "next" => Ok(Next), + "prev" | "previous" => Ok(Prev), + "shuffle" => Ok(Shuffle), + x => Err(x.to_string()), + } + } } impl PlayerctlDeviceRaw { - pub fn from(player: String) -> Result<Self, ()> { - use PlayerctlDeviceRaw::*; - match player.as_str() { - "auto" | "" => Ok(None), - "all" => Ok(All), - _ => Ok(Some(player)) - } - } + pub fn from(player: String) -> Result<Self, ()> { + use PlayerctlDeviceRaw::*; + match player.as_str() { + "auto" | "" => Ok(None), + "all" => Ok(All), + _ => Ok(Some(player)), + } + } } diff --git a/src/server/application.rs b/src/server/application.rs index e644a7d..76b2a89 100644 --- a/src/server/application.rs +++ b/src/server/application.rs @@ -5,15 +5,15 @@ use crate::osd_window::SwayosdWindow; use crate::utils::{self, *}; use async_channel::Receiver; use gtk::{ - gdk, - gio::{ - self, ApplicationFlags, BusNameWatcherFlags, BusType, DBusSignalFlags, SignalSubscriptionId, - }, - glib::{ - clone, variant::ToVariant, Char, ControlFlow::Break, MainContext, OptionArg, OptionFlags, - }, - prelude::*, - Application, + gdk, + gio::{ + self, ApplicationFlags, BusNameWatcherFlags, BusType, DBusSignalFlags, SignalSubscriptionId, + }, + glib::{ + clone, variant::ToVariant, Char, ControlFlow::Break, MainContext, OptionArg, OptionFlags, + }, + prelude::*, + Application, }; use pulsectl::controllers::{SinkController, SourceController}; use std::cell::RefCell; @@ -24,495 +24,505 @@ use super::config::user::ServerConfig; #[derive(Clone, Shrinkwrap)] pub struct SwayOSDApplication { - #[shrinkwrap(main_field)] - app: gtk::Application, - windows: Rc<RefCell<Vec<SwayosdWindow>>>, - _hold: Rc<gio::ApplicationHoldGuard>, + #[shrinkwrap(main_field)] + app: gtk::Application, + windows: Rc<RefCell<Vec<SwayosdWindow>>>, + _hold: Rc<gio::ApplicationHoldGuard>, } impl SwayOSDApplication { - pub fn new(server_config: ServerConfig, action_receiver: Receiver<(ArgTypes, String)>) -> Self { - let app = Application::new(Some(APPLICATION_NAME), ApplicationFlags::FLAGS_NONE); - let hold = Rc::new(app.hold()); - - app.add_main_option( - "config", - Char::from(0), - OptionFlags::NONE, - OptionArg::String, - "Use a custom config file instead of looking for one.", - Some("<CONFIG FILE PATH>"), - ); - - app.add_main_option( - "style", - Char::from('s' as u8), - OptionFlags::NONE, - OptionArg::String, - "Use a custom Stylesheet file instead of looking for one", - Some("<CSS FILE PATH>"), - ); - - app.add_main_option( - "top-margin", - Char::from(0), - OptionFlags::NONE, - OptionArg::String, - &format!( - "OSD margin from top edge (0.5 would be screen center). Default is {}", - *utils::TOP_MARGIN_DEFAULT - ), - Some("<from 0.0 to 1.0>"), - ); - - let osd_app = SwayOSDApplication { - app: app.clone(), - windows: Rc::new(RefCell::new(Vec::new())), - _hold: hold, - }; - - // Apply Server Config - if let Some(margin) = server_config.top_margin { - if (0_f32..1_f32).contains(&margin) { - set_top_margin(margin); - } - } - if let Some(max_volume) = server_config.max_volume { - set_default_max_volume(max_volume); - } - if let Some(show) = server_config.show_percentage { - set_show_percentage(show); - } - - let config_cloned = server_config.clone(); - - // Parse args - app.connect_handle_local_options(clone!( - #[strong] - osd_app, - move |_app, args| { - let actions = match handle_application_args(args.to_variant()) { - (HandleLocalStatus::SUCCESS | HandleLocalStatus::CONTINUE, actions) => actions, - (status @ HandleLocalStatus::FAILURE, _) => return status as i32, - }; - for (arg_type, data) in actions { - match (arg_type, data) { - (ArgTypes::TopMargin, margin) => { - let margin: Option<f32> = margin - .and_then(|margin| margin.parse().ok()) - .and_then(|margin| { - (0_f32..1_f32).contains(&margin).then_some(margin) - }); - - if let Some(margin) = margin { - set_top_margin(margin) - } - } - (ArgTypes::MaxVolume, max) => { - let max: Option<u8> = max.and_then(|max| max.parse().ok()); - - if let Some(max) = max { - set_default_max_volume(max); - } - } - (arg_type, data) => Self::action_activated(&osd_app, &config_cloned, arg_type, data), - } - } - - HandleLocalStatus::CONTINUE as i32 - } - )); - - // Listen to any Client actions - let config_cloned = server_config.clone(); - - MainContext::default().spawn_local(clone!( - #[strong] - osd_app, - async move { - while let Ok((arg_type, data)) = action_receiver.recv().await { - Self::action_activated(&osd_app, &config_cloned, arg_type, (!data.is_empty()).then_some(data)); - } - Break - } - )); - - // Listen to the LibInput Backend and activate the Application action - let (sender, receiver) = async_channel::bounded::<(u16, i32)>(1); - MainContext::default().spawn_local(clone!( - #[strong] - osd_app, - async move { - while let Ok((key_code, state)) = receiver.recv().await { - let (arg_type, data): (ArgTypes, Option<String>) = - match evdev_rs::enums::int_to_ev_key(key_code as u32) { - Some(evdev_rs::enums::EV_KEY::KEY_CAPSLOCK) => { - (ArgTypes::CapsLock, Some(state.to_string())) - } - Some(evdev_rs::enums::EV_KEY::KEY_NUMLOCK) => { - (ArgTypes::NumLock, Some(state.to_string())) - } - Some(evdev_rs::enums::EV_KEY::KEY_SCROLLLOCK) => { - (ArgTypes::ScrollLock, Some(state.to_string())) - } - _ => continue, - }; - Self::action_activated(&osd_app, &server_config, arg_type, data); - } - Break - } - )); - // Start watching for the LibInput Backend - let signal_id: Arc<Mutex<Option<SignalSubscriptionId>>> = Arc::new(Mutex::new(None)); - gio::bus_watch_name( - BusType::System, - DBUS_BACKEND_NAME, - BusNameWatcherFlags::NONE, - clone!( - #[strong] - sender, - #[strong] - signal_id, - move |connection, _, _| { - println!("Connecting to the SwayOSD LibInput Backend"); - let mut mutex = match signal_id.lock() { - Ok(mut mutex) => match mutex.as_mut() { - Some(_) => return, - None => mutex, - }, - Err(error) => return println!("Mutex lock Error: {}", error), - }; - mutex.replace(connection.signal_subscribe( - Some(config::DBUS_BACKEND_NAME), - Some(config::DBUS_BACKEND_NAME), - Some("KeyPressed"), - Some(config::DBUS_PATH), - None, - DBusSignalFlags::NONE, - clone!( - #[strong] - sender, - move |_, _, _, _, _, variant| { - let key_code = variant.try_child_get::<u16>(0); - let state = variant.try_child_get::<i32>(1); - match (key_code, state) { - (Ok(Some(key_code)), Ok(Some(state))) => { - MainContext::default().spawn_local(clone!( - #[strong] - sender, - async move { - if let Err(error) = - sender.send((key_code, state)).await - { - eprintln!("Channel Send error: {}", error); - } - } - )); - } - variables => { - return eprintln!("Variables don't match: {:?}", variables) - } - }; - } - ), - )); - } - ), - clone!( - #[strong] - signal_id, - move |connection, _| { - eprintln!("SwayOSD LibInput Backend isn't available, waiting..."); - match signal_id.lock() { - Ok(mut mutex) => { - if let Some(sig_id) = mutex.take() { - connection.signal_unsubscribe(sig_id); - } - } - Err(error) => println!("Mutex lock Error: {}", error), - } - } - ), - ); - - return osd_app; - } - - pub fn start(&self) -> i32 { - let s = self.clone(); - self.app.connect_activate(move |_| { - s.initialize(); - }); - - let _ = self.app.register(gio::Cancellable::NONE); - self.app.run().into() - } - - fn action_activated(osd_app: &SwayOSDApplication, server_config: &ServerConfig, arg_type: ArgTypes, value: Option<String>) { - match (arg_type, value) { - (ArgTypes::SinkVolumeRaise, step) => { - let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Raise, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SinkVolumeLower, step) => { - let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Lower, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SinkVolumeMuteToggle, _) => { - let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SourceVolumeRaise, step) => { - let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Raise, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SourceVolumeLower, step) => { - let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Lower, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SourceVolumeMuteToggle, _) => { - let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - // TODO: Brightness - (ArgTypes::BrightnessRaise, step) => { - if let Ok(mut brightness_backend) = - change_brightness(BrightnessChangeType::Raise, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_brightness(brightness_backend.as_mut()); - } - } - } - (ArgTypes::BrightnessLower, step) => { - if let Ok(mut brightness_backend) = - change_brightness(BrightnessChangeType::Lower, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_brightness(brightness_backend.as_mut()); - } - } - } - (ArgTypes::BrightnessSet, value) => { - if let Ok(mut brightness_backend) = - change_brightness(BrightnessChangeType::Set, value) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_brightness(brightness_backend.as_mut()); - } - } - } - (ArgTypes::CapsLock, value) => { - let i32_value = value.clone().unwrap_or("-1".to_owned()); - let state = match i32_value.parse::<i32>() { - Ok(value) if value >= 0 && value <= 1 => value == 1, - _ => get_key_lock_state(KeysLocks::CapsLock, value), - }; - for window in osd_app.windows.borrow().to_owned() { - window.changed_keylock(KeysLocks::CapsLock, state) - } - } - (ArgTypes::NumLock, value) => { - let i32_value = value.clone().unwrap_or("-1".to_owned()); - let state = match i32_value.parse::<i32>() { - Ok(value) if value >= 0 && value <= 1 => value == 1, - _ => get_key_lock_state(KeysLocks::NumLock, value), - }; - for window in osd_app.windows.borrow().to_owned() { - window.changed_keylock(KeysLocks::NumLock, state) - } - } - (ArgTypes::ScrollLock, value) => { - let i32_value = value.clone().unwrap_or("-1".to_owned()); - let state = match i32_value.parse::<i32>() { - Ok(value) if value >= 0 && value <= 1 => value == 1, - _ => get_key_lock_state(KeysLocks::ScrollLock, value), - }; - for window in osd_app.windows.borrow().to_owned() { - window.changed_keylock(KeysLocks::ScrollLock, state) - } - } - (ArgTypes::MaxVolume, max) => { - let volume: u8 = match max { - Some(max) => match max.parse() { - Ok(max) => max, - _ => get_default_max_volume(), - }, - _ => get_default_max_volume(), - }; - set_max_volume(volume) - } - (ArgTypes::Player, name) => { - set_player(name.unwrap_or("".to_string())) - } - (ArgTypes::Playerctl, value) => { - use crate::playerctl::*; - let value = &value.unwrap_or("".to_string()); - - let action = PlayerctlAction::from(value).unwrap(); - if let Ok(mut player) = Playerctl::new(action, server_config) { - match player.run() { - Ok(_) => { - let (icon, label) = (player.icon.unwrap(), player.label.unwrap()); - for window in osd_app.windows.borrow().to_owned() { - window.changed_player(&icon, &label) - } - }, - Err(x) => { - eprintln!("couldn't run player change: \"{:?}\"!", x) - }, - } - } else { - eprintln!("Unable to get players! are any opened?") - } - - reset_player(); - } - (ArgTypes::DeviceName, name) => { - set_device_name(name.unwrap_or(DEVICE_NAME_DEFAULT.to_string())) - } - (ArgTypes::CustomMessage, message) => { - if let Some(message) = message { - for window in osd_app.windows.borrow().to_owned() { - window.custom_message(message.as_str(), get_icon_name().as_deref()); - } - } - reset_icon_name(); - } - (ArgTypes::CustomIcon, icon) => { - set_icon_name(icon.unwrap_or(ICON_NAME_DEFAULT.to_string())) - } - (arg_type, data) => { - eprintln!( - "Failed to parse command... Type: {:?}, Data: {:?}", - arg_type, data - ) - } - }; - } - - fn initialize(&self) { - let display: gdk::Display = match gdk::Display::default() { - Some(x) => x, - _ => return, - }; - - self.init_windows(&display); - - let _self = self; - - display.connect_opened(clone!( - #[strong] - _self, - move |d| { - _self.init_windows(d); - } - )); - - display.connect_closed(clone!( - #[strong] - _self, - move |_d, is_error| { - if is_error { - eprintln!("Display closed due to errors..."); - } - _self.close_all_windows(); - } - )); - - display.monitors().connect_items_changed(clone!( - #[strong] - _self, - move |monitors, position, removed, added| { - if removed != 0 { - _self.init_windows(&display); - } else if added != 0 { - for i in 0..added { - if let Some(mon) = monitors - .item(position + i) - .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) - { - _self.add_window(&display, &mon); - } - } - } - } - )); - } - - fn add_window(&self, display: &gdk::Display, monitor: &gdk::Monitor) { - let win = SwayosdWindow::new(&self.app, display, monitor); - self.windows.borrow_mut().push(win); - } - - fn init_windows(&self, display: &gdk::Display) { - self.close_all_windows(); - - let monitors = display.monitors(); - for i in 0..monitors.n_items() { - let monitor = match monitors - .item(i) - .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) - { - Some(x) => x, - _ => continue, - }; - self.add_window(display, &monitor); - } - } - - fn close_all_windows(&self) { - self.windows.borrow_mut().retain(|window| { - window.close(); - false - }); - } + pub fn new(server_config: ServerConfig, action_receiver: Receiver<(ArgTypes, String)>) -> Self { + let app = Application::new(Some(APPLICATION_NAME), ApplicationFlags::FLAGS_NONE); + let hold = Rc::new(app.hold()); + + app.add_main_option( + "config", + Char::from(0), + OptionFlags::NONE, + OptionArg::String, + "Use a custom config file instead of looking for one.", + Some("<CONFIG FILE PATH>"), + ); + + app.add_main_option( + "style", + Char::from('s' as u8), + OptionFlags::NONE, + OptionArg::String, + "Use a custom Stylesheet file instead of looking for one", + Some("<CSS FILE PATH>"), + ); + + app.add_main_option( + "top-margin", + Char::from(0), + OptionFlags::NONE, + OptionArg::String, + &format!( + "OSD margin from top edge (0.5 would be screen center). Default is {}", + *utils::TOP_MARGIN_DEFAULT + ), + Some("<from 0.0 to 1.0>"), + ); + + let osd_app = SwayOSDApplication { + app: app.clone(), + windows: Rc::new(RefCell::new(Vec::new())), + _hold: hold, + }; + + // Apply Server Config + if let Some(margin) = server_config.top_margin { + if (0_f32..1_f32).contains(&margin) { + set_top_margin(margin); + } + } + if let Some(max_volume) = server_config.max_volume { + set_default_max_volume(max_volume); + } + if let Some(show) = server_config.show_percentage { + set_show_percentage(show); + } + + let config_cloned = server_config.clone(); + + // Parse args + app.connect_handle_local_options(clone!( + #[strong] + osd_app, + move |_app, args| { + let actions = match handle_application_args(args.to_variant()) { + (HandleLocalStatus::SUCCESS | HandleLocalStatus::CONTINUE, actions) => actions, + (status @ HandleLocalStatus::FAILURE, _) => return status as i32, + }; + for (arg_type, data) in actions { + match (arg_type, data) { + (ArgTypes::TopMargin, margin) => { + let margin: Option<f32> = margin + .and_then(|margin| margin.parse().ok()) + .and_then(|margin| { + (0_f32..1_f32).contains(&margin).then_some(margin) + }); + + if let Some(margin) = margin { + set_top_margin(margin) + } + } + (ArgTypes::MaxVolume, max) => { + let max: Option<u8> = max.and_then(|max| max.parse().ok()); + + if let Some(max) = max { + set_default_max_volume(max); + } + } + (arg_type, data) => { + Self::action_activated(&osd_app, &config_cloned, arg_type, data) + } + } + } + + HandleLocalStatus::CONTINUE as i32 + } + )); + + // Listen to any Client actions + let config_cloned = server_config.clone(); + + MainContext::default().spawn_local(clone!( + #[strong] + osd_app, + async move { + while let Ok((arg_type, data)) = action_receiver.recv().await { + Self::action_activated( + &osd_app, + &config_cloned, + arg_type, + (!data.is_empty()).then_some(data), + ); + } + Break + } + )); + + // Listen to the LibInput Backend and activate the Application action + let (sender, receiver) = async_channel::bounded::<(u16, i32)>(1); + MainContext::default().spawn_local(clone!( + #[strong] + osd_app, + async move { + while let Ok((key_code, state)) = receiver.recv().await { + let (arg_type, data): (ArgTypes, Option<String>) = + match evdev_rs::enums::int_to_ev_key(key_code as u32) { + Some(evdev_rs::enums::EV_KEY::KEY_CAPSLOCK) => { + (ArgTypes::CapsLock, Some(state.to_string())) + } + Some(evdev_rs::enums::EV_KEY::KEY_NUMLOCK) => { + (ArgTypes::NumLock, Some(state.to_string())) + } + Some(evdev_rs::enums::EV_KEY::KEY_SCROLLLOCK) => { + (ArgTypes::ScrollLock, Some(state.to_string())) + } + _ => continue, + }; + Self::action_activated(&osd_app, &server_config, arg_type, data); + } + Break + } + )); + // Start watching for the LibInput Backend + let signal_id: Arc<Mutex<Option<SignalSubscriptionId>>> = Arc::new(Mutex::new(None)); + gio::bus_watch_name( + BusType::System, + DBUS_BACKEND_NAME, + BusNameWatcherFlags::NONE, + clone!( + #[strong] + sender, + #[strong] + signal_id, + move |connection, _, _| { + println!("Connecting to the SwayOSD LibInput Backend"); + let mut mutex = match signal_id.lock() { + Ok(mut mutex) => match mutex.as_mut() { + Some(_) => return, + None => mutex, + }, + Err(error) => return println!("Mutex lock Error: {}", error), + }; + mutex.replace(connection.signal_subscribe( + Some(config::DBUS_BACKEND_NAME), + Some(config::DBUS_BACKEND_NAME), + Some("KeyPressed"), + Some(config::DBUS_PATH), + None, + DBusSignalFlags::NONE, + clone!( + #[strong] + sender, + move |_, _, _, _, _, variant| { + let key_code = variant.try_child_get::<u16>(0); + let state = variant.try_child_get::<i32>(1); + match (key_code, state) { + (Ok(Some(key_code)), Ok(Some(state))) => { + MainContext::default().spawn_local(clone!( + #[strong] + sender, + async move { + if let Err(error) = + sender.send((key_code, state)).await + { + eprintln!("Channel Send error: {}", error); + } + } + )); + } + variables => { + return eprintln!("Variables don't match: {:?}", variables) + } + }; + } + ), + )); + } + ), + clone!( + #[strong] + signal_id, + move |connection, _| { + eprintln!("SwayOSD LibInput Backend isn't available, waiting..."); + match signal_id.lock() { + Ok(mut mutex) => { + if let Some(sig_id) = mutex.take() { + connection.signal_unsubscribe(sig_id); + } + } + Err(error) => println!("Mutex lock Error: {}", error), + } + } + ), + ); + + return osd_app; + } + + pub fn start(&self) -> i32 { + let s = self.clone(); + self.app.connect_activate(move |_| { + s.initialize(); + }); + + let _ = self.app.register(gio::Cancellable::NONE); + self.app.run().into() + } + + fn action_activated( + osd_app: &SwayOSDApplication, + server_config: &ServerConfig, + arg_type: ArgTypes, + value: Option<String>, + ) { + match (arg_type, value) { + (ArgTypes::SinkVolumeRaise, step) => { + let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Raise, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SinkVolumeLower, step) => { + let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Lower, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SinkVolumeMuteToggle, _) => { + let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SourceVolumeRaise, step) => { + let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Raise, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SourceVolumeLower, step) => { + let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Lower, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SourceVolumeMuteToggle, _) => { + let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + // TODO: Brightness + (ArgTypes::BrightnessRaise, step) => { + if let Ok(mut brightness_backend) = + change_brightness(BrightnessChangeType::Raise, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_brightness(brightness_backend.as_mut()); + } + } + } + (ArgTypes::BrightnessLower, step) => { + if let Ok(mut brightness_backend) = + change_brightness(BrightnessChangeType::Lower, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_brightness(brightness_backend.as_mut()); + } + } + } + (ArgTypes::BrightnessSet, value) => { + if let Ok(mut brightness_backend) = + change_brightness(BrightnessChangeType::Set, value) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_brightness(brightness_backend.as_mut()); + } + } + } + (ArgTypes::CapsLock, value) => { + let i32_value = value.clone().unwrap_or("-1".to_owned()); + let state = match i32_value.parse::<i32>() { + Ok(value) if value >= 0 && value <= 1 => value == 1, + _ => get_key_lock_state(KeysLocks::CapsLock, value), + }; + for window in osd_app.windows.borrow().to_owned() { + window.changed_keylock(KeysLocks::CapsLock, state) + } + } + (ArgTypes::NumLock, value) => { + let i32_value = value.clone().unwrap_or("-1".to_owned()); + let state = match i32_value.parse::<i32>() { + Ok(value) if value >= 0 && value <= 1 => value == 1, + _ => get_key_lock_state(KeysLocks::NumLock, value), + }; + for window in osd_app.windows.borrow().to_owned() { + window.changed_keylock(KeysLocks::NumLock, state) + } + } + (ArgTypes::ScrollLock, value) => { + let i32_value = value.clone().unwrap_or("-1".to_owned()); + let state = match i32_value.parse::<i32>() { + Ok(value) if value >= 0 && value <= 1 => value == 1, + _ => get_key_lock_state(KeysLocks::ScrollLock, value), + }; + for window in osd_app.windows.borrow().to_owned() { + window.changed_keylock(KeysLocks::ScrollLock, state) + } + } + (ArgTypes::MaxVolume, max) => { + let volume: u8 = match max { + Some(max) => match max.parse() { + Ok(max) => max, + _ => get_default_max_volume(), + }, + _ => get_default_max_volume(), + }; + set_max_volume(volume) + } + (ArgTypes::Player, name) => set_player(name.unwrap_or("".to_string())), + (ArgTypes::Playerctl, value) => { + use crate::playerctl::*; + let value = &value.unwrap_or("".to_string()); + + let action = PlayerctlAction::from(value).unwrap(); + if let Ok(mut player) = Playerctl::new(action, server_config) { + match player.run() { + Ok(_) => { + let (icon, label) = (player.icon.unwrap(), player.label.unwrap()); + for window in osd_app.windows.borrow().to_owned() { + window.changed_player(&icon, &label) + } + } + Err(x) => { + eprintln!("couldn't run player change: \"{:?}\"!", x) + } + } + } else { + eprintln!("Unable to get players! are any opened?") + } + + reset_player(); + } + (ArgTypes::DeviceName, name) => { + set_device_name(name.unwrap_or(DEVICE_NAME_DEFAULT.to_string())) + } + (ArgTypes::CustomMessage, message) => { + if let Some(message) = message { + for window in osd_app.windows.borrow().to_owned() { + window.custom_message(message.as_str(), get_icon_name().as_deref()); + } + } + reset_icon_name(); + } + (ArgTypes::CustomIcon, icon) => { + set_icon_name(icon.unwrap_or(ICON_NAME_DEFAULT.to_string())) + } + (arg_type, data) => { + eprintln!( + "Failed to parse command... Type: {:?}, Data: {:?}", + arg_type, data + ) + } + }; + } + + fn initialize(&self) { + let display: gdk::Display = match gdk::Display::default() { + Some(x) => x, + _ => return, + }; + + self.init_windows(&display); + + let _self = self; + + display.connect_opened(clone!( + #[strong] + _self, + move |d| { + _self.init_windows(d); + } + )); + + display.connect_closed(clone!( + #[strong] + _self, + move |_d, is_error| { + if is_error { + eprintln!("Display closed due to errors..."); + } + _self.close_all_windows(); + } + )); + + display.monitors().connect_items_changed(clone!( + #[strong] + _self, + move |monitors, position, removed, added| { + if removed != 0 { + _self.init_windows(&display); + } else if added != 0 { + for i in 0..added { + if let Some(mon) = monitors + .item(position + i) + .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) + { + _self.add_window(&display, &mon); + } + } + } + } + )); + } + + fn add_window(&self, display: &gdk::Display, monitor: &gdk::Monitor) { + let win = SwayosdWindow::new(&self.app, display, monitor); + self.windows.borrow_mut().push(win); + } + + fn init_windows(&self, display: &gdk::Display) { + self.close_all_windows(); + + let monitors = display.monitors(); + for i in 0..monitors.n_items() { + let monitor = match monitors + .item(i) + .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) + { + Some(x) => x, + _ => continue, + }; + self.add_window(display, &monitor); + } + } + + fn close_all_windows(&self) { + self.windows.borrow_mut().retain(|window| { + window.close(); + false + }); + } } diff --git a/src/server/main.rs b/src/server/main.rs index be4ee26..5b6c55e 100644 --- a/src/server/main.rs +++ b/src/server/main.rs @@ -26,10 +26,10 @@ use argtypes::ArgTypes; use async_channel::Sender; use config::{DBUS_PATH, DBUS_SERVER_NAME}; use gtk::{ - gdk::Display, - gio::{self, Resource}, - glib::Bytes, - CssProvider, IconTheme, + gdk::Display, + gio::{self, Resource}, + glib::Bytes, + CssProvider, IconTheme, }; use std::env::args_os; use std::future::pending; @@ -39,125 +39,125 @@ use utils::{get_system_css_path, user_style_path}; use zbus::{interface, ConnectionBuilder}; struct DbusServer { - sender: Sender<(ArgTypes, String)>, + sender: Sender<(ArgTypes, String)>, } #[interface(name = "org.erikreider.swayosd")] impl DbusServer { - pub async fn handle_action(&self, arg_type: String, data: String) -> bool { - let arg_type = match ArgTypes::from_str(&arg_type) { - Ok(arg_type) => arg_type, - Err(other_type) => { - eprintln!("Unknown action in Dbus handle_action: {:?}", other_type); - return false; - } - }; - if let Err(error) = self.sender.send((arg_type, data)).await { - eprintln!("Channel Send error: {}", error); - return false; - } - true - } + pub async fn handle_action(&self, arg_type: String, data: String) -> bool { + let arg_type = match ArgTypes::from_str(&arg_type) { + Ok(arg_type) => arg_type, + Err(other_type) => { + eprintln!("Unknown action in Dbus handle_action: {:?}", other_type); + return false; + } + }; + if let Err(error) = self.sender.send((arg_type, data)).await { + eprintln!("Channel Send error: {}", error); + return false; + } + true + } } impl DbusServer { - async fn new(sender: Sender<(ArgTypes, String)>) -> zbus::Result<()> { - let _connection = ConnectionBuilder::session()? - .name(DBUS_SERVER_NAME)? - .serve_at(DBUS_PATH, DbusServer { sender })? - .build() - .await?; - pending::<()>().await; - Ok(()) - } + async fn new(sender: Sender<(ArgTypes, String)>) -> zbus::Result<()> { + let _connection = ConnectionBuilder::session()? + .name(DBUS_SERVER_NAME)? + .serve_at(DBUS_PATH, DbusServer { sender })? + .build() + .await?; + pending::<()>().await; + Ok(()) + } } const GRESOURCE_BASE_PATH: &str = "/org/erikreider/swayosd"; fn main() { - if gtk::init().is_err() { - eprintln!("failed to initialize GTK Application"); - std::process::exit(1); - } - - // Load the compiled resource bundle - let resources_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/swayosd.gresource")); - let resource_data = Bytes::from(&resources_bytes[..]); - let res = Resource::from_data(&resource_data).unwrap(); - gio::resources_register(&res); - - // Load the icon theme - let theme = IconTheme::default(); - theme.add_resource_path(&format!("{}/icons", GRESOURCE_BASE_PATH)); - - // Load the CSS themes - let display = Display::default().expect("Failed getting the default screen"); - - // Load the provided default CSS theme - let provider = CssProvider::new(); - provider.connect_parsing_error(|_provider, _section, error| { - eprintln!("Could not load default CSS stylesheet: {}", error); - }); - match get_system_css_path() { - Some(path) => { - provider.load_from_path(path.to_str().unwrap()); - gtk::style_context_add_provider_for_display( - &display, - &provider, - gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, - ); - } - None => eprintln!("Could not find the system CSS file..."), - } - - // Get config path and CSS theme path from command line - let mut config_path: Option<PathBuf> = None; - let mut custom_user_css: Option<PathBuf> = None; - let mut args = args_os().into_iter(); - while let Some(arg) = args.next() { - match arg.to_str() { - Some("--config") => { - if let Some(path) = args.next() { - config_path = Some(path.into()); - } - } - Some("-s") | Some("--style") => { - if let Some(path) = args.next() { - custom_user_css = Some(path.into()); - } - } - _ => (), - } - } - - // Parse Config - let server_config = config::user::read_user_config(config_path.as_deref()) - .expect("Failed to parse config file") - .server; - - // Load style path from config if none is given on CLI - if custom_user_css.is_none() { - custom_user_css = server_config.style.clone(); - } - - // Try loading the users CSS theme - if let Some(user_config_path) = user_style_path(custom_user_css) { - let user_provider = CssProvider::new(); - user_provider.connect_parsing_error(|_provider, _section, error| { - eprintln!("Failed loading user defined style.css: {}", error); - }); - user_provider.load_from_path(&user_config_path); - gtk::style_context_add_provider_for_display( - &display, - &user_provider, - gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, - ); - println!("Loaded user defined CSS file"); - } - - let (sender, receiver) = async_channel::bounded::<(ArgTypes, String)>(1); - // Start the DBus Server - async_std::task::spawn(DbusServer::new(sender)); - // Start the GTK Application - std::process::exit(SwayOSDApplication::new(server_config, receiver).start()); + if gtk::init().is_err() { + eprintln!("failed to initialize GTK Application"); + std::process::exit(1); + } + + // Load the compiled resource bundle + let resources_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/swayosd.gresource")); + let resource_data = Bytes::from(&resources_bytes[..]); + let res = Resource::from_data(&resource_data).unwrap(); + gio::resources_register(&res); + + // Load the icon theme + let theme = IconTheme::default(); + theme.add_resource_path(&format!("{}/icons", GRESOURCE_BASE_PATH)); + + // Load the CSS themes + let display = Display::default().expect("Failed getting the default screen"); + + // Load the provided default CSS theme + let provider = CssProvider::new(); + provider.connect_parsing_error(|_provider, _section, error| { + eprintln!("Could not load default CSS stylesheet: {}", error); + }); + match get_system_css_path() { + Some(path) => { + provider.load_from_path(path.to_str().unwrap()); + gtk::style_context_add_provider_for_display( + &display, + &provider, + gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, + ); + } + None => eprintln!("Could not find the system CSS file..."), + } + + // Get config path and CSS theme path from command line + let mut config_path: Option<PathBuf> = None; + let mut custom_user_css: Option<PathBuf> = None; + let mut args = args_os().into_iter(); + while let Some(arg) = args.next() { + match arg.to_str() { + Some("--config") => { + if let Some(path) = args.next() { + config_path = Some(path.into()); + } + } + Some("-s") | Some("--style") => { + if let Some(path) = args.next() { + custom_user_css = Some(path.into()); + } + } + _ => (), + } + } + + // Parse Config + let server_config = config::user::read_user_config(config_path.as_deref()) + .expect("Failed to parse config file") + .server; + + // Load style path from config if none is given on CLI + if custom_user_css.is_none() { + custom_user_css = server_config.style.clone(); + } + + // Try loading the users CSS theme + if let Some(user_config_path) = user_style_path(custom_user_css) { + let user_provider = CssProvider::new(); + user_provider.connect_parsing_error(|_provider, _section, error| { + eprintln!("Failed loading user defined style.css: {}", error); + }); + user_provider.load_from_path(&user_config_path); + gtk::style_context_add_provider_for_display( + &display, + &user_provider, + gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, + ); + println!("Loaded user defined CSS file"); + } + + let (sender, receiver) = async_channel::bounded::<(ArgTypes, String)>(1); + // Start the DBus Server + async_std::task::spawn(DbusServer::new(sender)); + // Start the GTK Application + std::process::exit(SwayOSDApplication::new(server_config, receiver).start()); } diff --git a/src/server/osd_window.rs b/src/server/osd_window.rs index 9e6e74e..27240a2 100644 --- a/src/server/osd_window.rs +++ b/src/server/osd_window.rs @@ -3,18 +3,18 @@ use std::rc::Rc; use std::time::Duration; use gtk::{ - gdk, - glib::{self, clone}, - prelude::*, + gdk, + glib::{self, clone}, + prelude::*, }; use pulsectl::controllers::types::DeviceInfo; use crate::{ - brightness_backend::BrightnessBackend, - utils::{ - get_max_volume, get_show_percentage, get_top_margin, volume_to_f64, KeysLocks, - VolumeDeviceType, - }, + brightness_backend::BrightnessBackend, + utils::{ + get_max_volume, get_show_percentage, get_top_margin, volume_to_f64, KeysLocks, + VolumeDeviceType, + }, }; use gtk_layer_shell::LayerShell; @@ -24,274 +24,274 @@ const ICON_SIZE: i32 = 32; /// A window that our application can open that contains the main project view. #[derive(Clone, Debug)] pub struct SwayosdWindow { - pub window: gtk::ApplicationWindow, - pub display: gdk::Display, - pub monitor: gdk::Monitor, - container: gtk::Box, - timeout_id: Rc<RefCell<Option<glib::SourceId>>>, + pub window: gtk::ApplicationWindow, + pub display: gdk::Display, + pub monitor: gdk::Monitor, + container: gtk::Box, + timeout_id: Rc<RefCell<Option<glib::SourceId>>>, } impl SwayosdWindow { - /// Create a new window and assign it to the given application. - pub fn new(app: >k::Application, display: &gdk::Display, monitor: &gdk::Monitor) -> Self { - let window = gtk::ApplicationWindow::new(app); - window.set_widget_name("osd"); - window.add_css_class("osd"); - - window.init_layer_shell(); - window.set_monitor(monitor); - window.set_namespace("swayosd"); - - window.set_exclusive_zone(-1); - window.set_layer(gtk_layer_shell::Layer::Overlay); - window.set_anchor(gtk_layer_shell::Edge::Top, true); - - // Set up the widgets - window.set_width_request(250); - - let container = cascade! { - gtk::Box::new(gtk::Orientation::Horizontal, 12); - ..set_widget_name("container"); - }; - - window.set_child(Some(&container)); - - // Disable mouse input - window.connect_map(|window| { - if let Some(surface) = window.surface() { - let region = gtk::cairo::Region::create(); - surface.set_input_region(®ion); - } - }); - - let update_margins = |window: >k::ApplicationWindow, monitor: &gdk::Monitor| { - // Monitor scale factor is not always correct - // Transform monitor height into coordinate system of window - let mon_height = - monitor.geometry().height() * monitor.scale_factor() / window.scale_factor(); - // Calculate new margin - let bottom = mon_height - window.allocated_height(); - let margin = (bottom as f32 * get_top_margin()).round() as i32; - window.set_margin(gtk_layer_shell::Edge::Top, margin); - }; - - // Set the window margin - update_margins(&window, monitor); - // Ensure window margin is updated when necessary - window.connect_scale_factor_notify(clone!( - #[weak] - monitor, - move |window| update_margins(window, &monitor) - )); - monitor.connect_scale_factor_notify(clone!( - #[weak] - window, - move |monitor| update_margins(&window, monitor) - )); - monitor.connect_geometry_notify(clone!( - #[weak] - window, - move |monitor| update_margins(&window, monitor) - )); - - Self { - window, - container, - display: display.clone(), - monitor: monitor.clone(), - timeout_id: Rc::new(RefCell::new(None)), - } - } - - pub fn close(&self) { - self.window.close(); - } - - pub fn changed_volume(&self, device: &DeviceInfo, device_type: &VolumeDeviceType) { - self.clear_osd(); - - let volume = volume_to_f64(&device.volume.avg()); - let icon_prefix = match device_type { - VolumeDeviceType::Sink(_) => "sink", - VolumeDeviceType::Source(_) => "source", - }; - let icon_state = &match (device.mute, volume) { - (true, _) => "muted", - (_, x) if x == 0.0 => "muted", - (false, x) if x > 0.0 && x <= 33.0 => "low", - (false, x) if x > 33.0 && x <= 66.0 => "medium", - (false, x) if x > 66.0 && x <= 100.0 => "high", - (false, x) if x > 100.0 => match device_type { - VolumeDeviceType::Sink(_) => "high", - VolumeDeviceType::Source(_) => "overamplified", - }, - (_, _) => "high", - }; - let icon_name = &format!("{}-volume-{}-symbolic", icon_prefix, icon_state); - - let max_volume: f64 = get_max_volume().into(); - - let icon = self.build_icon_widget(icon_name); - let progress = self.build_progress_widget(volume / max_volume); - let label = self.build_text_widget(Some(&format!("{}%", volume))); - - progress.set_sensitive(!device.mute); - - self.container.append(&icon); - self.container.append(&progress); - if get_show_percentage() { - self.container.append(&label); - } - - self.run_timeout(); - } - - pub fn changed_brightness(&self, brightness_backend: &mut dyn BrightnessBackend) { - self.clear_osd(); - - let icon_name = "display-brightness-symbolic"; - let icon = self.build_icon_widget(icon_name); - - let brightness = brightness_backend.get_current() as f64; - let max = brightness_backend.get_max() as f64; - let progress = self.build_progress_widget(brightness / max); - let label = self.build_text_widget(Some(&format!("{}%", (brightness / max * 100.) as i32))); - - self.container.append(&icon); - self.container.append(&progress); - if get_show_percentage() { - self.container.append(&label); - } - - self.run_timeout(); - } - - pub fn changed_player(&self, icon: &str, label: &str) { - self.clear_osd(); - - let icon = self.build_icon_widget(&icon); - let label = self.build_text_widget(Some(&label)); - - self.container.append(&icon); - self.container.append(&label); - - self.run_timeout(); - } - - pub fn changed_keylock(&self, key: KeysLocks, state: bool) { - self.clear_osd(); - - let label = self.build_text_widget(None); - - let on_off_text = match state { - true => "On", - false => "Off", - }; - - let (label_text, symbol) = match key { - KeysLocks::CapsLock => { - let symbol = "caps-lock-symbolic"; - let text = "Caps Lock ".to_string() + on_off_text; - (text, symbol) - } - KeysLocks::NumLock => { - let symbol = "num-lock-symbolic"; - let text = "Num Lock ".to_string() + on_off_text; - (text, symbol) - } - KeysLocks::ScrollLock => { - let symbol = "scroll-lock-symbolic"; - let text = "Scroll Lock ".to_string() + on_off_text; - (text, symbol) - } - }; - - label.set_text(&label_text); - let icon = self.build_icon_widget(symbol); - - icon.set_sensitive(state); - - self.container.append(&icon); - self.container.append(&label); - - self.run_timeout(); - } - - pub fn custom_message(&self, message: &str, icon_name: Option<&str>) { - self.clear_osd(); - - let label = self.build_text_widget(Some(message)); - - if let Some(icon_name) = icon_name { - let icon = self.build_icon_widget(icon_name); - self.container.append(&icon); - self.container.append(&label); - let box_spacing = self.container.spacing(); - icon.connect_realize(move |icon| { - label.set_margin_end( - icon.allocation().width() - + icon.margin_start() - + icon.margin_end() - + box_spacing, - ); - }); - } else { - self.container.append(&label); - } - - self.run_timeout(); - } - - /// Clear all container children - fn clear_osd(&self) { - let mut next = self.container.first_child(); - while let Some(widget) = next { - next = widget.next_sibling(); - self.container.remove(&widget); - } - } - - fn run_timeout(&self) { - // Hide window after timeout - if let Some(timeout_id) = self.timeout_id.take() { - timeout_id.remove() - } - let s = self.clone(); - self.timeout_id.replace(Some(glib::timeout_add_local_once( - Duration::from_millis(1000), - move || { - s.window.hide(); - s.timeout_id.replace(None); - }, - ))); - - self.window.show(); - } - - fn build_icon_widget(&self, icon_name: &str) -> gtk::Image { - let icon = gtk::gio::ThemedIcon::from_names(&[icon_name, "missing-symbolic"]); - - cascade! { - gtk::Image::from_gicon(&icon.upcast::<gtk::gio::Icon>()); - ..set_pixel_size(ICON_SIZE); - } - } - - fn build_text_widget(&self, text: Option<&str>) -> gtk::Label { - cascade! { - gtk::Label::new(text); - ..set_halign(gtk::Align::Center); - ..set_hexpand(true); - ..add_css_class("title-4"); - } - } - - fn build_progress_widget(&self, fraction: f64) -> gtk::ProgressBar { - cascade! { - gtk::ProgressBar::new(); - ..set_fraction(fraction); - ..set_valign(gtk::Align::Center); - ..set_hexpand(true); - } - } + /// Create a new window and assign it to the given application. + pub fn new(app: >k::Application, display: &gdk::Display, monitor: &gdk::Monitor) -> Self { + let window = gtk::ApplicationWindow::new(app); + window.set_widget_name("osd"); + window.add_css_class("osd"); + + window.init_layer_shell(); + window.set_monitor(monitor); + window.set_namespace("swayosd"); + + window.set_exclusive_zone(-1); + window.set_layer(gtk_layer_shell::Layer::Overlay); + window.set_anchor(gtk_layer_shell::Edge::Top, true); + + // Set up the widgets + window.set_width_request(250); + + let container = cascade! { + gtk::Box::new(gtk::Orientation::Horizontal, 12); + ..set_widget_name("container"); + }; + + window.set_child(Some(&container)); + + // Disable mouse input + window.connect_map(|window| { + if let Some(surface) = window.surface() { + let region = gtk::cairo::Region::create(); + surface.set_input_region(®ion); + } + }); + + let update_margins = |window: >k::ApplicationWindow, monitor: &gdk::Monitor| { + // Monitor scale factor is not always correct + // Transform monitor height into coordinate system of window + let mon_height = + monitor.geometry().height() * monitor.scale_factor() / window.scale_factor(); + // Calculate new margin + let bottom = mon_height - window.allocated_height(); + let margin = (bottom as f32 * get_top_margin()).round() as i32; + window.set_margin(gtk_layer_shell::Edge::Top, margin); + }; + + // Set the window margin + update_margins(&window, monitor); + // Ensure window margin is updated when necessary + window.connect_scale_factor_notify(clone!( + #[weak] + monitor, + move |window| update_margins(window, &monitor) + )); + monitor.connect_scale_factor_notify(clone!( + #[weak] + window, + move |monitor| update_margins(&window, monitor) + )); + monitor.connect_geometry_notify(clone!( + #[weak] + window, + move |monitor| update_margins(&window, monitor) + )); + + Self { + window, + container, + display: display.clone(), + monitor: monitor.clone(), + timeout_id: Rc::new(RefCell::new(None)), + } + } + + pub fn close(&self) { + self.window.close(); + } + + pub fn changed_volume(&self, device: &DeviceInfo, device_type: &VolumeDeviceType) { + self.clear_osd(); + + let volume = volume_to_f64(&device.volume.avg()); + let icon_prefix = match device_type { + VolumeDeviceType::Sink(_) => "sink", + VolumeDeviceType::Source(_) => "source", + }; + let icon_state = &match (device.mute, volume) { + (true, _) => "muted", + (_, x) if x == 0.0 => "muted", + (false, x) if x > 0.0 && x <= 33.0 => "low", + (false, x) if x > 33.0 && x <= 66.0 => "medium", + (false, x) if x > 66.0 && x <= 100.0 => "high", + (false, x) if x > 100.0 => match device_type { + VolumeDeviceType::Sink(_) => "high", + VolumeDeviceType::Source(_) => "overamplified", + }, + (_, _) => "high", + }; + let icon_name = &format!("{}-volume-{}-symbolic", icon_prefix, icon_state); + + let max_volume: f64 = get_max_volume().into(); + + let icon = self.build_icon_widget(icon_name); + let progress = self.build_progress_widget(volume / max_volume); + let label = self.build_text_widget(Some(&format!("{}%", volume))); + + progress.set_sensitive(!device.mute); + + self.container.append(&icon); + self.container.append(&progress); + if get_show_percentage() { + self.container.append(&label); + } + + self.run_timeout(); + } + + pub fn changed_brightness(&self, brightness_backend: &mut dyn BrightnessBackend) { + self.clear_osd(); + + let icon_name = "display-brightness-symbolic"; + let icon = self.build_icon_widget(icon_name); + + let brightness = brightness_backend.get_current() as f64; + let max = brightness_backend.get_max() as f64; + let progress = self.build_progress_widget(brightness / max); + let label = self.build_text_widget(Some(&format!("{}%", (brightness / max * 100.) as i32))); + + self.container.append(&icon); + self.container.append(&progress); + if get_show_percentage() { + self.container.append(&label); + } + + self.run_timeout(); + } + + pub fn changed_player(&self, icon: &str, label: &str) { + self.clear_osd(); + + let icon = self.build_icon_widget(&icon); + let label = self.build_text_widget(Some(&label)); + + self.container.append(&icon); + self.container.append(&label); + + self.run_timeout(); + } + + pub fn changed_keylock(&self, key: KeysLocks, state: bool) { + self.clear_osd(); + + let label = self.build_text_widget(None); + + let on_off_text = match state { + true => "On", + false => "Off", + }; + + let (label_text, symbol) = match key { + KeysLocks::CapsLock => { + let symbol = "caps-lock-symbolic"; + let text = "Caps Lock ".to_string() + on_off_text; + (text, symbol) + } + KeysLocks::NumLock => { + let symbol = "num-lock-symbolic"; + let text = "Num Lock ".to_string() + on_off_text; + (text, symbol) + } + KeysLocks::ScrollLock => { + let symbol = "scroll-lock-symbolic"; + let text = "Scroll Lock ".to_string() + on_off_text; + (text, symbol) + } + }; + + label.set_text(&label_text); + let icon = self.build_icon_widget(symbol); + + icon.set_sensitive(state); + + self.container.append(&icon); + self.container.append(&label); + + self.run_timeout(); + } + + pub fn custom_message(&self, message: &str, icon_name: Option<&str>) { + self.clear_osd(); + + let label = self.build_text_widget(Some(message)); + + if let Some(icon_name) = icon_name { + let icon = self.build_icon_widget(icon_name); + self.container.append(&icon); + self.container.append(&label); + let box_spacing = self.container.spacing(); + icon.connect_realize(move |icon| { + label.set_margin_end( + icon.allocation().width() + + icon.margin_start() + + icon.margin_end() + + box_spacing, + ); + }); + } else { + self.container.append(&label); + } + + self.run_timeout(); + } + + /// Clear all container children + fn clear_osd(&self) { + let mut next = self.container.first_child(); + while let Some(widget) = next { + next = widget.next_sibling(); + self.container.remove(&widget); + } + } + + fn run_timeout(&self) { + // Hide window after timeout + if let Some(timeout_id) = self.timeout_id.take() { + timeout_id.remove() + } + let s = self.clone(); + self.timeout_id.replace(Some(glib::timeout_add_local_once( + Duration::from_millis(1000), + move || { + s.window.hide(); + s.timeout_id.replace(None); + }, + ))); + + self.window.show(); + } + + fn build_icon_widget(&self, icon_name: &str) -> gtk::Image { + let icon = gtk::gio::ThemedIcon::from_names(&[icon_name, "missing-symbolic"]); + + cascade! { + gtk::Image::from_gicon(&icon.upcast::<gtk::gio::Icon>()); + ..set_pixel_size(ICON_SIZE); + } + } + + fn build_text_widget(&self, text: Option<&str>) -> gtk::Label { + cascade! { + gtk::Label::new(text); + ..set_halign(gtk::Align::Center); + ..set_hexpand(true); + ..add_css_class("title-4"); + } + } + + fn build_progress_widget(&self, fraction: f64) -> gtk::ProgressBar { + cascade! { + gtk::ProgressBar::new(); + ..set_fraction(fraction); + ..set_valign(gtk::Align::Center); + ..set_hexpand(true); + } + } } From 9b79c04aedbfcbc791aaac08b65fe66f2442d016 Mon Sep 17 00:00:00 2001 From: Jan Luca Oster <janluca.oster@proton.me> Date: Fri, 10 Jan 2025 12:24:37 +0100 Subject: [PATCH 07/18] implement Arc for config to avoid cloning often --- src/mpris-backend/mod.rs | 355 ++++++------- src/server/application.rs | 1015 +++++++++++++++++++------------------ src/server/main.rs | 238 ++++----- 3 files changed, 808 insertions(+), 800 deletions(-) diff --git a/src/mpris-backend/mod.rs b/src/mpris-backend/mod.rs index 6da91e8..e509c63 100644 --- a/src/mpris-backend/mod.rs +++ b/src/mpris-backend/mod.rs @@ -1,208 +1,211 @@ use mpris::{PlaybackStatus, Player, PlayerFinder}; use crate::utils::get_player; -use std::error::Error; +use std::{ + error::Error, + sync::Arc, +}; pub enum PlayerctlAction { - PlayPause, - Play, - Pause, - Stop, - Next, - Prev, - Shuffle, + PlayPause, + Play, + Pause, + Stop, + Next, + Prev, + Shuffle, } use super::config::user::ServerConfig; #[derive(Clone, Debug)] pub enum PlayerctlDeviceRaw { - None, - All, - Some(String), + None, + All, + Some(String), } pub enum PlayerctlDevice { - All(Vec<Player>), - Some(Player), + All(Vec<Player>), + Some(Player), } pub struct Playerctl { - player: PlayerctlDevice, - action: PlayerctlAction, - pub icon: Option<String>, - pub label: Option<String>, - fmt_str: Option<String>, + player: PlayerctlDevice, + action: PlayerctlAction, + pub icon: Option<String>, + pub label: Option<String>, + fmt_str: Option<String>, } impl Playerctl { - pub fn new( - action: PlayerctlAction, - config: &ServerConfig, - ) -> Result<Playerctl, Box<dyn Error>> { - let playerfinder = PlayerFinder::new()?; - let player = get_player(); - let player = match player { - PlayerctlDeviceRaw::None => PlayerctlDevice::Some(playerfinder.find_active()?), - PlayerctlDeviceRaw::Some(name) => { - PlayerctlDevice::Some(playerfinder.find_by_name(name.as_str())?) - } - PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), - }; - let fmt_str = config.playerctl_format.clone(); - Ok(Self { - player, - action, - icon: None, - label: None, - fmt_str, - }) - } - pub fn run(&mut self) -> Result<(), Box<dyn Error>> { - use PlaybackStatus::*; - use PlayerctlAction::*; - let run_single = |player: &Player| -> Result<&str, Box<dyn Error>> { - let out = match self.action { - PlayPause => match player.get_playback_status()? { - Playing => { - player.pause()?; - "pause-large -symbolic" - } - Paused | Stopped => { - player.play()?; - "play-large-symbolic" - } - }, - Shuffle => { - let shuffle = player.get_shuffle()?; - player.set_shuffle(!shuffle)?; - if shuffle { - "playlist-consecutive-symbolic" - } else { - "playlist-shuffle-symbolic" - } - } - Play => { - player.play()?; - "play-large-symbolic" - } - Pause => { - player.pause()?; - "pause-large-symbolic" - } - Stop => { - player.stop()?; - "stop-large-symbolic" - } - Next => { - player.next()?; - "media-seek-forward-symbolic" - } - Prev => { - player.previous()?; - "media-seek-backward-symbolic" - } - }; - Ok(out) - }; - let mut metadata = Err("some errro"); - let icon = match &self.player { - PlayerctlDevice::Some(player) => { - metadata = player.get_metadata().or_else(|_| Err("")); - run_single(player)? - } - PlayerctlDevice::All(players) => { - let mut icon = Err("couldn't change any players!"); - for player in players { - let icon_new = run_single(player); - if let Ok(icon_new) = icon_new { - if icon.is_err() { - icon = Ok(icon_new); - } - }; - if let Err(_) = metadata { - metadata = player.get_metadata().or_else(|_| Err("")); - } - } - icon? - } - }; + pub fn new( + action: PlayerctlAction, + config: Arc<ServerConfig>, + ) -> Result<Playerctl, Box<dyn Error>> { + let playerfinder = PlayerFinder::new()?; + let player = get_player(); + let player = match player { + PlayerctlDeviceRaw::None => PlayerctlDevice::Some(playerfinder.find_active()?), + PlayerctlDeviceRaw::Some(name) => { + PlayerctlDevice::Some(playerfinder.find_by_name(name.as_str())?) + } + PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), + }; + let fmt_str = config.playerctl_format.clone(); + Ok(Self { + player, + action, + icon: None, + label: None, + fmt_str, + }) + } + pub fn run(&mut self) -> Result<(), Box<dyn Error>> { + use PlaybackStatus::*; + use PlayerctlAction::*; + let run_single = |player: &Player| -> Result<&str, Box<dyn Error>> { + let out = match self.action { + PlayPause => match player.get_playback_status()? { + Playing => { + player.pause()?; + "pause-large -symbolic" + } + Paused | Stopped => { + player.play()?; + "play-large-symbolic" + } + }, + Shuffle => { + let shuffle = player.get_shuffle()?; + player.set_shuffle(!shuffle)?; + if shuffle { + "playlist-consecutive-symbolic" + } else { + "playlist-shuffle-symbolic" + } + } + Play => { + player.play()?; + "play-large-symbolic" + } + Pause => { + player.pause()?; + "pause-large-symbolic" + } + Stop => { + player.stop()?; + "stop-large-symbolic" + } + Next => { + player.next()?; + "media-seek-forward-symbolic" + } + Prev => { + player.previous()?; + "media-seek-backward-symbolic" + } + }; + Ok(out) + }; + let mut metadata = Err("some errro"); + let icon = match &self.player { + PlayerctlDevice::Some(player) => { + metadata = player.get_metadata().or_else(|_| Err("")); + run_single(player)? + } + PlayerctlDevice::All(players) => { + let mut icon = Err("couldn't change any players!"); + for player in players { + let icon_new = run_single(player); + if let Ok(icon_new) = icon_new { + if icon.is_err() { + icon = Ok(icon_new); + } + }; + if let Err(_) = metadata { + metadata = player.get_metadata().or_else(|_| Err("")); + } + } + icon? + } + }; - self.icon = Some(icon.to_string()); - let label = if let Ok(metadata) = metadata { - Some(self.fmt_string(metadata)) - } else { - None - }; - self.label = label; - Ok(()) - } - fn fmt_string(&self, metadata: mpris::Metadata) -> String { - use std::collections::HashMap; - use strfmt::{strfmt, Format}; + self.icon = Some(icon.to_string()); + let label = if let Ok(metadata) = metadata { + Some(self.fmt_string(metadata)) + } else { + None + }; + self.label = label; + Ok(()) + } + fn fmt_string(&self, metadata: mpris::Metadata) -> String { + use std::collections::HashMap; + use strfmt::{strfmt, Format}; - let mut vars = HashMap::new(); - let artists = metadata.artists().unwrap_or(vec![""]); - let artists_album = metadata.album_artists().unwrap_or(vec![""]); - let artist = artists.get(0).map_or("", |v| v); - let artist_album = artists_album.get(0).map_or("", |v| v); + let mut vars = HashMap::new(); + let artists = metadata.artists().unwrap_or(vec![""]); + let artists_album = metadata.album_artists().unwrap_or(vec![""]); + let artist = artists.get(0).map_or("", |v| v); + let artist_album = artists_album.get(0).map_or("", |v| v); - let title = metadata.title().unwrap_or(""); - let album = metadata.album_name().unwrap_or(""); - let track_num = metadata - .track_number() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); - let disc_num = metadata - .disc_number() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); - let autorating = metadata - .auto_rating() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); + let title = metadata.title().unwrap_or(""); + let album = metadata.album_name().unwrap_or(""); + let track_num = metadata + .track_number() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); + let disc_num = metadata + .disc_number() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); + let autorating = metadata + .auto_rating() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); - vars.insert("artist".to_string(), artist); - vars.insert("albumArtist".to_string(), artist_album); - vars.insert("title".to_string(), title); - vars.insert("trackNumber".to_string(), &track_num); - vars.insert("discNumber".to_string(), &disc_num); - vars.insert("autoRating".to_string(), &autorating); + vars.insert("artist".to_string(), artist); + vars.insert("albumArtist".to_string(), artist_album); + vars.insert("title".to_string(), title); + vars.insert("trackNumber".to_string(), &track_num); + vars.insert("discNumber".to_string(), &disc_num); + vars.insert("autoRating".to_string(), &autorating); - self.fmt_str - .clone() - .unwrap_or("{artist} - {title}".into()) - .format(&vars) - .unwrap_or_else(|e| { - eprintln!("error: {}. using default string", e); - "{artist} - {title}".format(&vars).unwrap() - }) - } + self.fmt_str + .clone() + .unwrap_or("{artist} - {title}".into()) + .format(&vars) + .unwrap_or_else(|e| { + eprintln!("error: {}. using default string", e); + "{artist} - {title}".format(&vars).unwrap() + }) + } } impl PlayerctlAction { - pub fn from(action: &str) -> Result<Self, String> { - use PlayerctlAction::*; - match action { - "play-pause" => Ok(PlayPause), - "play" => Ok(Play), - "pause" => Ok(Pause), - "stop" => Ok(Stop), - "next" => Ok(Next), - "prev" | "previous" => Ok(Prev), - "shuffle" => Ok(Shuffle), - x => Err(x.to_string()), - } - } + pub fn from(action: &str) -> Result<Self, String> { + use PlayerctlAction::*; + match action { + "play-pause" => Ok(PlayPause), + "play" => Ok(Play), + "pause" => Ok(Pause), + "stop" => Ok(Stop), + "next" => Ok(Next), + "prev" | "previous" => Ok(Prev), + "shuffle" => Ok(Shuffle), + x => Err(x.to_string()), + } + } } impl PlayerctlDeviceRaw { - pub fn from(player: String) -> Result<Self, ()> { - use PlayerctlDeviceRaw::*; - match player.as_str() { - "auto" | "" => Ok(None), - "all" => Ok(All), - _ => Ok(Some(player)), - } - } + pub fn from(player: String) -> Result<Self, ()> { + use PlayerctlDeviceRaw::*; + match player.as_str() { + "auto" | "" => Ok(None), + "all" => Ok(All), + _ => Ok(Some(player)), + } + } } diff --git a/src/server/application.rs b/src/server/application.rs index 76b2a89..22c2e33 100644 --- a/src/server/application.rs +++ b/src/server/application.rs @@ -5,15 +5,15 @@ use crate::osd_window::SwayosdWindow; use crate::utils::{self, *}; use async_channel::Receiver; use gtk::{ - gdk, - gio::{ - self, ApplicationFlags, BusNameWatcherFlags, BusType, DBusSignalFlags, SignalSubscriptionId, - }, - glib::{ - clone, variant::ToVariant, Char, ControlFlow::Break, MainContext, OptionArg, OptionFlags, - }, - prelude::*, - Application, + gdk, + gio::{ + self, ApplicationFlags, BusNameWatcherFlags, BusType, DBusSignalFlags, SignalSubscriptionId, + }, + glib::{ + clone, variant::ToVariant, Char, ControlFlow::Break, MainContext, OptionArg, OptionFlags, + }, + prelude::*, + Application, }; use pulsectl::controllers::{SinkController, SourceController}; use std::cell::RefCell; @@ -24,505 +24,506 @@ use super::config::user::ServerConfig; #[derive(Clone, Shrinkwrap)] pub struct SwayOSDApplication { - #[shrinkwrap(main_field)] - app: gtk::Application, - windows: Rc<RefCell<Vec<SwayosdWindow>>>, - _hold: Rc<gio::ApplicationHoldGuard>, + #[shrinkwrap(main_field)] + app: gtk::Application, + windows: Rc<RefCell<Vec<SwayosdWindow>>>, + _hold: Rc<gio::ApplicationHoldGuard>, } impl SwayOSDApplication { - pub fn new(server_config: ServerConfig, action_receiver: Receiver<(ArgTypes, String)>) -> Self { - let app = Application::new(Some(APPLICATION_NAME), ApplicationFlags::FLAGS_NONE); - let hold = Rc::new(app.hold()); - - app.add_main_option( - "config", - Char::from(0), - OptionFlags::NONE, - OptionArg::String, - "Use a custom config file instead of looking for one.", - Some("<CONFIG FILE PATH>"), - ); - - app.add_main_option( - "style", - Char::from('s' as u8), - OptionFlags::NONE, - OptionArg::String, - "Use a custom Stylesheet file instead of looking for one", - Some("<CSS FILE PATH>"), - ); - - app.add_main_option( - "top-margin", - Char::from(0), - OptionFlags::NONE, - OptionArg::String, - &format!( - "OSD margin from top edge (0.5 would be screen center). Default is {}", - *utils::TOP_MARGIN_DEFAULT - ), - Some("<from 0.0 to 1.0>"), - ); - - let osd_app = SwayOSDApplication { - app: app.clone(), - windows: Rc::new(RefCell::new(Vec::new())), - _hold: hold, - }; - - // Apply Server Config - if let Some(margin) = server_config.top_margin { - if (0_f32..1_f32).contains(&margin) { - set_top_margin(margin); - } - } - if let Some(max_volume) = server_config.max_volume { - set_default_max_volume(max_volume); - } - if let Some(show) = server_config.show_percentage { - set_show_percentage(show); - } - - let config_cloned = server_config.clone(); - - // Parse args - app.connect_handle_local_options(clone!( - #[strong] - osd_app, - move |_app, args| { - let actions = match handle_application_args(args.to_variant()) { - (HandleLocalStatus::SUCCESS | HandleLocalStatus::CONTINUE, actions) => actions, - (status @ HandleLocalStatus::FAILURE, _) => return status as i32, - }; - for (arg_type, data) in actions { - match (arg_type, data) { - (ArgTypes::TopMargin, margin) => { - let margin: Option<f32> = margin - .and_then(|margin| margin.parse().ok()) - .and_then(|margin| { - (0_f32..1_f32).contains(&margin).then_some(margin) - }); - - if let Some(margin) = margin { - set_top_margin(margin) - } - } - (ArgTypes::MaxVolume, max) => { - let max: Option<u8> = max.and_then(|max| max.parse().ok()); - - if let Some(max) = max { - set_default_max_volume(max); - } - } - (arg_type, data) => { - Self::action_activated(&osd_app, &config_cloned, arg_type, data) - } - } - } - - HandleLocalStatus::CONTINUE as i32 - } - )); - - // Listen to any Client actions - let config_cloned = server_config.clone(); - - MainContext::default().spawn_local(clone!( - #[strong] - osd_app, - async move { - while let Ok((arg_type, data)) = action_receiver.recv().await { - Self::action_activated( - &osd_app, - &config_cloned, - arg_type, - (!data.is_empty()).then_some(data), - ); - } - Break - } - )); - - // Listen to the LibInput Backend and activate the Application action - let (sender, receiver) = async_channel::bounded::<(u16, i32)>(1); - MainContext::default().spawn_local(clone!( - #[strong] - osd_app, - async move { - while let Ok((key_code, state)) = receiver.recv().await { - let (arg_type, data): (ArgTypes, Option<String>) = - match evdev_rs::enums::int_to_ev_key(key_code as u32) { - Some(evdev_rs::enums::EV_KEY::KEY_CAPSLOCK) => { - (ArgTypes::CapsLock, Some(state.to_string())) - } - Some(evdev_rs::enums::EV_KEY::KEY_NUMLOCK) => { - (ArgTypes::NumLock, Some(state.to_string())) - } - Some(evdev_rs::enums::EV_KEY::KEY_SCROLLLOCK) => { - (ArgTypes::ScrollLock, Some(state.to_string())) - } - _ => continue, - }; - Self::action_activated(&osd_app, &server_config, arg_type, data); - } - Break - } - )); - // Start watching for the LibInput Backend - let signal_id: Arc<Mutex<Option<SignalSubscriptionId>>> = Arc::new(Mutex::new(None)); - gio::bus_watch_name( - BusType::System, - DBUS_BACKEND_NAME, - BusNameWatcherFlags::NONE, - clone!( - #[strong] - sender, - #[strong] - signal_id, - move |connection, _, _| { - println!("Connecting to the SwayOSD LibInput Backend"); - let mut mutex = match signal_id.lock() { - Ok(mut mutex) => match mutex.as_mut() { - Some(_) => return, - None => mutex, - }, - Err(error) => return println!("Mutex lock Error: {}", error), - }; - mutex.replace(connection.signal_subscribe( - Some(config::DBUS_BACKEND_NAME), - Some(config::DBUS_BACKEND_NAME), - Some("KeyPressed"), - Some(config::DBUS_PATH), - None, - DBusSignalFlags::NONE, - clone!( - #[strong] - sender, - move |_, _, _, _, _, variant| { - let key_code = variant.try_child_get::<u16>(0); - let state = variant.try_child_get::<i32>(1); - match (key_code, state) { - (Ok(Some(key_code)), Ok(Some(state))) => { - MainContext::default().spawn_local(clone!( - #[strong] - sender, - async move { - if let Err(error) = - sender.send((key_code, state)).await - { - eprintln!("Channel Send error: {}", error); - } - } - )); - } - variables => { - return eprintln!("Variables don't match: {:?}", variables) - } - }; - } - ), - )); - } - ), - clone!( - #[strong] - signal_id, - move |connection, _| { - eprintln!("SwayOSD LibInput Backend isn't available, waiting..."); - match signal_id.lock() { - Ok(mut mutex) => { - if let Some(sig_id) = mutex.take() { - connection.signal_unsubscribe(sig_id); - } - } - Err(error) => println!("Mutex lock Error: {}", error), - } - } - ), - ); - - return osd_app; - } - - pub fn start(&self) -> i32 { - let s = self.clone(); - self.app.connect_activate(move |_| { - s.initialize(); - }); - - let _ = self.app.register(gio::Cancellable::NONE); - self.app.run().into() - } - - fn action_activated( - osd_app: &SwayOSDApplication, - server_config: &ServerConfig, - arg_type: ArgTypes, - value: Option<String>, - ) { - match (arg_type, value) { - (ArgTypes::SinkVolumeRaise, step) => { - let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Raise, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SinkVolumeLower, step) => { - let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Lower, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SinkVolumeMuteToggle, _) => { - let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SourceVolumeRaise, step) => { - let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Raise, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SourceVolumeLower, step) => { - let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Lower, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SourceVolumeMuteToggle, _) => { - let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - // TODO: Brightness - (ArgTypes::BrightnessRaise, step) => { - if let Ok(mut brightness_backend) = - change_brightness(BrightnessChangeType::Raise, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_brightness(brightness_backend.as_mut()); - } - } - } - (ArgTypes::BrightnessLower, step) => { - if let Ok(mut brightness_backend) = - change_brightness(BrightnessChangeType::Lower, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_brightness(brightness_backend.as_mut()); - } - } - } - (ArgTypes::BrightnessSet, value) => { - if let Ok(mut brightness_backend) = - change_brightness(BrightnessChangeType::Set, value) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_brightness(brightness_backend.as_mut()); - } - } - } - (ArgTypes::CapsLock, value) => { - let i32_value = value.clone().unwrap_or("-1".to_owned()); - let state = match i32_value.parse::<i32>() { - Ok(value) if value >= 0 && value <= 1 => value == 1, - _ => get_key_lock_state(KeysLocks::CapsLock, value), - }; - for window in osd_app.windows.borrow().to_owned() { - window.changed_keylock(KeysLocks::CapsLock, state) - } - } - (ArgTypes::NumLock, value) => { - let i32_value = value.clone().unwrap_or("-1".to_owned()); - let state = match i32_value.parse::<i32>() { - Ok(value) if value >= 0 && value <= 1 => value == 1, - _ => get_key_lock_state(KeysLocks::NumLock, value), - }; - for window in osd_app.windows.borrow().to_owned() { - window.changed_keylock(KeysLocks::NumLock, state) - } - } - (ArgTypes::ScrollLock, value) => { - let i32_value = value.clone().unwrap_or("-1".to_owned()); - let state = match i32_value.parse::<i32>() { - Ok(value) if value >= 0 && value <= 1 => value == 1, - _ => get_key_lock_state(KeysLocks::ScrollLock, value), - }; - for window in osd_app.windows.borrow().to_owned() { - window.changed_keylock(KeysLocks::ScrollLock, state) - } - } - (ArgTypes::MaxVolume, max) => { - let volume: u8 = match max { - Some(max) => match max.parse() { - Ok(max) => max, - _ => get_default_max_volume(), - }, - _ => get_default_max_volume(), - }; - set_max_volume(volume) - } - (ArgTypes::Player, name) => set_player(name.unwrap_or("".to_string())), - (ArgTypes::Playerctl, value) => { - use crate::playerctl::*; - let value = &value.unwrap_or("".to_string()); - - let action = PlayerctlAction::from(value).unwrap(); - if let Ok(mut player) = Playerctl::new(action, server_config) { - match player.run() { - Ok(_) => { - let (icon, label) = (player.icon.unwrap(), player.label.unwrap()); - for window in osd_app.windows.borrow().to_owned() { - window.changed_player(&icon, &label) - } - } - Err(x) => { - eprintln!("couldn't run player change: \"{:?}\"!", x) - } - } - } else { - eprintln!("Unable to get players! are any opened?") - } - - reset_player(); - } - (ArgTypes::DeviceName, name) => { - set_device_name(name.unwrap_or(DEVICE_NAME_DEFAULT.to_string())) - } - (ArgTypes::CustomMessage, message) => { - if let Some(message) = message { - for window in osd_app.windows.borrow().to_owned() { - window.custom_message(message.as_str(), get_icon_name().as_deref()); - } - } - reset_icon_name(); - } - (ArgTypes::CustomIcon, icon) => { - set_icon_name(icon.unwrap_or(ICON_NAME_DEFAULT.to_string())) - } - (arg_type, data) => { - eprintln!( - "Failed to parse command... Type: {:?}, Data: {:?}", - arg_type, data - ) - } - }; - } - - fn initialize(&self) { - let display: gdk::Display = match gdk::Display::default() { - Some(x) => x, - _ => return, - }; - - self.init_windows(&display); - - let _self = self; - - display.connect_opened(clone!( - #[strong] - _self, - move |d| { - _self.init_windows(d); - } - )); - - display.connect_closed(clone!( - #[strong] - _self, - move |_d, is_error| { - if is_error { - eprintln!("Display closed due to errors..."); - } - _self.close_all_windows(); - } - )); - - display.monitors().connect_items_changed(clone!( - #[strong] - _self, - move |monitors, position, removed, added| { - if removed != 0 { - _self.init_windows(&display); - } else if added != 0 { - for i in 0..added { - if let Some(mon) = monitors - .item(position + i) - .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) - { - _self.add_window(&display, &mon); - } - } - } - } - )); - } - - fn add_window(&self, display: &gdk::Display, monitor: &gdk::Monitor) { - let win = SwayosdWindow::new(&self.app, display, monitor); - self.windows.borrow_mut().push(win); - } - - fn init_windows(&self, display: &gdk::Display) { - self.close_all_windows(); - - let monitors = display.monitors(); - for i in 0..monitors.n_items() { - let monitor = match monitors - .item(i) - .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) - { - Some(x) => x, - _ => continue, - }; - self.add_window(display, &monitor); - } - } - - fn close_all_windows(&self) { - self.windows.borrow_mut().retain(|window| { - window.close(); - false - }); - } + pub fn new(server_config: Arc<ServerConfig>, action_receiver: Receiver<(ArgTypes, String)>) -> Self { + let app = Application::new(Some(APPLICATION_NAME), ApplicationFlags::FLAGS_NONE); + let hold = Rc::new(app.hold()); + + app.add_main_option( + "config", + Char::from(0), + OptionFlags::NONE, + OptionArg::String, + "Use a custom config file instead of looking for one.", + Some("<CONFIG FILE PATH>"), + ); + + app.add_main_option( + "style", + Char::from('s' as u8), + OptionFlags::NONE, + OptionArg::String, + "Use a custom Stylesheet file instead of looking for one", + Some("<CSS FILE PATH>"), + ); + + app.add_main_option( + "top-margin", + Char::from(0), + OptionFlags::NONE, + OptionArg::String, + &format!( + "OSD margin from top edge (0.5 would be screen center). Default is {}", + *utils::TOP_MARGIN_DEFAULT + ), + Some("<from 0.0 to 1.0>"), + ); + + let osd_app = SwayOSDApplication { + app: app.clone(), + windows: Rc::new(RefCell::new(Vec::new())), + _hold: hold, + }; + + // Apply Server Config + if let Some(margin) = server_config.top_margin { + if (0_f32..1_f32).contains(&margin) { + set_top_margin(margin); + } + } + if let Some(max_volume) = server_config.max_volume { + set_default_max_volume(max_volume); + } + if let Some(show) = server_config.show_percentage { + set_show_percentage(show); + } + + let server_config_shared = server_config.clone(); + + // Parse args + app.connect_handle_local_options(clone!( + #[strong] + osd_app, + move |_app, args| { + let actions = match handle_application_args(args.to_variant()) { + (HandleLocalStatus::SUCCESS | HandleLocalStatus::CONTINUE, actions) => actions, + (status @ HandleLocalStatus::FAILURE, _) => return status as i32, + }; + for (arg_type, data) in actions { + match (arg_type, data) { + (ArgTypes::TopMargin, margin) => { + let margin: Option<f32> = margin + .and_then(|margin| margin.parse().ok()) + .and_then(|margin| { + (0_f32..1_f32).contains(&margin).then_some(margin) + }); + + if let Some(margin) = margin { + set_top_margin(margin) + } + } + (ArgTypes::MaxVolume, max) => { + let max: Option<u8> = max.and_then(|max| max.parse().ok()); + + if let Some(max) = max { + set_default_max_volume(max); + } + } + (arg_type, data) => { + Self::action_activated(&osd_app, server_config_shared.clone(), arg_type, data) + } + } + } + + HandleLocalStatus::CONTINUE as i32 + } + )); + + let server_config_shared = server_config.clone(); + + MainContext::default().spawn_local(clone!( + #[strong] + osd_app, + async move { + while let Ok((arg_type, data)) = action_receiver.recv().await { + Self::action_activated( + &osd_app, + server_config_shared.clone(), + arg_type, + (!data.is_empty()).then_some(data), + ); + } + Break + } + )); + + let server_config_shared = server_config.clone(); + + // Listen to the LibInput Backend and activate the Application action + let (sender, receiver) = async_channel::bounded::<(u16, i32)>(1); + MainContext::default().spawn_local(clone!( + #[strong] + osd_app, + async move { + while let Ok((key_code, state)) = receiver.recv().await { + let (arg_type, data): (ArgTypes, Option<String>) = + match evdev_rs::enums::int_to_ev_key(key_code as u32) { + Some(evdev_rs::enums::EV_KEY::KEY_CAPSLOCK) => { + (ArgTypes::CapsLock, Some(state.to_string())) + } + Some(evdev_rs::enums::EV_KEY::KEY_NUMLOCK) => { + (ArgTypes::NumLock, Some(state.to_string())) + } + Some(evdev_rs::enums::EV_KEY::KEY_SCROLLLOCK) => { + (ArgTypes::ScrollLock, Some(state.to_string())) + } + _ => continue, + }; + Self::action_activated(&osd_app, server_config_shared.clone(), arg_type, data); + } + Break + } + )); + // Start watching for the LibInput Backend + let signal_id: Arc<Mutex<Option<SignalSubscriptionId>>> = Arc::new(Mutex::new(None)); + gio::bus_watch_name( + BusType::System, + DBUS_BACKEND_NAME, + BusNameWatcherFlags::NONE, + clone!( + #[strong] + sender, + #[strong] + signal_id, + move |connection, _, _| { + println!("Connecting to the SwayOSD LibInput Backend"); + let mut mutex = match signal_id.lock() { + Ok(mut mutex) => match mutex.as_mut() { + Some(_) => return, + None => mutex, + }, + Err(error) => return println!("Mutex lock Error: {}", error), + }; + mutex.replace(connection.signal_subscribe( + Some(config::DBUS_BACKEND_NAME), + Some(config::DBUS_BACKEND_NAME), + Some("KeyPressed"), + Some(config::DBUS_PATH), + None, + DBusSignalFlags::NONE, + clone!( + #[strong] + sender, + move |_, _, _, _, _, variant| { + let key_code = variant.try_child_get::<u16>(0); + let state = variant.try_child_get::<i32>(1); + match (key_code, state) { + (Ok(Some(key_code)), Ok(Some(state))) => { + MainContext::default().spawn_local(clone!( + #[strong] + sender, + async move { + if let Err(error) = + sender.send((key_code, state)).await + { + eprintln!("Channel Send error: {}", error); + } + } + )); + } + variables => { + return eprintln!("Variables don't match: {:?}", variables) + } + }; + } + ), + )); + } + ), + clone!( + #[strong] + signal_id, + move |connection, _| { + eprintln!("SwayOSD LibInput Backend isn't available, waiting..."); + match signal_id.lock() { + Ok(mut mutex) => { + if let Some(sig_id) = mutex.take() { + connection.signal_unsubscribe(sig_id); + } + } + Err(error) => println!("Mutex lock Error: {}", error), + } + } + ), + ); + + return osd_app; + } + + pub fn start(&self) -> i32 { + let s = self.clone(); + self.app.connect_activate(move |_| { + s.initialize(); + }); + + let _ = self.app.register(gio::Cancellable::NONE); + self.app.run().into() + } + + fn action_activated( + osd_app: &SwayOSDApplication, + server_config: Arc<ServerConfig>, + arg_type: ArgTypes, + value: Option<String>, + ) { + match (arg_type, value) { + (ArgTypes::SinkVolumeRaise, step) => { + let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Raise, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SinkVolumeLower, step) => { + let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Lower, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SinkVolumeMuteToggle, _) => { + let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SourceVolumeRaise, step) => { + let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Raise, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SourceVolumeLower, step) => { + let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Lower, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SourceVolumeMuteToggle, _) => { + let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + // TODO: Brightness + (ArgTypes::BrightnessRaise, step) => { + if let Ok(mut brightness_backend) = + change_brightness(BrightnessChangeType::Raise, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_brightness(brightness_backend.as_mut()); + } + } + } + (ArgTypes::BrightnessLower, step) => { + if let Ok(mut brightness_backend) = + change_brightness(BrightnessChangeType::Lower, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_brightness(brightness_backend.as_mut()); + } + } + } + (ArgTypes::BrightnessSet, value) => { + if let Ok(mut brightness_backend) = + change_brightness(BrightnessChangeType::Set, value) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_brightness(brightness_backend.as_mut()); + } + } + } + (ArgTypes::CapsLock, value) => { + let i32_value = value.clone().unwrap_or("-1".to_owned()); + let state = match i32_value.parse::<i32>() { + Ok(value) if value >= 0 && value <= 1 => value == 1, + _ => get_key_lock_state(KeysLocks::CapsLock, value), + }; + for window in osd_app.windows.borrow().to_owned() { + window.changed_keylock(KeysLocks::CapsLock, state) + } + } + (ArgTypes::NumLock, value) => { + let i32_value = value.clone().unwrap_or("-1".to_owned()); + let state = match i32_value.parse::<i32>() { + Ok(value) if value >= 0 && value <= 1 => value == 1, + _ => get_key_lock_state(KeysLocks::NumLock, value), + }; + for window in osd_app.windows.borrow().to_owned() { + window.changed_keylock(KeysLocks::NumLock, state) + } + } + (ArgTypes::ScrollLock, value) => { + let i32_value = value.clone().unwrap_or("-1".to_owned()); + let state = match i32_value.parse::<i32>() { + Ok(value) if value >= 0 && value <= 1 => value == 1, + _ => get_key_lock_state(KeysLocks::ScrollLock, value), + }; + for window in osd_app.windows.borrow().to_owned() { + window.changed_keylock(KeysLocks::ScrollLock, state) + } + } + (ArgTypes::MaxVolume, max) => { + let volume: u8 = match max { + Some(max) => match max.parse() { + Ok(max) => max, + _ => get_default_max_volume(), + }, + _ => get_default_max_volume(), + }; + set_max_volume(volume) + } + (ArgTypes::Player, name) => set_player(name.unwrap_or("".to_string())), + (ArgTypes::Playerctl, value) => { + use crate::playerctl::*; + let value = &value.unwrap_or("".to_string()); + + let action = PlayerctlAction::from(value).unwrap(); + if let Ok(mut player) = Playerctl::new(action, server_config) { + match player.run() { + Ok(_) => { + let (icon, label) = (player.icon.unwrap(), player.label.unwrap()); + for window in osd_app.windows.borrow().to_owned() { + window.changed_player(&icon, &label) + } + } + Err(x) => { + eprintln!("couldn't run player change: \"{:?}\"!", x) + } + } + } else { + eprintln!("Unable to get players! are any opened?") + } + + reset_player(); + } + (ArgTypes::DeviceName, name) => { + set_device_name(name.unwrap_or(DEVICE_NAME_DEFAULT.to_string())) + } + (ArgTypes::CustomMessage, message) => { + if let Some(message) = message { + for window in osd_app.windows.borrow().to_owned() { + window.custom_message(message.as_str(), get_icon_name().as_deref()); + } + } + reset_icon_name(); + } + (ArgTypes::CustomIcon, icon) => { + set_icon_name(icon.unwrap_or(ICON_NAME_DEFAULT.to_string())) + } + (arg_type, data) => { + eprintln!( + "Failed to parse command... Type: {:?}, Data: {:?}", + arg_type, data + ) + } + }; + } + + fn initialize(&self) { + let display: gdk::Display = match gdk::Display::default() { + Some(x) => x, + _ => return, + }; + + self.init_windows(&display); + + let _self = self; + + display.connect_opened(clone!( + #[strong] + _self, + move |d| { + _self.init_windows(d); + } + )); + + display.connect_closed(clone!( + #[strong] + _self, + move |_d, is_error| { + if is_error { + eprintln!("Display closed due to errors..."); + } + _self.close_all_windows(); + } + )); + + display.monitors().connect_items_changed(clone!( + #[strong] + _self, + move |monitors, position, removed, added| { + if removed != 0 { + _self.init_windows(&display); + } else if added != 0 { + for i in 0..added { + if let Some(mon) = monitors + .item(position + i) + .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) + { + _self.add_window(&display, &mon); + } + } + } + } + )); + } + + fn add_window(&self, display: &gdk::Display, monitor: &gdk::Monitor) { + let win = SwayosdWindow::new(&self.app, display, monitor); + self.windows.borrow_mut().push(win); + } + + fn init_windows(&self, display: &gdk::Display) { + self.close_all_windows(); + + let monitors = display.monitors(); + for i in 0..monitors.n_items() { + let monitor = match monitors + .item(i) + .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) + { + Some(x) => x, + _ => continue, + }; + self.add_window(display, &monitor); + } + } + + fn close_all_windows(&self) { + self.windows.borrow_mut().retain(|window| { + window.close(); + false + }); + } } diff --git a/src/server/main.rs b/src/server/main.rs index 5b6c55e..264e166 100644 --- a/src/server/main.rs +++ b/src/server/main.rs @@ -26,138 +26,142 @@ use argtypes::ArgTypes; use async_channel::Sender; use config::{DBUS_PATH, DBUS_SERVER_NAME}; use gtk::{ - gdk::Display, - gio::{self, Resource}, - glib::Bytes, - CssProvider, IconTheme, + gdk::Display, + gio::{self, Resource}, + glib::Bytes, + CssProvider, IconTheme, +}; +use std::{ + env::args_os, + future::pending, + path::PathBuf, + str::FromStr, + sync::Arc, }; -use std::env::args_os; -use std::future::pending; -use std::path::PathBuf; -use std::str::FromStr; use utils::{get_system_css_path, user_style_path}; use zbus::{interface, ConnectionBuilder}; struct DbusServer { - sender: Sender<(ArgTypes, String)>, + sender: Sender<(ArgTypes, String)>, } #[interface(name = "org.erikreider.swayosd")] impl DbusServer { - pub async fn handle_action(&self, arg_type: String, data: String) -> bool { - let arg_type = match ArgTypes::from_str(&arg_type) { - Ok(arg_type) => arg_type, - Err(other_type) => { - eprintln!("Unknown action in Dbus handle_action: {:?}", other_type); - return false; - } - }; - if let Err(error) = self.sender.send((arg_type, data)).await { - eprintln!("Channel Send error: {}", error); - return false; - } - true - } + pub async fn handle_action(&self, arg_type: String, data: String) -> bool { + let arg_type = match ArgTypes::from_str(&arg_type) { + Ok(arg_type) => arg_type, + Err(other_type) => { + eprintln!("Unknown action in Dbus handle_action: {:?}", other_type); + return false; + } + }; + if let Err(error) = self.sender.send((arg_type, data)).await { + eprintln!("Channel Send error: {}", error); + return false; + } + true + } } impl DbusServer { - async fn new(sender: Sender<(ArgTypes, String)>) -> zbus::Result<()> { - let _connection = ConnectionBuilder::session()? - .name(DBUS_SERVER_NAME)? - .serve_at(DBUS_PATH, DbusServer { sender })? - .build() - .await?; - pending::<()>().await; - Ok(()) - } + async fn new(sender: Sender<(ArgTypes, String)>) -> zbus::Result<()> { + let _connection = ConnectionBuilder::session()? + .name(DBUS_SERVER_NAME)? + .serve_at(DBUS_PATH, DbusServer { sender })? + .build() + .await?; + pending::<()>().await; + Ok(()) + } } const GRESOURCE_BASE_PATH: &str = "/org/erikreider/swayosd"; fn main() { - if gtk::init().is_err() { - eprintln!("failed to initialize GTK Application"); - std::process::exit(1); - } - - // Load the compiled resource bundle - let resources_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/swayosd.gresource")); - let resource_data = Bytes::from(&resources_bytes[..]); - let res = Resource::from_data(&resource_data).unwrap(); - gio::resources_register(&res); - - // Load the icon theme - let theme = IconTheme::default(); - theme.add_resource_path(&format!("{}/icons", GRESOURCE_BASE_PATH)); - - // Load the CSS themes - let display = Display::default().expect("Failed getting the default screen"); - - // Load the provided default CSS theme - let provider = CssProvider::new(); - provider.connect_parsing_error(|_provider, _section, error| { - eprintln!("Could not load default CSS stylesheet: {}", error); - }); - match get_system_css_path() { - Some(path) => { - provider.load_from_path(path.to_str().unwrap()); - gtk::style_context_add_provider_for_display( - &display, - &provider, - gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, - ); - } - None => eprintln!("Could not find the system CSS file..."), - } - - // Get config path and CSS theme path from command line - let mut config_path: Option<PathBuf> = None; - let mut custom_user_css: Option<PathBuf> = None; - let mut args = args_os().into_iter(); - while let Some(arg) = args.next() { - match arg.to_str() { - Some("--config") => { - if let Some(path) = args.next() { - config_path = Some(path.into()); - } - } - Some("-s") | Some("--style") => { - if let Some(path) = args.next() { - custom_user_css = Some(path.into()); - } - } - _ => (), - } - } - - // Parse Config - let server_config = config::user::read_user_config(config_path.as_deref()) - .expect("Failed to parse config file") - .server; - - // Load style path from config if none is given on CLI - if custom_user_css.is_none() { - custom_user_css = server_config.style.clone(); - } - - // Try loading the users CSS theme - if let Some(user_config_path) = user_style_path(custom_user_css) { - let user_provider = CssProvider::new(); - user_provider.connect_parsing_error(|_provider, _section, error| { - eprintln!("Failed loading user defined style.css: {}", error); - }); - user_provider.load_from_path(&user_config_path); - gtk::style_context_add_provider_for_display( - &display, - &user_provider, - gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, - ); - println!("Loaded user defined CSS file"); - } - - let (sender, receiver) = async_channel::bounded::<(ArgTypes, String)>(1); - // Start the DBus Server - async_std::task::spawn(DbusServer::new(sender)); - // Start the GTK Application - std::process::exit(SwayOSDApplication::new(server_config, receiver).start()); + if gtk::init().is_err() { + eprintln!("failed to initialize GTK Application"); + std::process::exit(1); + } + + // Load the compiled resource bundle + let resources_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/swayosd.gresource")); + let resource_data = Bytes::from(&resources_bytes[..]); + let res = Resource::from_data(&resource_data).unwrap(); + gio::resources_register(&res); + + // Load the icon theme + let theme = IconTheme::default(); + theme.add_resource_path(&format!("{}/icons", GRESOURCE_BASE_PATH)); + + // Load the CSS themes + let display = Display::default().expect("Failed getting the default screen"); + + // Load the provided default CSS theme + let provider = CssProvider::new(); + provider.connect_parsing_error(|_provider, _section, error| { + eprintln!("Could not load default CSS stylesheet: {}", error); + }); + match get_system_css_path() { + Some(path) => { + provider.load_from_path(path.to_str().unwrap()); + gtk::style_context_add_provider_for_display( + &display, + &provider, + gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, + ); + } + None => eprintln!("Could not find the system CSS file..."), + } + + // Get config path and CSS theme path from command line + let mut config_path: Option<PathBuf> = None; + let mut custom_user_css: Option<PathBuf> = None; + let mut args = args_os().into_iter(); + while let Some(arg) = args.next() { + match arg.to_str() { + Some("--config") => { + if let Some(path) = args.next() { + config_path = Some(path.into()); + } + } + Some("-s") | Some("--style") => { + if let Some(path) = args.next() { + custom_user_css = Some(path.into()); + } + } + _ => (), + } + } + + // Parse Config + let server_config = Arc::new(config::user::read_user_config(config_path.as_deref()) + .expect("Failed to parse config file") + .server + ); + + // Load style path from config if none is given on CLI + if custom_user_css.is_none() { + custom_user_css = server_config.style.clone(); + } + + // Try loading the users CSS theme + if let Some(user_config_path) = user_style_path(custom_user_css) { + let user_provider = CssProvider::new(); + user_provider.connect_parsing_error(|_provider, _section, error| { + eprintln!("Failed loading user defined style.css: {}", error); + }); + user_provider.load_from_path(&user_config_path); + gtk::style_context_add_provider_for_display( + &display, + &user_provider, + gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, + ); + println!("Loaded user defined CSS file"); + } + + let (sender, receiver) = async_channel::bounded::<(ArgTypes, String)>(1); + // Start the DBus Server + async_std::task::spawn(DbusServer::new(sender)); + // Start the GTK Application + std::process::exit(SwayOSDApplication::new(server_config, receiver).start()); } From bbca70a4dc4b5f92ef0ecf363210e0833c4d050c Mon Sep 17 00:00:00 2001 From: Jan Luca Oster <janluca.oster@proton.me> Date: Fri, 10 Jan 2025 12:25:09 +0100 Subject: [PATCH 08/18] cargo fmt --- src/mpris-backend/mod.rs | 355 +++++++------ src/server/application.rs | 1022 +++++++++++++++++++------------------ src/server/main.rs | 237 +++++---- 3 files changed, 806 insertions(+), 808 deletions(-) diff --git a/src/mpris-backend/mod.rs b/src/mpris-backend/mod.rs index e509c63..cee4eaf 100644 --- a/src/mpris-backend/mod.rs +++ b/src/mpris-backend/mod.rs @@ -1,211 +1,208 @@ use mpris::{PlaybackStatus, Player, PlayerFinder}; use crate::utils::get_player; -use std::{ - error::Error, - sync::Arc, -}; +use std::{error::Error, sync::Arc}; pub enum PlayerctlAction { - PlayPause, - Play, - Pause, - Stop, - Next, - Prev, - Shuffle, + PlayPause, + Play, + Pause, + Stop, + Next, + Prev, + Shuffle, } use super::config::user::ServerConfig; #[derive(Clone, Debug)] pub enum PlayerctlDeviceRaw { - None, - All, - Some(String), + None, + All, + Some(String), } pub enum PlayerctlDevice { - All(Vec<Player>), - Some(Player), + All(Vec<Player>), + Some(Player), } pub struct Playerctl { - player: PlayerctlDevice, - action: PlayerctlAction, - pub icon: Option<String>, - pub label: Option<String>, - fmt_str: Option<String>, + player: PlayerctlDevice, + action: PlayerctlAction, + pub icon: Option<String>, + pub label: Option<String>, + fmt_str: Option<String>, } impl Playerctl { - pub fn new( - action: PlayerctlAction, - config: Arc<ServerConfig>, - ) -> Result<Playerctl, Box<dyn Error>> { - let playerfinder = PlayerFinder::new()?; - let player = get_player(); - let player = match player { - PlayerctlDeviceRaw::None => PlayerctlDevice::Some(playerfinder.find_active()?), - PlayerctlDeviceRaw::Some(name) => { - PlayerctlDevice::Some(playerfinder.find_by_name(name.as_str())?) - } - PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), - }; - let fmt_str = config.playerctl_format.clone(); - Ok(Self { - player, - action, - icon: None, - label: None, - fmt_str, - }) - } - pub fn run(&mut self) -> Result<(), Box<dyn Error>> { - use PlaybackStatus::*; - use PlayerctlAction::*; - let run_single = |player: &Player| -> Result<&str, Box<dyn Error>> { - let out = match self.action { - PlayPause => match player.get_playback_status()? { - Playing => { - player.pause()?; - "pause-large -symbolic" - } - Paused | Stopped => { - player.play()?; - "play-large-symbolic" - } - }, - Shuffle => { - let shuffle = player.get_shuffle()?; - player.set_shuffle(!shuffle)?; - if shuffle { - "playlist-consecutive-symbolic" - } else { - "playlist-shuffle-symbolic" - } - } - Play => { - player.play()?; - "play-large-symbolic" - } - Pause => { - player.pause()?; - "pause-large-symbolic" - } - Stop => { - player.stop()?; - "stop-large-symbolic" - } - Next => { - player.next()?; - "media-seek-forward-symbolic" - } - Prev => { - player.previous()?; - "media-seek-backward-symbolic" - } - }; - Ok(out) - }; - let mut metadata = Err("some errro"); - let icon = match &self.player { - PlayerctlDevice::Some(player) => { - metadata = player.get_metadata().or_else(|_| Err("")); - run_single(player)? - } - PlayerctlDevice::All(players) => { - let mut icon = Err("couldn't change any players!"); - for player in players { - let icon_new = run_single(player); - if let Ok(icon_new) = icon_new { - if icon.is_err() { - icon = Ok(icon_new); - } - }; - if let Err(_) = metadata { - metadata = player.get_metadata().or_else(|_| Err("")); - } - } - icon? - } - }; + pub fn new( + action: PlayerctlAction, + config: Arc<ServerConfig>, + ) -> Result<Playerctl, Box<dyn Error>> { + let playerfinder = PlayerFinder::new()?; + let player = get_player(); + let player = match player { + PlayerctlDeviceRaw::None => PlayerctlDevice::Some(playerfinder.find_active()?), + PlayerctlDeviceRaw::Some(name) => { + PlayerctlDevice::Some(playerfinder.find_by_name(name.as_str())?) + } + PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), + }; + let fmt_str = config.playerctl_format.clone(); + Ok(Self { + player, + action, + icon: None, + label: None, + fmt_str, + }) + } + pub fn run(&mut self) -> Result<(), Box<dyn Error>> { + use PlaybackStatus::*; + use PlayerctlAction::*; + let run_single = |player: &Player| -> Result<&str, Box<dyn Error>> { + let out = match self.action { + PlayPause => match player.get_playback_status()? { + Playing => { + player.pause()?; + "pause-large -symbolic" + } + Paused | Stopped => { + player.play()?; + "play-large-symbolic" + } + }, + Shuffle => { + let shuffle = player.get_shuffle()?; + player.set_shuffle(!shuffle)?; + if shuffle { + "playlist-consecutive-symbolic" + } else { + "playlist-shuffle-symbolic" + } + } + Play => { + player.play()?; + "play-large-symbolic" + } + Pause => { + player.pause()?; + "pause-large-symbolic" + } + Stop => { + player.stop()?; + "stop-large-symbolic" + } + Next => { + player.next()?; + "media-seek-forward-symbolic" + } + Prev => { + player.previous()?; + "media-seek-backward-symbolic" + } + }; + Ok(out) + }; + let mut metadata = Err("some errro"); + let icon = match &self.player { + PlayerctlDevice::Some(player) => { + metadata = player.get_metadata().or_else(|_| Err("")); + run_single(player)? + } + PlayerctlDevice::All(players) => { + let mut icon = Err("couldn't change any players!"); + for player in players { + let icon_new = run_single(player); + if let Ok(icon_new) = icon_new { + if icon.is_err() { + icon = Ok(icon_new); + } + }; + if let Err(_) = metadata { + metadata = player.get_metadata().or_else(|_| Err("")); + } + } + icon? + } + }; - self.icon = Some(icon.to_string()); - let label = if let Ok(metadata) = metadata { - Some(self.fmt_string(metadata)) - } else { - None - }; - self.label = label; - Ok(()) - } - fn fmt_string(&self, metadata: mpris::Metadata) -> String { - use std::collections::HashMap; - use strfmt::{strfmt, Format}; + self.icon = Some(icon.to_string()); + let label = if let Ok(metadata) = metadata { + Some(self.fmt_string(metadata)) + } else { + None + }; + self.label = label; + Ok(()) + } + fn fmt_string(&self, metadata: mpris::Metadata) -> String { + use std::collections::HashMap; + use strfmt::{strfmt, Format}; - let mut vars = HashMap::new(); - let artists = metadata.artists().unwrap_or(vec![""]); - let artists_album = metadata.album_artists().unwrap_or(vec![""]); - let artist = artists.get(0).map_or("", |v| v); - let artist_album = artists_album.get(0).map_or("", |v| v); + let mut vars = HashMap::new(); + let artists = metadata.artists().unwrap_or(vec![""]); + let artists_album = metadata.album_artists().unwrap_or(vec![""]); + let artist = artists.get(0).map_or("", |v| v); + let artist_album = artists_album.get(0).map_or("", |v| v); - let title = metadata.title().unwrap_or(""); - let album = metadata.album_name().unwrap_or(""); - let track_num = metadata - .track_number() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); - let disc_num = metadata - .disc_number() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); - let autorating = metadata - .auto_rating() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); + let title = metadata.title().unwrap_or(""); + let album = metadata.album_name().unwrap_or(""); + let track_num = metadata + .track_number() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); + let disc_num = metadata + .disc_number() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); + let autorating = metadata + .auto_rating() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); - vars.insert("artist".to_string(), artist); - vars.insert("albumArtist".to_string(), artist_album); - vars.insert("title".to_string(), title); - vars.insert("trackNumber".to_string(), &track_num); - vars.insert("discNumber".to_string(), &disc_num); - vars.insert("autoRating".to_string(), &autorating); + vars.insert("artist".to_string(), artist); + vars.insert("albumArtist".to_string(), artist_album); + vars.insert("title".to_string(), title); + vars.insert("trackNumber".to_string(), &track_num); + vars.insert("discNumber".to_string(), &disc_num); + vars.insert("autoRating".to_string(), &autorating); - self.fmt_str - .clone() - .unwrap_or("{artist} - {title}".into()) - .format(&vars) - .unwrap_or_else(|e| { - eprintln!("error: {}. using default string", e); - "{artist} - {title}".format(&vars).unwrap() - }) - } + self.fmt_str + .clone() + .unwrap_or("{artist} - {title}".into()) + .format(&vars) + .unwrap_or_else(|e| { + eprintln!("error: {}. using default string", e); + "{artist} - {title}".format(&vars).unwrap() + }) + } } impl PlayerctlAction { - pub fn from(action: &str) -> Result<Self, String> { - use PlayerctlAction::*; - match action { - "play-pause" => Ok(PlayPause), - "play" => Ok(Play), - "pause" => Ok(Pause), - "stop" => Ok(Stop), - "next" => Ok(Next), - "prev" | "previous" => Ok(Prev), - "shuffle" => Ok(Shuffle), - x => Err(x.to_string()), - } - } + pub fn from(action: &str) -> Result<Self, String> { + use PlayerctlAction::*; + match action { + "play-pause" => Ok(PlayPause), + "play" => Ok(Play), + "pause" => Ok(Pause), + "stop" => Ok(Stop), + "next" => Ok(Next), + "prev" | "previous" => Ok(Prev), + "shuffle" => Ok(Shuffle), + x => Err(x.to_string()), + } + } } impl PlayerctlDeviceRaw { - pub fn from(player: String) -> Result<Self, ()> { - use PlayerctlDeviceRaw::*; - match player.as_str() { - "auto" | "" => Ok(None), - "all" => Ok(All), - _ => Ok(Some(player)), - } - } + pub fn from(player: String) -> Result<Self, ()> { + use PlayerctlDeviceRaw::*; + match player.as_str() { + "auto" | "" => Ok(None), + "all" => Ok(All), + _ => Ok(Some(player)), + } + } } diff --git a/src/server/application.rs b/src/server/application.rs index 22c2e33..b6dbfda 100644 --- a/src/server/application.rs +++ b/src/server/application.rs @@ -5,15 +5,15 @@ use crate::osd_window::SwayosdWindow; use crate::utils::{self, *}; use async_channel::Receiver; use gtk::{ - gdk, - gio::{ - self, ApplicationFlags, BusNameWatcherFlags, BusType, DBusSignalFlags, SignalSubscriptionId, - }, - glib::{ - clone, variant::ToVariant, Char, ControlFlow::Break, MainContext, OptionArg, OptionFlags, - }, - prelude::*, - Application, + gdk, + gio::{ + self, ApplicationFlags, BusNameWatcherFlags, BusType, DBusSignalFlags, SignalSubscriptionId, + }, + glib::{ + clone, variant::ToVariant, Char, ControlFlow::Break, MainContext, OptionArg, OptionFlags, + }, + prelude::*, + Application, }; use pulsectl::controllers::{SinkController, SourceController}; use std::cell::RefCell; @@ -24,506 +24,512 @@ use super::config::user::ServerConfig; #[derive(Clone, Shrinkwrap)] pub struct SwayOSDApplication { - #[shrinkwrap(main_field)] - app: gtk::Application, - windows: Rc<RefCell<Vec<SwayosdWindow>>>, - _hold: Rc<gio::ApplicationHoldGuard>, + #[shrinkwrap(main_field)] + app: gtk::Application, + windows: Rc<RefCell<Vec<SwayosdWindow>>>, + _hold: Rc<gio::ApplicationHoldGuard>, } impl SwayOSDApplication { - pub fn new(server_config: Arc<ServerConfig>, action_receiver: Receiver<(ArgTypes, String)>) -> Self { - let app = Application::new(Some(APPLICATION_NAME), ApplicationFlags::FLAGS_NONE); - let hold = Rc::new(app.hold()); - - app.add_main_option( - "config", - Char::from(0), - OptionFlags::NONE, - OptionArg::String, - "Use a custom config file instead of looking for one.", - Some("<CONFIG FILE PATH>"), - ); - - app.add_main_option( - "style", - Char::from('s' as u8), - OptionFlags::NONE, - OptionArg::String, - "Use a custom Stylesheet file instead of looking for one", - Some("<CSS FILE PATH>"), - ); - - app.add_main_option( - "top-margin", - Char::from(0), - OptionFlags::NONE, - OptionArg::String, - &format!( - "OSD margin from top edge (0.5 would be screen center). Default is {}", - *utils::TOP_MARGIN_DEFAULT - ), - Some("<from 0.0 to 1.0>"), - ); - - let osd_app = SwayOSDApplication { - app: app.clone(), - windows: Rc::new(RefCell::new(Vec::new())), - _hold: hold, - }; - - // Apply Server Config - if let Some(margin) = server_config.top_margin { - if (0_f32..1_f32).contains(&margin) { - set_top_margin(margin); - } - } - if let Some(max_volume) = server_config.max_volume { - set_default_max_volume(max_volume); - } - if let Some(show) = server_config.show_percentage { - set_show_percentage(show); - } - - let server_config_shared = server_config.clone(); - - // Parse args - app.connect_handle_local_options(clone!( - #[strong] - osd_app, - move |_app, args| { - let actions = match handle_application_args(args.to_variant()) { - (HandleLocalStatus::SUCCESS | HandleLocalStatus::CONTINUE, actions) => actions, - (status @ HandleLocalStatus::FAILURE, _) => return status as i32, - }; - for (arg_type, data) in actions { - match (arg_type, data) { - (ArgTypes::TopMargin, margin) => { - let margin: Option<f32> = margin - .and_then(|margin| margin.parse().ok()) - .and_then(|margin| { - (0_f32..1_f32).contains(&margin).then_some(margin) - }); - - if let Some(margin) = margin { - set_top_margin(margin) - } - } - (ArgTypes::MaxVolume, max) => { - let max: Option<u8> = max.and_then(|max| max.parse().ok()); - - if let Some(max) = max { - set_default_max_volume(max); - } - } - (arg_type, data) => { - Self::action_activated(&osd_app, server_config_shared.clone(), arg_type, data) - } - } - } - - HandleLocalStatus::CONTINUE as i32 - } - )); - - let server_config_shared = server_config.clone(); - - MainContext::default().spawn_local(clone!( - #[strong] - osd_app, - async move { - while let Ok((arg_type, data)) = action_receiver.recv().await { - Self::action_activated( - &osd_app, - server_config_shared.clone(), - arg_type, - (!data.is_empty()).then_some(data), - ); - } - Break - } - )); - - let server_config_shared = server_config.clone(); - - // Listen to the LibInput Backend and activate the Application action - let (sender, receiver) = async_channel::bounded::<(u16, i32)>(1); - MainContext::default().spawn_local(clone!( - #[strong] - osd_app, - async move { - while let Ok((key_code, state)) = receiver.recv().await { - let (arg_type, data): (ArgTypes, Option<String>) = - match evdev_rs::enums::int_to_ev_key(key_code as u32) { - Some(evdev_rs::enums::EV_KEY::KEY_CAPSLOCK) => { - (ArgTypes::CapsLock, Some(state.to_string())) - } - Some(evdev_rs::enums::EV_KEY::KEY_NUMLOCK) => { - (ArgTypes::NumLock, Some(state.to_string())) - } - Some(evdev_rs::enums::EV_KEY::KEY_SCROLLLOCK) => { - (ArgTypes::ScrollLock, Some(state.to_string())) - } - _ => continue, - }; - Self::action_activated(&osd_app, server_config_shared.clone(), arg_type, data); - } - Break - } - )); - // Start watching for the LibInput Backend - let signal_id: Arc<Mutex<Option<SignalSubscriptionId>>> = Arc::new(Mutex::new(None)); - gio::bus_watch_name( - BusType::System, - DBUS_BACKEND_NAME, - BusNameWatcherFlags::NONE, - clone!( - #[strong] - sender, - #[strong] - signal_id, - move |connection, _, _| { - println!("Connecting to the SwayOSD LibInput Backend"); - let mut mutex = match signal_id.lock() { - Ok(mut mutex) => match mutex.as_mut() { - Some(_) => return, - None => mutex, - }, - Err(error) => return println!("Mutex lock Error: {}", error), - }; - mutex.replace(connection.signal_subscribe( - Some(config::DBUS_BACKEND_NAME), - Some(config::DBUS_BACKEND_NAME), - Some("KeyPressed"), - Some(config::DBUS_PATH), - None, - DBusSignalFlags::NONE, - clone!( - #[strong] - sender, - move |_, _, _, _, _, variant| { - let key_code = variant.try_child_get::<u16>(0); - let state = variant.try_child_get::<i32>(1); - match (key_code, state) { - (Ok(Some(key_code)), Ok(Some(state))) => { - MainContext::default().spawn_local(clone!( - #[strong] - sender, - async move { - if let Err(error) = - sender.send((key_code, state)).await - { - eprintln!("Channel Send error: {}", error); - } - } - )); - } - variables => { - return eprintln!("Variables don't match: {:?}", variables) - } - }; - } - ), - )); - } - ), - clone!( - #[strong] - signal_id, - move |connection, _| { - eprintln!("SwayOSD LibInput Backend isn't available, waiting..."); - match signal_id.lock() { - Ok(mut mutex) => { - if let Some(sig_id) = mutex.take() { - connection.signal_unsubscribe(sig_id); - } - } - Err(error) => println!("Mutex lock Error: {}", error), - } - } - ), - ); - - return osd_app; - } - - pub fn start(&self) -> i32 { - let s = self.clone(); - self.app.connect_activate(move |_| { - s.initialize(); - }); - - let _ = self.app.register(gio::Cancellable::NONE); - self.app.run().into() - } - - fn action_activated( - osd_app: &SwayOSDApplication, - server_config: Arc<ServerConfig>, - arg_type: ArgTypes, - value: Option<String>, - ) { - match (arg_type, value) { - (ArgTypes::SinkVolumeRaise, step) => { - let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Raise, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SinkVolumeLower, step) => { - let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Lower, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SinkVolumeMuteToggle, _) => { - let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SourceVolumeRaise, step) => { - let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Raise, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SourceVolumeLower, step) => { - let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::Lower, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - (ArgTypes::SourceVolumeMuteToggle, _) => { - let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); - if let Some(device) = - change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_volume(&device, &device_type); - } - } - reset_max_volume(); - reset_device_name(); - } - // TODO: Brightness - (ArgTypes::BrightnessRaise, step) => { - if let Ok(mut brightness_backend) = - change_brightness(BrightnessChangeType::Raise, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_brightness(brightness_backend.as_mut()); - } - } - } - (ArgTypes::BrightnessLower, step) => { - if let Ok(mut brightness_backend) = - change_brightness(BrightnessChangeType::Lower, step) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_brightness(brightness_backend.as_mut()); - } - } - } - (ArgTypes::BrightnessSet, value) => { - if let Ok(mut brightness_backend) = - change_brightness(BrightnessChangeType::Set, value) - { - for window in osd_app.windows.borrow().to_owned() { - window.changed_brightness(brightness_backend.as_mut()); - } - } - } - (ArgTypes::CapsLock, value) => { - let i32_value = value.clone().unwrap_or("-1".to_owned()); - let state = match i32_value.parse::<i32>() { - Ok(value) if value >= 0 && value <= 1 => value == 1, - _ => get_key_lock_state(KeysLocks::CapsLock, value), - }; - for window in osd_app.windows.borrow().to_owned() { - window.changed_keylock(KeysLocks::CapsLock, state) - } - } - (ArgTypes::NumLock, value) => { - let i32_value = value.clone().unwrap_or("-1".to_owned()); - let state = match i32_value.parse::<i32>() { - Ok(value) if value >= 0 && value <= 1 => value == 1, - _ => get_key_lock_state(KeysLocks::NumLock, value), - }; - for window in osd_app.windows.borrow().to_owned() { - window.changed_keylock(KeysLocks::NumLock, state) - } - } - (ArgTypes::ScrollLock, value) => { - let i32_value = value.clone().unwrap_or("-1".to_owned()); - let state = match i32_value.parse::<i32>() { - Ok(value) if value >= 0 && value <= 1 => value == 1, - _ => get_key_lock_state(KeysLocks::ScrollLock, value), - }; - for window in osd_app.windows.borrow().to_owned() { - window.changed_keylock(KeysLocks::ScrollLock, state) - } - } - (ArgTypes::MaxVolume, max) => { - let volume: u8 = match max { - Some(max) => match max.parse() { - Ok(max) => max, - _ => get_default_max_volume(), - }, - _ => get_default_max_volume(), - }; - set_max_volume(volume) - } - (ArgTypes::Player, name) => set_player(name.unwrap_or("".to_string())), - (ArgTypes::Playerctl, value) => { - use crate::playerctl::*; - let value = &value.unwrap_or("".to_string()); - - let action = PlayerctlAction::from(value).unwrap(); - if let Ok(mut player) = Playerctl::new(action, server_config) { - match player.run() { - Ok(_) => { - let (icon, label) = (player.icon.unwrap(), player.label.unwrap()); - for window in osd_app.windows.borrow().to_owned() { - window.changed_player(&icon, &label) - } - } - Err(x) => { - eprintln!("couldn't run player change: \"{:?}\"!", x) - } - } - } else { - eprintln!("Unable to get players! are any opened?") - } - - reset_player(); - } - (ArgTypes::DeviceName, name) => { - set_device_name(name.unwrap_or(DEVICE_NAME_DEFAULT.to_string())) - } - (ArgTypes::CustomMessage, message) => { - if let Some(message) = message { - for window in osd_app.windows.borrow().to_owned() { - window.custom_message(message.as_str(), get_icon_name().as_deref()); - } - } - reset_icon_name(); - } - (ArgTypes::CustomIcon, icon) => { - set_icon_name(icon.unwrap_or(ICON_NAME_DEFAULT.to_string())) - } - (arg_type, data) => { - eprintln!( - "Failed to parse command... Type: {:?}, Data: {:?}", - arg_type, data - ) - } - }; - } - - fn initialize(&self) { - let display: gdk::Display = match gdk::Display::default() { - Some(x) => x, - _ => return, - }; - - self.init_windows(&display); - - let _self = self; - - display.connect_opened(clone!( - #[strong] - _self, - move |d| { - _self.init_windows(d); - } - )); - - display.connect_closed(clone!( - #[strong] - _self, - move |_d, is_error| { - if is_error { - eprintln!("Display closed due to errors..."); - } - _self.close_all_windows(); - } - )); - - display.monitors().connect_items_changed(clone!( - #[strong] - _self, - move |monitors, position, removed, added| { - if removed != 0 { - _self.init_windows(&display); - } else if added != 0 { - for i in 0..added { - if let Some(mon) = monitors - .item(position + i) - .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) - { - _self.add_window(&display, &mon); - } - } - } - } - )); - } - - fn add_window(&self, display: &gdk::Display, monitor: &gdk::Monitor) { - let win = SwayosdWindow::new(&self.app, display, monitor); - self.windows.borrow_mut().push(win); - } - - fn init_windows(&self, display: &gdk::Display) { - self.close_all_windows(); - - let monitors = display.monitors(); - for i in 0..monitors.n_items() { - let monitor = match monitors - .item(i) - .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) - { - Some(x) => x, - _ => continue, - }; - self.add_window(display, &monitor); - } - } - - fn close_all_windows(&self) { - self.windows.borrow_mut().retain(|window| { - window.close(); - false - }); - } + pub fn new( + server_config: Arc<ServerConfig>, + action_receiver: Receiver<(ArgTypes, String)>, + ) -> Self { + let app = Application::new(Some(APPLICATION_NAME), ApplicationFlags::FLAGS_NONE); + let hold = Rc::new(app.hold()); + + app.add_main_option( + "config", + Char::from(0), + OptionFlags::NONE, + OptionArg::String, + "Use a custom config file instead of looking for one.", + Some("<CONFIG FILE PATH>"), + ); + + app.add_main_option( + "style", + Char::from('s' as u8), + OptionFlags::NONE, + OptionArg::String, + "Use a custom Stylesheet file instead of looking for one", + Some("<CSS FILE PATH>"), + ); + + app.add_main_option( + "top-margin", + Char::from(0), + OptionFlags::NONE, + OptionArg::String, + &format!( + "OSD margin from top edge (0.5 would be screen center). Default is {}", + *utils::TOP_MARGIN_DEFAULT + ), + Some("<from 0.0 to 1.0>"), + ); + + let osd_app = SwayOSDApplication { + app: app.clone(), + windows: Rc::new(RefCell::new(Vec::new())), + _hold: hold, + }; + + // Apply Server Config + if let Some(margin) = server_config.top_margin { + if (0_f32..1_f32).contains(&margin) { + set_top_margin(margin); + } + } + if let Some(max_volume) = server_config.max_volume { + set_default_max_volume(max_volume); + } + if let Some(show) = server_config.show_percentage { + set_show_percentage(show); + } + + let server_config_shared = server_config.clone(); + + // Parse args + app.connect_handle_local_options(clone!( + #[strong] + osd_app, + move |_app, args| { + let actions = match handle_application_args(args.to_variant()) { + (HandleLocalStatus::SUCCESS | HandleLocalStatus::CONTINUE, actions) => actions, + (status @ HandleLocalStatus::FAILURE, _) => return status as i32, + }; + for (arg_type, data) in actions { + match (arg_type, data) { + (ArgTypes::TopMargin, margin) => { + let margin: Option<f32> = margin + .and_then(|margin| margin.parse().ok()) + .and_then(|margin| { + (0_f32..1_f32).contains(&margin).then_some(margin) + }); + + if let Some(margin) = margin { + set_top_margin(margin) + } + } + (ArgTypes::MaxVolume, max) => { + let max: Option<u8> = max.and_then(|max| max.parse().ok()); + + if let Some(max) = max { + set_default_max_volume(max); + } + } + (arg_type, data) => Self::action_activated( + &osd_app, + server_config_shared.clone(), + arg_type, + data, + ), + } + } + + HandleLocalStatus::CONTINUE as i32 + } + )); + + let server_config_shared = server_config.clone(); + + MainContext::default().spawn_local(clone!( + #[strong] + osd_app, + async move { + while let Ok((arg_type, data)) = action_receiver.recv().await { + Self::action_activated( + &osd_app, + server_config_shared.clone(), + arg_type, + (!data.is_empty()).then_some(data), + ); + } + Break + } + )); + + let server_config_shared = server_config.clone(); + + // Listen to the LibInput Backend and activate the Application action + let (sender, receiver) = async_channel::bounded::<(u16, i32)>(1); + MainContext::default().spawn_local(clone!( + #[strong] + osd_app, + async move { + while let Ok((key_code, state)) = receiver.recv().await { + let (arg_type, data): (ArgTypes, Option<String>) = + match evdev_rs::enums::int_to_ev_key(key_code as u32) { + Some(evdev_rs::enums::EV_KEY::KEY_CAPSLOCK) => { + (ArgTypes::CapsLock, Some(state.to_string())) + } + Some(evdev_rs::enums::EV_KEY::KEY_NUMLOCK) => { + (ArgTypes::NumLock, Some(state.to_string())) + } + Some(evdev_rs::enums::EV_KEY::KEY_SCROLLLOCK) => { + (ArgTypes::ScrollLock, Some(state.to_string())) + } + _ => continue, + }; + Self::action_activated(&osd_app, server_config_shared.clone(), arg_type, data); + } + Break + } + )); + // Start watching for the LibInput Backend + let signal_id: Arc<Mutex<Option<SignalSubscriptionId>>> = Arc::new(Mutex::new(None)); + gio::bus_watch_name( + BusType::System, + DBUS_BACKEND_NAME, + BusNameWatcherFlags::NONE, + clone!( + #[strong] + sender, + #[strong] + signal_id, + move |connection, _, _| { + println!("Connecting to the SwayOSD LibInput Backend"); + let mut mutex = match signal_id.lock() { + Ok(mut mutex) => match mutex.as_mut() { + Some(_) => return, + None => mutex, + }, + Err(error) => return println!("Mutex lock Error: {}", error), + }; + mutex.replace(connection.signal_subscribe( + Some(config::DBUS_BACKEND_NAME), + Some(config::DBUS_BACKEND_NAME), + Some("KeyPressed"), + Some(config::DBUS_PATH), + None, + DBusSignalFlags::NONE, + clone!( + #[strong] + sender, + move |_, _, _, _, _, variant| { + let key_code = variant.try_child_get::<u16>(0); + let state = variant.try_child_get::<i32>(1); + match (key_code, state) { + (Ok(Some(key_code)), Ok(Some(state))) => { + MainContext::default().spawn_local(clone!( + #[strong] + sender, + async move { + if let Err(error) = + sender.send((key_code, state)).await + { + eprintln!("Channel Send error: {}", error); + } + } + )); + } + variables => { + return eprintln!("Variables don't match: {:?}", variables) + } + }; + } + ), + )); + } + ), + clone!( + #[strong] + signal_id, + move |connection, _| { + eprintln!("SwayOSD LibInput Backend isn't available, waiting..."); + match signal_id.lock() { + Ok(mut mutex) => { + if let Some(sig_id) = mutex.take() { + connection.signal_unsubscribe(sig_id); + } + } + Err(error) => println!("Mutex lock Error: {}", error), + } + } + ), + ); + + return osd_app; + } + + pub fn start(&self) -> i32 { + let s = self.clone(); + self.app.connect_activate(move |_| { + s.initialize(); + }); + + let _ = self.app.register(gio::Cancellable::NONE); + self.app.run().into() + } + + fn action_activated( + osd_app: &SwayOSDApplication, + server_config: Arc<ServerConfig>, + arg_type: ArgTypes, + value: Option<String>, + ) { + match (arg_type, value) { + (ArgTypes::SinkVolumeRaise, step) => { + let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Raise, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SinkVolumeLower, step) => { + let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Lower, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SinkVolumeMuteToggle, _) => { + let mut device_type = VolumeDeviceType::Sink(SinkController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SourceVolumeRaise, step) => { + let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Raise, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SourceVolumeLower, step) => { + let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::Lower, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + (ArgTypes::SourceVolumeMuteToggle, _) => { + let mut device_type = VolumeDeviceType::Source(SourceController::create().unwrap()); + if let Some(device) = + change_device_volume(&mut device_type, VolumeChangeType::MuteToggle, None) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_volume(&device, &device_type); + } + } + reset_max_volume(); + reset_device_name(); + } + // TODO: Brightness + (ArgTypes::BrightnessRaise, step) => { + if let Ok(mut brightness_backend) = + change_brightness(BrightnessChangeType::Raise, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_brightness(brightness_backend.as_mut()); + } + } + } + (ArgTypes::BrightnessLower, step) => { + if let Ok(mut brightness_backend) = + change_brightness(BrightnessChangeType::Lower, step) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_brightness(brightness_backend.as_mut()); + } + } + } + (ArgTypes::BrightnessSet, value) => { + if let Ok(mut brightness_backend) = + change_brightness(BrightnessChangeType::Set, value) + { + for window in osd_app.windows.borrow().to_owned() { + window.changed_brightness(brightness_backend.as_mut()); + } + } + } + (ArgTypes::CapsLock, value) => { + let i32_value = value.clone().unwrap_or("-1".to_owned()); + let state = match i32_value.parse::<i32>() { + Ok(value) if value >= 0 && value <= 1 => value == 1, + _ => get_key_lock_state(KeysLocks::CapsLock, value), + }; + for window in osd_app.windows.borrow().to_owned() { + window.changed_keylock(KeysLocks::CapsLock, state) + } + } + (ArgTypes::NumLock, value) => { + let i32_value = value.clone().unwrap_or("-1".to_owned()); + let state = match i32_value.parse::<i32>() { + Ok(value) if value >= 0 && value <= 1 => value == 1, + _ => get_key_lock_state(KeysLocks::NumLock, value), + }; + for window in osd_app.windows.borrow().to_owned() { + window.changed_keylock(KeysLocks::NumLock, state) + } + } + (ArgTypes::ScrollLock, value) => { + let i32_value = value.clone().unwrap_or("-1".to_owned()); + let state = match i32_value.parse::<i32>() { + Ok(value) if value >= 0 && value <= 1 => value == 1, + _ => get_key_lock_state(KeysLocks::ScrollLock, value), + }; + for window in osd_app.windows.borrow().to_owned() { + window.changed_keylock(KeysLocks::ScrollLock, state) + } + } + (ArgTypes::MaxVolume, max) => { + let volume: u8 = match max { + Some(max) => match max.parse() { + Ok(max) => max, + _ => get_default_max_volume(), + }, + _ => get_default_max_volume(), + }; + set_max_volume(volume) + } + (ArgTypes::Player, name) => set_player(name.unwrap_or("".to_string())), + (ArgTypes::Playerctl, value) => { + use crate::playerctl::*; + let value = &value.unwrap_or("".to_string()); + + let action = PlayerctlAction::from(value).unwrap(); + if let Ok(mut player) = Playerctl::new(action, server_config) { + match player.run() { + Ok(_) => { + let (icon, label) = (player.icon.unwrap(), player.label.unwrap()); + for window in osd_app.windows.borrow().to_owned() { + window.changed_player(&icon, &label) + } + } + Err(x) => { + eprintln!("couldn't run player change: \"{:?}\"!", x) + } + } + } else { + eprintln!("Unable to get players! are any opened?") + } + + reset_player(); + } + (ArgTypes::DeviceName, name) => { + set_device_name(name.unwrap_or(DEVICE_NAME_DEFAULT.to_string())) + } + (ArgTypes::CustomMessage, message) => { + if let Some(message) = message { + for window in osd_app.windows.borrow().to_owned() { + window.custom_message(message.as_str(), get_icon_name().as_deref()); + } + } + reset_icon_name(); + } + (ArgTypes::CustomIcon, icon) => { + set_icon_name(icon.unwrap_or(ICON_NAME_DEFAULT.to_string())) + } + (arg_type, data) => { + eprintln!( + "Failed to parse command... Type: {:?}, Data: {:?}", + arg_type, data + ) + } + }; + } + + fn initialize(&self) { + let display: gdk::Display = match gdk::Display::default() { + Some(x) => x, + _ => return, + }; + + self.init_windows(&display); + + let _self = self; + + display.connect_opened(clone!( + #[strong] + _self, + move |d| { + _self.init_windows(d); + } + )); + + display.connect_closed(clone!( + #[strong] + _self, + move |_d, is_error| { + if is_error { + eprintln!("Display closed due to errors..."); + } + _self.close_all_windows(); + } + )); + + display.monitors().connect_items_changed(clone!( + #[strong] + _self, + move |monitors, position, removed, added| { + if removed != 0 { + _self.init_windows(&display); + } else if added != 0 { + for i in 0..added { + if let Some(mon) = monitors + .item(position + i) + .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) + { + _self.add_window(&display, &mon); + } + } + } + } + )); + } + + fn add_window(&self, display: &gdk::Display, monitor: &gdk::Monitor) { + let win = SwayosdWindow::new(&self.app, display, monitor); + self.windows.borrow_mut().push(win); + } + + fn init_windows(&self, display: &gdk::Display) { + self.close_all_windows(); + + let monitors = display.monitors(); + for i in 0..monitors.n_items() { + let monitor = match monitors + .item(i) + .and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) + { + Some(x) => x, + _ => continue, + }; + self.add_window(display, &monitor); + } + } + + fn close_all_windows(&self) { + self.windows.borrow_mut().retain(|window| { + window.close(); + false + }); + } } diff --git a/src/server/main.rs b/src/server/main.rs index 264e166..5f56b6b 100644 --- a/src/server/main.rs +++ b/src/server/main.rs @@ -26,142 +26,137 @@ use argtypes::ArgTypes; use async_channel::Sender; use config::{DBUS_PATH, DBUS_SERVER_NAME}; use gtk::{ - gdk::Display, - gio::{self, Resource}, - glib::Bytes, - CssProvider, IconTheme, -}; -use std::{ - env::args_os, - future::pending, - path::PathBuf, - str::FromStr, - sync::Arc, + gdk::Display, + gio::{self, Resource}, + glib::Bytes, + CssProvider, IconTheme, }; +use std::{env::args_os, future::pending, path::PathBuf, str::FromStr, sync::Arc}; use utils::{get_system_css_path, user_style_path}; use zbus::{interface, ConnectionBuilder}; struct DbusServer { - sender: Sender<(ArgTypes, String)>, + sender: Sender<(ArgTypes, String)>, } #[interface(name = "org.erikreider.swayosd")] impl DbusServer { - pub async fn handle_action(&self, arg_type: String, data: String) -> bool { - let arg_type = match ArgTypes::from_str(&arg_type) { - Ok(arg_type) => arg_type, - Err(other_type) => { - eprintln!("Unknown action in Dbus handle_action: {:?}", other_type); - return false; - } - }; - if let Err(error) = self.sender.send((arg_type, data)).await { - eprintln!("Channel Send error: {}", error); - return false; - } - true - } + pub async fn handle_action(&self, arg_type: String, data: String) -> bool { + let arg_type = match ArgTypes::from_str(&arg_type) { + Ok(arg_type) => arg_type, + Err(other_type) => { + eprintln!("Unknown action in Dbus handle_action: {:?}", other_type); + return false; + } + }; + if let Err(error) = self.sender.send((arg_type, data)).await { + eprintln!("Channel Send error: {}", error); + return false; + } + true + } } impl DbusServer { - async fn new(sender: Sender<(ArgTypes, String)>) -> zbus::Result<()> { - let _connection = ConnectionBuilder::session()? - .name(DBUS_SERVER_NAME)? - .serve_at(DBUS_PATH, DbusServer { sender })? - .build() - .await?; - pending::<()>().await; - Ok(()) - } + async fn new(sender: Sender<(ArgTypes, String)>) -> zbus::Result<()> { + let _connection = ConnectionBuilder::session()? + .name(DBUS_SERVER_NAME)? + .serve_at(DBUS_PATH, DbusServer { sender })? + .build() + .await?; + pending::<()>().await; + Ok(()) + } } const GRESOURCE_BASE_PATH: &str = "/org/erikreider/swayosd"; fn main() { - if gtk::init().is_err() { - eprintln!("failed to initialize GTK Application"); - std::process::exit(1); - } - - // Load the compiled resource bundle - let resources_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/swayosd.gresource")); - let resource_data = Bytes::from(&resources_bytes[..]); - let res = Resource::from_data(&resource_data).unwrap(); - gio::resources_register(&res); - - // Load the icon theme - let theme = IconTheme::default(); - theme.add_resource_path(&format!("{}/icons", GRESOURCE_BASE_PATH)); - - // Load the CSS themes - let display = Display::default().expect("Failed getting the default screen"); - - // Load the provided default CSS theme - let provider = CssProvider::new(); - provider.connect_parsing_error(|_provider, _section, error| { - eprintln!("Could not load default CSS stylesheet: {}", error); - }); - match get_system_css_path() { - Some(path) => { - provider.load_from_path(path.to_str().unwrap()); - gtk::style_context_add_provider_for_display( - &display, - &provider, - gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, - ); - } - None => eprintln!("Could not find the system CSS file..."), - } - - // Get config path and CSS theme path from command line - let mut config_path: Option<PathBuf> = None; - let mut custom_user_css: Option<PathBuf> = None; - let mut args = args_os().into_iter(); - while let Some(arg) = args.next() { - match arg.to_str() { - Some("--config") => { - if let Some(path) = args.next() { - config_path = Some(path.into()); - } - } - Some("-s") | Some("--style") => { - if let Some(path) = args.next() { - custom_user_css = Some(path.into()); - } - } - _ => (), - } - } - - // Parse Config - let server_config = Arc::new(config::user::read_user_config(config_path.as_deref()) - .expect("Failed to parse config file") - .server - ); - - // Load style path from config if none is given on CLI - if custom_user_css.is_none() { - custom_user_css = server_config.style.clone(); - } - - // Try loading the users CSS theme - if let Some(user_config_path) = user_style_path(custom_user_css) { - let user_provider = CssProvider::new(); - user_provider.connect_parsing_error(|_provider, _section, error| { - eprintln!("Failed loading user defined style.css: {}", error); - }); - user_provider.load_from_path(&user_config_path); - gtk::style_context_add_provider_for_display( - &display, - &user_provider, - gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, - ); - println!("Loaded user defined CSS file"); - } - - let (sender, receiver) = async_channel::bounded::<(ArgTypes, String)>(1); - // Start the DBus Server - async_std::task::spawn(DbusServer::new(sender)); - // Start the GTK Application - std::process::exit(SwayOSDApplication::new(server_config, receiver).start()); + if gtk::init().is_err() { + eprintln!("failed to initialize GTK Application"); + std::process::exit(1); + } + + // Load the compiled resource bundle + let resources_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/swayosd.gresource")); + let resource_data = Bytes::from(&resources_bytes[..]); + let res = Resource::from_data(&resource_data).unwrap(); + gio::resources_register(&res); + + // Load the icon theme + let theme = IconTheme::default(); + theme.add_resource_path(&format!("{}/icons", GRESOURCE_BASE_PATH)); + + // Load the CSS themes + let display = Display::default().expect("Failed getting the default screen"); + + // Load the provided default CSS theme + let provider = CssProvider::new(); + provider.connect_parsing_error(|_provider, _section, error| { + eprintln!("Could not load default CSS stylesheet: {}", error); + }); + match get_system_css_path() { + Some(path) => { + provider.load_from_path(path.to_str().unwrap()); + gtk::style_context_add_provider_for_display( + &display, + &provider, + gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, + ); + } + None => eprintln!("Could not find the system CSS file..."), + } + + // Get config path and CSS theme path from command line + let mut config_path: Option<PathBuf> = None; + let mut custom_user_css: Option<PathBuf> = None; + let mut args = args_os().into_iter(); + while let Some(arg) = args.next() { + match arg.to_str() { + Some("--config") => { + if let Some(path) = args.next() { + config_path = Some(path.into()); + } + } + Some("-s") | Some("--style") => { + if let Some(path) = args.next() { + custom_user_css = Some(path.into()); + } + } + _ => (), + } + } + + // Parse Config + let server_config = Arc::new( + config::user::read_user_config(config_path.as_deref()) + .expect("Failed to parse config file") + .server, + ); + + // Load style path from config if none is given on CLI + if custom_user_css.is_none() { + custom_user_css = server_config.style.clone(); + } + + // Try loading the users CSS theme + if let Some(user_config_path) = user_style_path(custom_user_css) { + let user_provider = CssProvider::new(); + user_provider.connect_parsing_error(|_provider, _section, error| { + eprintln!("Failed loading user defined style.css: {}", error); + }); + user_provider.load_from_path(&user_config_path); + gtk::style_context_add_provider_for_display( + &display, + &user_provider, + gtk::STYLE_PROVIDER_PRIORITY_APPLICATION as u32, + ); + println!("Loaded user defined CSS file"); + } + + let (sender, receiver) = async_channel::bounded::<(ArgTypes, String)>(1); + // Start the DBus Server + async_std::task::spawn(DbusServer::new(sender)); + // Start the GTK Application + std::process::exit(SwayOSDApplication::new(server_config, receiver).start()); } From a874115c0cd4cd338cb32cbb530b2356b6827cc3 Mon Sep 17 00:00:00 2001 From: Jan Luca Oster <janluca.oster@proton.me> Date: Fri, 10 Jan 2025 13:00:51 +0100 Subject: [PATCH 09/18] Add default config examples --- data/meson.build | 4 + src/mpris-backend/mod.rs | 351 ++++++++++++++++++++------------------- 2 files changed, 180 insertions(+), 175 deletions(-) diff --git a/data/meson.build b/data/meson.build index b3099cf..5224b7f 100644 --- a/data/meson.build +++ b/data/meson.build @@ -70,3 +70,7 @@ style_css = custom_target( ) message(style_css.full_path()) + +install_data(['config/config.toml', 'config/backend.toml'], + install_dir : config_path +) diff --git a/src/mpris-backend/mod.rs b/src/mpris-backend/mod.rs index cee4eaf..1e5ffb8 100644 --- a/src/mpris-backend/mod.rs +++ b/src/mpris-backend/mod.rs @@ -4,205 +4,206 @@ use crate::utils::get_player; use std::{error::Error, sync::Arc}; pub enum PlayerctlAction { - PlayPause, - Play, - Pause, - Stop, - Next, - Prev, - Shuffle, + PlayPause, + Play, + Pause, + Stop, + Next, + Prev, + Shuffle, } use super::config::user::ServerConfig; #[derive(Clone, Debug)] pub enum PlayerctlDeviceRaw { - None, - All, - Some(String), + None, + All, + Some(String), } pub enum PlayerctlDevice { - All(Vec<Player>), - Some(Player), + All(Vec<Player>), + Some(Player), } pub struct Playerctl { - player: PlayerctlDevice, - action: PlayerctlAction, - pub icon: Option<String>, - pub label: Option<String>, - fmt_str: Option<String>, + player: PlayerctlDevice, + action: PlayerctlAction, + pub icon: Option<String>, + pub label: Option<String>, + fmt_str: Option<String>, } impl Playerctl { - pub fn new( - action: PlayerctlAction, - config: Arc<ServerConfig>, - ) -> Result<Playerctl, Box<dyn Error>> { - let playerfinder = PlayerFinder::new()?; - let player = get_player(); - let player = match player { - PlayerctlDeviceRaw::None => PlayerctlDevice::Some(playerfinder.find_active()?), - PlayerctlDeviceRaw::Some(name) => { - PlayerctlDevice::Some(playerfinder.find_by_name(name.as_str())?) - } - PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), - }; - let fmt_str = config.playerctl_format.clone(); - Ok(Self { - player, - action, - icon: None, - label: None, - fmt_str, - }) - } - pub fn run(&mut self) -> Result<(), Box<dyn Error>> { - use PlaybackStatus::*; - use PlayerctlAction::*; - let run_single = |player: &Player| -> Result<&str, Box<dyn Error>> { - let out = match self.action { - PlayPause => match player.get_playback_status()? { - Playing => { - player.pause()?; - "pause-large -symbolic" - } - Paused | Stopped => { - player.play()?; - "play-large-symbolic" - } - }, - Shuffle => { - let shuffle = player.get_shuffle()?; - player.set_shuffle(!shuffle)?; - if shuffle { - "playlist-consecutive-symbolic" - } else { - "playlist-shuffle-symbolic" - } - } - Play => { - player.play()?; - "play-large-symbolic" - } - Pause => { - player.pause()?; - "pause-large-symbolic" - } - Stop => { - player.stop()?; - "stop-large-symbolic" - } - Next => { - player.next()?; - "media-seek-forward-symbolic" - } - Prev => { - player.previous()?; - "media-seek-backward-symbolic" - } - }; - Ok(out) - }; - let mut metadata = Err("some errro"); - let icon = match &self.player { - PlayerctlDevice::Some(player) => { - metadata = player.get_metadata().or_else(|_| Err("")); - run_single(player)? - } - PlayerctlDevice::All(players) => { - let mut icon = Err("couldn't change any players!"); - for player in players { - let icon_new = run_single(player); - if let Ok(icon_new) = icon_new { - if icon.is_err() { - icon = Ok(icon_new); - } - }; - if let Err(_) = metadata { - metadata = player.get_metadata().or_else(|_| Err("")); - } - } - icon? - } - }; + pub fn new( + action: PlayerctlAction, + config: Arc<ServerConfig>, + ) -> Result<Playerctl, Box<dyn Error>> { + let playerfinder = PlayerFinder::new()?; + let player = get_player(); + let player = match player { + PlayerctlDeviceRaw::None => PlayerctlDevice::Some(playerfinder.find_active()?), + PlayerctlDeviceRaw::Some(name) => { + PlayerctlDevice::Some(playerfinder.find_by_name(name.as_str())?) + } + PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), + }; + let fmt_str = config.playerctl_format.clone(); + Ok(Self { + player, + action, + icon: None, + label: None, + fmt_str, + }) + } + pub fn run(&mut self) -> Result<(), Box<dyn Error>> { + use PlaybackStatus::*; + use PlayerctlAction::*; + let run_single = |player: &Player| -> Result<&str, Box<dyn Error>> { + let out = match self.action { + PlayPause => match player.get_playback_status()? { + Playing => { + player.pause()?; + "pause-large-symbolic" + } + Paused | Stopped => { + player.play()?; + "play-large-symbolic" + } + }, + Shuffle => { + let shuffle = player.get_shuffle()?; + player.set_shuffle(!shuffle)?; + if shuffle { + "playlist-consecutive-symbolic" + } else { + "playlist-shuffle-symbolic" + } + } + Play => { + player.play()?; + "play-large-symbolic" + } + Pause => { + player.pause()?; + "pause-large-symbolic" + } + Stop => { + player.stop()?; + "stop-large-symbolic" + } + Next => { + player.next()?; + "media-seek-forward-symbolic" + } + Prev => { + player.previous()?; + "media-seek-backward-symbolic" + } + }; + Ok(out) + }; + let mut metadata = Err("some errro"); + let icon = match &self.player { + PlayerctlDevice::Some(player) => { + metadata = player.get_metadata().or_else(|_| Err("")); + run_single(player)? + } + PlayerctlDevice::All(players) => { + let mut icon = Err("couldn't change any players!"); + for player in players { + let icon_new = run_single(player); + if let Ok(icon_new) = icon_new { + if icon.is_err() { + icon = Ok(icon_new); + } + }; + if let Err(_) = metadata { + metadata = player.get_metadata().or_else(|_| Err("")); + } + } + icon? + } + }; - self.icon = Some(icon.to_string()); - let label = if let Ok(metadata) = metadata { - Some(self.fmt_string(metadata)) - } else { - None - }; - self.label = label; - Ok(()) - } - fn fmt_string(&self, metadata: mpris::Metadata) -> String { - use std::collections::HashMap; - use strfmt::{strfmt, Format}; + self.icon = Some(icon.to_string()); + let label = if let Ok(metadata) = metadata { + Some(self.fmt_string(metadata)) + } else { + None + }; + self.label = label; + Ok(()) + } + fn fmt_string(&self, metadata: mpris::Metadata) -> String { + use std::collections::HashMap; + use strfmt::Format; - let mut vars = HashMap::new(); - let artists = metadata.artists().unwrap_or(vec![""]); - let artists_album = metadata.album_artists().unwrap_or(vec![""]); - let artist = artists.get(0).map_or("", |v| v); - let artist_album = artists_album.get(0).map_or("", |v| v); + let mut vars = HashMap::new(); + let artists = metadata.artists().unwrap_or(vec![""]); + let artists_album = metadata.album_artists().unwrap_or(vec![""]); + let artist = artists.get(0).map_or("", |v| v); + let artist_album = artists_album.get(0).map_or("", |v| v); - let title = metadata.title().unwrap_or(""); - let album = metadata.album_name().unwrap_or(""); - let track_num = metadata - .track_number() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); - let disc_num = metadata - .disc_number() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); - let autorating = metadata - .auto_rating() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); + let title = metadata.title().unwrap_or(""); + let album = metadata.album_name().unwrap_or(""); + let track_num = metadata + .track_number() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); + let disc_num = metadata + .disc_number() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); + let autorating = metadata + .auto_rating() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); - vars.insert("artist".to_string(), artist); - vars.insert("albumArtist".to_string(), artist_album); - vars.insert("title".to_string(), title); - vars.insert("trackNumber".to_string(), &track_num); - vars.insert("discNumber".to_string(), &disc_num); - vars.insert("autoRating".to_string(), &autorating); + vars.insert("artist".to_string(), artist); + vars.insert("albumArtist".to_string(), artist_album); + vars.insert("title".to_string(), title); + vars.insert("album".to_string(), album); + vars.insert("trackNumber".to_string(), &track_num); + vars.insert("discNumber".to_string(), &disc_num); + vars.insert("autoRating".to_string(), &autorating); - self.fmt_str - .clone() - .unwrap_or("{artist} - {title}".into()) - .format(&vars) - .unwrap_or_else(|e| { - eprintln!("error: {}. using default string", e); - "{artist} - {title}".format(&vars).unwrap() - }) - } + self.fmt_str + .clone() + .unwrap_or("{artist} - {title}".into()) + .format(&vars) + .unwrap_or_else(|e| { + eprintln!("error: {}. using default string", e); + "{artist} - {title}".format(&vars).unwrap() + }) + } } impl PlayerctlAction { - pub fn from(action: &str) -> Result<Self, String> { - use PlayerctlAction::*; - match action { - "play-pause" => Ok(PlayPause), - "play" => Ok(Play), - "pause" => Ok(Pause), - "stop" => Ok(Stop), - "next" => Ok(Next), - "prev" | "previous" => Ok(Prev), - "shuffle" => Ok(Shuffle), - x => Err(x.to_string()), - } - } + pub fn from(action: &str) -> Result<Self, String> { + use PlayerctlAction::*; + match action { + "play-pause" => Ok(PlayPause), + "play" => Ok(Play), + "pause" => Ok(Pause), + "stop" => Ok(Stop), + "next" => Ok(Next), + "prev" | "previous" => Ok(Prev), + "shuffle" => Ok(Shuffle), + x => Err(x.to_string()), + } + } } impl PlayerctlDeviceRaw { - pub fn from(player: String) -> Result<Self, ()> { - use PlayerctlDeviceRaw::*; - match player.as_str() { - "auto" | "" => Ok(None), - "all" => Ok(All), - _ => Ok(Some(player)), - } - } + pub fn from(player: String) -> Result<Self, ()> { + use PlayerctlDeviceRaw::*; + match player.as_str() { + "auto" | "" => Ok(None), + "all" => Ok(All), + _ => Ok(Some(player)), + } + } } From 743cd78dd3dc626aa6d56700b934711558fab5c2 Mon Sep 17 00:00:00 2001 From: Jan Luca Oster <janluca.oster@proton.me> Date: Fri, 10 Jan 2025 13:07:37 +0100 Subject: [PATCH 10/18] add config files --- data/config/backend.toml | 1 + data/config/config.toml | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 data/config/backend.toml create mode 100644 data/config/config.toml diff --git a/data/config/backend.toml b/data/config/backend.toml new file mode 100644 index 0000000..00fce1a --- /dev/null +++ b/data/config/backend.toml @@ -0,0 +1 @@ +[input] diff --git a/data/config/config.toml b/data/config/config.toml new file mode 100644 index 0000000..5089aa9 --- /dev/null +++ b/data/config/config.toml @@ -0,0 +1,19 @@ +[client] +## style file for the OSD +# style = /etc/xdg/swayosd/style.css + +## on which height to show the OSD +# top_margin = 0.85 + +## The maximum volume that can be reached in % +# max_volume = 150 + +## show percentage on the right of the OSD +# show_percentage = true + +## set format for the media player OSD +# playerctl_format = "{artist} - {title}" +## Available values: +## artist, albumArtist, title, album, trackNumber, discNumber, autoRating + +[server] From ea3a4b01e767ba9e7cc321a5eeecf210cdb94ac4 Mon Sep 17 00:00:00 2001 From: Jan Luca <janluca.oster@proton.me> Date: Fri, 10 Jan 2025 18:45:37 +0100 Subject: [PATCH 11/18] fix cargo fmt because "version" is deprecated and has been superseeded by "style_edition". 2021 should be equivelent to the onle "One" --- rustfmt.toml | 2 +- src/mpris-backend/mod.rs | 352 +++++++++++++++++++-------------------- 2 files changed, 177 insertions(+), 177 deletions(-) diff --git a/rustfmt.toml b/rustfmt.toml index 2940225..0a52b15 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -55,7 +55,7 @@ match_block_trailing_comma = false blank_lines_upper_bound = 1 blank_lines_lower_bound = 0 edition = "2015" -version = "One" +style_edition = "2021" inline_attribute_width = 0 format_generated_files = true merge_derives = true diff --git a/src/mpris-backend/mod.rs b/src/mpris-backend/mod.rs index 1e5ffb8..e2f63dc 100644 --- a/src/mpris-backend/mod.rs +++ b/src/mpris-backend/mod.rs @@ -4,206 +4,206 @@ use crate::utils::get_player; use std::{error::Error, sync::Arc}; pub enum PlayerctlAction { - PlayPause, - Play, - Pause, - Stop, - Next, - Prev, - Shuffle, + PlayPause, + Play, + Pause, + Stop, + Next, + Prev, + Shuffle, } use super::config::user::ServerConfig; #[derive(Clone, Debug)] pub enum PlayerctlDeviceRaw { - None, - All, - Some(String), + None, + All, + Some(String), } pub enum PlayerctlDevice { - All(Vec<Player>), - Some(Player), + All(Vec<Player>), + Some(Player), } pub struct Playerctl { - player: PlayerctlDevice, - action: PlayerctlAction, - pub icon: Option<String>, - pub label: Option<String>, - fmt_str: Option<String>, + player: PlayerctlDevice, + action: PlayerctlAction, + pub icon: Option<String>, + pub label: Option<String>, + fmt_str: Option<String>, } impl Playerctl { - pub fn new( - action: PlayerctlAction, - config: Arc<ServerConfig>, - ) -> Result<Playerctl, Box<dyn Error>> { - let playerfinder = PlayerFinder::new()?; - let player = get_player(); - let player = match player { - PlayerctlDeviceRaw::None => PlayerctlDevice::Some(playerfinder.find_active()?), - PlayerctlDeviceRaw::Some(name) => { - PlayerctlDevice::Some(playerfinder.find_by_name(name.as_str())?) - } - PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), - }; - let fmt_str = config.playerctl_format.clone(); - Ok(Self { - player, - action, - icon: None, - label: None, - fmt_str, - }) - } - pub fn run(&mut self) -> Result<(), Box<dyn Error>> { - use PlaybackStatus::*; - use PlayerctlAction::*; - let run_single = |player: &Player| -> Result<&str, Box<dyn Error>> { - let out = match self.action { - PlayPause => match player.get_playback_status()? { - Playing => { - player.pause()?; - "pause-large-symbolic" - } - Paused | Stopped => { - player.play()?; - "play-large-symbolic" - } - }, - Shuffle => { - let shuffle = player.get_shuffle()?; - player.set_shuffle(!shuffle)?; - if shuffle { - "playlist-consecutive-symbolic" - } else { - "playlist-shuffle-symbolic" - } - } - Play => { - player.play()?; - "play-large-symbolic" - } - Pause => { - player.pause()?; - "pause-large-symbolic" - } - Stop => { - player.stop()?; - "stop-large-symbolic" - } - Next => { - player.next()?; - "media-seek-forward-symbolic" - } - Prev => { - player.previous()?; - "media-seek-backward-symbolic" - } - }; - Ok(out) - }; - let mut metadata = Err("some errro"); - let icon = match &self.player { - PlayerctlDevice::Some(player) => { - metadata = player.get_metadata().or_else(|_| Err("")); - run_single(player)? - } - PlayerctlDevice::All(players) => { - let mut icon = Err("couldn't change any players!"); - for player in players { - let icon_new = run_single(player); - if let Ok(icon_new) = icon_new { - if icon.is_err() { - icon = Ok(icon_new); - } - }; - if let Err(_) = metadata { - metadata = player.get_metadata().or_else(|_| Err("")); - } - } - icon? - } - }; + pub fn new( + action: PlayerctlAction, + config: Arc<ServerConfig>, + ) -> Result<Playerctl, Box<dyn Error>> { + let playerfinder = PlayerFinder::new()?; + let player = get_player(); + let player = match player { + PlayerctlDeviceRaw::None => PlayerctlDevice::Some(playerfinder.find_active()?), + PlayerctlDeviceRaw::Some(name) => { + PlayerctlDevice::Some(playerfinder.find_by_name(name.as_str())?) + } + PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), + }; + let fmt_str = config.playerctl_format.clone(); + Ok(Self { + player, + action, + icon: None, + label: None, + fmt_str, + }) + } + pub fn run(&mut self) -> Result<(), Box<dyn Error>> { + use PlaybackStatus::*; + use PlayerctlAction::*; + let run_single = |player: &Player| -> Result<&str, Box<dyn Error>> { + let out = match self.action { + PlayPause => match player.get_playback_status()? { + Playing => { + player.pause()?; + "pause-large-symbolic" + } + Paused | Stopped => { + player.play()?; + "play-large-symbolic" + } + }, + Shuffle => { + let shuffle = player.get_shuffle()?; + player.set_shuffle(!shuffle)?; + if shuffle { + "playlist-consecutive-symbolic" + } else { + "playlist-shuffle-symbolic" + } + } + Play => { + player.play()?; + "play-large-symbolic" + } + Pause => { + player.pause()?; + "pause-large-symbolic" + } + Stop => { + player.stop()?; + "stop-large-symbolic" + } + Next => { + player.next()?; + "media-seek-forward-symbolic" + } + Prev => { + player.previous()?; + "media-seek-backward-symbolic" + } + }; + Ok(out) + }; + let mut metadata = Err("some errro"); + let icon = match &self.player { + PlayerctlDevice::Some(player) => { + metadata = player.get_metadata().or_else(|_| Err("")); + run_single(player)? + } + PlayerctlDevice::All(players) => { + let mut icon = Err("couldn't change any players!"); + for player in players { + let icon_new = run_single(player); + if let Ok(icon_new) = icon_new { + if icon.is_err() { + icon = Ok(icon_new); + } + }; + if let Err(_) = metadata { + metadata = player.get_metadata().or_else(|_| Err("")); + } + } + icon? + } + }; - self.icon = Some(icon.to_string()); - let label = if let Ok(metadata) = metadata { - Some(self.fmt_string(metadata)) - } else { - None - }; - self.label = label; - Ok(()) - } - fn fmt_string(&self, metadata: mpris::Metadata) -> String { - use std::collections::HashMap; - use strfmt::Format; + self.icon = Some(icon.to_string()); + let label = if let Ok(metadata) = metadata { + Some(self.fmt_string(metadata)) + } else { + None + }; + self.label = label; + Ok(()) + } + fn fmt_string(&self, metadata: mpris::Metadata) -> String { + use std::collections::HashMap; + use strfmt::Format; - let mut vars = HashMap::new(); - let artists = metadata.artists().unwrap_or(vec![""]); - let artists_album = metadata.album_artists().unwrap_or(vec![""]); - let artist = artists.get(0).map_or("", |v| v); - let artist_album = artists_album.get(0).map_or("", |v| v); + let mut vars = HashMap::new(); + let artists = metadata.artists().unwrap_or(vec![""]); + let artists_album = metadata.album_artists().unwrap_or(vec![""]); + let artist = artists.get(0).map_or("", |v| v); + let artist_album = artists_album.get(0).map_or("", |v| v); - let title = metadata.title().unwrap_or(""); - let album = metadata.album_name().unwrap_or(""); - let track_num = metadata - .track_number() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); - let disc_num = metadata - .disc_number() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); - let autorating = metadata - .auto_rating() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); + let title = metadata.title().unwrap_or(""); + let album = metadata.album_name().unwrap_or(""); + let track_num = metadata + .track_number() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); + let disc_num = metadata + .disc_number() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); + let autorating = metadata + .auto_rating() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); - vars.insert("artist".to_string(), artist); - vars.insert("albumArtist".to_string(), artist_album); - vars.insert("title".to_string(), title); - vars.insert("album".to_string(), album); - vars.insert("trackNumber".to_string(), &track_num); - vars.insert("discNumber".to_string(), &disc_num); - vars.insert("autoRating".to_string(), &autorating); + vars.insert("artist".to_string(), artist); + vars.insert("albumArtist".to_string(), artist_album); + vars.insert("title".to_string(), title); + vars.insert("album".to_string(), album); + vars.insert("trackNumber".to_string(), &track_num); + vars.insert("discNumber".to_string(), &disc_num); + vars.insert("autoRating".to_string(), &autorating); - self.fmt_str - .clone() - .unwrap_or("{artist} - {title}".into()) - .format(&vars) - .unwrap_or_else(|e| { - eprintln!("error: {}. using default string", e); - "{artist} - {title}".format(&vars).unwrap() - }) - } + self.fmt_str + .clone() + .unwrap_or("{artist} - {title}".into()) + .format(&vars) + .unwrap_or_else(|e| { + eprintln!("error: {}. using default string", e); + "{artist} - {title}".format(&vars).unwrap() + }) + } } impl PlayerctlAction { - pub fn from(action: &str) -> Result<Self, String> { - use PlayerctlAction::*; - match action { - "play-pause" => Ok(PlayPause), - "play" => Ok(Play), - "pause" => Ok(Pause), - "stop" => Ok(Stop), - "next" => Ok(Next), - "prev" | "previous" => Ok(Prev), - "shuffle" => Ok(Shuffle), - x => Err(x.to_string()), - } - } + pub fn from(action: &str) -> Result<Self, String> { + use PlayerctlAction::*; + match action { + "play-pause" => Ok(PlayPause), + "play" => Ok(Play), + "pause" => Ok(Pause), + "stop" => Ok(Stop), + "next" => Ok(Next), + "prev" | "previous" => Ok(Prev), + "shuffle" => Ok(Shuffle), + x => Err(x.to_string()), + } + } } impl PlayerctlDeviceRaw { - pub fn from(player: String) -> Result<Self, ()> { - use PlayerctlDeviceRaw::*; - match player.as_str() { - "auto" | "" => Ok(None), - "all" => Ok(All), - _ => Ok(Some(player)), - } - } + pub fn from(player: String) -> Result<Self, ()> { + use PlayerctlDeviceRaw::*; + match player.as_str() { + "auto" | "" => Ok(None), + "all" => Ok(All), + _ => Ok(Some(player)), + } + } } From 49d6ec28f617cf09e4d21e68c9d08e2a438e196f Mon Sep 17 00:00:00 2001 From: Jan Luca Oster <janluca.oster@proton.me> Date: Mon, 20 Jan 2025 10:44:09 +0100 Subject: [PATCH 12/18] resolve conversations --- src/mpris-backend/mod.rs | 385 +++++++++++++++++++++------------------ 1 file changed, 205 insertions(+), 180 deletions(-) diff --git a/src/mpris-backend/mod.rs b/src/mpris-backend/mod.rs index e2f63dc..8ded2d4 100644 --- a/src/mpris-backend/mod.rs +++ b/src/mpris-backend/mod.rs @@ -1,209 +1,234 @@ -use mpris::{PlaybackStatus, Player, PlayerFinder}; +use mpris::{PlaybackStatus, Player, PlayerFinder, Metadata}; use crate::utils::get_player; -use std::{error::Error, sync::Arc}; +use std::{ + error::Error, + sync::Arc, + time::Duration, + thread::sleep, +}; +use PlaybackStatus::*; +use PlayerctlAction::*; +use super::config::user::ServerConfig; pub enum PlayerctlAction { - PlayPause, - Play, - Pause, - Stop, - Next, - Prev, - Shuffle, + PlayPause, + Play, + Pause, + Stop, + Next, + Prev, + Shuffle, } -use super::config::user::ServerConfig; - #[derive(Clone, Debug)] pub enum PlayerctlDeviceRaw { - None, - All, - Some(String), + None, + All, + Some(String), } pub enum PlayerctlDevice { - All(Vec<Player>), - Some(Player), + All(Vec<Player>), + Some(Player), } pub struct Playerctl { - player: PlayerctlDevice, - action: PlayerctlAction, - pub icon: Option<String>, - pub label: Option<String>, - fmt_str: Option<String>, + player: PlayerctlDevice, + action: PlayerctlAction, + pub icon: Option<String>, + pub label: Option<String>, + fmt_str: Option<String>, } impl Playerctl { - pub fn new( - action: PlayerctlAction, - config: Arc<ServerConfig>, - ) -> Result<Playerctl, Box<dyn Error>> { - let playerfinder = PlayerFinder::new()?; - let player = get_player(); - let player = match player { - PlayerctlDeviceRaw::None => PlayerctlDevice::Some(playerfinder.find_active()?), - PlayerctlDeviceRaw::Some(name) => { - PlayerctlDevice::Some(playerfinder.find_by_name(name.as_str())?) - } - PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), - }; - let fmt_str = config.playerctl_format.clone(); - Ok(Self { - player, - action, - icon: None, - label: None, - fmt_str, - }) - } - pub fn run(&mut self) -> Result<(), Box<dyn Error>> { - use PlaybackStatus::*; - use PlayerctlAction::*; - let run_single = |player: &Player| -> Result<&str, Box<dyn Error>> { - let out = match self.action { - PlayPause => match player.get_playback_status()? { - Playing => { - player.pause()?; - "pause-large-symbolic" - } - Paused | Stopped => { - player.play()?; - "play-large-symbolic" - } - }, - Shuffle => { - let shuffle = player.get_shuffle()?; - player.set_shuffle(!shuffle)?; - if shuffle { - "playlist-consecutive-symbolic" - } else { - "playlist-shuffle-symbolic" - } - } - Play => { - player.play()?; - "play-large-symbolic" - } - Pause => { - player.pause()?; - "pause-large-symbolic" - } - Stop => { - player.stop()?; - "stop-large-symbolic" - } - Next => { - player.next()?; - "media-seek-forward-symbolic" - } - Prev => { - player.previous()?; - "media-seek-backward-symbolic" - } - }; - Ok(out) - }; - let mut metadata = Err("some errro"); - let icon = match &self.player { - PlayerctlDevice::Some(player) => { - metadata = player.get_metadata().or_else(|_| Err("")); - run_single(player)? - } - PlayerctlDevice::All(players) => { - let mut icon = Err("couldn't change any players!"); - for player in players { - let icon_new = run_single(player); - if let Ok(icon_new) = icon_new { - if icon.is_err() { - icon = Ok(icon_new); - } - }; - if let Err(_) = metadata { - metadata = player.get_metadata().or_else(|_| Err("")); - } - } - icon? - } - }; + pub fn new( + action: PlayerctlAction, + config: Arc<ServerConfig>, + ) -> Result<Playerctl, Box<dyn Error>> { + let playerfinder = PlayerFinder::new()?; + let player = get_player(); + let player = match player { + PlayerctlDeviceRaw::None => PlayerctlDevice::Some(playerfinder.find_active()?), + PlayerctlDeviceRaw::Some(name) => { + PlayerctlDevice::Some(playerfinder.find_by_name(name.as_str())?) + } + PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), + }; + let fmt_str = config.playerctl_format.clone(); + Ok(Self { + player, + action, + icon: None, + label: None, + fmt_str, + }) + } + pub fn run(&mut self) -> Result<(), Box<dyn Error>> { + let mut metadata = Err("some errro"); + let mut icon = Err("some errro"); + match &self.player { + PlayerctlDevice::Some(player) => { + icon = Ok(self.run_single(player)?); + metadata = self.get_metadata(player).or_else(|_| Err("")); + } + PlayerctlDevice::All(players) => { + for player in players { + let icon_new = self.run_single(player); + if let Ok(icon_new) = icon_new { + if icon.is_err() { + icon = Ok(icon_new); + } + }; + if let Err(_) = metadata { + metadata = self.get_metadata(player).or_else(|_| Err("")); + } + } + } + }; - self.icon = Some(icon.to_string()); - let label = if let Ok(metadata) = metadata { - Some(self.fmt_string(metadata)) - } else { - None - }; - self.label = label; - Ok(()) - } - fn fmt_string(&self, metadata: mpris::Metadata) -> String { - use std::collections::HashMap; - use strfmt::Format; + self.icon = Some(icon.unwrap_or("").to_string()); + let label = if let Ok(metadata) = metadata { + Some(self.fmt_string(metadata)) + } else { + None + }; + self.label = label; + Ok(()) + } + fn run_single(&self, player: &Player) -> Result<&str, Box<dyn Error>> { + let out = match self.action { + PlayPause => match player.get_playback_status()? { + Playing => { + player.pause()?; + "pause-large-symbolic" + } + Paused | Stopped => { + player.play()?; + "play-large-symbolic" + } + }, + Shuffle => { + let shuffle = player.get_shuffle()?; + player.set_shuffle(!shuffle)?; + if shuffle { + "playlist-consecutive-symbolic" + } else { + "playlist-shuffle-symbolic" + } + } + Play => { + player.play()?; + "play-large-symbolic" + } + Pause => { + player.pause()?; + "pause-large-symbolic" + } + Stop => { + player.stop()?; + "stop-large-symbolic" + } + Next => { + player.next()?; + "media-seek-forward-symbolic" + } + Prev => { + player.previous()?; + "media-seek-backward-symbolic" + } + }; + Ok(out) + } + fn get_metadata(&self, player: &Player) -> Result<Metadata, mpris::DBusError> { + match self.action { + Next | Prev => { + // loop until metadata updates or to a max of 200ms + // the music won't have changed before that anyway + let metadata = player.get_metadata()?; + let name1 = metadata.url().unwrap(); + let mut counter = 0; + while counter < 20 { + std::thread::sleep(std::time::Duration::from_millis(5)); + counter += 1; + let metadata = player.get_metadata()?; + let name2 = metadata.url().unwrap(); + if name1 != name2 { + return Ok(metadata) + } + } + Ok(metadata) + }, + _ => player.get_metadata(), + } + } + fn fmt_string(&self, metadata: mpris::Metadata) -> String { + use std::collections::HashMap; + use strfmt::Format; - let mut vars = HashMap::new(); - let artists = metadata.artists().unwrap_or(vec![""]); - let artists_album = metadata.album_artists().unwrap_or(vec![""]); - let artist = artists.get(0).map_or("", |v| v); - let artist_album = artists_album.get(0).map_or("", |v| v); + let mut vars = HashMap::new(); + let artists = metadata.artists().unwrap_or(vec![""]); + let artists_album = metadata.album_artists().unwrap_or(vec![""]); + let artist = artists.get(0).map_or("", |v| v); + let artist_album = artists_album.get(0).map_or("", |v| v); - let title = metadata.title().unwrap_or(""); - let album = metadata.album_name().unwrap_or(""); - let track_num = metadata - .track_number() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); - let disc_num = metadata - .disc_number() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); - let autorating = metadata - .auto_rating() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); + let title = metadata.title().unwrap_or(""); + let album = metadata.album_name().unwrap_or(""); + let track_num = metadata + .track_number() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); + let disc_num = metadata + .disc_number() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); + let autorating = metadata + .auto_rating() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); - vars.insert("artist".to_string(), artist); - vars.insert("albumArtist".to_string(), artist_album); - vars.insert("title".to_string(), title); - vars.insert("album".to_string(), album); - vars.insert("trackNumber".to_string(), &track_num); - vars.insert("discNumber".to_string(), &disc_num); - vars.insert("autoRating".to_string(), &autorating); + vars.insert("artist".to_string(), artist); + vars.insert("albumArtist".to_string(), artist_album); + vars.insert("title".to_string(), title); + vars.insert("album".to_string(), album); + vars.insert("trackNumber".to_string(), &track_num); + vars.insert("discNumber".to_string(), &disc_num); + vars.insert("autoRating".to_string(), &autorating); - self.fmt_str - .clone() - .unwrap_or("{artist} - {title}".into()) - .format(&vars) - .unwrap_or_else(|e| { - eprintln!("error: {}. using default string", e); - "{artist} - {title}".format(&vars).unwrap() - }) - } + self.fmt_str + .clone() + .unwrap_or("{artist} - {title}".into()) + .format(&vars) + .unwrap_or_else(|e| { + eprintln!("error: {}. using default string", e); + "{artist} - {title}".format(&vars).unwrap() + }) + } } impl PlayerctlAction { - pub fn from(action: &str) -> Result<Self, String> { - use PlayerctlAction::*; - match action { - "play-pause" => Ok(PlayPause), - "play" => Ok(Play), - "pause" => Ok(Pause), - "stop" => Ok(Stop), - "next" => Ok(Next), - "prev" | "previous" => Ok(Prev), - "shuffle" => Ok(Shuffle), - x => Err(x.to_string()), - } - } + pub fn from(action: &str) -> Result<Self, String> { + use PlayerctlAction::*; + match action { + "play-pause" => Ok(PlayPause), + "play" => Ok(Play), + "pause" => Ok(Pause), + "stop" => Ok(Stop), + "next" => Ok(Next), + "prev" | "previous" => Ok(Prev), + "shuffle" => Ok(Shuffle), + x => Err(x.to_string()), + } + } } impl PlayerctlDeviceRaw { - pub fn from(player: String) -> Result<Self, ()> { - use PlayerctlDeviceRaw::*; - match player.as_str() { - "auto" | "" => Ok(None), - "all" => Ok(All), - _ => Ok(Some(player)), - } - } + pub fn from(player: String) -> Result<Self, ()> { + use PlayerctlDeviceRaw::*; + match player.as_str() { + "auto" | "" => Ok(None), + "all" => Ok(All), + _ => Ok(Some(player)), + } + } } From a96b3b10580b8e34e80c1ff8d08c5fe185c30fcc Mon Sep 17 00:00:00 2001 From: Jan Luca Oster <janluca.oster@proton.me> Date: Mon, 20 Jan 2025 10:55:35 +0100 Subject: [PATCH 13/18] add track_list stuff and fallback to delaying --- src/mpris-backend/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mpris-backend/mod.rs b/src/mpris-backend/mod.rs index 8ded2d4..302d7ee 100644 --- a/src/mpris-backend/mod.rs +++ b/src/mpris-backend/mod.rs @@ -143,10 +143,14 @@ impl Playerctl { fn get_metadata(&self, player: &Player) -> Result<Metadata, mpris::DBusError> { match self.action { Next | Prev => { - // loop until metadata updates or to a max of 200ms - // the music won't have changed before that anyway + if let Ok(track_list) = player.get_track_list() { + if let Some(track) = track_list.get(0) { + return player.get_track_metadata(track) + } + } let metadata = player.get_metadata()?; let name1 = metadata.url().unwrap(); + println!("{name1}"); let mut counter = 0; while counter < 20 { std::thread::sleep(std::time::Duration::from_millis(5)); From 6e6be52709454d2639573c66925c70f47dc722e4 Mon Sep 17 00:00:00 2001 From: Jan Luca Oster <janluca.oster@proton.me> Date: Mon, 20 Jan 2025 10:58:05 +0100 Subject: [PATCH 14/18] d --- src/mpris-backend/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mpris-backend/mod.rs b/src/mpris-backend/mod.rs index 302d7ee..ad00d46 100644 --- a/src/mpris-backend/mod.rs +++ b/src/mpris-backend/mod.rs @@ -150,7 +150,6 @@ impl Playerctl { } let metadata = player.get_metadata()?; let name1 = metadata.url().unwrap(); - println!("{name1}"); let mut counter = 0; while counter < 20 { std::thread::sleep(std::time::Duration::from_millis(5)); From 0cb912651b38ca5570023766ed64ee8118e06b41 Mon Sep 17 00:00:00 2001 From: Jan Luca <janluca.oster@proton.me> Date: Fri, 24 Jan 2025 17:27:23 +0100 Subject: [PATCH 15/18] cargo fmt --- src/mpris-backend/mod.rs | 407 +++++++++++++++++++-------------------- 1 file changed, 201 insertions(+), 206 deletions(-) diff --git a/src/mpris-backend/mod.rs b/src/mpris-backend/mod.rs index ad00d46..35c3ce1 100644 --- a/src/mpris-backend/mod.rs +++ b/src/mpris-backend/mod.rs @@ -1,237 +1,232 @@ -use mpris::{PlaybackStatus, Player, PlayerFinder, Metadata}; +use mpris::{Metadata, PlaybackStatus, Player, PlayerFinder}; +use super::config::user::ServerConfig; use crate::utils::get_player; -use std::{ - error::Error, - sync::Arc, - time::Duration, - thread::sleep, -}; +use std::{error::Error, sync::Arc, thread::sleep, time::Duration}; use PlaybackStatus::*; use PlayerctlAction::*; -use super::config::user::ServerConfig; pub enum PlayerctlAction { - PlayPause, - Play, - Pause, - Stop, - Next, - Prev, - Shuffle, + PlayPause, + Play, + Pause, + Stop, + Next, + Prev, + Shuffle, } #[derive(Clone, Debug)] pub enum PlayerctlDeviceRaw { - None, - All, - Some(String), + None, + All, + Some(String), } pub enum PlayerctlDevice { - All(Vec<Player>), - Some(Player), + All(Vec<Player>), + Some(Player), } pub struct Playerctl { - player: PlayerctlDevice, - action: PlayerctlAction, - pub icon: Option<String>, - pub label: Option<String>, - fmt_str: Option<String>, + player: PlayerctlDevice, + action: PlayerctlAction, + pub icon: Option<String>, + pub label: Option<String>, + fmt_str: Option<String>, } impl Playerctl { - pub fn new( - action: PlayerctlAction, - config: Arc<ServerConfig>, - ) -> Result<Playerctl, Box<dyn Error>> { - let playerfinder = PlayerFinder::new()?; - let player = get_player(); - let player = match player { - PlayerctlDeviceRaw::None => PlayerctlDevice::Some(playerfinder.find_active()?), - PlayerctlDeviceRaw::Some(name) => { - PlayerctlDevice::Some(playerfinder.find_by_name(name.as_str())?) - } - PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), - }; - let fmt_str = config.playerctl_format.clone(); - Ok(Self { - player, - action, - icon: None, - label: None, - fmt_str, - }) - } - pub fn run(&mut self) -> Result<(), Box<dyn Error>> { - let mut metadata = Err("some errro"); - let mut icon = Err("some errro"); - match &self.player { - PlayerctlDevice::Some(player) => { - icon = Ok(self.run_single(player)?); - metadata = self.get_metadata(player).or_else(|_| Err("")); - } - PlayerctlDevice::All(players) => { - for player in players { - let icon_new = self.run_single(player); - if let Ok(icon_new) = icon_new { - if icon.is_err() { - icon = Ok(icon_new); - } - }; - if let Err(_) = metadata { - metadata = self.get_metadata(player).or_else(|_| Err("")); - } - } - } - }; + pub fn new( + action: PlayerctlAction, + config: Arc<ServerConfig>, + ) -> Result<Playerctl, Box<dyn Error>> { + let playerfinder = PlayerFinder::new()?; + let player = get_player(); + let player = match player { + PlayerctlDeviceRaw::None => PlayerctlDevice::Some(playerfinder.find_active()?), + PlayerctlDeviceRaw::Some(name) => { + PlayerctlDevice::Some(playerfinder.find_by_name(name.as_str())?) + } + PlayerctlDeviceRaw::All => PlayerctlDevice::All(playerfinder.find_all()?), + }; + let fmt_str = config.playerctl_format.clone(); + Ok(Self { + player, + action, + icon: None, + label: None, + fmt_str, + }) + } + pub fn run(&mut self) -> Result<(), Box<dyn Error>> { + let mut metadata = Err("some errro"); + let mut icon = Err("some errro"); + match &self.player { + PlayerctlDevice::Some(player) => { + icon = Ok(self.run_single(player)?); + metadata = self.get_metadata(player).or_else(|_| Err("")); + } + PlayerctlDevice::All(players) => { + for player in players { + let icon_new = self.run_single(player); + if let Ok(icon_new) = icon_new { + if icon.is_err() { + icon = Ok(icon_new); + } + }; + if let Err(_) = metadata { + metadata = self.get_metadata(player).or_else(|_| Err("")); + } + } + } + }; - self.icon = Some(icon.unwrap_or("").to_string()); - let label = if let Ok(metadata) = metadata { - Some(self.fmt_string(metadata)) - } else { - None - }; - self.label = label; - Ok(()) - } - fn run_single(&self, player: &Player) -> Result<&str, Box<dyn Error>> { - let out = match self.action { - PlayPause => match player.get_playback_status()? { - Playing => { - player.pause()?; - "pause-large-symbolic" - } - Paused | Stopped => { - player.play()?; - "play-large-symbolic" - } - }, - Shuffle => { - let shuffle = player.get_shuffle()?; - player.set_shuffle(!shuffle)?; - if shuffle { - "playlist-consecutive-symbolic" - } else { - "playlist-shuffle-symbolic" - } - } - Play => { - player.play()?; - "play-large-symbolic" - } - Pause => { - player.pause()?; - "pause-large-symbolic" - } - Stop => { - player.stop()?; - "stop-large-symbolic" - } - Next => { - player.next()?; - "media-seek-forward-symbolic" - } - Prev => { - player.previous()?; - "media-seek-backward-symbolic" - } - }; - Ok(out) - } - fn get_metadata(&self, player: &Player) -> Result<Metadata, mpris::DBusError> { - match self.action { - Next | Prev => { - if let Ok(track_list) = player.get_track_list() { - if let Some(track) = track_list.get(0) { - return player.get_track_metadata(track) - } - } - let metadata = player.get_metadata()?; - let name1 = metadata.url().unwrap(); - let mut counter = 0; - while counter < 20 { - std::thread::sleep(std::time::Duration::from_millis(5)); - counter += 1; - let metadata = player.get_metadata()?; - let name2 = metadata.url().unwrap(); - if name1 != name2 { - return Ok(metadata) - } - } - Ok(metadata) - }, - _ => player.get_metadata(), - } - } - fn fmt_string(&self, metadata: mpris::Metadata) -> String { - use std::collections::HashMap; - use strfmt::Format; + self.icon = Some(icon.unwrap_or("").to_string()); + let label = if let Ok(metadata) = metadata { + Some(self.fmt_string(metadata)) + } else { + None + }; + self.label = label; + Ok(()) + } + fn run_single(&self, player: &Player) -> Result<&str, Box<dyn Error>> { + let out = match self.action { + PlayPause => match player.get_playback_status()? { + Playing => { + player.pause()?; + "pause-large-symbolic" + } + Paused | Stopped => { + player.play()?; + "play-large-symbolic" + } + }, + Shuffle => { + let shuffle = player.get_shuffle()?; + player.set_shuffle(!shuffle)?; + if shuffle { + "playlist-consecutive-symbolic" + } else { + "playlist-shuffle-symbolic" + } + } + Play => { + player.play()?; + "play-large-symbolic" + } + Pause => { + player.pause()?; + "pause-large-symbolic" + } + Stop => { + player.stop()?; + "stop-large-symbolic" + } + Next => { + player.next()?; + "media-seek-forward-symbolic" + } + Prev => { + player.previous()?; + "media-seek-backward-symbolic" + } + }; + Ok(out) + } + fn get_metadata(&self, player: &Player) -> Result<Metadata, mpris::DBusError> { + match self.action { + Next | Prev => { + if let Ok(track_list) = player.get_track_list() { + if let Some(track) = track_list.get(0) { + return player.get_track_metadata(track); + } + } + let metadata = player.get_metadata()?; + let name1 = metadata.url().unwrap(); + let mut counter = 0; + while counter < 20 { + std::thread::sleep(std::time::Duration::from_millis(5)); + counter += 1; + let metadata = player.get_metadata()?; + let name2 = metadata.url().unwrap(); + if name1 != name2 { + return Ok(metadata); + } + } + Ok(metadata) + } + _ => player.get_metadata(), + } + } + fn fmt_string(&self, metadata: mpris::Metadata) -> String { + use std::collections::HashMap; + use strfmt::Format; - let mut vars = HashMap::new(); - let artists = metadata.artists().unwrap_or(vec![""]); - let artists_album = metadata.album_artists().unwrap_or(vec![""]); - let artist = artists.get(0).map_or("", |v| v); - let artist_album = artists_album.get(0).map_or("", |v| v); + let mut vars = HashMap::new(); + let artists = metadata.artists().unwrap_or(vec![""]); + let artists_album = metadata.album_artists().unwrap_or(vec![""]); + let artist = artists.get(0).map_or("", |v| v); + let artist_album = artists_album.get(0).map_or("", |v| v); - let title = metadata.title().unwrap_or(""); - let album = metadata.album_name().unwrap_or(""); - let track_num = metadata - .track_number() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); - let disc_num = metadata - .disc_number() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); - let autorating = metadata - .auto_rating() - .and_then(|x| Some(x.to_string())) - .unwrap_or(String::new()); + let title = metadata.title().unwrap_or(""); + let album = metadata.album_name().unwrap_or(""); + let track_num = metadata + .track_number() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); + let disc_num = metadata + .disc_number() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); + let autorating = metadata + .auto_rating() + .and_then(|x| Some(x.to_string())) + .unwrap_or(String::new()); - vars.insert("artist".to_string(), artist); - vars.insert("albumArtist".to_string(), artist_album); - vars.insert("title".to_string(), title); - vars.insert("album".to_string(), album); - vars.insert("trackNumber".to_string(), &track_num); - vars.insert("discNumber".to_string(), &disc_num); - vars.insert("autoRating".to_string(), &autorating); + vars.insert("artist".to_string(), artist); + vars.insert("albumArtist".to_string(), artist_album); + vars.insert("title".to_string(), title); + vars.insert("album".to_string(), album); + vars.insert("trackNumber".to_string(), &track_num); + vars.insert("discNumber".to_string(), &disc_num); + vars.insert("autoRating".to_string(), &autorating); - self.fmt_str - .clone() - .unwrap_or("{artist} - {title}".into()) - .format(&vars) - .unwrap_or_else(|e| { - eprintln!("error: {}. using default string", e); - "{artist} - {title}".format(&vars).unwrap() - }) - } + self.fmt_str + .clone() + .unwrap_or("{artist} - {title}".into()) + .format(&vars) + .unwrap_or_else(|e| { + eprintln!("error: {}. using default string", e); + "{artist} - {title}".format(&vars).unwrap() + }) + } } impl PlayerctlAction { - pub fn from(action: &str) -> Result<Self, String> { - use PlayerctlAction::*; - match action { - "play-pause" => Ok(PlayPause), - "play" => Ok(Play), - "pause" => Ok(Pause), - "stop" => Ok(Stop), - "next" => Ok(Next), - "prev" | "previous" => Ok(Prev), - "shuffle" => Ok(Shuffle), - x => Err(x.to_string()), - } - } + pub fn from(action: &str) -> Result<Self, String> { + use PlayerctlAction::*; + match action { + "play-pause" => Ok(PlayPause), + "play" => Ok(Play), + "pause" => Ok(Pause), + "stop" => Ok(Stop), + "next" => Ok(Next), + "prev" | "previous" => Ok(Prev), + "shuffle" => Ok(Shuffle), + x => Err(x.to_string()), + } + } } impl PlayerctlDeviceRaw { - pub fn from(player: String) -> Result<Self, ()> { - use PlayerctlDeviceRaw::*; - match player.as_str() { - "auto" | "" => Ok(None), - "all" => Ok(All), - _ => Ok(Some(player)), - } - } + pub fn from(player: String) -> Result<Self, ()> { + use PlayerctlDeviceRaw::*; + match player.as_str() { + "auto" | "" => Ok(None), + "all" => Ok(All), + _ => Ok(Some(player)), + } + } } From 2ed1f140911f3d64648d6de61b7a86df97e5c374 Mon Sep 17 00:00:00 2001 From: Jan Luca Oster <janluca.oster@proton.me> Date: Mon, 3 Feb 2025 08:43:56 +0100 Subject: [PATCH 16/18] get rid of ghost changes while rebasing --- Cargo.lock | 231 ++++++--------------------------------------- src/server/main.rs | 4 +- 2 files changed, 29 insertions(+), 206 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ddb8351..c8471fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -237,15 +237,6 @@ dependencies = [ "fs4", ] -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "blocking" version = "1.6.1" @@ -265,12 +256,6 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "cairo-rs" version = "0.20.5" @@ -350,31 +335,12 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "cpufeatures" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" -dependencies = [ - "libc", -] - [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - [[package]] name = "darling" version = "0.14.4" @@ -432,16 +398,6 @@ dependencies = [ "syn 0.11.11", ] -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - [[package]] name = "either" version = "1.13.0" @@ -564,7 +520,7 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset 0.9.1", + "memoffset", "rustc_version", ] @@ -661,12 +617,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - [[package]] name = "futures-task" version = "0.3.31" @@ -682,7 +632,6 @@ dependencies = [ "futures-core", "futures-io", "futures-macro", - "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -748,27 +697,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - [[package]] name = "gio" version = "0.20.6" @@ -1219,15 +1147,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -1250,19 +1169,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.7.1", - "pin-utils", -] - [[package]] name = "nix" version = "0.29.0" @@ -1273,7 +1179,7 @@ dependencies = [ "cfg-if", "cfg_aliases", "libc", - "memoffset 0.9.1", + "memoffset", ] [[package]] @@ -1386,15 +1292,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - [[package]] name = "proc-macro-crate" version = "3.2.0" @@ -1437,36 +1334,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - [[package]] name = "runtime-format" version = "0.1.3" @@ -1544,17 +1411,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "shlex" version = "1.3.0" @@ -1642,7 +1498,7 @@ dependencies = [ "libc", "libpulse-binding", "mpris", - "nix 0.26.4", + "nix", "pulsectl-rs", "runtime-format", "serde", @@ -1829,12 +1685,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - [[package]] name = "udev" version = "0.7.0" @@ -1852,7 +1702,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" dependencies = [ - "memoffset 0.9.1", + "memoffset", "tempfile", "winapi", ] @@ -1887,18 +1737,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - [[package]] name = "wasm-bindgen" version = "0.2.99" @@ -2173,9 +2011,9 @@ checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432" [[package]] name = "zbus" -version = "4.4.0" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +checksum = "2494e4b3f44d8363eef79a8a75fc0649efb710eef65a66b5e688a5eb4afe678a" dependencies = [ "async-broadcast", "async-executor", @@ -2190,19 +2028,17 @@ dependencies = [ "enumflags2", "event-listener 5.3.1", "futures-core", - "futures-sink", "futures-util", "hex", - "nix 0.29.0", + "nix", "ordered-stream", - "rand", "serde", "serde_repr", - "sha1", "static_assertions", "tracing", "uds_windows", - "windows-sys 0.52.0", + "windows-sys 0.59.0", + "winnow", "xdg-home", "zbus_macros", "zbus_names", @@ -2211,67 +2047,51 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "4.4.0" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" +checksum = "445efc01929302aee95e2b25bbb62a301ea8a6369466e4278e58e7d1dfb23631" dependencies = [ "proc-macro-crate", "proc-macro2", "quote 1.0.37", "syn 2.0.90", + "zbus_names", + "zvariant", "zvariant_utils", ] [[package]] name = "zbus_names" -version = "3.0.0" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +checksum = "519629a3f80976d89c575895b05677cbc45eaf9f70d62a364d819ba646409cc8" dependencies = [ "serde", "static_assertions", + "winnow", "zvariant", ] -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote 1.0.37", - "syn 2.0.90", -] - [[package]] name = "zvariant" -version = "4.2.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +checksum = "55e6b9b5f1361de2d5e7d9fd1ee5f6f7fcb6060618a1f82f3472f58f2b8d4be9" dependencies = [ "endi", "enumflags2", "serde", "static_assertions", + "winnow", "zvariant_derive", + "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "4.2.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +checksum = "573a8dd76961957108b10f7a45bac6ab1ea3e9b7fe01aff88325dc57bb8f5c8b" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2282,11 +2102,14 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "2.1.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" +checksum = "ddd46446ea2a1f353bfda53e35f17633afa79f4fe290a611c94645c69fe96a50" dependencies = [ "proc-macro2", "quote 1.0.37", + "serde", + "static_assertions", "syn 2.0.90", + "winnow", ] diff --git a/src/server/main.rs b/src/server/main.rs index 5f56b6b..f03b9ef 100644 --- a/src/server/main.rs +++ b/src/server/main.rs @@ -33,7 +33,7 @@ use gtk::{ }; use std::{env::args_os, future::pending, path::PathBuf, str::FromStr, sync::Arc}; use utils::{get_system_css_path, user_style_path}; -use zbus::{interface, ConnectionBuilder}; +use zbus::{connection, interface}; struct DbusServer { sender: Sender<(ArgTypes, String)>, @@ -59,7 +59,7 @@ impl DbusServer { impl DbusServer { async fn new(sender: Sender<(ArgTypes, String)>) -> zbus::Result<()> { - let _connection = ConnectionBuilder::session()? + let _connection = connection::builder::session()? .name(DBUS_SERVER_NAME)? .serve_at(DBUS_PATH, DbusServer { sender })? .build() From ed82d47aa4da463db73ba0c6dd4583350e212a1a Mon Sep 17 00:00:00 2001 From: Jan Luca Oster <janluca.oster@proton.me> Date: Mon, 3 Feb 2025 08:47:49 +0100 Subject: [PATCH 17/18] capitalize to resolve compile issues --- src/server/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/main.rs b/src/server/main.rs index f03b9ef..8e58f29 100644 --- a/src/server/main.rs +++ b/src/server/main.rs @@ -59,7 +59,7 @@ impl DbusServer { impl DbusServer { async fn new(sender: Sender<(ArgTypes, String)>) -> zbus::Result<()> { - let _connection = connection::builder::session()? + let _connection = connection::Builder::session()? .name(DBUS_SERVER_NAME)? .serve_at(DBUS_PATH, DbusServer { sender })? .build() From 2cd0b0cdd4a707a09170c8f1c8d9d0953079dd03 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Sat, 1 Mar 2025 15:35:05 +0100 Subject: [PATCH 18/18] Fixed my nits --- src/client/main.rs | 2 +- src/mpris-backend/mod.rs | 2 +- src/server/application.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/main.rs b/src/client/main.rs index 6d695a3..a3015dd 100644 --- a/src/client/main.rs +++ b/src/client/main.rs @@ -26,7 +26,7 @@ trait Server { async fn handle_action(&self, arg_type: String, data: String) -> zbus::Result<bool>; } -pub fn get_proxy() -> zbus::Result<ServerProxyBlocking<'static>> { +fn get_proxy() -> zbus::Result<ServerProxyBlocking<'static>> { let connection = Connection::session()?; Ok(ServerProxyBlocking::new(&connection)?) } diff --git a/src/mpris-backend/mod.rs b/src/mpris-backend/mod.rs index 35c3ce1..bbe56ec 100644 --- a/src/mpris-backend/mod.rs +++ b/src/mpris-backend/mod.rs @@ -147,7 +147,7 @@ impl Playerctl { let name1 = metadata.url().unwrap(); let mut counter = 0; while counter < 20 { - std::thread::sleep(std::time::Duration::from_millis(5)); + sleep(Duration::from_millis(5)); counter += 1; let metadata = player.get_metadata()?; let name2 = metadata.url().unwrap(); diff --git a/src/server/application.rs b/src/server/application.rs index b6dbfda..a537ac4 100644 --- a/src/server/application.rs +++ b/src/server/application.rs @@ -2,6 +2,7 @@ use crate::argtypes::ArgTypes; use crate::config::{self, APPLICATION_NAME, DBUS_BACKEND_NAME}; use crate::global_utils::{handle_application_args, HandleLocalStatus}; use crate::osd_window::SwayosdWindow; +use crate::playerctl::*; use crate::utils::{self, *}; use async_channel::Receiver; use gtk::{ @@ -411,7 +412,6 @@ impl SwayOSDApplication { } (ArgTypes::Player, name) => set_player(name.unwrap_or("".to_string())), (ArgTypes::Playerctl, value) => { - use crate::playerctl::*; let value = &value.unwrap_or("".to_string()); let action = PlayerctlAction::from(value).unwrap();