From 0e62b3d51d04a9938b8b5843ae3db3f7a13fc949 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 17 Apr 2023 17:07:13 -0700 Subject: [PATCH] WIP status-line-applet status line: Custom widget for mouse input handling --- Cargo.lock | 382 +++++++++++++++--- Cargo.toml | 1 + cosmic-applet-status-line/Cargo.toml | 12 + cosmic-applet-status-line/src/bar_widget.rs | 145 +++++++ cosmic-applet-status-line/src/main.rs | 100 +++++ cosmic-applet-status-line/src/protocol/mod.rs | 111 +++++ .../src/protocol/serialization.rs | 142 +++++++ justfile | 3 +- 8 files changed, 841 insertions(+), 55 deletions(-) create mode 100644 cosmic-applet-status-line/Cargo.toml create mode 100644 cosmic-applet-status-line/src/bar_widget.rs create mode 100644 cosmic-applet-status-line/src/main.rs create mode 100644 cosmic-applet-status-line/src/protocol/mod.rs create mode 100644 cosmic-applet-status-line/src/protocol/serialization.rs diff --git a/Cargo.lock b/Cargo.lock index 03b5d8ca..319710ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -691,7 +691,7 @@ dependencies = [ "i18n-embed", "i18n-embed-fl", "itertools 0.11.0", - "libcosmic", + "libcosmic 0.1.0 (git+https://github.com/pop-os/libcosmic)", "log", "nix 0.26.2", "once_cell", @@ -715,7 +715,7 @@ dependencies = [ "i18n-embed", "i18n-embed-fl", "icon-loader", - "libcosmic", + "libcosmic 0.1.0 (git+https://github.com/pop-os/libcosmic)", "libpulse-binding", "libpulse-glib-binding", "log", @@ -733,7 +733,7 @@ dependencies = [ "futures", "i18n-embed", "i18n-embed-fl", - "libcosmic", + "libcosmic 0.1.0 (git+https://github.com/pop-os/libcosmic)", "log", "once_cell", "pretty_env_logger 0.5.0", @@ -753,7 +753,7 @@ dependencies = [ "i18n-embed", "i18n-embed-fl", "itertools 0.10.5", - "libcosmic", + "libcosmic 0.1.0 (git+https://github.com/pop-os/libcosmic)", "log", "once_cell", "pretty_env_logger 0.5.0", @@ -769,7 +769,7 @@ version = "0.1.0" dependencies = [ "i18n-embed", "i18n-embed-fl", - "libcosmic", + "libcosmic 0.1.0 (git+https://github.com/pop-os/libcosmic)", "once_cell", "rust-embed", "tracing", @@ -788,7 +788,7 @@ dependencies = [ "i18n-embed", "i18n-embed-fl", "itertools 0.10.5", - "libcosmic", + "libcosmic 0.1.0 (git+https://github.com/pop-os/libcosmic)", "log", "pretty_env_logger 0.5.0", "rust-embed", @@ -809,7 +809,7 @@ dependencies = [ "cosmic-time", "i18n-embed", "i18n-embed-fl", - "libcosmic", + "libcosmic 0.1.0 (git+https://github.com/pop-os/libcosmic)", "nix 0.26.2", "ron", "rust-embed", @@ -829,7 +829,7 @@ dependencies = [ "i18n-embed", "i18n-embed-fl", "icon-loader", - "libcosmic", + "libcosmic 0.1.0 (git+https://github.com/pop-os/libcosmic)", "libpulse-binding", "libpulse-glib-binding", "logind-zbus", @@ -846,19 +846,31 @@ name = "cosmic-applet-status-area" version = "0.1.0" dependencies = [ "futures", - "libcosmic", + "libcosmic 0.1.0 (git+https://github.com/pop-os/libcosmic)", "serde", "tokio", "zbus", ] +[[package]] +name = "cosmic-applet-status-line" +version = "0.1.0" +dependencies = [ + "delegate", + "libcosmic 0.1.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "serde", + "serde_json", + "tokio", + "tokio-stream", +] + [[package]] name = "cosmic-applet-time" version = "0.1.0" dependencies = [ "chrono", "icon-loader", - "libcosmic", + "libcosmic 0.1.0 (git+https://github.com/pop-os/libcosmic)", "nix 0.26.2", ] @@ -872,7 +884,7 @@ dependencies = [ "futures", "i18n-embed", "i18n-embed-fl", - "libcosmic", + "libcosmic 0.1.0 (git+https://github.com/pop-os/libcosmic)", "log", "nix 0.26.2", "once_cell", @@ -891,20 +903,43 @@ dependencies = [ "wayland-client 0.30.2", ] +[[package]] +name = "cosmic-config" +version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "atomicwrites", + "cosmic-config-derive 0.1.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "dirs 5.0.1", + "iced_futures 0.7.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "notify", + "ron", + "serde", +] + [[package]] name = "cosmic-config" version = "0.1.0" source = "git+https://github.com/pop-os/libcosmic#fcdefcd8fbca53705ba761b97e25453bc35e40c0" dependencies = [ "atomicwrites", - "cosmic-config-derive", + "cosmic-config-derive 0.1.0 (git+https://github.com/pop-os/libcosmic)", "dirs 5.0.1", - "iced_futures", + "iced_futures 0.7.0 (git+https://github.com/pop-os/libcosmic)", "notify", "ron", "serde", ] +[[package]] +name = "cosmic-config-derive" +version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "cosmic-config-derive" version = "0.1.0" @@ -932,7 +967,7 @@ name = "cosmic-notifications-config" version = "0.1.0" source = "git+https://github.com/pop-os/cosmic-notifications#35fd49d2b8147859d8b08d68b346096c356a8d15" dependencies = [ - "cosmic-config", + "cosmic-config 0.1.0 (git+https://github.com/pop-os/libcosmic)", "serde", ] @@ -954,7 +989,7 @@ name = "cosmic-panel-button" version = "0.1.0" dependencies = [ "freedesktop-desktop-entry", - "libcosmic", + "libcosmic 0.1.0 (git+https://github.com/pop-os/libcosmic)", ] [[package]] @@ -963,7 +998,7 @@ version = "0.1.0" source = "git+https://github.com/pop-os/cosmic-panel#edfd24ed3b712de397057906924e4f7e8b6252c4" dependencies = [ "anyhow", - "cosmic-config", + "cosmic-config 0.1.0 (git+https://github.com/pop-os/libcosmic)", "ron", "serde", "tracing", @@ -1004,13 +1039,27 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cosmic-theme" +version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "almost", + "cosmic-config 0.1.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "csscolorparser", + "lazy_static", + "palette", + "ron", + "serde", +] + [[package]] name = "cosmic-theme" version = "0.1.0" source = "git+https://github.com/pop-os/libcosmic#fcdefcd8fbca53705ba761b97e25453bc35e40c0" dependencies = [ "almost", - "cosmic-config", + "cosmic-config 0.1.0 (git+https://github.com/pop-os/libcosmic)", "csscolorparser", "lazy_static", "palette", @@ -1024,7 +1073,7 @@ version = "0.3.0" source = "git+https://github.com/pop-os/cosmic-time#4013946f9bd9d2e53bf44310b7db783fc8105b79" dependencies = [ "float-cmp", - "libcosmic", + "libcosmic 0.1.0 (git+https://github.com/pop-os/libcosmic)", "once_cell", ] @@ -1274,6 +1323,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "delegate" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d358e0ec5c59a5e1603b933def447096886121660fc680dc1e64a0753981fe3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "deranged" version = "0.3.8" @@ -2301,21 +2361,45 @@ dependencies = [ "cc", ] +[[package]] +name = "iced" +version = "0.10.0" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "iced_accessibility 0.1.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_core 0.10.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_futures 0.7.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_renderer 0.1.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_sctk 0.1.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_widget 0.1.3 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "image", + "thiserror", +] + [[package]] name = "iced" version = "0.10.0" source = "git+https://github.com/pop-os/libcosmic#fcdefcd8fbca53705ba761b97e25453bc35e40c0" dependencies = [ - "iced_accessibility", - "iced_core", - "iced_futures", - "iced_renderer", - "iced_sctk", - "iced_widget", + "iced_accessibility 0.1.0 (git+https://github.com/pop-os/libcosmic)", + "iced_core 0.10.0 (git+https://github.com/pop-os/libcosmic)", + "iced_futures 0.7.0 (git+https://github.com/pop-os/libcosmic)", + "iced_renderer 0.1.0 (git+https://github.com/pop-os/libcosmic)", + "iced_sctk 0.1.0 (git+https://github.com/pop-os/libcosmic)", + "iced_widget 0.1.3 (git+https://github.com/pop-os/libcosmic)", "image", "thiserror", ] +[[package]] +name = "iced_accessibility" +version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "accesskit", + "accesskit_unix", +] + [[package]] name = "iced_accessibility" version = "0.1.0" @@ -2325,13 +2409,28 @@ dependencies = [ "accesskit_unix", ] +[[package]] +name = "iced_core" +version = "0.10.0" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "bitflags 1.3.2", + "iced_accessibility 0.1.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "instant", + "log", + "palette", + "smithay-client-toolkit 0.17.0", + "thiserror", + "twox-hash", +] + [[package]] name = "iced_core" version = "0.10.0" source = "git+https://github.com/pop-os/libcosmic#fcdefcd8fbca53705ba761b97e25453bc35e40c0" dependencies = [ "bitflags 1.3.2", - "iced_accessibility", + "iced_accessibility 0.1.0 (git+https://github.com/pop-os/libcosmic)", "instant", "log", "palette", @@ -2340,19 +2439,49 @@ dependencies = [ "twox-hash", ] +[[package]] +name = "iced_futures" +version = "0.7.0" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "futures", + "iced_core 0.10.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "log", + "tokio", + "wasm-bindgen-futures", + "wasm-timer", +] + [[package]] name = "iced_futures" version = "0.7.0" source = "git+https://github.com/pop-os/libcosmic#fcdefcd8fbca53705ba761b97e25453bc35e40c0" dependencies = [ "futures", - "iced_core", + "iced_core 0.10.0 (git+https://github.com/pop-os/libcosmic)", "log", "tokio", "wasm-bindgen-futures", "wasm-timer", ] +[[package]] +name = "iced_graphics" +version = "0.9.0" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "bitflags 1.3.2", + "bytemuck", + "glam", + "half", + "iced_core 0.10.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "image", + "kamadak-exif", + "log", + "raw-window-handle", + "thiserror", +] + [[package]] name = "iced_graphics" version = "0.9.0" @@ -2362,7 +2491,7 @@ dependencies = [ "bytemuck", "glam", "half", - "iced_core", + "iced_core 0.10.0 (git+https://github.com/pop-os/libcosmic)", "image", "kamadak-exif", "log", @@ -2370,31 +2499,78 @@ dependencies = [ "thiserror", ] +[[package]] +name = "iced_renderer" +version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "iced_graphics 0.9.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_tiny_skia 0.1.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_wgpu 0.11.1 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "log", + "raw-window-handle", + "thiserror", +] + [[package]] name = "iced_renderer" version = "0.1.0" source = "git+https://github.com/pop-os/libcosmic#fcdefcd8fbca53705ba761b97e25453bc35e40c0" dependencies = [ - "iced_graphics", - "iced_tiny_skia", - "iced_wgpu", + "iced_graphics 0.9.0 (git+https://github.com/pop-os/libcosmic)", + "iced_tiny_skia 0.1.0 (git+https://github.com/pop-os/libcosmic)", + "iced_wgpu 0.11.1 (git+https://github.com/pop-os/libcosmic)", "log", "raw-window-handle", "thiserror", ] +[[package]] +name = "iced_runtime" +version = "0.1.1" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "iced_accessibility 0.1.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_core 0.10.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_futures 0.7.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "smithay-client-toolkit 0.17.0", + "thiserror", +] + [[package]] name = "iced_runtime" version = "0.1.1" source = "git+https://github.com/pop-os/libcosmic#fcdefcd8fbca53705ba761b97e25453bc35e40c0" dependencies = [ - "iced_accessibility", - "iced_core", - "iced_futures", + "iced_accessibility 0.1.0 (git+https://github.com/pop-os/libcosmic)", + "iced_core 0.10.0 (git+https://github.com/pop-os/libcosmic)", + "iced_futures 0.7.0 (git+https://github.com/pop-os/libcosmic)", "smithay-client-toolkit 0.17.0", "thiserror", ] +[[package]] +name = "iced_sctk" +version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "enum-repr", + "float-cmp", + "futures", + "iced_futures 0.7.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_graphics 0.9.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_runtime 0.1.1 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_style 0.9.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "itertools 0.10.5", + "raw-window-handle", + "smithay-client-toolkit 0.17.0", + "smithay-clipboard", + "thiserror", + "tracing", + "wayland-backend", + "wayland-protocols 0.30.1", +] + [[package]] name = "iced_sctk" version = "0.1.0" @@ -2403,10 +2579,10 @@ dependencies = [ "enum-repr", "float-cmp", "futures", - "iced_futures", - "iced_graphics", - "iced_runtime", - "iced_style", + "iced_futures 0.7.0 (git+https://github.com/pop-os/libcosmic)", + "iced_graphics 0.9.0 (git+https://github.com/pop-os/libcosmic)", + "iced_runtime 0.1.1 (git+https://github.com/pop-os/libcosmic)", + "iced_style 0.9.0 (git+https://github.com/pop-os/libcosmic)", "itertools 0.10.5", "raw-window-handle", "smithay-client-toolkit 0.17.0", @@ -2417,16 +2593,44 @@ dependencies = [ "wayland-protocols 0.30.1", ] +[[package]] +name = "iced_style" +version = "0.9.0" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "iced_core 0.10.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "once_cell", + "palette", +] + [[package]] name = "iced_style" version = "0.9.0" source = "git+https://github.com/pop-os/libcosmic#fcdefcd8fbca53705ba761b97e25453bc35e40c0" dependencies = [ - "iced_core", + "iced_core 0.10.0 (git+https://github.com/pop-os/libcosmic)", "once_cell", "palette", ] +[[package]] +name = "iced_tiny_skia" +version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "bytemuck", + "cosmic-text", + "iced_graphics 0.9.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "kurbo", + "log", + "raw-window-handle", + "resvg", + "rustc-hash", + "softbuffer", + "tiny-skia", + "twox-hash", +] + [[package]] name = "iced_tiny_skia" version = "0.1.0" @@ -2434,7 +2638,7 @@ source = "git+https://github.com/pop-os/libcosmic#fcdefcd8fbca53705ba761b97e2545 dependencies = [ "bytemuck", "cosmic-text", - "iced_graphics", + "iced_graphics 0.9.0 (git+https://github.com/pop-os/libcosmic)", "kurbo", "log", "raw-window-handle", @@ -2445,6 +2649,27 @@ dependencies = [ "twox-hash", ] +[[package]] +name = "iced_wgpu" +version = "0.11.1" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "bitflags 1.3.2", + "bytemuck", + "futures", + "glam", + "glyphon", + "guillotiere", + "iced_graphics 0.9.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "log", + "once_cell", + "raw-window-handle", + "resvg", + "rustc-hash", + "twox-hash", + "wgpu", +] + [[package]] name = "iced_wgpu" version = "0.11.1" @@ -2456,7 +2681,7 @@ dependencies = [ "glam", "glyphon", "guillotiere", - "iced_graphics", + "iced_graphics 0.9.0 (git+https://github.com/pop-os/libcosmic)", "log", "once_cell", "raw-window-handle", @@ -2466,14 +2691,29 @@ dependencies = [ "wgpu", ] +[[package]] +name = "iced_widget" +version = "0.1.3" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "iced_renderer 0.1.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_runtime 0.1.1 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_style 0.9.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "num-traits", + "ouroboros", + "smithay-client-toolkit 0.17.0", + "thiserror", + "unicode-segmentation", +] + [[package]] name = "iced_widget" version = "0.1.3" source = "git+https://github.com/pop-os/libcosmic#fcdefcd8fbca53705ba761b97e25453bc35e40c0" dependencies = [ - "iced_renderer", - "iced_runtime", - "iced_style", + "iced_renderer 0.1.0 (git+https://github.com/pop-os/libcosmic)", + "iced_runtime 0.1.1 (git+https://github.com/pop-os/libcosmic)", + "iced_style 0.9.0 (git+https://github.com/pop-os/libcosmic)", "num-traits", "ouroboros", "smithay-client-toolkit 0.17.0", @@ -2732,6 +2972,40 @@ version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "libcosmic" +version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#6457481ae5c1435526a8538bc792ebc55c6dff02" +dependencies = [ + "apply", + "ashpd", + "cosmic-config 0.1.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "cosmic-panel-config", + "cosmic-theme 0.1.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "derive_setters", + "fraction", + "freedesktop-icons", + "iced 0.10.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_core 0.10.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_futures 0.7.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_renderer 0.1.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_runtime 0.1.1 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_sctk 0.1.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_style 0.9.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_tiny_skia 0.1.0 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "iced_widget 0.1.3 (git+https://github.com/pop-os/libcosmic/?branch=master)", + "lazy_static", + "palette", + "ron", + "slotmap", + "smithay-client-toolkit 0.17.0", + "thiserror", + "tokio", + "tracing", + "unicode-segmentation", + "url", +] + [[package]] name = "libcosmic" version = "0.1.0" @@ -2739,21 +3013,21 @@ source = "git+https://github.com/pop-os/libcosmic#fcdefcd8fbca53705ba761b97e2545 dependencies = [ "apply", "ashpd", - "cosmic-config", + "cosmic-config 0.1.0 (git+https://github.com/pop-os/libcosmic)", "cosmic-panel-config", - "cosmic-theme", + "cosmic-theme 0.1.0 (git+https://github.com/pop-os/libcosmic)", "derive_setters", "fraction", "freedesktop-icons", - "iced", - "iced_core", - "iced_futures", - "iced_renderer", - "iced_runtime", - "iced_sctk", - "iced_style", - "iced_tiny_skia", - "iced_widget", + "iced 0.10.0 (git+https://github.com/pop-os/libcosmic)", + "iced_core 0.10.0 (git+https://github.com/pop-os/libcosmic)", + "iced_futures 0.7.0 (git+https://github.com/pop-os/libcosmic)", + "iced_renderer 0.1.0 (git+https://github.com/pop-os/libcosmic)", + "iced_runtime 0.1.1 (git+https://github.com/pop-os/libcosmic)", + "iced_sctk 0.1.0 (git+https://github.com/pop-os/libcosmic)", + "iced_style 0.9.0 (git+https://github.com/pop-os/libcosmic)", + "iced_tiny_skia 0.1.0 (git+https://github.com/pop-os/libcosmic)", + "iced_widget 0.1.3 (git+https://github.com/pop-os/libcosmic)", "lazy_static", "palette", "ron", diff --git a/Cargo.toml b/Cargo.toml index 4cade2a6..53699da6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "cosmic-applet-notifications", "cosmic-applet-power", "cosmic-applet-status-area", + "cosmic-applet-status-line", "cosmic-applet-time", "cosmic-applet-workspaces", "cosmic-panel-button", diff --git a/cosmic-applet-status-line/Cargo.toml b/cosmic-applet-status-line/Cargo.toml new file mode 100644 index 00000000..6cf43c74 --- /dev/null +++ b/cosmic-applet-status-line/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "cosmic-applet-status-line" +version = "0.1.0" +edition = "2021" + +[dependencies] +delegate = "0.9" +libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["tokio", "wayland", "applet"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +tokio = { version = "1.27", features = ["io-util", "process", "sync"] } +tokio-stream = "0.1" diff --git a/cosmic-applet-status-line/src/bar_widget.rs b/cosmic-applet-status-line/src/bar_widget.rs new file mode 100644 index 00000000..6f6c38f4 --- /dev/null +++ b/cosmic-applet-status-line/src/bar_widget.rs @@ -0,0 +1,145 @@ +use cosmic::{ + iced::{self, widget, Length, Rectangle}, + iced_core::{ + clipboard::Clipboard, + event::{self, Event}, + layout::{Layout, Limits, Node}, + mouse, + renderer::Style, + touch, + widget::{ + operation::{Operation, OperationOutputWrapper}, + Tree, Widget, + }, + Shell, + }, +}; + +use crate::protocol::ClickEvent; + +const BTN_LEFT: u32 = 0x110; +const BTN_RIGHT: u32 = 0x111; +const BTN_MIDDLE: u32 = 0x112; + +/// Wraps a `Row` widget, handling mouse input +pub struct BarWidget<'a, Msg> { + pub row: widget::Row<'a, Msg, cosmic::Renderer>, + pub name_instance: Vec<(Option<&'a str>, Option<&'a str>)>, + pub on_press: fn(ClickEvent) -> Msg, +} + +impl<'a, Msg> Widget for BarWidget<'a, Msg> { + delegate::delegate! { + to self.row { + fn children(&self) -> Vec; + fn diff(&mut self, tree: &mut Tree); + fn layout(&self, renderer: &cosmic::Renderer, limits: &Limits) -> Node; + fn operate( + &self, + tree: &mut Tree, + layout: Layout<'_>, + renderer: &cosmic::Renderer, + operation: &mut dyn Operation>, + ); + fn draw( + &self, + state: &Tree, + renderer: &mut cosmic::Renderer, + theme: &cosmic::Theme, + style: &Style, + layout: Layout, + cursor: iced::mouse::Cursor, + viewport: &Rectangle, + ); + } + } + + fn width(&self) -> Length { + Widget::width(&self.row) + } + + fn height(&self) -> Length { + Widget::height(&self.row) + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: Event, + layout: Layout<'_>, + cursor: iced::mouse::Cursor, + renderer: &cosmic::Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Msg>, + viewport: &Rectangle, + ) -> event::Status { + if self.update(&event, layout, cursor, shell) == event::Status::Captured { + return event::Status::Captured; + } + self.row.on_event( + tree, event, layout, cursor, renderer, clipboard, shell, viewport, + ) + } +} + +impl<'a, Msg> From> for cosmic::Element<'a, Msg> +where + Msg: 'a, +{ + fn from(widget: BarWidget<'a, Msg>) -> cosmic::Element<'a, Msg> { + cosmic::Element::new(widget) + } +} + +impl<'a, Msg> BarWidget<'a, Msg> { + fn update( + &mut self, + event: &Event, + layout: Layout<'_>, + cursor: iced::mouse::Cursor, + shell: &mut Shell<'_, Msg>, + ) -> event::Status { + let Some(cursor_position) = cursor.position() else { + return event::Status::Ignored; + }; + + let (button, event_code) = match event { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => (1, BTN_LEFT), + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Middle)) => (2, BTN_MIDDLE), + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) => (3, BTN_RIGHT), + Event::Touch(touch::Event::FingerPressed { .. }) => (1, BTN_LEFT), + _ => { + return event::Status::Ignored; + } + }; + + let Some((n, bounds)) = layout.children().map(|x| x.bounds()).enumerate().find(|(_, bounds)| bounds.contains(cursor_position)) else { + return event::Status::Ignored; + }; + + let (name, instance) = self.name_instance.get(n).cloned().unwrap_or((None, None)); + + // TODO coordinate space? int conversion? + let x = cursor_position.x as u32; + let y = cursor_position.y as u32; + let relative_x = (cursor_position.x - bounds.x) as u32; + let relative_y = (cursor_position.y - bounds.y) as u32; + let width = bounds.width as u32; + let height = bounds.height as u32; + + shell.publish((self.on_press)(ClickEvent { + name: name.map(str::to_owned), + instance: instance.map(str::to_owned), + x, + y, + button, + event: event_code, + relative_x, + relative_y, + width, + height, + })); + + event::Status::Captured + } +} diff --git a/cosmic-applet-status-line/src/main.rs b/cosmic-applet-status-line/src/main.rs new file mode 100644 index 00000000..c6acd522 --- /dev/null +++ b/cosmic-applet-status-line/src/main.rs @@ -0,0 +1,100 @@ +// TODO: work vertically + +use cosmic::{app, iced, iced_style::application, Theme}; + +mod bar_widget; +use bar_widget::BarWidget; +mod protocol; + +#[derive(Clone, Debug)] +enum Msg { + Protocol(protocol::StatusLine), + ClickEvent(protocol::ClickEvent), +} + +struct App { + core: app::Core, + status_line: protocol::StatusLine, +} + +impl cosmic::Application for App { + type Message = Msg; + type Executor = cosmic::SingleThreadExecutor; + type Flags = (); + const APP_ID: &'static str = "com.system76.CosmicAppletStatusLine"; + + fn init(core: app::Core, _flags: ()) -> (Self, app::Command) { + ( + App { + core, + status_line: Default::default(), + }, + iced::Command::none(), + ) + } + + fn core(&self) -> &app::Core { + &self.core + } + + fn core_mut(&mut self) -> &mut app::Core { + &mut self.core + } + + fn style(&self) -> Option<::Style> { + Some(app::applet::style()) + } + + fn subscription(&self) -> iced::Subscription { + protocol::subscription().map(Msg::Protocol) + } + + fn update(&mut self, message: Msg) -> app::Command { + match message { + Msg::Protocol(status_line) => { + println!("{:?}", status_line); + self.status_line = status_line; + } + Msg::ClickEvent(click_event) => { + println!("{:?}", click_event); + if self.status_line.click_events { + // TODO: pass click event to backend + } + } + } + iced::Command::none() + } + + fn view(&self) -> cosmic::Element { + let (block_views, name_instance): (Vec<_>, Vec<_>) = self + .status_line + .blocks + .iter() + .map(|block| { + ( + block_view(block), + (block.name.as_deref(), block.instance.as_deref()), + ) + }) + .unzip(); + BarWidget { + row: iced::widget::row(block_views), + name_instance, + on_press: Msg::ClickEvent, + } + .into() + } +} + +// TODO seperator +fn block_view(block: &protocol::Block) -> cosmic::Element { + let theme = block + .color + .map(cosmic::theme::Text::Color) + .unwrap_or(cosmic::theme::Text::Default); + cosmic::widget::text(&block.full_text).style(theme).into() +} + +fn main() -> iced::Result { + app::applet::run::(true, ()) +} diff --git a/cosmic-applet-status-line/src/protocol/mod.rs b/cosmic-applet-status-line/src/protocol/mod.rs new file mode 100644 index 00000000..01de3378 --- /dev/null +++ b/cosmic-applet-status-line/src/protocol/mod.rs @@ -0,0 +1,111 @@ +/// TODO: if we get an error, terminate process with exit code 1. Let cosmic-panel restart us. +/// TODO: configuration for command? Use cosmic config system. +use cosmic::iced::{self, futures::FutureExt}; +use std::{ + fmt, + io::{BufRead, BufReader}, + process::{self, Stdio}, + thread, +}; +use tokio::sync::mpsc; + +mod serialization; +use serialization::Header; +pub use serialization::{Block, ClickEvent}; + +#[derive(Clone, Debug, Default)] +pub struct StatusLine { + pub blocks: Vec, + pub click_events: bool, +} + +pub fn subscription() -> iced::Subscription { + iced::subscription::run_with_id( + "status-cmd", + async { + let (sender, reciever) = mpsc::channel(20); + thread::spawn(move || { + let mut status_cmd = StatusCmd::spawn(); + let mut deserializer = + serde_json::Deserializer::from_reader(&mut status_cmd.stdout); + deserialize_status_lines(&mut deserializer, |blocks| { + sender + .blocking_send(StatusLine { + blocks, + click_events: status_cmd.header.click_events, + }) + .unwrap(); + }) + .unwrap(); + status_cmd.wait(); + }); + tokio_stream::wrappers::ReceiverStream::new(reciever) + } + .flatten_stream(), + ) +} + +pub struct StatusCmd { + header: Header, + stdin: process::ChildStdin, + stdout: BufReader, + child: process::Child, +} + +impl StatusCmd { + fn spawn() -> StatusCmd { + // XXX command + // XXX unwrap + let mut child = process::Command::new("i3status") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .unwrap(); + + let mut stdout = BufReader::new(child.stdout.take().unwrap()); + let mut header = String::new(); + stdout.read_line(&mut header).unwrap(); + + StatusCmd { + header: serde_json::from_str(&header).unwrap(), + stdin: child.stdin.take().unwrap(), + stdout, + child, + } + } + + fn wait(mut self) { + drop(self.stdin); + drop(self.stdout); + self.child.wait(); + } +} + +/// Deserialize a sequence of `Vec`s, executing a callback for each one. +/// Blocks thread until end of status line sequence. +fn deserialize_status_lines<'de, D: serde::Deserializer<'de>, F: FnMut(Vec)>( + deserializer: D, + cb: F, +) -> Result<(), D::Error> { + struct Visitor)> { + cb: F, + } + + impl<'de, F: FnMut(Vec)> serde::de::Visitor<'de> for Visitor { + type Value = (); + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a sequence of status lines") + } + + fn visit_seq>(mut self, mut seq: S) -> Result<(), S::Error> { + while let Some(blocks) = seq.next_element()? { + (self.cb)(blocks); + } + Ok(()) + } + } + + let visitor = Visitor { cb }; + deserializer.deserialize_seq(visitor) +} diff --git a/cosmic-applet-status-line/src/protocol/serialization.rs b/cosmic-applet-status-line/src/protocol/serialization.rs new file mode 100644 index 00000000..4fe18cfc --- /dev/null +++ b/cosmic-applet-status-line/src/protocol/serialization.rs @@ -0,0 +1,142 @@ +// This implementation may be stricter in parsing than swaybar or i3bar. If this is an issue, the +// status command should probably be the one that's corrected to conform. + +use cosmic::iced; +use serde::de::{Deserialize, Error}; + +fn sigcont() -> u8 { + 18 +} + +fn sigstop() -> u8 { + 19 +} + +#[derive(Clone, Debug, serde::Deserialize)] +pub struct Header { + pub version: u8, + #[serde(default)] + pub click_events: bool, + #[serde(default = "sigcont")] + pub cont_signal: u8, + #[serde(default = "sigstop")] + pub stop_signal: u8, +} + +fn default_border() -> u32 { + 1 +} + +fn default_seperator_block_width() -> u32 { + 9 +} + +/// Deserialize string with RGB or RGBA color into `iced::Color` +fn deserialize_color<'de, D: serde::Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + let s = String::deserialize(deserializer)?; + + let unexpected_err = || { + D::Error::invalid_value( + serde::de::Unexpected::Str(&s), + &"a color string #RRGGBBAA or #RRGGBB", + ) + }; + + // Must be 8 or 9 character string starting with # + if !s.starts_with("#") || (s.len() != 7 && s.len() != 9) { + return Err(unexpected_err()); + } + + let parse_hex = |component| u8::from_str_radix(component, 16).map_err(|_| unexpected_err()); + let r = parse_hex(&s[1..3])?; + let g = parse_hex(&s[3..5])?; + let b = parse_hex(&s[5..7])?; + let a = if s.len() == 9 { + parse_hex(&s[7..])? as f32 / 1.0 + } else { + 1.0 + }; + Ok(Some(iced::Color::from_rgba8(r, g, b, a))) +} + +#[derive(Clone, Debug, Default, serde::Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Align { + #[default] + Left, + Right, + Center, +} + +#[derive(Clone, Debug, serde::Deserialize)] +#[serde(untagged)] +pub enum MinWidth { + Int(u32), + Str(String), +} + +impl Default for MinWidth { + fn default() -> Self { + Self::Int(0) + } +} + +#[derive(Clone, Debug, Default, serde::Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Markup { + #[default] + None, + Pango, +} + +#[derive(Clone, Debug, serde::Deserialize)] +pub struct Block { + pub full_text: String, + pub short_text: Option, + #[serde(default)] + #[serde(deserialize_with = "deserialize_color")] + pub color: Option, + #[serde(default)] + #[serde(deserialize_with = "deserialize_color")] + pub background: Option, + #[serde(default)] + #[serde(deserialize_with = "deserialize_color")] + pub border: Option, + #[serde(default = "default_border")] + pub border_top: u32, + #[serde(default = "default_border")] + pub border_bottom: u32, + #[serde(default = "default_border")] + pub border_left: u32, + #[serde(default = "default_border")] + pub border_right: u32, + #[serde(default)] + pub min_width: MinWidth, + #[serde(default)] + pub align: Align, + pub name: Option, + pub instance: Option, + #[serde(default)] + pub urgent: bool, + #[serde(default)] + pub separator: bool, + #[serde(default = "default_seperator_block_width")] + pub separator_block_width: u32, + pub markup: Markup, +} + +#[derive(Clone, Debug, serde::Serialize)] +pub struct ClickEvent { + pub name: Option, + pub instance: Option, + pub x: u32, + pub y: u32, + pub button: u32, + pub event: u32, + pub relative_x: u32, + pub relative_y: u32, + pub width: u32, + pub height: u32, +} diff --git a/justfile b/justfile index 923b763a..f1c53611 100644 --- a/justfile +++ b/justfile @@ -46,6 +46,7 @@ _install_power: (_install 'com.system76.CosmicAppletPower' 'cosmic-applet-power' _install_workspace: (_install 'com.system76.CosmicAppletWorkspaces' 'cosmic-applet-workspaces') _install_time: (_install 'com.system76.CosmicAppletTime' 'cosmic-applet-time') _install_status_area: (_install 'com.system76.CosmicAppletStatusArea' 'cosmic-applet-status-area') +_install_status_line: (_install 'com.system76.CosmicAppletStatusLine' 'cosmic-applet-status-line') # TODO: Turn this into one configurable applet? _install_panel_button: (_install_bin 'cosmic-panel-button') @@ -54,7 +55,7 @@ _install_app_button: (_install_button 'com.system76.CosmicPanelAppButton' 'cosmi _install_workspaces_button: (_install_button 'com.system76.CosmicPanelWorkspacesButton' 'cosmic-panel-workspaces-button') # Installs files into the system -install: _install_app_list _install_audio _install_battery _install_bluetooth _install_graphics _install_network _install_notifications _install_power _install_workspace _install_time _install_panel_button _install_app_button _install_workspaces_button _install_status_area +install: _install_app_list _install_audio _install_battery _install_bluetooth _install_graphics _install_network _install_notifications _install_power _install_workspace _install_time _install_panel_button _install_app_button _install_workspaces_button _install_status_area _install_status_line # Extracts vendored dependencies if vendor=1 _extract_vendor: