diff --git a/Cargo.lock b/Cargo.lock
index 2c13ef14..0bbe16a4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -229,7 +229,7 @@ source = "git+https://github.com/gtk-rs/gtk-rs-core#49d9100919c51b248bc176763b20
dependencies = [
"bitflags",
"cairo-sys-rs",
- "glib 0.16.0",
+ "glib",
"libc",
"once_cell",
"thiserror",
@@ -240,7 +240,7 @@ name = "cairo-sys-rs"
version = "0.16.0"
source = "git+https://github.com/gtk-rs/gtk-rs-core#49d9100919c51b248bc176763b203bce23efe0ee"
dependencies = [
- "glib-sys 0.16.0",
+ "glib-sys",
"libc",
"system-deps",
]
@@ -348,29 +348,6 @@ dependencies = [
"xdg",
]
-[[package]]
-name = "cosmic-applet-audio"
-version = "0.1.0"
-dependencies = [
- "async-io",
- "freedesktop-desktop-entry",
- "futures",
- "futures-util",
- "gtk4",
- "libadwaita",
- "libcosmic",
- "libcosmic-applet",
- "libpulse-binding",
- "libpulse-glib-binding",
- "mpris2-zbus",
- "once_cell",
- "relm4",
- "relm4-macros",
- "tokio",
- "tracker",
- "zbus 2.3.2",
-]
-
[[package]]
name = "cosmic-applet-network"
version = "0.1.0"
@@ -625,15 +602,6 @@ dependencies = [
"generic-array",
]
-[[package]]
-name = "dirs"
-version = "3.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
-dependencies = [
- "dirs-sys",
-]
-
[[package]]
name = "dirs"
version = "4.0.0"
@@ -806,19 +774,6 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-[[package]]
-name = "freedesktop-desktop-entry"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45157175a725e81f3f594382430b6b78af5f8f72db9bd51b94f0785f80fc6d29"
-dependencies = [
- "dirs 3.0.2",
- "gettext-rs",
- "memchr",
- "thiserror",
- "xdg",
-]
-
[[package]]
name = "futures"
version = "0.3.23"
@@ -940,7 +895,7 @@ dependencies = [
"bitflags",
"gdk-pixbuf-sys",
"gio",
- "glib 0.16.0",
+ "glib",
"libc",
]
@@ -950,8 +905,8 @@ version = "0.16.0"
source = "git+https://github.com/gtk-rs/gtk-rs-core#49d9100919c51b248bc176763b203bce23efe0ee"
dependencies = [
"gio-sys",
- "glib-sys 0.16.0",
- "gobject-sys 0.16.0",
+ "glib-sys",
+ "gobject-sys",
"libc",
"system-deps",
]
@@ -966,7 +921,7 @@ dependencies = [
"gdk-pixbuf",
"gdk4-sys",
"gio",
- "glib 0.16.0",
+ "glib",
"libc",
"pango",
]
@@ -979,8 +934,8 @@ dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
"gio-sys",
- "glib-sys 0.16.0",
- "gobject-sys 0.16.0",
+ "glib-sys",
+ "gobject-sys",
"libc",
"pango-sys",
"pkg-config",
@@ -1010,26 +965,6 @@ dependencies = [
"wasm-bindgen",
]
-[[package]]
-name = "gettext-rs"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e49ea8a8fad198aaa1f9655a2524b64b70eb06b2f3ff37da407566c93054f364"
-dependencies = [
- "gettext-sys",
- "locale_config",
-]
-
-[[package]]
-name = "gettext-sys"
-version = "0.21.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c63ce2e00f56a206778276704bbe38564c8695249fdc8f354b4ef71c57c3839d"
-dependencies = [
- "cc",
- "temp-dir",
-]
-
[[package]]
name = "gio"
version = "0.16.0"
@@ -1041,7 +976,7 @@ dependencies = [
"futures-io",
"futures-util",
"gio-sys",
- "glib 0.16.0",
+ "glib",
"libc",
"once_cell",
"thiserror",
@@ -1052,33 +987,13 @@ name = "gio-sys"
version = "0.16.0"
source = "git+https://github.com/gtk-rs/gtk-rs-core#49d9100919c51b248bc176763b203bce23efe0ee"
dependencies = [
- "glib-sys 0.16.0",
- "gobject-sys 0.16.0",
+ "glib-sys",
+ "gobject-sys",
"libc",
"system-deps",
"winapi",
]
-[[package]]
-name = "glib"
-version = "0.15.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d"
-dependencies = [
- "bitflags",
- "futures-channel",
- "futures-core",
- "futures-executor",
- "futures-task",
- "glib-macros 0.15.11",
- "glib-sys 0.15.10",
- "gobject-sys 0.15.10",
- "libc",
- "once_cell",
- "smallvec",
- "thiserror",
-]
-
[[package]]
name = "glib"
version = "0.16.0"
@@ -1090,9 +1005,9 @@ dependencies = [
"futures-executor",
"futures-task",
"futures-util",
- "glib-macros 0.16.0",
- "glib-sys 0.16.0",
- "gobject-sys 0.16.0",
+ "glib-macros",
+ "glib-sys",
+ "gobject-sys",
"libc",
"once_cell",
"smallvec",
@@ -1104,21 +1019,6 @@ name = "glib-build-tools"
version = "0.1.0"
source = "git+https://github.com/gtk-rs/gtk-rs-core#4ca23ca05bd95655717605eaee349bbb5abc29b7"
-[[package]]
-name = "glib-macros"
-version = "0.15.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25a68131a662b04931e71891fb14aaf65ee4b44d08e8abc10f49e77418c86c64"
-dependencies = [
- "anyhow",
- "heck",
- "proc-macro-crate",
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn",
-]
-
[[package]]
name = "glib-macros"
version = "0.16.0"
@@ -1133,16 +1033,6 @@ dependencies = [
"syn",
]
-[[package]]
-name = "glib-sys"
-version = "0.15.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4"
-dependencies = [
- "libc",
- "system-deps",
-]
-
[[package]]
name = "glib-sys"
version = "0.16.0"
@@ -1152,23 +1042,12 @@ dependencies = [
"system-deps",
]
-[[package]]
-name = "gobject-sys"
-version = "0.15.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a"
-dependencies = [
- "glib-sys 0.15.10",
- "libc",
- "system-deps",
-]
-
[[package]]
name = "gobject-sys"
version = "0.16.0"
source = "git+https://github.com/gtk-rs/gtk-rs-core#49d9100919c51b248bc176763b203bce23efe0ee"
dependencies = [
- "glib-sys 0.16.0",
+ "glib-sys",
"libc",
"system-deps",
]
@@ -1178,7 +1057,7 @@ name = "graphene-rs"
version = "0.16.0"
source = "git+https://github.com/gtk-rs/gtk-rs-core#49d9100919c51b248bc176763b203bce23efe0ee"
dependencies = [
- "glib 0.16.0",
+ "glib",
"graphene-sys",
"libc",
]
@@ -1188,7 +1067,7 @@ name = "graphene-sys"
version = "0.16.0"
source = "git+https://github.com/gtk-rs/gtk-rs-core#49d9100919c51b248bc176763b203bce23efe0ee"
dependencies = [
- "glib-sys 0.16.0",
+ "glib-sys",
"libc",
"pkg-config",
"system-deps",
@@ -1202,7 +1081,7 @@ dependencies = [
"bitflags",
"cairo-rs",
"gdk4",
- "glib 0.16.0",
+ "glib",
"graphene-rs",
"gsk4-sys",
"libc",
@@ -1216,8 +1095,8 @@ source = "git+https://github.com/gtk-rs/gtk4-rs#e4178e68237503c93ca98193e7832b7e
dependencies = [
"cairo-sys-rs",
"gdk4-sys",
- "glib-sys 0.16.0",
- "gobject-sys 0.16.0",
+ "glib-sys",
+ "gobject-sys",
"graphene-sys",
"libc",
"pango-sys",
@@ -1236,7 +1115,7 @@ dependencies = [
"gdk-pixbuf",
"gdk4",
"gio",
- "glib 0.16.0",
+ "glib",
"graphene-rs",
"gsk4",
"gtk4-macros",
@@ -1268,8 +1147,8 @@ dependencies = [
"gdk-pixbuf-sys",
"gdk4-sys",
"gio-sys",
- "glib-sys 0.16.0",
- "gobject-sys 0.16.0",
+ "glib-sys",
+ "gobject-sys",
"graphene-sys",
"gsk4-sys",
"libc",
@@ -1479,7 +1358,7 @@ dependencies = [
"gdk-pixbuf",
"gdk4",
"gio",
- "glib 0.16.0",
+ "glib",
"gtk4",
"libadwaita-sys",
"libc",
@@ -1494,8 +1373,8 @@ source = "git+https://gitlab.gnome.org/World/Rust/libadwaita-rs#01881b0c9f67ed5a
dependencies = [
"gdk4-sys",
"gio-sys",
- "glib-sys 0.16.0",
- "gobject-sys 0.16.0",
+ "glib-sys",
+ "gobject-sys",
"gtk4-sys",
"libc",
"system-deps",
@@ -1551,56 +1430,6 @@ dependencies = [
"winapi",
]
-[[package]]
-name = "libpulse-binding"
-version = "2.26.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17be42160017e0ae993c03bfdab4ecb6f82ce3f8d515bd8da8fdf18d10703663"
-dependencies = [
- "bitflags",
- "libc",
- "libpulse-sys",
- "num-derive",
- "num-traits",
- "winapi",
-]
-
-[[package]]
-name = "libpulse-glib-binding"
-version = "2.25.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df0e7a964c9f7e95d4f073affc19adfda009fa0d55e8831dbb66c78be1d0e6e5"
-dependencies = [
- "glib 0.15.12",
- "glib-sys 0.15.10",
- "libpulse-binding",
- "libpulse-mainloop-glib-sys",
-]
-
-[[package]]
-name = "libpulse-mainloop-glib-sys"
-version = "1.19.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36f61c4064926cc77ea14bb206a21ce1d5a06e175e5c0ce078804bb6c4527b28"
-dependencies = [
- "glib-sys 0.15.10",
- "libpulse-sys",
- "pkg-config",
-]
-
-[[package]]
-name = "libpulse-sys"
-version = "1.19.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "991e6bd0efe2a36e6534e136e7996925e4c1a8e35b7807fe533f2beffff27c30"
-dependencies = [
- "libc",
- "num-derive",
- "num-traits",
- "pkg-config",
- "winapi",
-]
-
[[package]]
name = "locale_config"
version = "0.3.0"
@@ -1680,18 +1509,6 @@ dependencies = [
"windows-sys",
]
-[[package]]
-name = "mpris2-zbus"
-version = "0.1.0"
-source = "git+https://github.com/pop-os/mpris2-zbus#4e853c5a62a8a89ce58af11daddb62427523bc07"
-dependencies = [
- "serde",
- "thiserror",
- "time 0.3.13",
- "zbus 2.3.2",
- "zvariant",
-]
-
[[package]]
name = "nanorand"
version = "0.7.0"
@@ -1740,17 +1557,6 @@ dependencies = [
"pin-utils",
]
-[[package]]
-name = "num-derive"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
[[package]]
name = "num-integer"
version = "0.1.45"
@@ -1872,7 +1678,7 @@ source = "git+https://github.com/gtk-rs/gtk-rs-core#49d9100919c51b248bc176763b20
dependencies = [
"bitflags",
"gio",
- "glib 0.16.0",
+ "glib",
"libc",
"once_cell",
"pango-sys",
@@ -1883,8 +1689,8 @@ name = "pango-sys"
version = "0.16.0"
source = "git+https://github.com/gtk-rs/gtk-rs-core#49d9100919c51b248bc176763b203bce23efe0ee"
dependencies = [
- "glib-sys 0.16.0",
- "gobject-sys 0.16.0",
+ "glib-sys",
+ "gobject-sys",
"libc",
"system-deps",
]
@@ -2188,7 +1994,7 @@ checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
[[package]]
name = "relm4"
version = "0.5.0-beta.1"
-source = "git+https://github.com/relm4/relm4?branch=next#55231e300ce9e2b51d66f078b7759eba0759537c"
+source = "git+https://github.com/Relm4/Relm4.git?branch=next#55231e300ce9e2b51d66f078b7759eba0759537c"
dependencies = [
"async-broadcast",
"async-oneshot",
@@ -2197,7 +2003,6 @@ dependencies = [
"gtk4",
"log",
"once_cell",
- "relm4-macros",
"tokio",
"tracing",
]
@@ -2205,7 +2010,7 @@ dependencies = [
[[package]]
name = "relm4-macros"
version = "0.5.0-beta.1"
-source = "git+https://github.com/relm4/relm4?branch=next#55231e300ce9e2b51d66f078b7759eba0759537c"
+source = "git+https://github.com/Relm4/Relm4.git?branch=next#55231e300ce9e2b51d66f078b7759eba0759537c"
dependencies = [
"proc-macro2",
"quote",
@@ -2513,12 +2318,6 @@ dependencies = [
"version-compare",
]
-[[package]]
-name = "temp-dir"
-version = "0.1.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af547b166dd1ea4b472165569fc456cfb6818116f854690b0ff205e636523dab"
-
[[package]]
name = "tempfile"
version = "3.3.0"
@@ -3001,7 +2800,7 @@ version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6"
dependencies = [
- "dirs 4.0.0",
+ "dirs",
]
[[package]]
@@ -3029,7 +2828,7 @@ dependencies = [
"async-trait",
"byteorder",
"derivative",
- "dirs 4.0.0",
+ "dirs",
"enumflags2",
"event-listener",
"futures-core",
@@ -3069,7 +2868,7 @@ dependencies = [
"async-trait",
"byteorder",
"derivative",
- "dirs 4.0.0",
+ "dirs",
"enumflags2",
"event-listener",
"futures-core",
diff --git a/Cargo.toml b/Cargo.toml
index 52f8240b..a0ece26b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,5 @@
[workspace]
members = [
- "applets/cosmic-applet-audio",
"applets/cosmic-applet-network",
"applets/cosmic-applet-notifications",
"applets/cosmic-applet-power",
@@ -14,13 +13,13 @@ exclude = [
"applets/cosmic-applet-graphics",
"applets/cosmic-applet-workspaces",
"applets/cosmic-applet-battery",
-
+ "applets/cosmic-applet-audio",
]
[patch.crates-io]
wayland-protocols = { git = "https://github.com/smithay/wayland-rs", version = "0.30.0-beta.9"}
wayland-protocols-wlr = { git = "https://github.com/smithay/wayland-rs", version = "0.1.0-beta.9"}
-wayland-sys = { git = "https://github.com/smithay/wayland-rs", version = "0.30.0-beta.13"}
-wayland-backend = { git = "https://github.com/smithay/wayland-rs", version = "0.1.0-beta.13"}
-wayland-scanner = { git = "https://github.com/smithay/wayland-rs", version = "0.30.0-beta.13"}
-wayland-client = { git = "https://github.com/smithay/wayland-rs", version = "0.30.0-beta.13"}
+wayland-sys = { git = "https://github.com/smithay/wayland-rs", version = "0.30.0-beta.9"}
+wayland-backend = { git = "https://github.com/smithay/wayland-rs", version = "0.1.0-beta.9"}
+wayland-scanner = { git = "https://github.com/smithay/wayland-rs", version = "0.30.0-beta.9"}
+wayland-client = { git = "https://github.com/smithay/wayland-rs", version = "0.30.0-beta.9"}
diff --git a/applets/cosmic-applet-audio/Cargo.lock b/applets/cosmic-applet-audio/Cargo.lock
new file mode 100644
index 00000000..853fa626
--- /dev/null
+++ b/applets/cosmic-applet-audio/Cargo.lock
@@ -0,0 +1,3165 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "Inflector"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
+
+[[package]]
+name = "ab_glyph"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dcdbc68024b653943864d436fe8a24b028095bc1cf91a8926f8241e4aaffe59"
+dependencies = [
+ "ab_glyph_rasterizer",
+ "owned_ttf_parser",
+]
+
+[[package]]
+name = "ab_glyph_rasterizer"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "330223a1aecc308757b9926e9391c9b47f8ef2dbd8aea9df88312aea18c5e8d6"
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "aliasable"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
+
+[[package]]
+name = "apply"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f47b57fc4521e3cae26a4d45b5227f8fadee4c345be0fefd8d5d1711afb8aeb9"
+
+[[package]]
+name = "approx"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "arrayref"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
+
+[[package]]
+name = "arrayvec"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
+
+[[package]]
+name = "ash"
+version = "0.37.1+1.3.235"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "911015c962d56e2e4052f40182ca5462ba60a3d2ff04e827c365a0ab3d65726d"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bit_field"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
+name = "bumpalo"
+version = "3.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
+
+[[package]]
+name = "bytemuck"
+version = "1.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fe233b960f12f8007e3db2d136e3cb1c291bfd7396e384ee76025fc1a3932b4"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "bytes"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
+
+[[package]]
+name = "calloop"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19457a0da465234abd76134a5c2a910c14bd3c5558463e4396ab9a37a328e465"
+dependencies = [
+ "log",
+ "nix",
+ "slotmap",
+ "thiserror",
+ "vec_map",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
+
+[[package]]
+name = "cfg-expr"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa"
+dependencies = [
+ "smallvec",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+
+[[package]]
+name = "cgl"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cocoa"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a"
+dependencies = [
+ "bitflags",
+ "block",
+ "cocoa-foundation",
+ "core-foundation",
+ "core-graphics",
+ "foreign-types",
+ "libc",
+ "objc",
+]
+
+[[package]]
+name = "cocoa-foundation"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318"
+dependencies = [
+ "bitflags",
+ "block",
+ "core-foundation",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+ "objc",
+]
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "const_panic"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58baae561b85ca19b3122a9ddd35c8ec40c3bcd14fe89921824eae73f7baffbf"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
+
+[[package]]
+name = "core-graphics"
+version = "0.22.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "cosmic-applet-audio"
+version = "0.1.0"
+dependencies = [
+ "iced",
+ "iced_futures",
+ "iced_native",
+ "iced_sctk",
+ "icon-loader",
+ "libcosmic",
+ "libpulse-binding",
+ "libpulse-glib-binding",
+ "smithay-client-toolkit",
+ "tokio",
+]
+
+[[package]]
+name = "cosmic-panel-config"
+version = "0.1.0"
+source = "git+https://github.com/pop-os/cosmic-panel#35d61a3a617b2ef552931645ec35c545a3a688f6"
+dependencies = [
+ "anyhow",
+ "ron",
+ "serde",
+ "slog",
+ "xdg",
+]
+
+[[package]]
+name = "cosmic-theme"
+version = "0.1.0"
+source = "git+https://github.com/pop-os/cosmic-theme.git#896b228e4462c2fdeea0e89d1a657458b4a0a144"
+dependencies = [
+ "anyhow",
+ "csscolorparser",
+ "lazy_static",
+ "palette",
+ "ron",
+ "serde",
+ "xdg",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset 0.7.1",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
+[[package]]
+name = "csscolorparser"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf"
+dependencies = [
+ "phf",
+ "serde",
+]
+
+[[package]]
+name = "cty"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
+
+[[package]]
+name = "d3d12"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "827914e1f53b1e0e025ecd3d967a7836b7bcb54520f90e21ef8df7b4d88a2759"
+dependencies = [
+ "bitflags",
+ "libloading",
+ "winapi",
+]
+
+[[package]]
+name = "darling"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "dashmap"
+version = "5.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
+dependencies = [
+ "cfg-if",
+ "hashbrown",
+ "lock_api",
+ "once_cell",
+ "parking_lot_core 0.9.5",
+]
+
+[[package]]
+name = "data-url"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193"
+dependencies = [
+ "matches",
+]
+
+[[package]]
+name = "derive_setters"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1cf41b4580a37cca5ef2ada2cc43cf5d6be3983f4522e83010d67ab6925e84b"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "dirs"
+version = "4.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "dlib"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "dlv-list"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
+
+[[package]]
+name = "downcast-rs"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
+
+[[package]]
+name = "either"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+
+[[package]]
+name = "encase"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a516181e9a36e8982cb37933c5e7dba638c42938cacde46ee4e5b4156f881b9"
+dependencies = [
+ "const_panic",
+ "encase_derive",
+ "glam",
+ "thiserror",
+]
+
+[[package]]
+name = "encase_derive"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5b802412eea315f29f2bb2da3a5963cd6121f56eaa06aebcdc0c54eea578f22"
+dependencies = [
+ "encase_derive_impl",
+]
+
+[[package]]
+name = "encase_derive_impl"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f2f4de457d974f548d2c2a16f709ebd81013579e543bd1a9b19ced88132c2cf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "enum-repr"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bad30c9c0fa1aaf1ae5010dab11f1117b15d35faf62cda4bbbc53b9987950f18"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "euclid"
+version = "0.22.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b52c2ef4a78da0ba68fbe1fd920627411096d2ac478f7f4c9f3a54ba6705bade"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "exr"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eb5f255b5980bb0c8cf676b675d1a99be40f316881444f44e0462eaf5df5ded"
+dependencies = [
+ "bit_field",
+ "flume",
+ "half",
+ "lebe",
+ "miniz_oxide",
+ "smallvec",
+ "threadpool",
+]
+
+[[package]]
+name = "find-crate"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2"
+dependencies = [
+ "toml",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "float-cmp"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
+
+[[package]]
+name = "flume"
+version = "0.10.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+ "nanorand",
+ "pin-project",
+ "spin",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "fontdb"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d66551cc28351f0bc6a73da86459ee7765caaad03ce284f2dc36472dbf539cd"
+dependencies = [
+ "log",
+ "memmap2 0.3.1",
+ "ttf-parser 0.12.3",
+]
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "freedesktop-icons"
+version = "0.2.1"
+source = "git+https://github.com/wash2/freedestkop-icons#ac6343d76f1d2b3b666771b5ec0d22aa571a912e"
+dependencies = [
+ "dirs",
+ "once_cell",
+ "rust-ini",
+ "thiserror",
+ "xdg",
+]
+
+[[package]]
+name = "futures"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+ "num_cpus",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
+
+[[package]]
+name = "futures-task"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
+
+[[package]]
+name = "futures-util"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gif"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
+[[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 = "glam"
+version = "0.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "518faa5064866338b013ff9b2350dc318e14cc4fcd6cb8206d7e7c9886c98815"
+
+[[package]]
+name = "glib"
+version = "0.16.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cd04d150a2c63e6779f43aec7e04f5374252479b7bed5f45146d9c0e821f161"
+dependencies = [
+ "bitflags",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-task",
+ "futures-util",
+ "glib-macros",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "once_cell",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "glib-macros"
+version = "0.16.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e084807350b01348b6d9dbabb724d1a0bb987f47a2c85de200e98e12e30733bf"
+dependencies = [
+ "anyhow",
+ "heck",
+ "proc-macro-crate",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "glib-sys"
+version = "0.16.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61a4f46316d06bfa33a7ac22df6f0524c8be58e3db2d9ca99ccb1f357b62a65"
+dependencies = [
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "glow"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8bd5877156a19b8ac83a29b2306fe20537429d318f3ff0a1a2119f8d9c61919"
+dependencies = [
+ "js-sys",
+ "slotmap",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "glow_glyph"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f4e62c64947b9a24fe20e2bba9ad819ecb506ef5c8df7ffc4737464c6df9510"
+dependencies = [
+ "bytemuck",
+ "glow",
+ "glyph_brush",
+ "log",
+]
+
+[[package]]
+name = "glutin"
+version = "0.30.0-beta.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8ad8ba6f4be6b573f2da485715baaae46a6fae00ba1ae683117f58f1da29c09"
+dependencies = [
+ "bitflags",
+ "cfg_aliases",
+ "cgl",
+ "cocoa",
+ "core-foundation",
+ "glutin_egl_sys",
+ "glutin_glx_sys",
+ "glutin_wgl_sys",
+ "libloading",
+ "objc",
+ "once_cell",
+ "raw-window-handle",
+ "wayland-sys",
+ "windows-sys 0.36.1",
+ "x11-dl",
+]
+
+[[package]]
+name = "glutin_egl_sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3adbb8fec0e18e340f990c78f79f5f0e142d0d83f46b10909aaa7d251c00afdf"
+dependencies = [
+ "gl_generator",
+ "windows-sys 0.36.1",
+]
+
+[[package]]
+name = "glutin_glx_sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "947c4850c58211c9627969c2b4e2674764b81ae5b47bab2c9a477d7942f96e0f"
+dependencies = [
+ "gl_generator",
+ "x11-dl",
+]
+
+[[package]]
+name = "glutin_wgl_sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20c33975a6c9d49d72c8f032a60079bf8df536954fbf9e4cee90396ace815c57"
+dependencies = [
+ "gl_generator",
+]
+
+[[package]]
+name = "glyph_brush"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac02497410cdb5062cc056a33f2e1e19ff69fbf26a4be9a02bf29d6e17ea105b"
+dependencies = [
+ "glyph_brush_draw_cache",
+ "glyph_brush_layout",
+ "log",
+ "ordered-float",
+ "rustc-hash",
+ "twox-hash",
+]
+
+[[package]]
+name = "glyph_brush_draw_cache"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6010675390f6889e09a21e2c8b575b3ee25667ea8237a8d59423f73cb8c28610"
+dependencies = [
+ "ab_glyph",
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "linked-hash-map",
+ "rayon",
+ "rustc-hash",
+]
+
+[[package]]
+name = "glyph_brush_layout"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc32c2334f00ca5ac3695c5009ae35da21da8c62d255b5b96d56e2597a637a38"
+dependencies = [
+ "ab_glyph",
+ "approx",
+ "xi-unicode",
+]
+
+[[package]]
+name = "gobject-sys"
+version = "0.16.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3520bb9c07ae2a12c7f2fbb24d4efc11231c8146a86956413fb1a79bb760a0f1"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "gpu-alloc"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fc59e5f710e310e76e6707f86c561dd646f69a8876da9131703b2f717de818d"
+dependencies = [
+ "bitflags",
+ "gpu-alloc-types",
+]
+
+[[package]]
+name = "gpu-alloc-types"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "gpu-descriptor"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b0c02e1ba0bdb14e965058ca34e09c020f8e507a760df1121728e0aef68d57a"
+dependencies = [
+ "bitflags",
+ "gpu-descriptor-types",
+ "hashbrown",
+]
+
+[[package]]
+name = "gpu-descriptor-types"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "guillotiere"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b62d5865c036cb1393e23c50693df631d3f5d7bcca4c04fe4cc0fd592e74a782"
+dependencies = [
+ "euclid",
+ "svg_fmt",
+]
+
+[[package]]
+name = "half"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad6a9459c9c30b177b925162351f97e7d967c7ea8bab3b8352805327daf45554"
+dependencies = [
+ "crunchy",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hexf-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
+
+[[package]]
+name = "iced"
+version = "0.5.2"
+source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60"
+dependencies = [
+ "iced_core",
+ "iced_futures",
+ "iced_glow",
+ "iced_graphics",
+ "iced_native",
+ "iced_sctk",
+ "iced_wgpu",
+ "image",
+ "thiserror",
+]
+
+[[package]]
+name = "iced_core"
+version = "0.6.1"
+source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60"
+dependencies = [
+ "bitflags",
+ "palette",
+ "wasm-timer",
+]
+
+[[package]]
+name = "iced_futures"
+version = "0.5.1"
+source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60"
+dependencies = [
+ "futures",
+ "log",
+ "tokio",
+ "wasm-bindgen-futures",
+ "wasm-timer",
+]
+
+[[package]]
+name = "iced_glow"
+version = "0.4.1"
+source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60"
+dependencies = [
+ "bytemuck",
+ "euclid",
+ "glow",
+ "glow_glyph",
+ "glyph_brush",
+ "iced_graphics",
+ "iced_native",
+ "log",
+]
+
+[[package]]
+name = "iced_graphics"
+version = "0.4.0"
+source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60"
+dependencies = [
+ "bitflags",
+ "bytemuck",
+ "glam",
+ "iced_native",
+ "iced_style",
+ "image",
+ "kamadak-exif",
+ "log",
+ "raw-window-handle",
+ "resvg",
+ "thiserror",
+ "tiny-skia",
+ "usvg",
+]
+
+[[package]]
+name = "iced_lazy"
+version = "0.2.0"
+source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60"
+dependencies = [
+ "iced_native",
+ "ouroboros",
+]
+
+[[package]]
+name = "iced_native"
+version = "0.6.1"
+source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60"
+dependencies = [
+ "iced_core",
+ "iced_futures",
+ "iced_style",
+ "num-traits",
+ "smithay-client-toolkit",
+ "twox-hash",
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "iced_sctk"
+version = "0.1.0"
+source = "git+https://github.com/pop-os/iced-sctk#d126b62ef1001b22dc946db91928395890ff8d9f"
+dependencies = [
+ "enum-repr",
+ "futures",
+ "glow",
+ "glutin",
+ "iced_futures",
+ "iced_graphics",
+ "iced_native",
+ "log",
+ "raw-window-handle",
+ "smithay-client-toolkit",
+ "thiserror",
+ "wayland-backend",
+]
+
+[[package]]
+name = "iced_style"
+version = "0.5.0"
+source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60"
+dependencies = [
+ "iced_core",
+ "once_cell",
+ "palette",
+]
+
+[[package]]
+name = "iced_wgpu"
+version = "0.6.1"
+source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60"
+dependencies = [
+ "bitflags",
+ "bytemuck",
+ "encase",
+ "futures",
+ "glam",
+ "glyph_brush",
+ "guillotiere",
+ "iced_graphics",
+ "iced_native",
+ "log",
+ "raw-window-handle",
+ "wgpu",
+ "wgpu_glyph",
+]
+
+[[package]]
+name = "icon-loader"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4a8c09d5f0ce32daff2039f6f1830a4920f5eb23f00e62412bfb5f878a1be2e"
+dependencies = [
+ "dashmap",
+ "rust-ini",
+ "xdg",
+]
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "image"
+version = "0.24.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "exr",
+ "gif",
+ "jpeg-decoder 0.3.0",
+ "num-rational",
+ "num-traits",
+ "png",
+ "scoped_threadpool",
+ "tiff",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.0-rc1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f65dae1d3aa98e6877917ab4e6fdbfdfb00e95885ea7c4f4f29e3a5dfc08fdf"
+dependencies = [
+ "libc",
+ "windows-sys 0.36.1",
+]
+
+[[package]]
+name = "jpeg-decoder"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2"
+
+[[package]]
+name = "jpeg-decoder"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
+dependencies = [
+ "rayon",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "kamadak-exif"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef4fc70d0ab7e5b6bafa30216a6b48705ea964cdfc29c050f2412295eba58077"
+dependencies = [
+ "mutate_once",
+]
+
+[[package]]
+name = "khronos-egl"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3"
+dependencies = [
+ "libc",
+ "libloading",
+ "pkg-config",
+]
+
+[[package]]
+name = "khronos_api"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
+
+[[package]]
+name = "kurbo"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb348d766edbac91ba1eb83020d96f4f8867924d194393083c15a51f185e6a82"
+dependencies = [
+ "arrayvec 0.5.2",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "lebe"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
+
+[[package]]
+name = "libc"
+version = "0.2.138"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
+
+[[package]]
+name = "libcosmic"
+version = "0.1.0"
+source = "git+https://github.com/pop-os/libcosmic/?branch=master#91e826d8eacd03d8e7ef5b1a767b2164f8719ed0"
+dependencies = [
+ "apply",
+ "cosmic-panel-config",
+ "cosmic-theme",
+ "derive_setters",
+ "freedesktop-icons",
+ "iced",
+ "iced_core",
+ "iced_lazy",
+ "iced_native",
+ "iced_style",
+ "iced_wgpu",
+ "lazy_static",
+ "palette",
+ "smithay-client-toolkit",
+]
+
+[[package]]
+name = "libloading"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
+[[package]]
+name = "libpulse-binding"
+version = "2.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17be42160017e0ae993c03bfdab4ecb6f82ce3f8d515bd8da8fdf18d10703663"
+dependencies = [
+ "bitflags",
+ "libc",
+ "libpulse-sys",
+ "num-derive",
+ "num-traits",
+ "winapi",
+]
+
+[[package]]
+name = "libpulse-glib-binding"
+version = "2.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df0e7a964c9f7e95d4f073affc19adfda009fa0d55e8831dbb66c78be1d0e6e5"
+dependencies = [
+ "glib",
+ "glib-sys",
+ "libpulse-binding",
+ "libpulse-mainloop-glib-sys",
+]
+
+[[package]]
+name = "libpulse-mainloop-glib-sys"
+version = "1.19.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36f61c4064926cc77ea14bb206a21ce1d5a06e175e5c0ce078804bb6c4527b28"
+dependencies = [
+ "glib-sys",
+ "libpulse-sys",
+ "pkg-config",
+]
+
+[[package]]
+name = "libpulse-sys"
+version = "1.19.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "991e6bd0efe2a36e6534e136e7996925e4c1a8e35b7807fe533f2beffff27c30"
+dependencies = [
+ "libc",
+ "num-derive",
+ "num-traits",
+ "pkg-config",
+ "winapi",
+]
+
+[[package]]
+name = "linked-hash-map"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
+
+[[package]]
+name = "lock_api"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "matches"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memmap2"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memmap2"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "metal"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de11355d1f6781482d027a3b4d4de7825dcedb197bf573e0596d00008402d060"
+dependencies = [
+ "bitflags",
+ "block",
+ "core-graphics-types",
+ "foreign-types",
+ "log",
+ "objc",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
+dependencies = [
+ "libc",
+ "log",
+ "wasi",
+ "windows-sys 0.42.0",
+]
+
+[[package]]
+name = "mutate_once"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b"
+
+[[package]]
+name = "naga"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "262d2840e72dbe250e8cf2f522d080988dfca624c4112c096238a4845f591707"
+dependencies = [
+ "bit-set",
+ "bitflags",
+ "codespan-reporting",
+ "hexf-parse",
+ "indexmap",
+ "log",
+ "num-traits",
+ "rustc-hash",
+ "spirv",
+ "termcolor",
+ "thiserror",
+ "unicode-xid",
+]
+
+[[package]]
+name = "nanorand"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "nix"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
+dependencies = [
+ "autocfg",
+ "bitflags",
+ "cfg-if",
+ "libc",
+ "memoffset 0.6.5",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "num-derive"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+ "objc_exception",
+]
+
+[[package]]
+name = "objc_exception"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
+
+[[package]]
+name = "ordered-float"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d84eb1409416d254e4a9c8fa56cc24701755025b458f0fcd8e59e1f5f40c23bf"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "ordered-multimap"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
+dependencies = [
+ "dlv-list",
+ "hashbrown",
+]
+
+[[package]]
+name = "ouroboros"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f357ef82d1b4db66fbed0b8d542cbd3c22d0bf5b393b3c257b9ba4568e70c9c3"
+dependencies = [
+ "aliasable",
+ "ouroboros_macro",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "ouroboros_macro"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44a0b52c2cbaef7dffa5fec1a43274afe8bd2a644fa9fc50a9ef4ff0269b1257"
+dependencies = [
+ "Inflector",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "owned_ttf_parser"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18904d3c65493a9f0d7542293d1a7f69bfdc309a6b9ef4f46dc3e58b0577edc5"
+dependencies = [
+ "ttf-parser 0.17.1",
+]
+
+[[package]]
+name = "palette"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f9cd68f7112581033f157e56c77ac4a5538ec5836a2e39284e65bd7d7275e49"
+dependencies = [
+ "approx",
+ "num-traits",
+ "palette_derive",
+ "phf",
+ "serde",
+]
+
+[[package]]
+name = "palette_derive"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05eedf46a8e7c27f74af0c9cfcdb004ceca158cb1b918c6f68f8d7a549b3e427"
+dependencies = [
+ "find-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
+dependencies = [
+ "instant",
+ "lock_api",
+ "parking_lot_core 0.8.5",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core 0.9.5",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
+dependencies = [
+ "cfg-if",
+ "instant",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "winapi",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-sys 0.42.0",
+]
+
+[[package]]
+name = "phf"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
+dependencies = [
+ "phf_macros",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
+dependencies = [
+ "phf_shared",
+ "rand",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
+name = "pico-args"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
+
+[[package]]
+name = "pin-project"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
+
+[[package]]
+name = "png"
+version = "0.17.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638"
+dependencies = [
+ "bitflags",
+ "crc32fast",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "proc-macro-crate"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9"
+dependencies = [
+ "once_cell",
+ "thiserror",
+ "toml",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "profiling"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74605f360ce573babfe43964cbe520294dcb081afbf8c108fc6e23036b4da2df"
+
+[[package]]
+name = "quick-xml"
+version = "0.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11bafc859c6815fbaffbbbf4229ecb767ac913fecb27f9ad4343662e9ef099ea"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+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 = "range-alloc"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e935c45e09cc6dcf00d2f0b2d630a58f4095320223d47fc68918722f0538b6"
+
+[[package]]
+name = "raw-window-handle"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a"
+dependencies = [
+ "cty",
+]
+
+[[package]]
+name = "rayon"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
+dependencies = [
+ "crossbeam-deque",
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "num_cpus",
+]
+
+[[package]]
+name = "rctree"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ae028b272a6e99d9f8260ceefa3caa09300a8d6c8d2b2001316474bc52122e9"
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
+dependencies = [
+ "getrandom",
+ "redox_syscall",
+ "thiserror",
+]
+
+[[package]]
+name = "renderdoc-sys"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157"
+
+[[package]]
+name = "resvg"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "608c6e8aa6fb2c13bc06e4184d7c7b2cc1b7c138f88a539da8be55c3c033d7f4"
+dependencies = [
+ "jpeg-decoder 0.1.22",
+ "log",
+ "pico-args",
+ "png",
+ "rgb",
+ "svgfilters",
+ "tiny-skia",
+ "usvg",
+]
+
+[[package]]
+name = "rgb"
+version = "0.8.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3603b7d71ca82644f79b5a06d1220e9a58ede60bd32255f698cb1af8838b8db3"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "ron"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "300a51053b1cb55c80b7a9fde4120726ddf25ca241a1cbb926626f62fb136bff"
+dependencies = [
+ "base64",
+ "bitflags",
+ "serde",
+]
+
+[[package]]
+name = "roxmltree"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b"
+dependencies = [
+ "xmlparser",
+]
+
+[[package]]
+name = "rust-ini"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df"
+dependencies = [
+ "cfg-if",
+ "ordered-multimap",
+]
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustybuzz"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44561062e583c4873162861261f16fd1d85fe927c4904d71329a4fe43dc355ef"
+dependencies = [
+ "bitflags",
+ "bytemuck",
+ "smallvec",
+ "ttf-parser 0.12.3",
+ "unicode-bidi-mirroring",
+ "unicode-ccc",
+ "unicode-general-category",
+ "unicode-script",
+]
+
+[[package]]
+name = "safe_arch"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
+name = "scoped_threadpool"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "serde"
+version = "1.0.149"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.149"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "simplecss"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "siphasher"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
+
+[[package]]
+name = "slab"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "slog"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06"
+
+[[package]]
+name = "slotmap"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+
+[[package]]
+name = "smithay-client-toolkit"
+version = "0.16.0"
+source = "git+https://github.com/Smithay/client-toolkit#f1d9c3ef9cfbd508d986f7f98b2fc267fcc39b84"
+dependencies = [
+ "bitflags",
+ "calloop",
+ "dlib",
+ "lazy_static",
+ "log",
+ "memmap2 0.5.8",
+ "nix",
+ "pkg-config",
+ "thiserror",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-cursor",
+ "wayland-protocols",
+ "wayland-protocols-wlr",
+ "wayland-scanner",
+ "xkbcommon",
+]
+
+[[package]]
+name = "socket2"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "spin"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "spirv"
+version = "0.2.0+1.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830"
+dependencies = [
+ "bitflags",
+ "num-traits",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strsim"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
+
+[[package]]
+name = "svg_fmt"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2"
+
+[[package]]
+name = "svgfilters"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "639abcebc15fdc2df179f37d6f5463d660c1c79cd552c12343a4600827a04bce"
+dependencies = [
+ "float-cmp",
+ "rgb",
+]
+
+[[package]]
+name = "svgtypes"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22975e8a2bac6a76bb54f898a6b18764633b00e780330f0b689f65afb3975564"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.105"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "system-deps"
+version = "6.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff"
+dependencies = [
+ "cfg-expr",
+ "heck",
+ "pkg-config",
+ "toml",
+ "version-compare",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "threadpool"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
+dependencies = [
+ "num_cpus",
+]
+
+[[package]]
+name = "tiff"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f17def29300a156c19ae30814710d9c63cd50288a49c6fd3a10ccfbe4cf886fd"
+dependencies = [
+ "flate2",
+ "jpeg-decoder 0.3.0",
+ "weezl",
+]
+
+[[package]]
+name = "tiny-skia"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d049bfef0eaa2521e75d9ffb5ce86ad54480932ae19b85f78bec6f52c4d30d78"
+dependencies = [
+ "arrayref",
+ "arrayvec 0.5.2",
+ "bytemuck",
+ "cfg-if",
+ "png",
+ "safe_arch",
+]
+
+[[package]]
+name = "tokio"
+version = "1.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46"
+dependencies = [
+ "autocfg",
+ "bytes",
+ "libc",
+ "memchr",
+ "mio",
+ "num_cpus",
+ "parking_lot 0.12.1",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.42.0",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "ttf-parser"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ae2f58a822f08abdaf668897e96a5656fe72f5a9ce66422423e8849384872e6"
+
+[[package]]
+name = "ttf-parser"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff"
+
+[[package]]
+name = "twox-hash"
+version = "1.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
+dependencies = [
+ "cfg-if",
+ "rand",
+ "static_assertions",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
+
+[[package]]
+name = "unicode-bidi-mirroring"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694"
+
+[[package]]
+name = "unicode-ccc"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1"
+
+[[package]]
+name = "unicode-general-category"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07547e3ee45e28326cc23faac56d44f58f16ab23e413db526debce3b0bfd2742"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
+
+[[package]]
+name = "unicode-script"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
+
+[[package]]
+name = "unicode-vo"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
+
+[[package]]
+name = "usvg"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4058e0bd091a56f905e6963e40776ce6880b271275f0b493bff951433e303071"
+dependencies = [
+ "base64",
+ "data-url",
+ "flate2",
+ "float-cmp",
+ "fontdb",
+ "kurbo",
+ "log",
+ "pico-args",
+ "rctree",
+ "roxmltree",
+ "rustybuzz",
+ "simplecss",
+ "siphasher",
+ "svgtypes",
+ "ttf-parser 0.12.3",
+ "unicode-bidi",
+ "unicode-script",
+ "unicode-vo",
+ "xmlwriter",
+]
+
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+
+[[package]]
+name = "version-compare"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[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.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+
+[[package]]
+name = "wasm-timer"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f"
+dependencies = [
+ "futures",
+ "js-sys",
+ "parking_lot 0.11.2",
+ "pin-utils",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "wayland-backend"
+version = "0.1.0-beta.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3f96c52ca34b33e6bb55497327428be54dad648973aa5d3d1e02982d2fcc298"
+dependencies = [
+ "cc",
+ "downcast-rs",
+ "io-lifetimes",
+ "nix",
+ "scoped-tls",
+ "smallvec",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-client"
+version = "0.30.0-beta.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f6ab5911cc8a28e9d781933c1afe0f97ffa6885ad8941294784d025f06e5ad9"
+dependencies = [
+ "bitflags",
+ "futures-channel",
+ "futures-core",
+ "nix",
+ "thiserror",
+ "wayland-backend",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-cursor"
+version = "0.30.0-beta.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa5ce1bbc13a3d806648974cba1d53481fea2ac29483eb147e4cce1250fc5263"
+dependencies = [
+ "nix",
+ "wayland-client",
+ "xcursor",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.30.0-beta.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e32104ba51988754f4429dbb59c7b7fd2ccf1feaa0009bb59e49a5e2f91e985d"
+dependencies = [
+ "bitflags",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-wlr"
+version = "0.1.0-beta.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "130613a48629204e4ab50ea4b5de2efa2e17c58f71a5af15b39535f99421f8c3"
+dependencies = [
+ "bitflags",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-scanner"
+version = "0.30.0-beta.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fb55ca28710dcc155078db4e2bd29aacf24bd311261c48750f38c6c5430bf1f"
+dependencies = [
+ "proc-macro2",
+ "quick-xml",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "wayland-sys"
+version = "0.30.0-beta.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "882bda56e8397384ad66ec0ebe763e0b3b53153a6327344f7d74b85981a271e1"
+dependencies = [
+ "dlib",
+ "lazy_static",
+ "log",
+ "pkg-config",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "weezl"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
+
+[[package]]
+name = "wgpu"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81f643110d228fd62a60c5ed2ab56c4d5b3704520bd50561174ec4ec74932937"
+dependencies = [
+ "arrayvec 0.7.2",
+ "js-sys",
+ "log",
+ "naga",
+ "parking_lot 0.12.1",
+ "raw-window-handle",
+ "smallvec",
+ "static_assertions",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "wgpu-core",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-core"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6000d1284ef8eec6076fd5544a73125fd7eb9b635f18dceeb829d826f41724ca"
+dependencies = [
+ "arrayvec 0.7.2",
+ "bit-vec",
+ "bitflags",
+ "cfg_aliases",
+ "codespan-reporting",
+ "fxhash",
+ "log",
+ "naga",
+ "parking_lot 0.12.1",
+ "profiling",
+ "raw-window-handle",
+ "smallvec",
+ "thiserror",
+ "web-sys",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-hal"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cc320a61acb26be4f549c9b1b53405c10a223fbfea363ec39474c32c348d12f"
+dependencies = [
+ "android_system_properties",
+ "arrayvec 0.7.2",
+ "ash",
+ "bit-set",
+ "bitflags",
+ "block",
+ "core-graphics-types",
+ "d3d12",
+ "foreign-types",
+ "fxhash",
+ "glow",
+ "gpu-alloc",
+ "gpu-descriptor",
+ "js-sys",
+ "khronos-egl",
+ "libloading",
+ "log",
+ "metal",
+ "naga",
+ "objc",
+ "parking_lot 0.12.1",
+ "profiling",
+ "range-alloc",
+ "raw-window-handle",
+ "renderdoc-sys",
+ "smallvec",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+ "wgpu-types",
+ "winapi",
+]
+
+[[package]]
+name = "wgpu-types"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb6b28ef22cac17b9109b25b3bf8c9a103eeb293d7c5f78653979b09140375f6"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "wgpu_glyph"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cafb82773e0f124a33674dab5de4dff73175aeb921949047ab014efb58fb446"
+dependencies = [
+ "bytemuck",
+ "glyph_brush",
+ "log",
+ "wgpu",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
+dependencies = [
+ "windows_aarch64_msvc 0.36.1",
+ "windows_i686_gnu 0.36.1",
+ "windows_i686_msvc 0.36.1",
+ "windows_x86_64_gnu 0.36.1",
+ "windows_x86_64_msvc 0.36.1",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc 0.42.0",
+ "windows_i686_gnu 0.42.0",
+ "windows_i686_msvc 0.42.0",
+ "windows_x86_64_gnu 0.42.0",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc 0.42.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
+
+[[package]]
+name = "x11-dl"
+version = "2.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1536d6965a5d4e573c7ef73a2c15ebcd0b2de3347bdf526c34c297c00ac40f0"
+dependencies = [
+ "lazy_static",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "xcursor"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "xdg"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6"
+dependencies = [
+ "dirs",
+]
+
+[[package]]
+name = "xi-unicode"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a"
+
+[[package]]
+name = "xkbcommon"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbee136714379ab22da0280207fdb7f47e0bb940adea97731b65598b8c7a92e"
+dependencies = [
+ "libc",
+ "memmap2 0.5.8",
+]
+
+[[package]]
+name = "xml-rs"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
+
+[[package]]
+name = "xmlparser"
+version = "0.13.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd"
+
+[[package]]
+name = "xmlwriter"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
diff --git a/applets/cosmic-applet-audio/Cargo.toml b/applets/cosmic-applet-audio/Cargo.toml
index ce930f58..f50634f0 100644
--- a/applets/cosmic-applet-audio/Cargo.toml
+++ b/applets/cosmic-applet-audio/Cargo.toml
@@ -2,25 +2,32 @@
name = "cosmic-applet-audio"
version = "0.1.0"
edition = "2021"
-license = "GPL-3.0-or-later"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-futures = "0.3.21"
-futures-util = "0.3.21"
-libcosmic-applet = { path = "../../libcosmic-applet" }
+icon-loader = { version = "0.3.6", features = ["gtk"] }
libpulse-binding = "2.26.0"
libpulse-glib-binding = "2.25.0"
-tracker = "0.1.1"
-freedesktop-desktop-entry = "0.5.0"
-mpris2-zbus = { git = "https://github.com/pop-os/mpris2-zbus" }
-zbus = "2.1.1"
-tokio = { version = "1.17.0", features = ["full"] }
-relm4 = { git = "https://github.com/relm4/relm4", branch = "next", features = ["macros"] }
-relm4-macros = { git = "https://github.com/relm4/relm4", branch = "next" }
-once_cell = "1.10.0"
-gtk4 = { git = "https://github.com/gtk-rs/gtk4-rs", features = ["v4_2"] }
-adw = { git = "https://gitlab.gnome.org/World/Rust/libadwaita-rs", package = "libadwaita"}
-libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false, features = ["widgets"] }
-async-io = "1.6.0"
+tokio = { version = "1.20.1", features=["full"] }
+libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["wayland", "applet"] }
+iced_sctk = { git = "https://github.com/pop-os/iced-sctk" }
+sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", version = "0.16" }
-[features]
+[workspace]
+resolved = "2"
+
+[dependencies.iced]
+git = "https://github.com/pop-os/iced.git"
+branch = "sctk-cosmic"
+# path = "../iced"
+default-features = false
+features = ["image", "svg", "tokio", "wayland"]
+
+[dependencies.iced_native]
+git = "https://github.com/pop-os/iced.git"
+branch = "sctk-cosmic"
+
+[dependencies.iced_futures]
+git = "https://github.com/pop-os/iced.git"
+branch = "sctk-cosmic"
diff --git a/applets/cosmic-applet-audio/data/com.system76.CosmicAppletAudio.desktop b/applets/cosmic-applet-audio/data/com.system76.CosmicAppletAudio.desktop
index ea136a38..450b30a4 100644
--- a/applets/cosmic-applet-audio/data/com.system76.CosmicAppletAudio.desktop
+++ b/applets/cosmic-applet-audio/data/com.system76.CosmicAppletAudio.desktop
@@ -1,5 +1,5 @@
[Desktop Entry]
-Name=Cosmic Dock App List
+Name=Cosmic Applet Audio
Comment=Write a GTK + Rust application
Type=Application
Exec=cosmic-applet-audio
diff --git a/applets/cosmic-applet-audio/data/resources/resources.gresource.xml b/applets/cosmic-applet-audio/data/resources/resources.gresource.xml
index 2cf0970f..bdc8cdd8 100644
--- a/applets/cosmic-applet-audio/data/resources/resources.gresource.xml
+++ b/applets/cosmic-applet-audio/data/resources/resources.gresource.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/applets/cosmic-applet-audio/src/app.rs b/applets/cosmic-applet-audio/src/app.rs
deleted file mode 100644
index 988b45f4..00000000
--- a/applets/cosmic-applet-audio/src/app.rs
+++ /dev/null
@@ -1,451 +0,0 @@
-use crate::icons::{parse_desktop_icons, DesktopApplication};
-use futures_util::StreamExt;
-use libcosmic_widgets::LabeledItem;
-use libpulse_binding::{
- context::{subscribe::{Facility, InterestMaskSet, Operation}, State},
- volume::Volume,
-};
-use mpris2_zbus::media_player::MediaPlayer;
-use relm4::{
- component,
- gtk::{
- self,
- glib::{self, clone},
- prelude::*,
- Align, Box as GtkBox, Button, Image, Label, ListBox, Orientation, PositionType, Revealer,
- RevealerTransitionType, Scale, Separator, Window,
- },
- view, ComponentParts, ComponentSender, RelmContainerExt, Sender, SimpleComponent, send,
-};
-use std::{collections::HashMap, rc::Rc};
-use tracker::track;
-use zbus::Connection;
-
-use crate::pa::{DeviceInfo, PA};
-
-pub enum AppInput {
- Inputs,
- Outputs,
- InputVolume,
- OutputVolume,
- NowPlaying,
-}
-
-#[derive(Clone, PartialEq, Eq)]
-struct NowPlayingInfo {
- title: String,
- artist: String,
- album: String,
- art_url: String,
-}
-
-#[track]
-pub struct App {
- #[no_eq]
- default_input: Option,
- #[no_eq]
- inputs: Vec,
- #[no_eq]
- default_output: Option,
- #[no_eq]
- outputs: Vec,
- #[no_eq]
- now_playing: Vec,
- #[do_not_track]
- desktop_icons: HashMap,
- #[do_not_track]
- pa: PA,
-}
-
-impl Default for App {
- fn default() -> Self {
- let mut input_controller =
- SourceController::create().expect("failed to create input controller");
- let default_input = input_controller.get_default_device().ok();
- let inputs = input_controller.list_devices().unwrap_or_default();
- let mut output_controller =
- SinkController::create().expect("failed to create output controller");
- let default_output = output_controller.get_default_device().ok();
- let outputs = output_controller.list_devices().unwrap_or_default();
- let now_playing = Vec::new();
- let desktop_icons = parse_desktop_icons();
- // XXX handle no pulseaudio daemon?
- let pa = PA::new().unwrap();
- Self {
- default_input,
- inputs,
- default_output,
- outputs,
- now_playing,
- desktop_icons,
- pa,
- tracker: 0,
- }
- }
-}
-
-impl App {
- fn get_default_input_name(&self) -> &str {
- match &self.default_input {
- Some(input) => match &input.description {
- Some(name) => name.as_str(),
- None => "Input Device",
- },
- None => "No Input Device",
- }
- }
-
- fn get_default_output_name(&self) -> &str {
- match &self.default_output {
- Some(output) => match &output.description {
- Some(name) => name.as_str(),
- None => "Output Device",
- },
- None => "No Output Device",
- }
- }
-
- fn refresh_default_input(&mut self) {
- let mut input_controller =
- SourceController::create().expect("failed to create input controller");
- self.default_input = match self.default_input.as_ref() {
- Some(input) => match &input.name {
- Some(name) => input_controller.get_device_by_name(name.as_str()).ok(),
- None => input_controller.get_device_by_index(input.index).ok(),
- },
- None => return,
- };
- }
-
- fn refresh_default_output(&mut self) {
- let mut output_controller =
- SinkController::create().expect("failed to create output controller");
- self.default_output = match self.default_output.as_ref() {
- Some(output) => match &output.name {
- Some(name) => output_controller.get_device_by_name(name.as_str()).ok(),
- None => output_controller.get_device_by_index(output.index).ok(),
- },
- None => return,
- };
- }
-
- fn subscribe_for_updates(&self, input: &Sender) {
- let input_clone = input.clone();
- self.pa.set_subscribe_callback(move |facility, operation, _idx| {
- if !matches!(operation, Some(Operation::Changed)) {
- return;
- }
- match facility {
- Some(Facility::Sink) => {
- input_clone.send(AppInput::OutputVolume);
- }
- Some(Facility::Source) => {
- input_clone.send(AppInput::InputVolume);
- }
- _ => {}
- }
- });
- self.pa.set_state_callback(move |pa, state| {
- if state == State::Ready {
- pa.subscribe(InterestMaskSet::SINK | InterestMaskSet::SOURCE);
- }
- });
- }
-
- fn refresh_input_list(&mut self) {
- let mut input_controller =
- SourceController::create().expect("failed to create input controller");
- self.set_inputs(input_controller.list_devices().unwrap_or_default());
- }
-
- fn refresh_input_widgets(&self, widgets: &AppWidgets) {
- while let Some(row) = widgets.inputs.row_at_index(0) {
- widgets.inputs.remove(&row);
- }
- for input in self.get_inputs() {
- let input = Rc::new(input.clone());
- let name = match &input.name {
- Some(name) => name.to_owned(),
- None => continue, // Why doesn't this have a name? Whatever, it's invalid.
- };
- view! {
- item = LabeledItem {
- set_title: input.description
- .as_ref()
- .unwrap_or(&name),
- set_child: set_current_input_device = &Button {
- set_label: "Switch",
- connect_clicked: move |_| {
- SourceController::create()
- .expect("failed to create input controller")
- .set_default_device(&name)
- .expect("failed to set default device");
- }
- }
- }
- }
- widgets.inputs.container_add(&item);
- }
- }
-
- fn refresh_output_list(&mut self) {
- let mut output_controller =
- SinkController::create().expect("failed to create output controller");
- self.set_outputs(output_controller.list_devices().unwrap_or_default());
- }
-
- fn refresh_output_widgets(&self, widgets: &AppWidgets) {
- while let Some(row) = widgets.outputs.row_at_index(0) {
- widgets.outputs.remove(&row);
- }
- for output in self.get_outputs() {
- let output = Rc::new(output.clone());
- let name = match &output.name {
- Some(name) => name.to_owned(),
- None => continue, // Why doesn't this have a name? Whatever, it's invalid.
- };
- view! {
- item = LabeledItem {
- set_title: output.description
- .as_ref()
- .unwrap_or(&name),
- set_child: set_current_output_device = &Button {
- set_label: "Switch",
- connect_clicked: move |_| {
- SinkController::create()
- .expect("failed to create output controller")
- .set_default_device(&name)
- .expect("failed to set default device");
-
- }
- }
- }
- }
- widgets.outputs.container_add(&item);
- }
- }
-
- fn refresh_now_playing(&mut self) {
- let mut output_controller =
- SinkController::create().expect("failed to create output controller");
- self.set_now_playing(output_controller.list_applications().unwrap_or_default());
- }
-
- fn refresh_now_playing_widgets(&self, widgets: &AppWidgets) {
- while let Some(row) = widgets.playing_apps.row_at_index(0) {
- widgets.playing_apps.remove(&row);
- }
- for app in self.get_now_playing() {
- let index = app.index;
- let muted = app.mute;
- let icon_name = app
- .proplist
- .get_str("application.icon_name")
- .or_else(|| {
- app.proplist
- .get_str("application.name")
- .and_then(|name| self.desktop_icons.get(&DesktopApplication::Name(name)))
- .cloned()
- })
- .or_else(|| {
- app.proplist
- .get_str("application.process.binary")
- .and_then(|name| self.desktop_icons.get(&DesktopApplication::Binary(name)))
- .cloned()
- })
- .unwrap_or_default();
-
- let name = app.name.clone().unwrap_or_default();
- view! {
- item = GtkBox {
- set_orientation: Orientation::Horizontal,
- append: icon = &Image {
- set_icon_name: Some(&icon_name),
- set_pixel_size: 24,
- },
- append: title = &Label {
- set_label: &name,
- },
- append: media_buttons = &GtkBox {
- set_halign: Align::End,
- append: pause_button = &Button {
- #[wrap(Some)]
- set_child: pause_button_img = &Image {
- set_icon_name: Some("media-playback-pause-symbolic"),
- set_pixel_size: 24,
- },
- connect_clicked: move |_| {
- SinkController::create()
- .expect("failed to create output controller")
- .set_app_mute(index, !muted)
- .expect("failed to (un)mute application");
- }
- }
- }
- }
- }
- widgets.playing_apps.container_add(&item);
- }
- }
-}
-
-#[component(pub)]
-impl SimpleComponent for App {
- type Widgets = AppWidgets;
- type InitParams = ();
- type Input = AppInput;
- type Output = ();
-
- view! {
- Window {
- set_title: Some("COSMIC Network Applet"),
- set_default_width: 400,
- set_default_height: 300,
-
- GtkBox {
- set_orientation: Orientation::Vertical,
- set_spacing: 24,
- GtkBox {
- set_orientation: Orientation::Horizontal,
- set_spacing: 16,
- Image {
- set_icon_name: Some("audio-speakers-symbolic"),
- },
- append: output_volume = &Scale::with_range(Orientation::Horizontal, 0., 100., 1.) {
- set_format_value_func: |_, value| {
- format!("{:.0}%", value)
- },
- #[watch]
- set_value: model.default_output.as_ref().map(|info| (info.volume.avg().0 as f64 / Volume::NORMAL.0 as f64) * 100.).unwrap_or(0.),
- set_value_pos: PositionType::Right,
- set_hexpand: true
- }
- },
- GtkBox {
- set_orientation: Orientation::Horizontal,
- set_spacing: 16,
- Image {
- set_icon_name: Some("audio-input-microphone-symbolic"),
- },
- append: input_volume = &Scale::with_range(Orientation::Horizontal, 0., 100., 1.) {
- set_format_value_func: |_, value| {
- format!("{:.0}%", value)
- },
- #[watch]
- set_value: model.default_input
- .as_ref()
- .map(|info| (info.volume.avg().0 as f64 / Volume::NORMAL.0 as f64) * 100.)
- .unwrap_or(0.),
- set_value_pos: PositionType::Right,
- set_hexpand: true
- }
- },
- Separator {
- set_orientation: Orientation::Horizontal,
- },
- GtkBox {
- set_orientation: Orientation::Vertical,
- Button {
- #[wrap(Some)]
- set_child: current_output = &Label {
- #[watch]
- set_text: model.get_default_output_name()
- },
- connect_clicked[sender, outputs_revealer] => move |_| {
- sender.input(AppInput::Outputs);
- outputs_revealer.set_reveal_child(!outputs_revealer.reveals_child());
- }
- },
- append: outputs_revealer = &Revealer {
- set_transition_type: RevealerTransitionType::SlideDown,
- #[wrap(Some)]
- set_child: outputs = &ListBox {
- set_selection_mode: gtk::SelectionMode::None,
- set_activate_on_single_click: true
- }
- }
- },
- Separator {
- set_orientation: Orientation::Horizontal,
- },
- GtkBox {
- set_orientation: Orientation::Vertical,
- Button {
- #[wrap(Some)]
- set_child: current_input = &Label {
- #[watch]
- set_text: model.get_default_input_name()
- },
- connect_clicked[sender, inputs_revealer] => move |_| {
- sender.input(AppInput::Inputs);
- inputs_revealer.set_reveal_child(!inputs_revealer.reveals_child());
- }
- },
- append: inputs_revealer = &Revealer {
- set_transition_type: RevealerTransitionType::SlideDown,
- #[wrap(Some)]
- set_child: inputs = &ListBox {
- set_selection_mode: gtk::SelectionMode::None,
- set_activate_on_single_click: true
- }
- }
- },
- Separator {
- set_orientation: Orientation::Horizontal,
- },
- append: playing_apps = &ListBox {
- set_selection_mode: gtk::SelectionMode::None,
- }
- }
- }
- }
-
- fn init(
- _init_params: Self::InitParams,
- root: &Self::Root,
- sender: &ComponentSender,
- ) -> ComponentParts {
- let model = App::default();
- let widgets = view_output!();
- model.subscribe_for_updates(&sender.input);
-
- ComponentParts { model, widgets }
- }
-
- fn update(
- &mut self,
- msg: Self::Input,
- _sender: &ComponentSender,
- ) {
- self.reset();
- match msg {
- AppInput::Outputs => {
- self.refresh_output_list();
- }
- AppInput::Inputs => {
- self.refresh_input_list();
- }
- AppInput::InputVolume => {
- self.refresh_default_input();
- }
- AppInput::OutputVolume => {
- self.refresh_default_output();
- }
- AppInput::NowPlaying => {
- self.refresh_now_playing();
- }
- }
- }
-
- fn pre_view() {
- if model.changed(App::outputs()) {
- model.refresh_output_widgets(widgets);
- }
- if model.changed(App::inputs()) {
- model.refresh_input_widgets(widgets);
- }
- if model.changed(App::now_playing()) {
- model.refresh_now_playing_widgets(widgets);
- }
- }
-}
diff --git a/applets/cosmic-applet-audio/src/icons.rs b/applets/cosmic-applet-audio/src/icons.rs
deleted file mode 100644
index ab9dff14..00000000
--- a/applets/cosmic-applet-audio/src/icons.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-use std::collections::HashMap;
-
-use freedesktop_desktop_entry::{default_paths, DesktopEntry, Iter};
-
-#[derive(Debug, Hash, PartialEq, Eq)]
-pub enum DesktopApplication {
- Name(String),
- Binary(String),
-}
-
-pub fn parse_desktop_icons() -> HashMap {
- let mut out = HashMap::new();
- for path in Iter::new(default_paths()) {
- let file = match std::fs::read_to_string(&path) {
- Ok(data) => data,
- _ => continue,
- };
- let entry = match DesktopEntry::decode(&path, &file) {
- Ok(entry) => entry,
- _ => continue,
- };
- let icon = match entry.icon() {
- Some(icon) => icon,
- None => continue,
- };
- if let Some(name) = entry.name(None) {
- out.insert(DesktopApplication::Name(name.into_owned()), icon.to_owned());
- };
- if let Some(exec) = entry
- .exec()
- .and_then(|entry| entry.split_whitespace().next())
- {
- out.insert(DesktopApplication::Binary(exec.to_owned()), icon.to_owned());
- };
- }
- out
-}
diff --git a/applets/cosmic-applet-audio/src/input.rs b/applets/cosmic-applet-audio/src/input.rs
deleted file mode 100644
index 258b5189..00000000
--- a/applets/cosmic-applet-audio/src/input.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-use gtk4::{glib::clone, prelude::*, Button, Label, ListBox};
-use libcosmic::widgets::{relm4::RelmContainerExt, LabeledItem};
-
-use crate::pa::{DeviceInfo, PA};
-
-pub async fn get_inputs(pa: &PA) -> Vec {
- // XXX handle error
- pa.get_source_info_list()
- .await
- .expect("failed to list input devices")
-}
-
-pub async fn refresh_default_input(pa: &PA, label: &Label) -> DeviceInfo {
- // XXX handle error
- let default_input = pa
- .get_default_source()
- .await
- .expect("failed to get default input");
- label.set_text(match &default_input.description {
- Some(name) => name.as_str(),
- None => "Input Device",
- });
- default_input
-}
-
-pub async fn refresh_input_widgets(pa: &PA, inputs: &ListBox) {
- while let Some(row) = inputs.row_at_index(0) {
- inputs.remove(&row);
- }
- for input in get_inputs(pa).await {
- let name = match &input.name {
- Some(name) => name.to_owned(),
- None => continue, // Why doesn't this have a name? Whatever, it's invalid.
- };
- view! {
- item = LabeledItem {
- set_title: input.description
- .as_ref()
- .unwrap_or(&name),
- set_child: set_current_input_device = &Button {
- set_label: "Switch",
- connect_clicked: clone!(@strong pa => move |_| {
- pa.set_default_source(&name);
- })
- }
- }
- }
- inputs.container_add(&item);
- }
-}
diff --git a/applets/cosmic-applet-audio/src/main.rs b/applets/cosmic-applet-audio/src/main.rs
index f70bfc07..1aff5021 100644
--- a/applets/cosmic-applet-audio/src/main.rs
+++ b/applets/cosmic-applet-audio/src/main.rs
@@ -1,208 +1,372 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
+use iced::widget::Space;
-#[macro_use]
-extern crate relm4_macros;
+use cosmic::widget::{icon, toggler, horizontal_rule};
+use cosmic::applet::CosmicAppletHelper;
+use cosmic::Renderer;
-mod icons;
-mod input;
-mod now_playing;
-mod output;
-mod pa;
-use pa::PA;
-mod task;
-mod volume;
-mod volume_scale;
-use volume_scale::VolumeScale;
-
-use futures::{channel::mpsc, stream::StreamExt};
-use gtk4::{
- gio::ApplicationFlags,
- glib::{self, clone, MainContext, PRIORITY_DEFAULT},
- prelude::*,
- Align, Application, ApplicationWindow, Box as GtkBox, Button, Image, Label, ListBox,
- Orientation, PositionType, Revealer, RevealerTransitionType, Scale, SelectionMode, Separator,
+use cosmic::iced_native::window::Settings;
+use cosmic::iced_style::application::{self, Appearance};
+use cosmic::iced_style::svg;
+use cosmic::theme::{self, Svg};
+use cosmic::{iced_style, settings, Element, Theme};
+use cosmic::iced::{
+ executor,
+ widget::{button, column, row, text, slider},
+ window, Alignment, Application, Command, Length, Subscription,
};
-use libpulse_binding::{
- context::{
- subscribe::{Facility, InterestMaskSet, Operation},
- FlagSet, State,
- },
- volume::Volume,
-};
-use mpris2_zbus::metadata::Metadata;
-use once_cell::sync::Lazy;
-use tokio::runtime::Runtime;
-static RT: Lazy = Lazy::new(|| Runtime::new().expect("failed to build tokio runtime"));
+use iced_sctk::application::SurfaceIdWrapper;
+use iced_sctk::command::platform_specific::wayland::window::SctkWindowSettings;
+use iced_sctk::commands::popup::{destroy_popup, get_popup};
+use iced_sctk::settings::InitialSurface;
+use iced_sctk::Color;
+use iced_sctk::widget::container;
-fn main() {
- let _monitors = libcosmic::init();
- let application = Application::new(None, ApplicationFlags::default());
- application.connect_activate(app);
- application.run();
+mod pulse;
+use crate::pulse::DeviceInfo;
+use libpulse_binding::volume::{Volume, VolumeLinear};
+
+pub fn main() -> cosmic::iced::Result {
+ let helper = CosmicAppletHelper::default();
+ Audio::run(helper.window_settings())
}
-fn app(application: &Application) {
- // XXX handle no pulseaudio daemon?
- let pa = PA::new().unwrap();
- let (refresh_output_tx, mut refresh_output_rx) = mpsc::unbounded();
- let (refresh_input_tx, mut refresh_input_rx) = mpsc::unbounded();
- let (now_playing_tx, mut now_playing_rx) = mpsc::unbounded::>();
- pa
- .set_subscribe_callback(clone!(@strong refresh_output_tx, @strong refresh_input_tx => move |facility, operation, _idx| {
- if !matches!(operation, Some(Operation::Changed)) {
- return;
- }
- match facility {
- Some(Facility::Sink) => {
- refresh_output_tx.unbounded_send(()).expect("failed to send output refresh message");
+#[derive(Default)]
+struct Audio {
+ is_open: IsOpen,
+ current_output: Option,
+ current_input: Option,
+ outputs: Vec,
+ inputs: Vec,
+ pulse_state: PulseState,
+ applet_helper: CosmicAppletHelper,
+ icon_name: String,
+ theme: Theme,
+ popup: Option,
+ id_ctr: u32,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+enum IsOpen {
+ None,
+ Output,
+ Input,
+}
+
+#[derive(Debug, Clone)]
+enum Message {
+ SetOutputVolume(f64),
+ SetInputVolume(f64),
+ OutputToggle,
+ InputToggle,
+ OutputChanged(String),
+ InputChanged(String),
+ Pulse(pulse::Event),
+ Ignore,
+ TogglePopup,
+}
+
+impl Application for Audio {
+ type Message = Message;
+ type Theme = Theme;
+ type Executor = executor::Default;
+ type Flags = ();
+
+ fn new(_flags: ()) -> (Audio, Command) {
+ (
+ Audio {
+ is_open: IsOpen::None,
+ current_output: None,
+ current_input: None,
+ outputs: vec![],
+ inputs: vec![],
+ pulse_state: PulseState::Disconnected,
+ icon_name: "audio-volume-high-symbolic".to_string(),
+ ..Default::default()
+ },
+ Command::none(),
+ )
+ }
+
+ fn title(&self) -> String {
+ String::from("Audio")
+ }
+
+ fn theme(&self) -> Theme {
+ self.theme
+ }
+
+ fn close_requested(&self, _id: iced_sctk::application::SurfaceIdWrapper) -> Self::Message {
+ Message::Ignore
+ }
+
+ fn style(&self) -> ::Style {
+ ::Style::Custom(|theme| Appearance {
+ background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
+ text_color: theme.cosmic().on_bg_color().into(),
+ })
+ }
+
+ fn update(&mut self, message: Message) -> Command {
+ match message {
+ Message::TogglePopup => {
+ if let Some(p) = self.popup.take() {
+ return destroy_popup(p);
+ } else {
+ self.id_ctr += 1;
+ let new_id = window::Id::new(self.id_ctr);
+ self.popup.replace(new_id);
+
+ let popup_settings =
+ self.applet_helper.get_popup_settings(window::Id::new(0), new_id, (400, 300), None, None);
+ return get_popup(popup_settings);
}
- Some(Facility::Source) => {
- refresh_input_tx.unbounded_send(()).expect("failed to send output refresh message");
- }
- _ => {}
}
- }));
- pa.set_state_callback(move |pa, state| {
- if state == State::Ready {
- pa.subscribe(InterestMaskSet::SINK | InterestMaskSet::SOURCE);
- refresh_output_tx
- .unbounded_send(())
- .expect("failed to send output refresh message");
- refresh_input_tx
- .unbounded_send(())
- .expect("failed to send output refresh message");
- }
- });
- pa.connect().unwrap(); // XXX unwrap
- view! {
- window = libcosmic_applet::AppletWindow {
- set_application: Some(application),
- set_title: Some("COSMIC Network Applet"),
- #[wrap(Some)]
- set_child: button = &libcosmic_applet::AppletButton {
- set_button_icon_name: "audio-volume-medium-symbolic",
- #[wrap(Some)]
- set_popover_child: window_box = &GtkBox {
- set_orientation: Orientation::Vertical,
- set_spacing: 24,
- append: output_box = &GtkBox {
- set_orientation: Orientation::Horizontal,
- set_spacing: 16,
- append: output_icon = &Image {
- set_icon_name: Some("audio-speakers-symbolic"),
- },
- append: output_volume = &VolumeScale::new(pa.clone(), true) {
- set_format_value_func: |_, value| {
- format!("{:.0}%", value)
- },
- set_value_pos: PositionType::Right,
- set_hexpand: true
+ Message::SetOutputVolume(vol) => {
+ self.current_output.as_mut().map(|o| {
+ o.volume
+ .set(o.volume.len(), VolumeLinear(vol / 100.0).into())
+ });
+ if let PulseState::Connected(connection) = &mut self.pulse_state {
+ if let Some(device) = &self.current_output {
+ if let Some(name) = &device.name {
+ connection.send(pulse::Message::SetSinkVolumeByName(
+ name.clone().to_string(),
+ device.volume,
+ ))
}
- },
- append: input_box = &GtkBox {
- set_orientation: Orientation::Horizontal,
- set_spacing: 16,
- append: input_icon = &Image {
- set_icon_name: Some("audio-input-microphone-symbolic"),
- },
- append: input_volume = &VolumeScale::new(pa.clone(), false) {
- set_format_value_func: |_, value| {
- format!("{:.0}%", value)
- },
- set_value_pos: PositionType::Right,
- set_hexpand: true
- }
- },
- append = &Separator {
- set_orientation: Orientation::Horizontal,
- },
- append: output_list_box = &GtkBox {
- set_orientation: Orientation::Vertical,
- append: current_output_button = &Button {
- #[wrap(Some)]
- set_child: current_output = &Label {},
- connect_clicked[outputs_revealer] => move |_| {
- outputs_revealer.set_reveal_child(!outputs_revealer.reveals_child());
- }
- },
- append: outputs_revealer = &Revealer {
- set_transition_type: RevealerTransitionType::SlideDown,
- #[wrap(Some)]
- set_child: outputs = &ListBox {
- set_selection_mode: SelectionMode::None,
- set_activate_on_single_click: true
- }
- }
- },
- append = &Separator {
- set_orientation: Orientation::Horizontal,
- },
- append: input_list_box = &GtkBox {
- set_orientation: Orientation::Vertical,
- append: current_input_button = &Button {
- #[wrap(Some)]
- set_child: current_input = &Label {},
- connect_clicked[inputs_revealer] => move |_| {
- inputs_revealer.set_reveal_child(!inputs_revealer.reveals_child());
- }
- },
- append: inputs_revealer = &Revealer {
- set_transition_type: RevealerTransitionType::SlideDown,
- #[wrap(Some)]
- set_child: inputs = &ListBox {
- set_selection_mode: SelectionMode::None,
- set_activate_on_single_click: true
- }
- }
- },
- append = &Separator {
- set_orientation: Orientation::Horizontal,
- },
- append: playing_apps = &ListBox {
- set_selection_mode: SelectionMode::None,
}
}
}
+ Message::SetInputVolume(vol) => {
+ self.current_input.as_mut().map(|i| {
+ i.volume
+ .set(i.volume.len(), VolumeLinear(vol / 100.0).into())
+ });
+ if let PulseState::Connected(connection) = &mut self.pulse_state {
+ if let Some(device) = &self.current_input {
+ if let Some(name) = &device.name {
+ println!("increasing volume of {}", name);
+ connection.send(pulse::Message::SetSourceVolumeByName(
+ name.clone().to_string(),
+ device.volume,
+ ))
+ }
+ }
+ }
+ }
+ Message::OutputChanged(val) => println!("changed output {}", val),
+ Message::InputChanged(val) => println!("changed input {}", val),
+ Message::OutputToggle => {
+ self.is_open = if self.is_open == IsOpen::Output {
+ IsOpen::None
+ } else {
+ IsOpen::Output
+ }
+ }
+ Message::InputToggle => {
+ self.is_open = if self.is_open == IsOpen::Input {
+ IsOpen::None
+ } else {
+ IsOpen::Input
+ }
+ }
+ Message::Pulse(event) => match event {
+ pulse::Event::Connected(mut connection) => {
+ connection.send(pulse::Message::GetSinks);
+ connection.send(pulse::Message::GetSources);
+ connection.send(pulse::Message::GetDefaultSink);
+ connection.send(pulse::Message::GetDefaultSource);
+ self.pulse_state = PulseState::Connected(connection);
+ }
+ pulse::Event::MessageReceived(msg) => {
+ match msg {
+ // This is where we match messages from the subscription to app state
+ pulse::Message::SetSinks(sinks) => self.outputs = sinks,
+ pulse::Message::SetSources(sources) => {
+ self.inputs = sources
+ .into_iter()
+ .filter(|source| {
+ !source
+ .name
+ .as_ref()
+ .unwrap_or(&String::from("Generic"))
+ .contains("monitor")
+ })
+ .collect()
+ }
+ pulse::Message::SetDefaultSink(sink) => {
+ self.current_output = Some(sink);
+ }
+ pulse::Message::SetDefaultSource(source) => {
+ self.current_input = Some(source)
+ }
+ pulse::Message::Disconnected => {
+ panic!("Subscriton error handling is bad. This should never happen.")
+ }
+ _ => {
+ println!("Received misc message")
+ }
+ }
+ }
+ // TODO: view() should gray out buttons/slider when state is disconnected
+ pulse::Event::Disconnected => {
+ println!("setting state to disconnected");
+ self.pulse_state = PulseState::Disconnected
+ }
+ },
+ Message::Ignore => {},
+ };
+
+ Command::none()
+ }
+
+ fn subscription(&self) -> Subscription {
+ pulse::connect().map(Message::Pulse)
+ }
+
+ fn view(&self, id: SurfaceIdWrapper) -> Element {
+ match id {
+ SurfaceIdWrapper::LayerSurface(_) => unimplemented!(),
+ SurfaceIdWrapper::Window(_) => self.applet_helper.icon_button(
+ &self.icon_name,
+ )
+ .on_press(Message::TogglePopup)
+ .into(),
+ SurfaceIdWrapper::Popup(_) => {
+ let out_f64 = VolumeLinear::from(
+ self.current_output
+ .as_ref()
+ .map(|o| o.volume.avg())
+ .unwrap_or(Volume::default()),
+ )
+ .0 * 100.0;
+ let in_f64 = VolumeLinear::from(
+ self.current_input
+ .as_ref()
+ .map(|o| o.volume.avg())
+ .unwrap_or(Volume::default()),
+ )
+ .0 * 100.0;
+
+ let sink = row![
+ icon("status/audio-volume-high-symbolic", 24),
+ slider(0.0..=100.0, out_f64, Message::SetOutputVolume),
+ text(format!("{}%", out_f64.round()))
+ ]
+ .spacing(10)
+ .padding(10);
+ let source = row![
+ icon("devices/audio-input-microphone-symbolic", 24),
+ slider(0.0..=100.0, in_f64, Message::SetInputVolume),
+ text(format!("{}%", in_f64.round()))
+ ]
+ .spacing(10)
+ .padding(10);
+
+ // TODO change these from helper functions to iced components for improved reusability
+ let output_drop = revealer(
+ self.is_open == IsOpen::Output,
+ "Output",
+ match &self.current_output {
+ Some(output) => pretty_name(output.description.clone()),
+ None => String::from("No device selected"),
+ },
+ self.outputs
+ .clone()
+ .into_iter()
+ .map(|output| pretty_name(output.description))
+ .collect(),
+ Message::OutputToggle,
+ Message::OutputChanged(String::from("test")),
+ );
+ let input_drop = revealer(
+ self.is_open == IsOpen::Input,
+ "Input",
+ match &self.current_input {
+ Some(input) => pretty_name(input.description.clone()),
+ None => String::from("No device selected"),
+ },
+ self.inputs
+ .clone()
+ .into_iter()
+ .map(|input| pretty_name(input.description))
+ .collect(),
+ Message::InputToggle,
+ Message::InputChanged(String::from("test")),
+ );
+
+ let content = column![]
+ .align_items(Alignment::Start)
+ .spacing(20)
+ .push(sink)
+ .push(source)
+ .push(spacer())
+ .push(output_drop)
+ .push(input_drop);
+
+ self.applet_helper.popup_container(
+ container(content)
+ ).into()
+ }
}
}
-
- glib::MainContext::default().spawn_local(
- clone!(@weak inputs, @weak current_input, @weak input_volume, @strong pa => async move {
- while let Some(()) = refresh_input_rx.next().await {
- input::refresh_input_widgets(&pa, &inputs).await;
- let default_input = input::refresh_default_input(&pa, ¤t_input).await;
- volume::update_volume(&default_input, &input_volume);
- }
- }),
- );
- glib::MainContext::default().spawn_local(
- clone!(@weak outputs, @weak current_output, @weak output_volume, @strong pa, @strong button, => async move {
- while let Some(()) = refresh_output_rx.next().await {
- output::refresh_output_widgets(&pa, &outputs);
- let default_output = output::refresh_default_output(&pa, ¤t_output).await;
- volume::update_volume(&default_output, &output_volume);
- button.set_button_icon_name({
- let volume = default_output.volume.avg().0 as f64 / Volume::NORMAL.0 as f64;
- // XXX correct cutoffs?
- if default_output.mute {
- "audio-volume-muted"
- } else if volume > 1.0 {
- "audio-volume-overamplified-symbolic"
- } else if volume > 0.66 {
- "audio-volume-high-symbolic"
- } else if volume > 0.33 {
- "audio-volume-medium-symbolic"
- } else {
- "audio-volume-low-symbolic"
- }
- });
- }
- }),
- );
- glib::MainContext::default().spawn_local(clone!(@weak playing_apps => async move {
- while let Some(all_metadata) = now_playing_rx.next().await {
- }
- }));
- window.show();
+}
+
+// TODO: Make this a themeable widget like the mock-ups
+fn spacer() -> iced::widget::Space {
+ Space::with_width(Length::Fill)
+}
+
+fn revealer<'a>(
+ open: bool,
+ title: &'a str,
+ selected: String,
+ options: Vec,
+ toggle: Message,
+ _change: Message,
+) -> iced_sctk::widget::Column<'a, Message, Renderer> {
+ if open {
+ options.iter().fold(
+ column![revealer_head(open, title, selected, toggle)].width(Length::Fill),
+ |col, device| col.push(text(device)),
+ )
+ } else {
+ column![revealer_head(open, title, selected, toggle)]
+ }
+}
+
+fn revealer_head<'a>(
+ _open: bool,
+ title: &'a str,
+ selected: String,
+ toggle: Message,
+) -> iced_sctk::widget::Button {
+ button(row![row![title].width(Length::Fill), text(selected)])
+ .width(Length::Fill)
+ .on_press(toggle)
+}
+
+fn pretty_name(name: Option) -> String {
+ match name {
+ Some(n) => n,
+ None => String::from("Generic"),
+ }
+}
+
+enum PulseState {
+ Disconnected,
+ Connected(pulse::Connection),
+}
+
+impl Default for PulseState {
+ fn default() -> Self {
+ Self::Disconnected
+ }
+}
+
+impl Default for IsOpen {
+ fn default() -> Self {
+ IsOpen::None
+ }
}
diff --git a/applets/cosmic-applet-audio/src/now_playing.rs b/applets/cosmic-applet-audio/src/now_playing.rs
deleted file mode 100644
index 37d79a83..00000000
--- a/applets/cosmic-applet-audio/src/now_playing.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-use gtk4::{glib::Sender, prelude::*, Button, Image, Label, ListBox};
-use mpris2_zbus::{media_player::MediaPlayer, metadata::Metadata};
-use std::time::Duration;
-use tokio::time::sleep;
-use zbus::Connection;
-
-pub async fn metadata_update(tx: Sender>) {
- let connection = Connection::session()
- .await
- .expect("failed to connect to zbus");
- loop {
- sleep(Duration::from_secs(1)).await;
- let media_players = MediaPlayer::new_all(&connection)
- .await
- .expect("failed to get media players");
- }
-}
diff --git a/applets/cosmic-applet-audio/src/output.rs b/applets/cosmic-applet-audio/src/output.rs
deleted file mode 100644
index 72bc5ba6..00000000
--- a/applets/cosmic-applet-audio/src/output.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-use gtk4::{glib::clone, prelude::*, Button, Label, ListBox};
-use libcosmic::widgets::{relm4::RelmContainerExt, LabeledItem};
-
-use crate::pa::{DeviceInfo, PA};
-
-pub async fn get_outputs(pa: &PA) -> Vec {
- // XXX handle error
- pa.get_sink_info_list()
- .await
- .expect("failed to list output devices")
-}
-
-pub async fn refresh_default_output(pa: &PA, label: &Label) -> DeviceInfo {
- // XXX handle error
- let default_output = pa
- .get_default_sink()
- .await
- .expect("failed to get default output");
- label.set_text(match &default_output.description {
- Some(name) => name.as_str(),
- None => "Output Device",
- });
- default_output
-}
-
-pub async fn refresh_output_widgets(pa: &PA, outputs: &ListBox) {
- while let Some(row) = outputs.row_at_index(0) {
- outputs.remove(&row);
- }
- for output in get_outputs(pa).await {
- let name = match &output.name {
- Some(name) => name.to_owned(),
- None => continue, // Why doesn't this have a name? Whatever, it's invalid.
- };
- view! {
- item = LabeledItem {
- set_title: output.description
- .as_ref()
- .unwrap_or(&name),
- set_child: set_current_input_device = &Button {
- set_label: "Switch",
- connect_clicked: clone!(@strong pa => move |_| {
- pa.set_default_sink(&name);
- })
- }
- }
- }
- outputs.container_add(&item);
- }
-}
diff --git a/applets/cosmic-applet-audio/src/pa/future.rs b/applets/cosmic-applet-audio/src/pa/future.rs
deleted file mode 100644
index 0897c6c5..00000000
--- a/applets/cosmic-applet-audio/src/pa/future.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-use libpulse_binding::operation::Operation;
-use std::{
- cell::RefCell,
- future::{self, Future},
- pin::Pin,
- rc::Rc,
- task::{self, Poll, Waker},
-};
-
-struct PAFutInner {
- res: Option,
- waker: Option,
-}
-
-pub struct PAFutWaker(Rc>>);
-
-impl PAFutWaker {
- pub fn wake(&self, res: T) {
- let mut inner = self.0.borrow_mut();
- inner.res = Some(res);
- if let Some(waker) = inner.waker.take() {
- waker.wake();
- }
- }
-}
-
-pub struct PAFut {
- inner: Rc>>,
- operation: Operation,
-}
-
-impl PAFut {
- pub fn new(cb: impl FnOnce(PAFutWaker) -> Operation) -> Self {
- let inner = Rc::new(RefCell::new(PAFutInner {
- res: None,
- waker: None,
- }));
- let operation = cb(PAFutWaker(inner.clone()));
- Self { inner, operation }
- }
-}
-
-impl Future for PAFut {
- type Output = T;
-
- fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll {
- let mut inner = self.inner.borrow_mut();
- if let Some(res) = inner.res.take() {
- Poll::Ready(res)
- } else {
- inner.waker = Some(cx.waker().clone());
- Poll::Pending
- }
- }
-}
-
-impl Drop for PAFut {
- fn drop(&mut self) {
- self.operation.cancel();
- }
-}
diff --git a/applets/cosmic-applet-audio/src/pa/mod.rs b/applets/cosmic-applet-audio/src/pa/mod.rs
deleted file mode 100644
index 6872d907..00000000
--- a/applets/cosmic-applet-audio/src/pa/mod.rs
+++ /dev/null
@@ -1,232 +0,0 @@
-use gtk4::glib;
-use libpulse_binding::{
- callbacks::ListResult,
- context::{
- introspect::{Introspector, SinkInfo, SourceInfo},
- subscribe::{Facility, InterestMaskSet, Operation},
- Context, FlagSet, State,
- },
- error::PAErr,
- volume::ChannelVolumes,
-};
-use libpulse_glib_binding::Mainloop;
-use std::{cell::RefCell, rc::Rc};
-
-mod future;
-use future::{PAFut, PAFutWaker};
-
-pub struct DeviceInfo {
- pub name: Option,
- pub description: Option,
- pub volume: ChannelVolumes,
- pub mute: bool,
- pub index: u32,
-}
-
-impl<'a> From<&SinkInfo<'a>> for DeviceInfo {
- fn from(info: &SinkInfo<'a>) -> Self {
- Self {
- name: info.name.clone().map(|x| x.into_owned()),
- description: info.description.clone().map(|x| x.into_owned()),
- volume: info.volume,
- mute: info.mute,
- index: info.index,
- }
- }
-}
-
-impl<'a> From<&SourceInfo<'a>> for DeviceInfo {
- fn from(info: &SourceInfo<'a>) -> Self {
- Self {
- name: info.name.clone().map(|x| x.into_owned()),
- description: info.description.clone().map(|x| x.into_owned()),
- volume: info.volume,
- mute: info.mute,
- index: info.index,
- }
- }
-}
-
-pub struct ServerInfo {
- pub default_sink_name: Option,
- pub default_source_name: Option,
-}
-
-struct PAInner {
- main_loop: Mainloop,
- pub context: RefCell,
-}
-
-#[derive(Clone)]
-pub struct PA(Rc);
-
-impl PA {
- pub fn new() -> Option {
- let main_loop = Mainloop::new(None)?;
- let context = Context::new(&main_loop, "com.system76.cosmic.applets.audio")?;
- Some(Self(Rc::new(PAInner {
- main_loop,
- context: RefCell::new(context),
- })))
- }
-
- pub fn set_state_callback(&self, cb: F) {
- let pa = self.clone(); // TODO: weak ref?
- let cb = Rc::new(cb);
- self.0
- .context
- .borrow_mut()
- .set_state_callback(Some(Box::new(move || {
- let pa = pa.clone();
- let cb = cb.clone();
- glib::source::idle_add_local_once(move || {
- let state = pa.0.context.borrow().get_state();
- cb(&pa, state);
- });
- })));
- }
-
- // TODO: builder pattern?
- pub fn set_subscribe_callback, Option, u32) + 'static>(
- &self,
- cb: F,
- ) {
- self.0
- .context
- .borrow_mut()
- .set_subscribe_callback(Some(Box::new(cb)));
- }
-
- pub fn subscribe(&self, mask: InterestMaskSet) {
- // XXX cb; operation; async
- self.0.context.borrow_mut().subscribe(mask, |_| {});
- }
-
- pub fn connect(&self) -> Result<(), PAErr> {
- self.0
- .context
- .borrow_mut()
- .connect(None, FlagSet::empty(), None)
- }
-
- fn introspect(&self) -> Introspector {
- self.0.context.borrow().introspect()
- }
-
- pub async fn get_server_info(&self) -> ServerInfo {
- PAFut::new(|waker| {
- self.introspect().get_server_info(move |info| {
- waker.wake(ServerInfo {
- default_sink_name: info.default_sink_name.clone().map(|x| x.into_owned()),
- default_source_name: info.default_source_name.clone().map(|x| x.into_owned()),
- });
- })
- })
- .await
- }
-
- pub async fn get_sink_info_list(&self) -> Result, ()> {
- let mut items = Some(Vec::new());
- PAFut::new(|waker| {
- self.introspect()
- .get_sink_info_list(move |result| match result {
- ListResult::Item(item) => items.as_mut().unwrap().push(DeviceInfo::from(item)),
- ListResult::End => waker.wake(Ok(items.take().unwrap())),
- ListResult::Error => waker.wake(Err(())),
- })
- })
- .await
- }
-
- pub async fn get_default_sink(&self) -> Result {
- let name = match self.get_server_info().await.default_sink_name {
- Some(name) => name,
- None => {
- return Err(());
- }
- };
- let mut sink = None;
- PAFut::new(|waker| {
- self.introspect()
- .get_sink_info_by_name(&name, move |result| match result {
- ListResult::Item(item) => {
- sink = Some(DeviceInfo::from(item));
- }
- ListResult::End => waker.wake(sink.take().ok_or(())),
- ListResult::Error => waker.wake(Err(())),
- })
- })
- .await
- }
-
- // XXX async wait and handle error
- pub fn set_default_sink(&self, name: &str) {
- self.0.context.borrow_mut().set_default_sink(name, |_| {});
- }
-
- pub fn set_default_source(&self, name: &str) {
- self.0.context.borrow_mut().set_default_source(name, |_| {});
- }
-
- pub async fn get_source_info_list(&self) -> Result, ()> {
- let mut items = Some(Vec::new());
- PAFut::new(|waker| {
- self.introspect()
- .get_source_info_list(move |result| match result {
- ListResult::Item(item) => items.as_mut().unwrap().push(DeviceInfo::from(item)),
- ListResult::End => waker.wake(Ok(items.take().unwrap())),
- ListResult::Error => waker.wake(Err(())),
- })
- })
- .await
- }
-
- pub async fn get_default_source(&self) -> Result {
- let name = match self.get_server_info().await.default_source_name {
- Some(name) => name,
- None => {
- return Err(());
- }
- };
- let mut source = None;
- PAFut::new(|waker| {
- self.introspect()
- .get_source_info_by_name(&name, move |result| match result {
- ListResult::Item(item) => {
- source = Some(DeviceInfo::from(item));
- }
- ListResult::End => waker.wake(source.take().ok_or(())),
- ListResult::Error => waker.wake(Err(())),
- })
- })
- .await
- }
-
- pub fn set_sink_volume_by_name(
- &self,
- name: &str,
- volume: &ChannelVolumes,
- ) -> PAFut {
- PAFut::new(|waker| {
- self.introspect().set_sink_volume_by_name(
- name,
- volume,
- Some(Box::new(move |success| waker.wake(success))),
- )
- })
- }
-
- pub fn set_source_volume_by_name(
- &self,
- name: &str,
- volume: &ChannelVolumes,
- ) -> PAFut {
- PAFut::new(|waker| {
- self.introspect().set_source_volume_by_name(
- name,
- volume,
- Some(Box::new(move |success| waker.wake(success))),
- )
- })
- }
-}
diff --git a/applets/cosmic-applet-audio/src/pulse.rs b/applets/cosmic-applet-audio/src/pulse.rs
new file mode 100644
index 00000000..d849f1b4
--- /dev/null
+++ b/applets/cosmic-applet-audio/src/pulse.rs
@@ -0,0 +1,521 @@
+use iced_native::subscription::{self, Subscription};
+use std::cell::RefCell;
+use std::{rc::Rc, thread};
+
+extern crate libpulse_binding as pulse;
+//use futures::channel::mpsc;
+use libpulse_binding::{
+ callbacks::ListResult,
+ context::{
+ introspect::{Introspector, SinkInfo, SourceInfo},
+ subscribe::{Facility, InterestMaskSet, Operation},
+ Context,
+ },
+ error::PAErr,
+ mainloop::standard::{IterateResult, Mainloop},
+ proplist::Proplist,
+ volume::ChannelVolumes,
+};
+pub fn connect() -> Subscription {
+ struct Connect;
+
+ subscription::unfold(
+ std::any::TypeId::of::(),
+ State::Disconnected,
+ |state| async move {
+ match state {
+ // if app just started, or we are re-trying match here. Returns coenncting
+ // message. We should store this in our app's state, but it isn't safe to
+ // send messages until we get a conencted message. Which will be received
+ // by the `State::Connecting` message below
+ State::Disconnected => match PulseHandle::create() {
+ Ok(pulse_handle) => (None, State::Connecting(pulse_handle)),
+ Err(_) => (Some(Event::Disconnected), State::Disconnected),
+ },
+ // Just a buffer to make sure the GUI doesn't send messages until pulse is ready
+ // The GUI doesn't have to monitor this state, as it is never sent to the GUI
+ State::Connecting(mut pulse_handle) => {
+ match pulse_handle.from_pulse.recv().await {
+ Some(Message::Connected) => {(
+ Some(Event::Connected(Connection(pulse_handle.to_pulse))),
+ State::Connected(pulse_handle.from_pulse),
+ )}
+ Some(Message::Disconnected) => (Some(Event::Disconnected), State::Disconnected),
+ _ => panic!("Pulse subscription logic is faulty as the PulseServer shouldn't send unique messages until connection is successful")
+ }
+ },
+ State::Connected(mut from_pulse) => {
+ // This is where we match messages from the pulse server to pass to the gui
+ match from_pulse.recv().await {
+ Some(Message::SetSinks(sinks)) => (Some(Event::MessageReceived(Message::SetSinks(sinks))), State::Connected(from_pulse)),
+ Some(Message::SetSources(sources)) => (Some(Event::MessageReceived(Message::SetSources(sources))), State::Connected(from_pulse)),
+ Some(Message::SetDefaultSink(sink)) => (Some(Event::MessageReceived(Message::SetDefaultSink(sink))), State::Connected(from_pulse)),
+ Some(Message::SetDefaultSource(source)) => (Some(Event::MessageReceived(Message::SetDefaultSource(source))), State::Connected(from_pulse)),
+ Some(Message::Disconnected) => (Some(Event::Disconnected), State::Disconnected),
+ None => (Some(Event::Disconnected), State::Disconnected),
+ _ => (None, State::Connected(from_pulse)),
+ }
+ }
+ }
+ },
+ )
+}
+
+// #[derive(Debug)]
+enum State {
+ Disconnected,
+ Connecting(PulseHandle),
+ Connected(tokio::sync::mpsc::Receiver),
+}
+
+#[derive(Debug, Clone)]
+pub enum Event {
+ Connected(Connection),
+ Disconnected,
+ MessageReceived(Message),
+}
+
+#[derive(Debug, Clone)]
+pub struct Connection(tokio::sync::mpsc::Sender);
+
+impl Connection {
+ pub fn send(&mut self, message: Message) {
+ let _ = self
+ .0
+ .try_send(message)
+ .expect("Send message to PulseAudio server");
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub enum Message {
+ Connected,
+ Disconnected,
+ GetSinks,
+ GetSources,
+ SetSinks(Vec),
+ SetSources(Vec),
+ GetDefaultSink,
+ GetDefaultSource,
+ SetDefaultSink(DeviceInfo),
+ SetDefaultSource(DeviceInfo),
+ SetSinkVolumeByName(String, ChannelVolumes),
+ SetSourceVolumeByName(String, ChannelVolumes),
+}
+
+struct PulseHandle {
+ to_pulse: tokio::sync::mpsc::Sender,
+ from_pulse: tokio::sync::mpsc::Receiver,
+}
+
+impl PulseHandle {
+ // Create pulse server thread, and bidirectional comms
+ pub fn create() -> Result {
+ let (to_pulse, mut to_pulse_recv) = tokio::sync::mpsc::channel(10);
+ let (mut from_pulse_send, from_pulse) = tokio::sync::mpsc::channel(10);
+ //let from_pulse = Arc::new(Mutex::new(vec![]));
+ //let mut from_pulse2 = from_pulse.clone();
+ // this thread should complete by pushing a completed message,
+ // or fail message. This should never complete/fail without pushing
+ // a message. This lets the iced subscription go to sleep while init
+ // finishes. TLDR: be very careful with error handling
+ thread::spawn(move || {
+ if let Ok(mut server) = PulseServer::connect().and_then(|server| server.init()) {
+ PulseHandle::blocking_send_connected(&mut from_pulse_send);
+
+ // take `PulseServer` and handle reciver into async context
+ // to listen for messages that need to be passed to the pulseserver
+ // this lets us put the thread to sleep, but keep hold a single
+ // thread, because pulse audio's API is not multithreaded... at all
+ let rt = tokio::runtime::Builder::new_current_thread()
+ .enable_all()
+ .build()
+ .unwrap();
+
+ rt.block_on(async {
+ loop {
+ // This is where the we match messages from the GUI to pass to the pulse server
+ if let Some(msg) = to_pulse_recv.recv().await {
+ match msg {
+ Message::GetDefaultSink => match server.get_default_sink() {
+ Ok(sink) => from_pulse_send
+ .send(Message::SetDefaultSink(sink))
+ .await
+ .unwrap(),
+ Err(_) => {
+ PulseHandle::send_disconnected(&mut from_pulse_send).await
+ }
+ },
+ Message::GetDefaultSource => match server.get_default_source() {
+ Ok(source) => from_pulse_send
+ .send(Message::SetDefaultSource(source))
+ .await
+ .unwrap(),
+ Err(e) => {
+ println!("ERROR! {:?}", e);
+ PulseHandle::send_disconnected(&mut from_pulse_send).await;
+ }
+ },
+ Message::GetSinks => match server.get_sinks() {
+ Ok(sinks) => from_pulse_send
+ .send(Message::SetSinks(sinks))
+ .await
+ .unwrap(),
+ Err(_) => {
+ PulseHandle::send_disconnected(&mut from_pulse_send).await
+ }
+ },
+ Message::GetSources => match server.get_sources() {
+ Ok(sinks) => from_pulse_send
+ .send(Message::SetSources(sinks))
+ .await
+ .unwrap(),
+ Err(_) => {
+ PulseHandle::send_disconnected(&mut from_pulse_send).await
+ }
+ },
+ Message::SetSinkVolumeByName(name, channel_volumes) => {
+ server.set_sink_volume_by_name(&name, &channel_volumes)
+ }
+ Message::SetSourceVolumeByName(name, channel_volumes) => {
+ server.set_source_volume_by_name(&name, &channel_volumes)
+ }
+ _ => {
+ println!("message doesn't match")
+ }
+ }
+ }
+ }
+ });
+ }
+ // Always report that server is disconnected
+ PulseHandle::blocking_send_disconnected(&mut from_pulse_send);
+ });
+ Ok(PulseHandle {
+ to_pulse,
+ from_pulse,
+ })
+ }
+
+ fn blocking_send_disconnected(sender: &mut tokio::sync::mpsc::Sender) {
+ sender.blocking_send(Message::Disconnected);
+ }
+
+ fn blocking_send_connected(sender: &mut tokio::sync::mpsc::Sender) {
+ sender.blocking_send(Message::Connected).unwrap()
+ }
+
+ async fn send_disconnected(sender: &mut tokio::sync::mpsc::Sender) {
+ sender.send(Message::Disconnected).await.unwrap()
+ }
+
+ async fn send_connected(sender: &mut tokio::sync::mpsc::Sender) {
+ sender.send(Message::Connected).await.unwrap()
+ }
+}
+
+struct PulseServer {
+ mainloop: Rc>,
+ context: Rc>,
+ introspector: Introspector,
+}
+
+#[derive(Clone, Debug)]
+enum PulseServerError<'a> {
+ IterateErr(IterateResult),
+ ContextErr(pulse::context::State),
+ OperationErr(pulse::operation::State),
+ PAErr(PAErr),
+ Connect,
+ Misc(&'a str),
+}
+
+// `PulseServer` code is heavily inspired by Dave Patrick Caberto's pulsectl-rs (SeaDve)
+// https://crates.io/crates/pulsectl-rs
+impl PulseServer {
+ // connect() requires init() to be run after
+ pub fn connect() -> Result> {
+ // TODO: fix app name, should be variable
+ let mut proplist = Proplist::new().unwrap();
+ proplist
+ .set_str(
+ pulse::proplist::properties::APPLICATION_NAME,
+ "com.system76",
+ )
+ .or(Err(PulseServerError::Connect))?;
+
+ let mainloop = Rc::new(RefCell::new(
+ pulse::mainloop::standard::Mainloop::new().ok_or(PulseServerError::Connect)?,
+ ));
+
+ let context = Rc::new(RefCell::new(
+ Context::new_with_proplist(&*mainloop.borrow(), "MainConn", &proplist)
+ .ok_or(PulseServerError::Connect)?,
+ ));
+
+ let introspector = context.borrow_mut().introspect();
+
+ context
+ .borrow_mut()
+ .connect(None, pulse::context::FlagSet::NOFLAGS, None)
+ .map_err(|e| PulseServerError::PAErr(e))?;
+
+ Ok(PulseServer {
+ mainloop,
+ context,
+ introspector,
+ })
+ }
+
+ // Wait for pulse audio connection to complete
+ pub fn init(self) -> Result> {
+ loop {
+ match self.mainloop.borrow_mut().iterate(false) {
+ IterateResult::Success(_) => {}
+ IterateResult::Err(e) => {
+ return Err(PulseServerError::IterateErr(IterateResult::Err(e)))
+ }
+ IterateResult::Quit(e) => {
+ return Err(PulseServerError::IterateErr(IterateResult::Quit(e)))
+ }
+ }
+
+ match self.context.borrow().get_state() {
+ pulse::context::State::Ready => break,
+ pulse::context::State::Failed => {
+ return Err(PulseServerError::ContextErr(pulse::context::State::Failed))
+ }
+ pulse::context::State::Terminated => {
+ return Err(PulseServerError::ContextErr(
+ pulse::context::State::Terminated,
+ ))
+ }
+ _ => {}
+ }
+ }
+ Ok(self)
+ }
+
+ // Get a list of output devices
+ pub fn get_sinks(&self) -> Result, PulseServerError> {
+ let list: Rc>>> = Rc::new(RefCell::new(Some(Vec::new())));
+ let list_ref = list.clone();
+
+ let operation = self.introspector.get_sink_info_list(
+ move |sink_list: ListResult<&pulse::context::introspect::SinkInfo>| {
+ if let ListResult::Item(item) = sink_list {
+ list_ref.borrow_mut().as_mut().unwrap().push(item.into());
+ }
+ },
+ );
+ self.wait_for_result(operation)
+ .and_then(|_| {
+ list.borrow_mut().take().ok_or(PulseServerError::Misc(
+ "get_sinks(): failed to wait for operation",
+ ))
+ })
+ .and_then(|result| Ok(result))
+ }
+
+ // Get a list of input devices
+ pub fn get_sources(&self) -> Result, PulseServerError> {
+ let list: Rc>>> = Rc::new(RefCell::new(Some(Vec::new())));
+ let list_ref = list.clone();
+
+ let operation = self.introspector.get_source_info_list(
+ move |sink_list: ListResult<&pulse::context::introspect::SourceInfo>| {
+ if let ListResult::Item(item) = sink_list {
+ list_ref.borrow_mut().as_mut().unwrap().push(item.into());
+ }
+ },
+ );
+ self.wait_for_result(operation)
+ .and_then(|_| {
+ list.borrow_mut().take().ok_or(PulseServerError::Misc(
+ "get_sources(): Failed to wait for operation",
+ ))
+ })
+ .and_then(|result| Ok(result))
+ }
+
+ pub fn get_server_info(&mut self) -> Result {
+ let info = Rc::new(RefCell::new(Some(None)));
+ let info_ref = info.clone();
+
+ let op = self.introspector.get_server_info(move |res| {
+ info_ref.borrow_mut().as_mut().unwrap().replace(res.into());
+ });
+ self.wait_for_result(op)?;
+ info.take()
+ .flatten()
+ .ok_or(PulseServerError::Misc("get_server_info(): failed"))
+ }
+
+ fn get_default_sink(&mut self) -> Result {
+ let server_info = self.get_server_info();
+ match server_info {
+ Ok(info) => {
+ let name = &info.default_sink_name.unwrap_or(String::new());
+ let device = Rc::new(RefCell::new(Some(None)));
+ let dev_ref = device.clone();
+ let op = self.introspector.get_sink_info_by_name(
+ name,
+ move |sink_list: ListResult<&SinkInfo>| {
+ if let ListResult::Item(item) = sink_list {
+ dev_ref.borrow_mut().as_mut().unwrap().replace(item.into());
+ }
+ },
+ );
+ self.wait_for_result(op)?;
+ let mut result = device.borrow_mut();
+ result.take().unwrap().ok_or_else(|| {
+ PulseServerError::Misc("get_default_sink(): Error getting requested device")
+ })
+ }
+ Err(_) => Err(PulseServerError::Misc("get_default_sink() failed")),
+ }
+ }
+
+ fn get_default_source(&mut self) -> Result {
+ let server_info = self.get_server_info();
+ match server_info {
+ Ok(info) => {
+ let name = &info.default_source_name.unwrap_or(String::new());
+ let device = Rc::new(RefCell::new(Some(None)));
+ let dev_ref = device.clone();
+ let op = self.introspector.get_source_info_by_name(
+ name,
+ move |sink_list: ListResult<&SourceInfo>| {
+ if let ListResult::Item(item) = sink_list {
+ dev_ref.borrow_mut().as_mut().unwrap().replace(item.into());
+ }
+ },
+ );
+ self.wait_for_result(op)?;
+ let mut result = device.borrow_mut();
+ result.take().unwrap().ok_or_else(|| {
+ PulseServerError::Misc("get_default_source(): Error getting requested device")
+ })
+ }
+ Err(_) => Err(PulseServerError::Misc("get_default_source() failed")),
+ }
+ }
+
+ fn set_sink_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes) {
+ let op = self
+ .introspector
+ .set_sink_volume_by_name(name, volume, None);
+ self.wait_for_result(op).ok();
+ }
+
+ fn set_source_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes) {
+ let op = self
+ .introspector
+ .set_source_volume_by_name(name, volume, None);
+ self.wait_for_result(op).ok();
+ }
+
+ // after building an operation such as get_devices() we need to keep polling
+ // the pulse audio server to "wait" for the operation to complete
+ fn wait_for_result(
+ &self,
+ operation: pulse::operation::Operation,
+ ) -> Result<(), PulseServerError> {
+ // TODO: make this loop async. It is already in an async context, so
+ // we could make this thread sleep while waiting for the pulse server's
+ // response.
+ loop {
+ match self.mainloop.borrow_mut().iterate(false) {
+ IterateResult::Err(e) => {
+ return Err(PulseServerError::IterateErr(IterateResult::Err(e)))
+ }
+ IterateResult::Quit(e) => {
+ return Err(PulseServerError::IterateErr(IterateResult::Quit(e)))
+ }
+ IterateResult::Success(_) => {}
+ }
+ match operation.get_state() {
+ pulse::operation::State::Done => return Ok(()),
+ pulse::operation::State::Running => {}
+ pulse::operation::State::Cancelled => {
+ return Err(PulseServerError::OperationErr(
+ pulse::operation::State::Cancelled,
+ ))
+ }
+ }
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct DeviceInfo {
+ pub name: Option,
+ pub description: Option,
+ pub volume: ChannelVolumes,
+ pub mute: bool,
+ pub index: u32,
+}
+
+impl<'a> From<&SinkInfo<'a>> for DeviceInfo {
+ fn from(info: &SinkInfo<'a>) -> Self {
+ Self {
+ name: info.name.clone().map(|x| x.into_owned()),
+ description: info.description.clone().map(|x| x.into_owned()),
+ volume: info.volume,
+ mute: info.mute,
+ index: info.index,
+ }
+ }
+}
+
+impl<'a> From<&SourceInfo<'a>> for DeviceInfo {
+ fn from(info: &SourceInfo<'a>) -> Self {
+ Self {
+ name: info.name.clone().map(|x| x.into_owned()),
+ description: info.description.clone().map(|x| x.into_owned()),
+ volume: info.volume,
+ mute: info.mute,
+ index: info.index,
+ }
+ }
+}
+
+impl Eq for DeviceInfo {}
+
+#[derive(Debug)]
+pub struct ServerInfo {
+ /// User name of the daemon process.
+ pub user_name: Option,
+ /// Host name the daemon is running on.
+ pub host_name: Option,
+ /// Version string of the daemon.
+ pub server_version: Option,
+ /// Server package name (usually “pulseaudio”).
+ pub server_name: Option,
+ // Default sample specification.
+ //pub sample_spec: sample::Spec,
+ /// Name of default sink.
+ pub default_sink_name: Option,
+ /// Name of default source.
+ pub default_source_name: Option,
+ /// A random cookie for identifying this instance of PulseAudio.
+ pub cookie: u32,
+ // Default channel map.
+ //pub channel_map: channelmap::Map,
+}
+
+impl<'a> From<&'a pulse::context::introspect::ServerInfo<'a>> for ServerInfo {
+ fn from(info: &'a pulse::context::introspect::ServerInfo<'a>) -> Self {
+ ServerInfo {
+ user_name: info.user_name.as_ref().map(|cow| cow.to_string()),
+ host_name: info.host_name.as_ref().map(|cow| cow.to_string()),
+ server_version: info.server_version.as_ref().map(|cow| cow.to_string()),
+ server_name: info.server_name.as_ref().map(|cow| cow.to_string()),
+ //sample_spec: info.sample_spec,
+ default_sink_name: info.default_sink_name.as_ref().map(|cow| cow.to_string()),
+ default_source_name: info.default_source_name.as_ref().map(|cow| cow.to_string()),
+ cookie: info.cookie,
+ //channel_map: info.channel_map,
+ }
+ }
+}
diff --git a/applets/cosmic-applet-audio/src/task.rs b/applets/cosmic-applet-audio/src/task.rs
deleted file mode 100644
index fe6ab067..00000000
--- a/applets/cosmic-applet-audio/src/task.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-use std::future::Future;
-use tokio::sync::oneshot;
-
-pub fn spawn(future: F) -> tokio::task::JoinHandle
-where
- F: Future