From bea442ed960f513288cf857e8ee9a5c61f742dfa Mon Sep 17 00:00:00 2001 From: Jake Stanger Date: Sun, 17 Dec 2023 23:51:43 +0000 Subject: [PATCH] refactor: update gtk/glib, remove glib channels This is a major refactor which updates GTK, GLib and GTK Layer Shell to their latest versions. GLib channels, previously used for receiving events on the GLib Main Context thread have been deprecated and a new method for running Futures on the main thread has been added instead. This commit also replaces all the deprecated code with this. As part of the above, a bug was uncovered related to creating the GLib main context inside the Tokio runtime. Spawning of Tokio tasks has been refactored to fix this. --- Cargo.lock | 134 +++++++++++++++------------- Cargo.toml | 6 +- src/bar.rs | 38 ++++---- src/bridge_channel.rs | 44 --------- src/clients/clipboard.rs | 3 +- src/clients/compositor/hyprland.rs | 3 +- src/clients/compositor/sway.rs | 3 +- src/clients/music/mpd.rs | 3 +- src/clients/music/mpris.rs | 3 +- src/clients/system_tray.rs | 3 +- src/clients/wayland/client.rs | 3 +- src/config/common.rs | 9 +- src/dynamic_value/dynamic_bool.rs | 24 ++--- src/dynamic_value/dynamic_string.rs | 20 ++--- src/image/provider.rs | 14 +-- src/ipc/server.rs | 19 ++-- src/macros.rs | 49 ++++++++++ src/main.rs | 64 ++++++++++--- src/modules/clipboard.rs | 33 +++---- src/modules/clock.rs | 23 ++--- src/modules/custom/button.rs | 1 - src/modules/custom/image.rs | 2 - src/modules/custom/label.rs | 1 - src/modules/custom/mod.rs | 17 ++-- src/modules/custom/progress.rs | 14 ++- src/modules/custom/slider.rs | 18 ++-- src/modules/focused.rs | 8 +- src/modules/label.rs | 9 +- src/modules/launcher/item.rs | 5 +- src/modules/launcher/mod.rs | 29 +++--- src/modules/mod.rs | 93 ++++++++++--------- src/modules/music/mod.rs | 38 ++++---- src/modules/script.rs | 8 +- src/modules/sysinfo.rs | 7 +- src/modules/tray.rs | 13 ++- src/modules/upower.rs | 45 +++++----- src/modules/workspaces.rs | 7 +- src/popup.rs | 44 ++++----- src/style.rs | 34 +++---- 39 files changed, 426 insertions(+), 465 deletions(-) delete mode 100644 src/bridge_channel.rs diff --git a/Cargo.lock b/Cargo.lock index fd2f619..1197efe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -247,21 +247,20 @@ checksum = "2ce4f10ea3abcd6617873bae9f91d1c5332b4a778bd9ce34d0cd517474c1de82" [[package]] name = "atk" -version = "0.17.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba16453d10c712284061a05f6510f75abeb92b56ba88dfeb48c74775020cc22" +checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" dependencies = [ "atk-sys", - "bitflags 1.3.2", "glib", "libc", ] [[package]] name = "atk-sys" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf0a7ca572fbd5762fd8f8cd65a581e06767bc1234913fe1f43e370cff6e90" +checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" dependencies = [ "glib-sys", "gobject-sys", @@ -364,11 +363,11 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cairo-rs" -version = "0.17.10" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3603c4028a5e368d09b51c8b624b9a46edcd7c3778284077a6125af73c9f0a" +checksum = "f33613627f0dea6a731b0605101fad59ba4f193a52c96c4687728d822605a8a1" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "cairo-sys-rs", "glib", "libc", @@ -378,9 +377,9 @@ dependencies = [ [[package]] name = "cairo-sys-rs" -version = "0.17.10" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "691d0c66b1fb4881be80a760cb8fe76ea97218312f9dfe2c9cc0f496ca279cb1" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" dependencies = [ "glib-sys", "libc", @@ -1104,11 +1103,10 @@ dependencies = [ [[package]] name = "gdk" -version = "0.17.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1df5ea52cccd7e3a0897338b5564968274b52f5fd12601e0afa44f454c74d3" +checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" dependencies = [ - "bitflags 1.3.2", "cairo-rs", "gdk-pixbuf", "gdk-sys", @@ -1120,11 +1118,10 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.17.10" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "695d6bc846438c5708b07007537b9274d883373dd30858ca881d7d71b5540717" +checksum = "446f32b74d22c33b7b258d4af4ffde53c2bf96ca2e29abdf1a785fe59bd6c82c" dependencies = [ - "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", "glib", @@ -1134,9 +1131,9 @@ dependencies = [ [[package]] name = "gdk-pixbuf-sys" -version = "0.17.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9285ec3c113c66d7d0ab5676599176f1f42f4944ca1b581852215bf5694870cb" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" dependencies = [ "gio-sys", "glib-sys", @@ -1147,9 +1144,9 @@ dependencies = [ [[package]] name = "gdk-sys" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2152de9d38bc67a17b3fe49dc0823af5bf874df59ea088c5f28f31cf103de703" +checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1191,11 +1188,10 @@ checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" [[package]] name = "gio" -version = "0.17.10" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6973e92937cf98689b6a054a9e56c657ed4ff76de925e36fc331a15f0c5d30a" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" dependencies = [ - "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", @@ -1211,9 +1207,9 @@ dependencies = [ [[package]] name = "gio-sys" -version = "0.17.10" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ccf87c30a12c469b6d958950f6a9c09f2be20b7773f7e70d20b867fdf2628c3" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" dependencies = [ "glib-sys", "gobject-sys", @@ -1224,11 +1220,11 @@ dependencies = [ [[package]] name = "glib" -version = "0.17.10" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fad45ba8d4d2cea612b432717e834f48031cd8853c8aaf43b2c79fec8d144b" +checksum = "951bbd7fdc5c044ede9f05170f05a3ae9479239c3afdfe2d22d537a3add15c4e" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "futures-channel", "futures-core", "futures-executor", @@ -1247,24 +1243,23 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.17.10" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca5c79337338391f1ab8058d6698125034ce8ef31b72a442437fa6c8580de26" +checksum = "72793962ceece3863c2965d7f10c8786323b17c7adea75a515809fa20ab799a5" dependencies = [ - "anyhow", "heck 0.4.1", - "proc-macro-crate", + "proc-macro-crate 2.0.1", "proc-macro-error", "proc-macro2", "quote 1.0.32", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] name = "glib-sys" -version = "0.17.10" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d80aa6ea7bba0baac79222204aa786a6293078c210abe69ef1336911d4bdc4f0" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" dependencies = [ "libc", "system-deps", @@ -1272,9 +1267,9 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.17.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd34c3317740a6358ec04572c1bcfd3ac0b5b6529275fae255b237b314bb8062" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" dependencies = [ "glib-sys", "libc", @@ -1283,12 +1278,11 @@ dependencies = [ [[package]] name = "gtk" -version = "0.17.1" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c4222ab92b08d4d0bab90ddb6185b4e575ceeea8b8cdf00b938d7b6661d966" +checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" dependencies = [ "atk", - "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", @@ -1299,16 +1293,15 @@ dependencies = [ "gtk-sys", "gtk3-macros", "libc", - "once_cell", "pango", "pkg-config", ] [[package]] name = "gtk-layer-shell" -version = "0.6.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "992f5fedb31835424a5280acd162bf348995f617d26969fde8d3dfd389b3ff5f" +checksum = "19fd93acba7b8ea8918fc564843a22cd1eeffe234b85a8c7d5732c611a425bb0" dependencies = [ "bitflags 2.4.0", "gdk", @@ -1321,9 +1314,9 @@ dependencies = [ [[package]] name = "gtk-layer-shell-sys" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5754bcfaadfc3529116af6ae93559b267d88647f965382153a4b8ea9372be75a" +checksum = "90e46fa9aa7c926630b2483cc3d47de26a51173fc2fddb65737e5d813d4be448" dependencies = [ "gdk-sys", "glib-sys", @@ -1334,9 +1327,9 @@ dependencies = [ [[package]] name = "gtk-sys" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d8eb6a4b93e5a7e6980f7348d08c1cd93d31fae07cf97f20678c5ec41de3d7e" +checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" dependencies = [ "atk-sys", "cairo-sys-rs", @@ -1352,16 +1345,15 @@ dependencies = [ [[package]] name = "gtk3-macros" -version = "0.17.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3efb84d682c9a39c10bd9f24f5a4b9c15cc8c7edc45c19cb2ca2c4fc38b2d95e" +checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" dependencies = [ - "anyhow", - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro-error", "proc-macro2", "quote 1.0.32", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] @@ -2144,11 +2136,10 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "pango" -version = "0.17.10" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35be456fc620e61f62dff7ff70fbd54dcbaf0a4b920c0f16de1107c47d921d48" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" dependencies = [ - "bitflags 1.3.2", "gio", "glib", "libc", @@ -2158,9 +2149,9 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.17.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da69f9f3850b0d8990d462f8c709561975e95f689c1cdf0fecdebde78b35195" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" dependencies = [ "glib-sys", "gobject-sys", @@ -2314,7 +2305,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.14", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", ] [[package]] @@ -3205,7 +3206,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.19.14", ] [[package]] @@ -3230,6 +3231,17 @@ dependencies = [ "winnow", ] +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -4029,7 +4041,7 @@ version = "3.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote 1.0.32", "regex", @@ -4068,7 +4080,7 @@ version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote 1.0.32", "syn 1.0.109", diff --git a/Cargo.toml b/Cargo.toml index 73a6d31..df5961c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,9 +63,9 @@ workspaces = ["futures-util"] [dependencies] # core -gtk = "0.17.0" -gtk-layer-shell = "0.6.0" -glib = "0.17.10" +gtk = "0.18.1" +gtk-layer-shell = "0.8.0" +glib = "0.18.4" tokio = { version = "1.35.0", features = [ "macros", "rt-multi-thread", diff --git a/src/bar.rs b/src/bar.rs index 67a2910..76beed3 100644 --- a/src/bar.rs +++ b/src/bar.rs @@ -5,9 +5,11 @@ use crate::modules::{ use crate::popup::Popup; use crate::{Config, Ironbar}; use color_eyre::Result; +use glib::Propagation; use gtk::gdk::Monitor; use gtk::prelude::*; use gtk::{Application, ApplicationWindow, IconTheme, Orientation, Window, WindowType}; +use gtk_layer_shell::LayerShell; use std::cell::RefCell; use std::rc::Rc; use std::time::Duration; @@ -81,7 +83,7 @@ impl Bar { window.connect_destroy_event(|_, _| { info!("Shutting down"); gtk::main_quit(); - Inhibit(false) + Propagation::Proceed }); Bar { @@ -161,42 +163,38 @@ impl Bar { ) { let position = self.position; - gtk_layer_shell::init_for_window(win); - gtk_layer_shell::set_monitor(win, monitor); - gtk_layer_shell::set_layer(win, gtk_layer_shell::Layer::Top); - gtk_layer_shell::set_namespace(win, env!("CARGO_PKG_NAME")); + win.init_layer_shell(); + win.set_monitor(monitor); + win.set_layer(gtk_layer_shell::Layer::Top); + win.set_namespace(env!("CARGO_PKG_NAME")); if exclusive_zone { - gtk_layer_shell::auto_exclusive_zone_enable(win); + win.auto_exclusive_zone_enable(); } - gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Top, margin.top); - gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Bottom, margin.bottom); - gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Left, margin.left); - gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Right, margin.right); + win.set_layer_shell_margin(gtk_layer_shell::Edge::Top, margin.top); + win.set_layer_shell_margin(gtk_layer_shell::Edge::Bottom, margin.bottom); + win.set_layer_shell_margin(gtk_layer_shell::Edge::Left, margin.left); + win.set_layer_shell_margin(gtk_layer_shell::Edge::Right, margin.right); let bar_orientation = position.get_orientation(); - gtk_layer_shell::set_anchor( - win, + win.set_anchor( gtk_layer_shell::Edge::Top, position == BarPosition::Top || (bar_orientation == Orientation::Vertical && anchor_to_edges), ); - gtk_layer_shell::set_anchor( - win, + win.set_anchor( gtk_layer_shell::Edge::Bottom, position == BarPosition::Bottom || (bar_orientation == Orientation::Vertical && anchor_to_edges), ); - gtk_layer_shell::set_anchor( - win, + win.set_anchor( gtk_layer_shell::Edge::Left, position == BarPosition::Left || (bar_orientation == Orientation::Horizontal && anchor_to_edges), ); - gtk_layer_shell::set_anchor( - win, + win.set_anchor( gtk_layer_shell::Edge::Right, position == BarPosition::Right || (bar_orientation == Orientation::Horizontal && anchor_to_edges), @@ -221,7 +219,7 @@ impl Bar { win.hide(); hotspot_window.show(); }); - Inhibit(false) + Propagation::Proceed }); } @@ -232,7 +230,7 @@ impl Bar { hotspot_win.hide(); win.show(); - Inhibit(false) + Propagation::Proceed }); } } diff --git a/src/bridge_channel.rs b/src/bridge_channel.rs deleted file mode 100644 index 101eb10..0000000 --- a/src/bridge_channel.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::send; -use tokio::spawn; -use tokio::sync::mpsc; - -/// MPSC async -> GTK sync channel. -/// The sender uses `tokio::sync::mpsc` -/// while the receiver uses `glib::MainContext::channel`. -/// -/// This makes it possible to send events asynchronously -/// and receive them on the main thread, -/// allowing UI updates to be handled on the receiving end. -pub struct BridgeChannel { - async_tx: mpsc::Sender, - sync_rx: glib::Receiver, -} - -impl BridgeChannel { - /// Creates a new channel - pub fn new() -> Self { - let (async_tx, mut async_rx) = mpsc::channel(32); - let (sync_tx, sync_rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); - - spawn(async move { - while let Some(val) = async_rx.recv().await { - send!(sync_tx, val); - } - }); - - Self { async_tx, sync_rx } - } - - /// Gets a clone of the sender. - pub fn create_sender(&self) -> mpsc::Sender { - self.async_tx.clone() - } - - /// Attaches a callback to the receiver. - pub fn recv(self, f: F) -> glib::SourceId - where - F: FnMut(T) -> glib::Continue + 'static, - { - self.sync_rx.attach(None, f) - } -} diff --git a/src/clients/clipboard.rs b/src/clients/clipboard.rs index 383c8af..614c7e3 100644 --- a/src/clients/clipboard.rs +++ b/src/clients/clipboard.rs @@ -1,10 +1,9 @@ use super::wayland::{self, ClipboardItem}; -use crate::{arc_mut, lock, try_send}; +use crate::{arc_mut, lock, spawn, try_send}; use indexmap::map::Iter; use indexmap::IndexMap; use lazy_static::lazy_static; use std::sync::{Arc, Mutex}; -use tokio::spawn; use tokio::sync::mpsc; use tracing::{debug, trace}; diff --git a/src/clients/compositor/hyprland.rs b/src/clients/compositor/hyprland.rs index 6342ec1..f0f1ccf 100644 --- a/src/clients/compositor/hyprland.rs +++ b/src/clients/compositor/hyprland.rs @@ -1,5 +1,5 @@ use super::{Visibility, Workspace, WorkspaceClient, WorkspaceUpdate}; -use crate::{arc_mut, lock, send}; +use crate::{arc_mut, lock, send, spawn_blocking}; use color_eyre::Result; use hyprland::data::{Workspace as HWorkspace, Workspaces}; use hyprland::dispatch::{Dispatch, DispatchType, WorkspaceIdentifierWithSpecial}; @@ -8,7 +8,6 @@ use hyprland::prelude::*; use hyprland::shared::{HyprDataVec, WorkspaceType}; use lazy_static::lazy_static; use tokio::sync::broadcast::{channel, Receiver, Sender}; -use tokio::task::spawn_blocking; use tracing::{debug, error, info}; pub struct EventClient { diff --git a/src/clients/compositor/sway.rs b/src/clients/compositor/sway.rs index cde04e5..40f1b24 100644 --- a/src/clients/compositor/sway.rs +++ b/src/clients/compositor/sway.rs @@ -1,12 +1,11 @@ use super::{Visibility, Workspace, WorkspaceClient, WorkspaceUpdate}; -use crate::{await_sync, send}; +use crate::{await_sync, send, spawn}; use async_once::AsyncOnce; use color_eyre::Report; use futures_util::StreamExt; use lazy_static::lazy_static; use std::sync::Arc; use swayipc_async::{Connection, Event, EventType, Node, WorkspaceChange, WorkspaceEvent}; -use tokio::spawn; use tokio::sync::broadcast::{channel, Receiver, Sender}; use tokio::sync::Mutex; use tracing::{info, trace}; diff --git a/src/clients/music/mpd.rs b/src/clients/music/mpd.rs index 742b172..2d2ffaa 100644 --- a/src/clients/music/mpd.rs +++ b/src/clients/music/mpd.rs @@ -1,7 +1,7 @@ use super::{ MusicClient, PlayerState, PlayerUpdate, ProgressTick, Status, Track, TICK_INTERVAL_MS, }; -use crate::{await_sync, send}; +use crate::{await_sync, send, spawn}; use color_eyre::Result; use lazy_static::lazy_static; use mpd_client::client::{Connection, ConnectionEvent, Subsystem}; @@ -17,7 +17,6 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::Duration; use tokio::net::{TcpStream, UnixStream}; -use tokio::spawn; use tokio::sync::broadcast; use tokio::sync::Mutex; use tokio::time::sleep; diff --git a/src/clients/music/mpris.rs b/src/clients/music/mpris.rs index 2cbc5a2..103bd21 100644 --- a/src/clients/music/mpris.rs +++ b/src/clients/music/mpris.rs @@ -1,6 +1,6 @@ use super::{MusicClient, PlayerState, PlayerUpdate, Status, Track, TICK_INTERVAL_MS}; use crate::clients::music::ProgressTick; -use crate::{arc_mut, lock, send}; +use crate::{arc_mut, lock, send, spawn_blocking}; use color_eyre::Result; use lazy_static::lazy_static; use mpris::{DBusError, Event, Metadata, PlaybackStatus, Player, PlayerFinder}; @@ -10,7 +10,6 @@ use std::thread::sleep; use std::time::Duration; use std::{cmp, string}; use tokio::sync::broadcast; -use tokio::task::spawn_blocking; use tracing::{debug, error, trace}; lazy_static! { diff --git a/src/clients/system_tray.rs b/src/clients/system_tray.rs index 5978338..0c700c0 100644 --- a/src/clients/system_tray.rs +++ b/src/clients/system_tray.rs @@ -1,4 +1,4 @@ -use crate::{arc_mut, lock, send, Ironbar}; +use crate::{arc_mut, lock, send, spawn, Ironbar}; use async_once::AsyncOnce; use color_eyre::Report; use lazy_static::lazy_static; @@ -8,7 +8,6 @@ use system_tray::message::menu::TrayMenu; use system_tray::message::tray::StatusNotifierItem; use system_tray::message::{NotifierItemCommand, NotifierItemMessage}; use system_tray::StatusNotifierWatcher; -use tokio::spawn; use tokio::sync::{broadcast, mpsc}; use tracing::{debug, error, trace}; diff --git a/src/clients/wayland/client.rs b/src/clients/wayland/client.rs index 2c56210..c4ddb62 100644 --- a/src/clients/wayland/client.rs +++ b/src/clients/wayland/client.rs @@ -3,7 +3,7 @@ use super::wlr_foreign_toplevel::manager::ToplevelManagerState; use super::wlr_foreign_toplevel::ToplevelEvent; use super::Environment; use crate::error::ERR_CHANNEL_RECV; -use crate::send; +use crate::{send, spawn_blocking}; use cfg_if::cfg_if; use color_eyre::Report; use smithay_client_toolkit::output::{OutputInfo, OutputState}; @@ -15,7 +15,6 @@ use smithay_client_toolkit::seat::SeatState; use std::collections::HashMap; use std::sync::mpsc; use tokio::sync::broadcast; -use tokio::task::spawn_blocking; use tracing::{debug, error, trace}; use wayland_client::globals::registry_queue_init; use wayland_client::protocol::wl_seat::WlSeat; diff --git a/src/config/common.rs b/src/config/common.rs index b22b38e..3e3cc85 100644 --- a/src/config/common.rs +++ b/src/config/common.rs @@ -1,5 +1,6 @@ use crate::dynamic_value::{dynamic_string, DynamicBool}; use crate::script::{Script, ScriptInput}; +use glib::Propagation; use gtk::gdk::ScrollDirection; use gtk::prelude::*; use gtk::{EventBox, Orientation, Revealer, RevealerTransitionType}; @@ -75,7 +76,7 @@ impl CommonConfig { script.run_as_oneshot(None); } - Inhibit(false) + Propagation::Proceed }); let scroll_up_script = self.on_scroll_up.map(Script::new_polling); @@ -93,7 +94,7 @@ impl CommonConfig { script.run_as_oneshot(None); } - Inhibit(false) + Propagation::Proceed }); macro_rules! install_oneshot { @@ -101,7 +102,7 @@ impl CommonConfig { $option.map(Script::new_polling).map(|script| { container.$method(move |_, _| { script.run_as_oneshot(None); - Inhibit(false) + Propagation::Proceed }); }) }; @@ -114,7 +115,6 @@ impl CommonConfig { let container = container.clone(); dynamic_string(&tooltip, move |string| { container.set_tooltip_text(Some(&string)); - Continue(true) }); } } @@ -136,7 +136,6 @@ impl CommonConfig { container.show_all(); } revealer.set_reveal_child(success); - Continue(true) }); } diff --git a/src/dynamic_value/dynamic_bool.rs b/src/dynamic_value/dynamic_bool.rs index 2afde47..711a92a 100644 --- a/src/dynamic_value/dynamic_bool.rs +++ b/src/dynamic_value/dynamic_bool.rs @@ -1,11 +1,10 @@ use crate::script::Script; -use crate::send; +use crate::{glib_recv_mpsc, spawn, try_send}; #[cfg(feature = "ipc")] -use crate::Ironbar; +use crate::{send_async, Ironbar}; use cfg_if::cfg_if; -use glib::Continue; use serde::Deserialize; -use tokio::spawn; +use tokio::sync::mpsc; #[derive(Debug, Deserialize, Clone)] #[serde(untagged)] @@ -18,9 +17,9 @@ pub enum DynamicBool { } impl DynamicBool { - pub fn subscribe(self, f: F) + pub fn subscribe(self, mut f: F) where - F: FnMut(bool) -> Continue + 'static, + F: FnMut(bool) + 'static, { let value = match self { Self::Unknown(input) => { @@ -40,16 +39,16 @@ impl DynamicBool { _ => self, }; - let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); + let (tx, mut rx) = mpsc::channel(32); - rx.attach(None, f); + glib_recv_mpsc!(rx, val => f(val)); spawn(async move { match value { DynamicBool::Script(script) => { script .run(None, |_, success| { - send!(tx, success); + try_send!(tx, success); }) .await; } @@ -62,7 +61,7 @@ impl DynamicBool { while let Ok(value) = rx.recv().await { let has_value = value.map(|s| is_truthy(&s)).unwrap_or_default(); - send!(tx, has_value); + send_async!(tx, has_value); } } DynamicBool::Unknown(_) => unreachable!(), @@ -71,7 +70,10 @@ impl DynamicBool { } } -/// Check if a string ironvar is 'truthy' +/// Check if a string ironvar is 'truthy', +/// i.e should be evaluated to true. +/// +/// This loosely follows the common JavaScript cases. #[cfg(feature = "ipc")] fn is_truthy(string: &str) -> bool { !(string.is_empty() || string == "0" || string == "false") diff --git a/src/dynamic_value/dynamic_string.rs b/src/dynamic_value/dynamic_string.rs index cdfbd5c..7654f97 100644 --- a/src/dynamic_value/dynamic_string.rs +++ b/src/dynamic_value/dynamic_string.rs @@ -1,9 +1,8 @@ use crate::script::{OutputStream, Script}; #[cfg(feature = "ipc")] use crate::Ironbar; -use crate::{arc_mut, lock, send}; -use gtk::prelude::*; -use tokio::spawn; +use crate::{arc_mut, glib_recv_mpsc, lock, spawn, try_send}; +use tokio::sync::mpsc; /// A segment of a dynamic string, /// containing either a static string @@ -24,17 +23,16 @@ enum DynamicStringSegment { /// ```rs /// dynamic_string(&text, move |string| { /// label.set_markup(&string); -/// Continue(true) /// }); /// ``` -pub fn dynamic_string(input: &str, f: F) +pub fn dynamic_string(input: &str, mut f: F) where - F: FnMut(String) -> Continue + 'static, + F: FnMut(String) + 'static, { let tokens = parse_input(input); let label_parts = arc_mut!(vec![]); - let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); + let (tx, mut rx) = mpsc::channel(32); for (i, segment) in tokens.into_iter().enumerate() { match segment { @@ -57,7 +55,7 @@ where let _: String = std::mem::replace(&mut label_parts[i], out); let string = label_parts.join(""); - send!(tx, string); + try_send!(tx, string); } }) .await; @@ -82,7 +80,7 @@ where let _: String = std::mem::replace(&mut label_parts[i], value); let string = label_parts.join(""); - send!(tx, string); + try_send!(tx, string); } } }); @@ -90,12 +88,12 @@ where } } - rx.attach(None, f); + glib_recv_mpsc!(rx , val => f(val)); // initialize { let label_parts = lock!(label_parts).join(""); - send!(tx, label_parts); + try_send!(tx, label_parts); } } diff --git a/src/image/provider.rs b/src/image/provider.rs index 397ba62..defefb6 100644 --- a/src/image/provider.rs +++ b/src/image/provider.rs @@ -1,4 +1,6 @@ use crate::desktop_file::get_desktop_icon_name; +#[cfg(feature = "http")] +use crate::{glib_recv_mpsc, send_async, spawn}; use cfg_if::cfg_if; use color_eyre::{Help, Report, Result}; use gtk::cairo::Surface; @@ -7,13 +9,13 @@ use gtk::gdk_pixbuf::Pixbuf; use gtk::prelude::*; use gtk::{IconLookupFlags, IconTheme}; use std::path::{Path, PathBuf}; +#[cfg(feature = "http")] +use tokio::sync::mpsc; use tracing::warn; cfg_if!( if #[cfg(feature = "http")] { - use crate::send; use gtk::gio::{Cancellable, MemoryInputStream}; - use tokio::spawn; use tracing::error; } ); @@ -143,18 +145,18 @@ impl<'a> ImageProvider<'a> { #[cfg(feature = "http")] if let ImageLocation::Remote(url) = &self.location { let url = url.clone(); - let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); + let (tx, mut rx) = mpsc::channel(64); spawn(async move { let bytes = Self::get_bytes_from_http(url).await; if let Ok(bytes) = bytes { - send!(tx, bytes); + send_async!(tx, bytes); } }); { let size = self.size; - rx.attach(None, move |bytes| { + glib_recv_mpsc!(rx, bytes => { let stream = MemoryInputStream::from_bytes(&bytes); let scale = image.scale_factor(); @@ -175,8 +177,6 @@ impl<'a> ImageProvider<'a> { Err(err) => error!("{err:?}"), _ => {} } - - Continue(false) }); } } else { diff --git a/src/ipc/server.rs b/src/ipc/server.rs index 78721f4..26c76b3 100644 --- a/src/ipc/server.rs +++ b/src/ipc/server.rs @@ -3,20 +3,17 @@ use std::path::Path; use std::rc::Rc; use color_eyre::{Report, Result}; -use glib::Continue; use gtk::prelude::*; use gtk::Application; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::{UnixListener, UnixStream}; -use tokio::spawn; use tokio::sync::mpsc::{self, Receiver, Sender}; use tracing::{debug, error, info, warn}; -use crate::bridge_channel::BridgeChannel; use crate::ipc::{Command, Response}; use crate::modules::PopupButton; use crate::style::load_css; -use crate::{read_lock, send_async, try_send, write_lock, Ironbar}; +use crate::{glib_recv_mpsc, read_lock, send_async, spawn, try_send, write_lock, Ironbar}; use super::Ipc; @@ -25,8 +22,7 @@ impl Ipc { /// /// Once started, the server will begin accepting connections. pub fn start(&self, application: &Application, ironbar: Rc) { - let bridge = BridgeChannel::::new(); - let cmd_tx = bridge.create_sender(); + let (cmd_tx, mut cmd_rx) = mpsc::channel(32); let (res_tx, mut res_rx) = mpsc::channel(32); let path = self.path.clone(); @@ -68,10 +64,9 @@ impl Ipc { }); let application = application.clone(); - bridge.recv(move |command| { - let res = Self::handle_command(command, &application, ironbar.clone()); + glib_recv_mpsc!(cmd_rx, command => { + let res = Self::handle_command(command, &application, &ironbar); try_send!(res_tx, res); - Continue(true) }); } @@ -109,11 +104,7 @@ impl Ipc { /// Takes an input command, runs it and returns with the appropriate response. /// /// This runs on the main thread, allowing commands to interact with GTK. - fn handle_command( - command: Command, - application: &Application, - ironbar: Rc, - ) -> Response { + fn handle_command(command: Command, application: &Application, ironbar: &Ironbar) -> Response { match command { Command::Inspect => { gtk::Window::set_interactive_debugging(true); diff --git a/src/macros.rs b/src/macros.rs index 5e5e99f..deb8975 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -43,6 +43,55 @@ macro_rules! try_send { }; } +/// Spawns a `GLib` future on the local thread, and calls `rx.recv()` +/// in a loop. +/// +/// This allows use of `GObjects` and futures in the same context. +/// +/// For use with receivers which return a `Result`. +/// +/// # Example +/// +/// ```rs +/// let (tx, mut rx) = broadcast::channel(32); +/// glib_recv(rx, msg => println!("{msg}")); +/// ``` +#[macro_export] +macro_rules! glib_recv { + ($rx:expr, $val:ident => $expr:expr) => { + glib::spawn_future_local(async move { + while let Ok($val) = $rx.recv().await { + $expr + } + }); + }; +} + +/// Spawns a `GLib` future on the local thread, and calls `rx.recv()` +/// in a loop. +/// +/// This allows use of `GObjects` and futures in the same context. +/// +/// For use with receivers which return an `Option`, +/// such as Tokio's `mpsc` channel. +/// +/// # Example +/// +/// ```rs +/// let (tx, mut rx) = broadcast::channel(32); +/// glib_recv_mpsc(rx, msg => println!("{msg}")); +/// ``` +#[macro_export] +macro_rules! glib_recv_mpsc { + ($rx:expr, $val:ident => $expr:expr) => { + glib::spawn_future_local(async move { + while let Some($val) = $rx.recv().await { + $expr + } + }); + }; +} + /// Locks a `Mutex`. /// Panics if the `Mutex` cannot be locked. /// diff --git a/src/main.rs b/src/main.rs index 033c9b5..0fee3d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,9 +7,9 @@ use std::path::PathBuf; use std::process::exit; use std::rc::Rc; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::sync::mpsc; #[cfg(feature = "ipc")] -use std::sync::{Arc, RwLock}; +use std::sync::RwLock; +use std::sync::{mpsc, Arc}; use cfg_if::cfg_if; #[cfg(feature = "cli")] @@ -21,8 +21,8 @@ use glib::PropertySet; use gtk::gdk::Display; use gtk::prelude::*; use gtk::Application; -use tokio::runtime::Handle; -use tokio::task::{block_in_place, spawn_blocking}; +use tokio::runtime::{Handle, Runtime}; +use tokio::task::{block_in_place, JoinHandle}; use tracing::{debug, error, info, warn}; use universal_config::ConfigLoader; @@ -36,7 +36,6 @@ use crate::ironvar::VariableManager; use crate::style::load_css; mod bar; -mod bridge_channel; #[cfg(feature = "cli")] mod cli; mod clients; @@ -60,13 +59,12 @@ mod style; const GTK_APP_ID: &str = "dev.jstanger.ironbar"; const VERSION: &str = env!("CARGO_PKG_VERSION"); -#[tokio::main] -async fn main() { +fn main() { let _guard = logging::install_logging(); cfg_if! { if #[cfg(feature = "cli")] { - run_with_args().await; + run_with_args(); } else { start_ironbar(); } @@ -74,16 +72,19 @@ async fn main() { } #[cfg(feature = "cli")] -async fn run_with_args() { +fn run_with_args() { let args = cli::Args::parse(); match args.command { Some(command) => { - let ipc = ipc::Ipc::new(); - match ipc.send(command).await { - Ok(res) => cli::handle_response(res), - Err(err) => error!("{err:?}"), - }; + let rt = create_runtime(); + rt.block_on(async move { + let ipc = ipc::Ipc::new(); + match ipc.send(command).await { + Ok(res) => cli::handle_response(res), + Err(err) => error!("{err:?}"), + }; + }); } None => start_ironbar(), } @@ -91,6 +92,10 @@ async fn run_with_args() { static COUNTER: AtomicUsize = AtomicUsize::new(1); +lazy_static::lazy_static! { + static ref RUNTIME: Arc = Arc::new(create_runtime()); +} + #[cfg(feature = "ipc")] lazy_static::lazy_static! { static ref VARIABLE_MANAGER: Arc> = arc_rw!(VariableManager::new()); @@ -184,6 +189,12 @@ impl Ironbar { app.run_with_args(&Vec::<&str>::new()); } + /// Gets the current Tokio runtime. + #[must_use] + pub fn runtime() -> Arc { + RUNTIME.clone() + } + /// Gets a `usize` ID value that is unique to the entire Ironbar instance. /// This is just a static `AtomicUsize` that increments every time this function is called. pub fn unique_id() -> usize { @@ -323,6 +334,31 @@ fn create_bars(app: &Application, display: &Display, config: &Config) -> Result< Ok(all_bars) } +fn create_runtime() -> Runtime { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .expect("tokio to create a valid runtime") +} + +/// Calls `spawn` on the Tokio runtime. +pub fn spawn(f: F) -> JoinHandle +where + F: Future + Send + 'static, + F::Output: Send + 'static, +{ + Ironbar::runtime().spawn(f) +} + +/// Calls `spawn_blocking` on the Tokio runtime. +pub fn spawn_blocking(f: F) -> JoinHandle +where + F: FnOnce() -> R + Send + 'static, + R: Send + 'static, +{ + Ironbar::runtime().spawn_blocking(f) +} + /// Blocks on a `Future` until it resolves. /// /// This is not an `async` operation diff --git a/src/modules/clipboard.rs b/src/modules/clipboard.rs index e52afa5..cdc72fb 100644 --- a/src/modules/clipboard.rs +++ b/src/modules/clipboard.rs @@ -5,7 +5,8 @@ use crate::image::new_icon_button; use crate::modules::{ Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext, }; -use crate::try_send; +use crate::{glib_recv, spawn, try_send}; +use glib::Propagation; use gtk::gdk_pixbuf::Pixbuf; use gtk::gio::{Cancellable, MemoryInputStream}; use gtk::prelude::*; @@ -13,8 +14,7 @@ use gtk::{Button, EventBox, Image, Label, Orientation, RadioButton, Widget}; use serde::Deserialize; use std::collections::HashMap; use std::sync::Arc; -use tokio::spawn; -use tokio::sync::mpsc::{Receiver, Sender}; +use tokio::sync::{broadcast, mpsc}; use tracing::{debug, error}; #[derive(Debug, Deserialize, Clone)] @@ -72,8 +72,8 @@ impl Module