diff --git a/.github/scripts/ubuntu_setup.sh b/.github/scripts/ubuntu_setup.sh index db9ca763..74b93d24 100755 --- a/.github/scripts/ubuntu_setup.sh +++ b/.github/scripts/ubuntu_setup.sh @@ -17,5 +17,6 @@ $SUDO apt-get update && $SUDO apt-get install --assume-yes \ libssl-dev${CROSS_DEB_ARCH:+:$CROSS_DEB_ARCH} \ libgtk-3-dev${CROSS_DEB_ARCH:+:$CROSS_DEB_ARCH} \ libgtk-layer-shell-dev${CROSS_DEB_ARCH:+:$CROSS_DEB_ARCH} \ + libdbusmenu-gtk3-dev${CROSS_DEB_ARCH:+:$CROSS_DEB_ARCH} \ libpulse-dev${CROSS_DEB_ARCH:+:$CROSS_DEB_ARCH} \ libluajit-5.1-dev${CROSS_DEB_ARCH:+:$CROSS_DEB_ARCH} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8f8b4ddd..58d178ac 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,6 +55,8 @@ jobs: - name: Clippy run: cargo clippy --all-targets --all-features + env: + RUSTFLAGS: '-W clippy::unwrap_used' build: diff --git a/Cargo.lock b/Cargo.lock index d68eecb4..355db2e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,12 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - [[package]] name = "ahash" version = "0.8.3" @@ -285,7 +279,7 @@ dependencies = [ "cc", "cfg-if", "libc", - "miniz_oxide 0.6.2", + "miniz_oxide", "object", "rustc-demangle", ] @@ -462,9 +456,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -472,9 +466,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -567,15 +561,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - [[package]] name = "crossbeam-channel" version = "0.5.8" @@ -691,6 +676,34 @@ dependencies = [ "winapi", ] +[[package]] +name = "dbusmenu-glib-sys" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff9ed40330718c94342b953c997ac19d840db07a7710fe35b45a5d3a3a1d6eb" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "dbusmenu-gtk3-sys" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f30ba5f8aec0e38a84c579bc8ee3db6f6417b201e729fdd96a23d1f61cb6eca" +dependencies = [ + "dbusmenu-glib-sys", + "gdk-pixbuf-sys", + "gdk-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "libc", + "system-deps", +] + [[package]] name = "deranged" version = "0.3.11" @@ -876,15 +889,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" -[[package]] -name = "fdeflate" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb" -dependencies = [ - "simd-adler32", -] - [[package]] name = "field-offset" version = "0.3.5" @@ -907,16 +911,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "flate2" -version = "1.0.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" -dependencies = [ - "crc32fast", - "miniz_oxide 0.8.0", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1033,9 +1027,9 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f1fa2f9765705486b33fd2acf1577f8ec449c2ba1f318ae5447697b7c08d210" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ "fastrand 2.0.1", "futures-core", @@ -1690,7 +1684,7 @@ dependencies = [ "color-eyre", "ctrlc", "dirs", - "futures-lite 2.4.0", + "futures-lite 2.5.0", "futures-signals", "futures-util", "glib", @@ -1705,7 +1699,6 @@ dependencies = [ "mpris", "nix 0.29.0", "notify", - "png", "regex", "reqwest", "schemars", @@ -1930,16 +1923,6 @@ dependencies = [ "adler", ] -[[package]] -name = "miniz_oxide" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" -dependencies = [ - "adler2", - "simd-adler32", -] - [[package]] name = "mio" version = "1.0.1" @@ -2374,19 +2357,6 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" -[[package]] -name = "png" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide 0.8.0", -] - [[package]] name = "polling" version = "2.7.0" @@ -2882,18 +2852,18 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote 1.0.35", @@ -2913,9 +2883,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -3018,12 +2988,6 @@ dependencies = [ "libc", ] -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - [[package]] name = "slab" version = "0.4.8" @@ -3229,10 +3193,12 @@ dependencies = [ [[package]] name = "system-tray" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66066cf85f8a4985ae5a40ca4387036e4f870d95fbf113ffaab714796785d0f2" +checksum = "c5eff7a9f41bc02826920bf9da8b03fb74cb9c55bac250b4bc470af12d980285" dependencies = [ + "dbusmenu-gtk3-sys", + "gtk", "serde", "thiserror 2.0.0", "tokio", @@ -3351,9 +3317,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 1c702578..b4d871d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,7 +68,7 @@ notifications = ["zbus"] sys_info = ["sysinfo", "regex"] -tray = ["system-tray", "png"] +tray = ["system-tray"] upower = ["upower_dbus", "zbus", "futures-lite"] @@ -88,7 +88,7 @@ schema = ["dep:schemars"] gtk = "0.18.1" gtk-layer-shell = "0.8.1" glib = "0.18.5" -tokio = { version = "1.41.0", features = [ +tokio = { version = "1.41.1", features = [ "macros", "rt-multi-thread", "time", @@ -103,7 +103,7 @@ tracing-error = { version = "0.2.0" , default-features = false } tracing-appender = "0.2.3" strip-ansi-escapes = "0.2.0" color-eyre = "0.6.3" -serde = { version = "1.0.214", features = ["derive"] } +serde = { version = "1.0.215", features = ["derive"] } indexmap = "2.6.0" dirs = "5.0.1" walkdir = "2.5.0" @@ -118,10 +118,10 @@ ctrlc = "3.4.5" cfg-if = "1.0.0" # cli -clap = { version = "4.5.20", optional = true, features = ["derive"] } +clap = { version = "4.5.21", optional = true, features = ["derive"] } # ipc -serde_json = { version = "1.0.132", optional = true } +serde_json = { version = "1.0.133", optional = true } # http reqwest = { version = "0.12.9", default-features = false, features = ["default-tls", "http2"], optional = true } @@ -148,8 +148,7 @@ futures-signals = { version = "0.3.34", optional = true } sysinfo = { version = "0.29.11", optional = true } # tray -system-tray = { version = "0.3.0", optional = true } -png = { version = "0.17.14", optional = true } +system-tray = { version = "0.4.0", features = ["dbusmenu-gtk3"], optional = true } # upower upower_dbus = { version = "0.3.2", optional = true } @@ -163,7 +162,7 @@ hyprland = { version = "0.4.0-alpha.3", features = ["silent"], optional = true } futures-util = { version = "0.3.31", optional = true } # shared -futures-lite = { version = "2.4.0", optional = true } # network_manager, upower, workspaces +futures-lite = { version = "2.5.0", optional = true } # network_manager, upower, workspaces regex = { version = "1.11.1", default-features = false, features = [ "std", ], optional = true } # music, sys_info diff --git a/docs/Compiling.md b/docs/Compiling.md index 7a9a3e87..ceafa75f 100644 --- a/docs/Compiling.md +++ b/docs/Compiling.md @@ -22,6 +22,8 @@ You also need rust; only the latest stable version is supported. pacman -S gtk3 gtk-layer-shell # for http support pacman -S openssl +# for tray support +pacman -S libdbusmenu-gtk3 # for volume support pacman -S libpulse # for lua/cairo support @@ -34,6 +36,8 @@ pacman -S luajit lua51-lgi apt install build-essential libgtk-3-dev libgtk-layer-shell-dev # for http support apt install libssl-dev +# for tray support +apt install libdbusmenu-gtk3-dev # for volume support apt install libpulse-dev # for lua/cairo support @@ -46,6 +50,8 @@ apt install luajit-dev lua-lgi dnf install gtk3-devel gtk-layer-shell-devel # for http support dnf install openssl-devel +# for tray support +dnf install libdbusmenu-gtk3-devel # for volume support dnf install pulseaudio-libs-devel # for lua/cairo support diff --git a/docs/Controlling Ironbar.md b/docs/Controlling Ironbar.md index 3edfdcc3..49d1f8c2 100644 --- a/docs/Controlling Ironbar.md +++ b/docs/Controlling Ironbar.md @@ -10,7 +10,8 @@ You can also view help per sub-command or command, for example using `ironbar va The CLI supports plaintext and JSON output. Plaintext will: - Print `ok` for empty success responses -- Print the returned body for success responses +- Print the returned body for each success response + - Some commands act on multiple objects, in which case the CLI will print one line for each body. - Print `error` to followed by the error on the next line for error responses. This is printed to `stderr`. Example: @@ -150,6 +151,9 @@ Each key/value pair is on its own `\n` separated newline. The key and value are ### `bar` +> [!NOTE] +> If there are multiple bars by the same name, the `bar` subcommand will act on all of them and return a `multi` response for commands that get a value. + #### `show` Forces a bar to be shown, regardless of the current visibility state. @@ -324,6 +328,17 @@ The operation completed successfully, with response data. } ``` +### `multi` + +The operation completed successfully on multiple objects, with response data. + +```json +{ + "type": "multi", + "values": ["lorem ipsum", "dolor sit"] +} +``` + ### `error` The operation failed. @@ -335,4 +350,4 @@ Message is optional. "type": "error", "message": "lorem ipsum" } -``` \ No newline at end of file +``` diff --git a/docs/modules/Clock.md b/docs/modules/Clock.md index 627be83b..a1917631 100644 --- a/docs/modules/Clock.md +++ b/docs/modules/Clock.md @@ -8,12 +8,12 @@ Clicking on the widget opens a popup with the time and a calendar. > Type: `clock` -| Name | Type | Default | Description | -|----------------|----------|------------------------------------|-------------------------------------------------------------------------------------| -| `format` | `string` | `%d/%m/%Y %H:%M` | Date/time format string. Pango markup is supported. | -| `format_popup` | `string` | `%H:%M:%S` | Date/time format string to display in the popup header. Pango markup is supported. | -| `locale` | `string` | `$LC_TIME` or `$LANG` or `'POSIX'` | Locale to use (eg `en_GB`). Defaults to the system language (reading from env var). | -| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the time on the clock button. | +| Name | Type | Default | Description | +|----------------|------------------------------------------------------------|------------------------------------|-------------------------------------------------------------------------------------| +| `format` | `string` | `%d/%m/%Y %H:%M` | Date/time format string. Pango markup is supported. | +| `format_popup` | `string` | `%H:%M:%S` | Date/time format string to display in the popup header. Pango markup is supported. | +| `locale` | `string` | `$LC_TIME` or `$LANG` or `'POSIX'` | Locale to use (eg `en_GB`). Defaults to the system language (reading from env var). | +| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the time on the clock button. | > Detail on available tokens can be found here: diff --git a/docs/modules/Tray.md b/docs/modules/Tray.md index 6a6753b0..e40dd040 100644 --- a/docs/modules/Tray.md +++ b/docs/modules/Tray.md @@ -6,11 +6,11 @@ Displays a fully interactive icon tray using the KDE `libappindicator` protocol. > Type: `tray` -| Name | Type | Default | Description | -|----------------------|-----------|-----------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `direction` | `string` | `left_to_right` if bar is horizontal, `top_to_bottom` otherwise | Direction to display the tray items. Possible values: `top_to_bottom`, `bottom_to_top`, `left_to_right`, `right_to_left` | -| `icon_size` | `integer` | `16` | Size in pixels to display tray icons as. | -| `prefer_theme_icons` | `bool` | `true` | Requests that icons from the theme be used over the item-provided item. Most items only provide one or the other so this will have no effect in most circumstances. | +| Name | Type | Default | Description | +|----------------------|------------------------------------------------------------|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | Matches bar orientation | The direction in which to pack tray icons. | +| `icon_size` | `integer` | `16` | Size in pixels to display tray icons as. | +| `prefer_theme_icons` | `bool` | `true` | Requests that icons from the theme be used over the item-provided item. Most items only provide one or the other so this will have no effect in most circumstances. |
JSON @@ -55,12 +55,10 @@ end: ```corn { -end = [ - { + end = [{ type = "tray" direction = "top_to_bottom" - } - ] + }] } ``` diff --git a/flake.nix b/flake.nix index df751d22..1c5ef93a 100644 --- a/flake.nix +++ b/flake.nix @@ -129,6 +129,7 @@ hicolor-icon-theme gsettings-desktop-schemas libxkbcommon + libdbusmenu-gtk3 libpulseaudio luajit luajitPackages.lgi diff --git a/nix/default.nix b/nix/default.nix index db1e3b89..f5ac597d 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -12,11 +12,13 @@ gtk-layer-shell, gnome, libxkbcommon, + libdbusmenu-gtk3, libpulseaudio, openssl, luajit, luajitPackages, pkg-config, + adwaita-icon-theme, hicolor-icon-theme, rustPlatform, lib, @@ -50,13 +52,14 @@ gtk-layer-shell glib-networking shared-mime-info - gnome.adwaita-icon-theme + adwaita-icon-theme hicolor-icon-theme gsettings-desktop-schemas libxkbcommon ] - ++ (if hasFeature "http" then [ openssl ] else []) - ++ (if hasFeature "volume" then [ libpulseaudio ] else []) - ++ (if hasFeature "cairo" then [ luajit ] else []); + ++ lib.optionals (hasFeature "http") [ openssl ] + ++ lib.optionals (hasFeature "tray") [ libdbusmenu-gtk3 ] + ++ lib.optionals (hasFeature "volume")[ libpulseaudio ] + ++ lib.optionals (hasFeature "cairo") [ luajit ]; propagatedBuildInputs = [ gtk3 ]; @@ -72,10 +75,10 @@ # gtk-launch --suffix PATH : "${lib.makeBinPath [ gtk3 ]}" '' - + (if hasFeature "cairo" then '' + + lib.optionalString (hasFeature "cairo") '' --prefix LUA_PATH : "./?.lua;${lgi}/share/lua/5.1/?.lua;${lgi}/share/lua/5.1/?/init.lua;${luajit}/share/lua/5.1/\?.lua;${luajit}/share/lua/5.1/?/init.lua" --prefix LUA_CPATH : "./?.so;${lgi}/lib/lua/5.1/?.so;${luajit}/lib/lua/5.1/?.so;${luajit}/lib/lua/5.1/loadall.so" - '' else ""); + ''; preFixup = '' gappsWrapperArgs+=( diff --git a/shell.nix b/shell.nix index b15e8e9b..17db0f78 100644 --- a/shell.nix +++ b/shell.nix @@ -9,6 +9,7 @@ pkgs.mkShell { gtk-layer-shell gcc openssl + libdbusmenu-gtk3 libpulseaudio luajit luajitPackages.lgi diff --git a/src/cli.rs b/src/cli.rs index 769d706b..a273f961 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -46,6 +46,7 @@ pub fn handle_response(response: Response, format: Format) { Format::Plain => match response { Response::Ok => println!("ok"), Response::OkValue { value } => println!("{value}"), + Response::Multi { values } => println!("{}", values.join("\n")), Response::Err { message } => eprintln!("error\n{}", message.unwrap_or_default()), }, Format::Json => println!( diff --git a/src/config/common.rs b/src/config/common.rs index f1bc3c7c..d987e853 100644 --- a/src/config/common.rs +++ b/src/config/common.rs @@ -246,6 +246,13 @@ impl CommonConfig { let script = match event.direction() { ScrollDirection::Up => scroll_up_script.as_ref(), ScrollDirection::Down => scroll_down_script.as_ref(), + ScrollDirection::Smooth => { + if event.scroll_deltas().unwrap_or_default().1 > 0.0 { + scroll_down_script.as_ref() + } else { + scroll_up_script.as_ref() + } + } _ => None, }; diff --git a/src/ipc/responses.rs b/src/ipc/responses.rs index f033669b..a8c5048a 100644 --- a/src/ipc/responses.rs +++ b/src/ipc/responses.rs @@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize}; pub enum Response { Ok, OkValue { value: String }, + Multi { values: Vec }, Err { message: Option }, } diff --git a/src/ipc/server/bar.rs b/src/ipc/server/bar.rs index ab047d39..d5c42c53 100644 --- a/src/ipc/server/bar.rs +++ b/src/ipc/server/bar.rs @@ -8,48 +8,60 @@ use std::rc::Rc; pub fn handle_command(command: BarCommand, ironbar: &Rc) -> Response { use BarCommandType::*; - let bar = ironbar.bar_by_name(&command.name); - let Some(bar) = bar else { - return Response::error("Invalid bar name"); - }; + let bars = ironbar.bars_by_name(&command.name); - match command.subcommand { - Show => set_visible(&bar, true), - Hide => set_visible(&bar, false), - SetVisible { visible } => set_visible(&bar, visible), - ToggleVisible => set_visible(&bar, !bar.visible()), - GetVisible => Response::OkValue { - value: bar.visible().to_string(), - }, - - ShowPopup { widget_name } => show_popup(&bar, &widget_name), - HidePopup => hide_popup(&bar), - SetPopupVisible { - widget_name, - visible, - } => { - if visible { - show_popup(&bar, &widget_name) - } else { - hide_popup(&bar) + bars.into_iter() + .map(|bar| match &command.subcommand { + Show => set_visible(&bar, true), + Hide => set_visible(&bar, false), + SetVisible { visible } => set_visible(&bar, *visible), + ToggleVisible => set_visible(&bar, !bar.visible()), + GetVisible => Response::OkValue { + value: bar.visible().to_string(), + }, + ShowPopup { widget_name } => show_popup(&bar, widget_name), + HidePopup => hide_popup(&bar), + SetPopupVisible { + widget_name, + visible, + } => { + if *visible { + show_popup(&bar, widget_name) + } else { + hide_popup(&bar) + }; + Response::Ok } - } - TogglePopup { widget_name } => { - if bar.popup().visible() { - hide_popup(&bar) - } else { - show_popup(&bar, &widget_name) + TogglePopup { widget_name } => { + if bar.popup().visible() { + hide_popup(&bar) + } else { + show_popup(&bar, widget_name) + }; + Response::Ok } - } - GetPopupVisible => Response::OkValue { - value: bar.popup().visible().to_string(), - }, - SetExclusive { exclusive } => { - bar.set_exclusive(exclusive); - - Response::Ok - } - } + GetPopupVisible => Response::OkValue { + value: bar.popup().visible().to_string(), + }, + SetExclusive { exclusive } => { + bar.set_exclusive(*exclusive); + Response::Ok + } + }) + .reduce(|acc, rsp| match (acc, rsp) { + // If all responses are Ok, return one Ok. We assume we'll never mix Ok and OkValue. + (Response::Ok, _) => Response::Ok, + // Two or more OkValues create a multi: + (Response::OkValue { value: v1 }, Response::OkValue { value: v2 }) => Response::Multi { + values: vec![v1, v2], + }, + (Response::Multi { mut values }, Response::OkValue { value: v }) => { + values.push(v); + Response::Multi { values } + } + _ => unreachable!(), + }) + .unwrap_or(Response::error("Invalid bar name")) } fn set_visible(bar: &Bar, visible: bool) -> Response { diff --git a/src/logging.rs b/src/logging.rs index bfa73c09..6ba97c7f 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -1,8 +1,9 @@ use color_eyre::Result; use dirs::data_dir; +use glib::{LogLevel, LogWriterOutput}; use std::{env, panic}; use strip_ansi_escapes::Writer; -use tracing::error; +use tracing::{debug, error, info, warn}; use tracing_appender::non_blocking::{NonBlocking, WorkerGuard}; use tracing_appender::rolling::Rotation; use tracing_error::ErrorLayer; @@ -89,5 +90,33 @@ fn install_tracing() -> Result { ) .init(); + glib::log_set_writer_func(|level, fields| { + const KEY_DOMAIN: &str = "GLIB_DOMAIN"; + const KEY_MESSAGE: &str = "MESSAGE"; + + let domain = fields + .iter() + .find(|f| f.key() == KEY_DOMAIN) + .and_then(|f| f.value_str()) + .unwrap_or("Glib Unknown"); + + let message = fields + .iter() + .find(|f| f.key() == KEY_MESSAGE) + .and_then(|f| f.value_str()) + .unwrap_or_default(); + + match level { + LogLevel::Error => error!(target: "GTK", "[{domain}] {message}"), + LogLevel::Critical => error!(target: "GTK", "[{domain}] CRITICAL: {message}"), + LogLevel::Warning => warn!(target: "GTK", "[{domain}] {message}"), + LogLevel::Message => info!(target: "GTK", "[{domain}] MESSAGE: {message}"), + LogLevel::Info => info!(target: "GTK", "[{domain}] MESSAGE: {message}"), + LogLevel::Debug => debug!(target: "GTK", "[{domain}] {message}"), + } + + LogWriterOutput::Handled + }); + Ok(guard) } diff --git a/src/main.rs b/src/main.rs index a642e735..4711b7c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -267,17 +267,18 @@ impl Ironbar { .clone() } - /// Gets a clone of a bar by its unique name. + /// Gets clones of bars by their name. /// - /// Since the bar contains mostly GTK objects, + /// Since the bars contain mostly GTK objects, /// the clone is cheap enough to not worry about. #[must_use] - pub fn bar_by_name(&self, name: &str) -> Option { + pub fn bars_by_name(&self, name: &str) -> Vec { self.bars .borrow() .iter() - .find(|&bar| bar.name() == name) + .filter(|&bar| bar.name() == name) .cloned() + .collect() } /// Re-reads the config file from disk and replaces the active config. diff --git a/src/modules/tray/diff.rs b/src/modules/tray/diff.rs deleted file mode 100644 index 8afa3da0..00000000 --- a/src/modules/tray/diff.rs +++ /dev/null @@ -1,107 +0,0 @@ -use system_tray::menu::{MenuItem, ToggleState}; - -/// Diff change type and associated info. -#[derive(Debug, Clone)] -pub enum Diff { - Add(MenuItem), - Update(i32, MenuItemDiff), - Remove(i32), -} - -/// Diff info to be applied to an existing menu item as an update. -#[derive(Debug, Clone)] -pub struct MenuItemDiff { - /// Text of the item, - pub label: Option>, - /// Whether the item can be activated or not. - pub enabled: Option, - /// True if the item is visible in the menu. - pub visible: Option, - /// Icon name of the item, following the freedesktop.org icon spec. - pub icon_name: Option>, - /// PNG icon data. - pub icon_data: Option>>, - /// Describe the current state of a "togglable" item. Can be one of: - /// - Some(true): on - /// - Some(false): off - /// - None: indeterminate - pub toggle_state: Option, - /// A submenu for this item, typically this would ve revealed to the user by hovering the current item - pub submenu: Vec, -} - -impl MenuItemDiff { - fn new(old: &MenuItem, new: &MenuItem) -> Self { - macro_rules! diff { - ($field:ident) => { - if old.$field == new.$field { - None - } else { - Some(new.$field) - } - }; - - (&$field:ident) => { - if &old.$field == &new.$field { - None - } else { - Some(new.$field.clone()) - } - }; - } - - Self { - label: diff!(&label), - enabled: diff!(enabled), - visible: diff!(visible), - icon_name: diff!(&icon_name), - icon_data: diff!(&icon_data), - toggle_state: diff!(toggle_state), - submenu: get_diffs(&old.submenu, &new.submenu), - } - } - - /// Whether this diff contains any changes - fn has_diff(&self) -> bool { - self.label.is_some() - || self.enabled.is_some() - || self.visible.is_some() - // || self.icon_name.is_some() - || self.toggle_state.is_some() - || !self.submenu.is_empty() - } -} - -/// Gets a diff set between old and new state. -pub fn get_diffs(old: &[MenuItem], new: &[MenuItem]) -> Vec { - let mut diffs = vec![]; - - for new_item in new { - let old_item = old.iter().find(|&item| item.id == new_item.id); - - let diff = match old_item { - Some(old_item) => { - let item_diff = MenuItemDiff::new(old_item, new_item); - if item_diff.has_diff() { - Some(Diff::Update(old_item.id, item_diff)) - } else { - None - } - } - None => Some(Diff::Add(new_item.clone())), - }; - - if let Some(diff) = diff { - diffs.push(diff); - } - } - - for old_item in old { - let new_item = new.iter().find(|&item| item.id == old_item.id); - if new_item.is_none() { - diffs.push(Diff::Remove(old_item.id)); - } - } - - diffs -} diff --git a/src/modules/tray/icon.rs b/src/modules/tray/icon.rs index 3b78a9cb..a1877028 100644 --- a/src/modules/tray/icon.rs +++ b/src/modules/tray/icon.rs @@ -7,7 +7,6 @@ use gtk::ffi::gtk_icon_theme_get_search_path; use gtk::gdk_pixbuf::{Colorspace, InterpType, Pixbuf}; use gtk::prelude::IconThemeExt; use gtk::{IconLookupFlags, IconTheme, Image}; -use png::ColorType; use std::collections::HashSet; use std::ffi::CStr; use std::os::raw::{c_char, c_int}; @@ -126,36 +125,3 @@ fn get_image_from_pixmap(item: &TrayMenu, size: u32) -> Result { ImageProvider::create_and_load_surface(&pixbuf, &image)?; Ok(image) } - -pub struct PngData<'a>(pub &'a [u8]); -impl TryFrom> for Image { - type Error = Report; - - fn try_from(value: PngData) -> std::result::Result { - let data = value.0; - - let decoder = png::Decoder::new(data); - let mut reader = decoder.read_info()?; - let mut buf = vec![0; reader.output_buffer_size()]; - - let info = reader.next_frame(&mut buf)?; - let bytes = glib::Bytes::from(&buf[..info.buffer_size()]); - - let has_alpha = matches!(info.color_type, ColorType::Rgba | ColorType::GrayscaleAlpha); - let row_stride_multiplier = if has_alpha { 4 } else { 3 }; - - let pixbuf = Pixbuf::from_bytes( - &bytes, - Colorspace::Rgb, - has_alpha, - info.bit_depth as i32, - info.width as i32, - info.height as i32, - (info.width * row_stride_multiplier) as i32, - ); - - let image = Image::new(); - ImageProvider::create_and_load_surface(&pixbuf, &image)?; - Ok(image) - } -} diff --git a/src/modules/tray/interface.rs b/src/modules/tray/interface.rs index 95162bce..9eed83a8 100644 --- a/src/modules/tray/interface.rs +++ b/src/modules/tray/interface.rs @@ -1,120 +1,49 @@ -use super::diff::{Diff, MenuItemDiff}; -use crate::image::ImageProvider; -use crate::modules::tray::icon::PngData; -use crate::{spawn, try_send}; use glib::Propagation; +use gtk::gdk::Gravity; use gtk::prelude::*; -use gtk::{ - CheckMenuItem, Container, IconTheme, Image, Label, Menu, MenuItem, Orientation, - SeparatorMenuItem, -}; -use std::collections::HashMap; -use system_tray::client::ActivateRequest; -use system_tray::item::{IconPixmap, StatusNotifierItem}; -use system_tray::menu::{MenuItem as MenuItemInfo, MenuType, ToggleState, ToggleType}; -use tokio::sync::mpsc; -use tracing::{error, warn}; - -/// Calls a method on the underlying widget, -/// passing in a single argument. -/// -/// This is useful to avoid matching on -/// `TrayMenuWidget` constantly. -/// -/// # Example -/// ```rust -/// call!(container, add, my_widget) -/// ``` -/// is the same as: -/// ``` -/// match &my_widget { -/// TrayMenuWidget::Separator(w) => { -/// container.add(w); -/// } -/// TrayMenuWidget::Standard(w) => { -/// container.add(w); -/// } -/// TrayMenuWidget::Checkbox(w) => { -/// container.add(w); -/// } -/// } -/// ``` -macro_rules! call { - ($parent:expr, $method:ident, $child:expr) => { - match &$child { - TrayMenuWidget::Separator(w) => { - $parent.$method(w); - } - TrayMenuWidget::Standard(w) => { - $parent.$method(w); - } - TrayMenuWidget::Checkbox(w) => { - $parent.$method(w); - } - } - }; -} +use gtk::{EventBox, Image, Label, MenuItem}; +use system_tray::item::{IconPixmap, StatusNotifierItem, Tooltip}; /// Main tray icon to show on the bar pub(crate) struct TrayMenu { - pub widget: MenuItem, - menu_widget: Menu, + pub event_box: EventBox, + widget: MenuItem, image_widget: Option, label_widget: Option