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: &gtk::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(&region);
-			}
-		});
-
-		let update_margins = |window: &gtk::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: &gtk::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(&region);
+            }
+        });
+
+        let update_margins = |window: &gtk::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: &gtk::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(&region);
-            }
-        });
-
-        let update_margins = |window: &gtk::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: &gtk::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(&region);
+			}
+		});
+
+		let update_margins = |window: &gtk::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();