1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-07-01 10:41:03 +02:00

Merge pull request #380 from JakeStanger/refactor/update-gtk

Update GTK-related crates, replace GLib channels with Tokio channels, refactor Tokio runtime code
This commit is contained in:
Jake Stanger 2023-12-18 22:17:44 +00:00 committed by GitHub
commit 29eeefbd24
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 430 additions and 469 deletions

134
Cargo.lock generated
View file

@ -247,21 +247,20 @@ checksum = "2ce4f10ea3abcd6617873bae9f91d1c5332b4a778bd9ce34d0cd517474c1de82"
[[package]] [[package]]
name = "atk" name = "atk"
version = "0.17.1" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba16453d10c712284061a05f6510f75abeb92b56ba88dfeb48c74775020cc22" checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4"
dependencies = [ dependencies = [
"atk-sys", "atk-sys",
"bitflags 1.3.2",
"glib", "glib",
"libc", "libc",
] ]
[[package]] [[package]]
name = "atk-sys" name = "atk-sys"
version = "0.17.0" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf0a7ca572fbd5762fd8f8cd65a581e06767bc1234913fe1f43e370cff6e90" checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009"
dependencies = [ dependencies = [
"glib-sys", "glib-sys",
"gobject-sys", "gobject-sys",
@ -364,11 +363,11 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]] [[package]]
name = "cairo-rs" name = "cairo-rs"
version = "0.17.10" version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab3603c4028a5e368d09b51c8b624b9a46edcd7c3778284077a6125af73c9f0a" checksum = "f33613627f0dea6a731b0605101fad59ba4f193a52c96c4687728d822605a8a1"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 2.4.0",
"cairo-sys-rs", "cairo-sys-rs",
"glib", "glib",
"libc", "libc",
@ -378,9 +377,9 @@ dependencies = [
[[package]] [[package]]
name = "cairo-sys-rs" name = "cairo-sys-rs"
version = "0.17.10" version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "691d0c66b1fb4881be80a760cb8fe76ea97218312f9dfe2c9cc0f496ca279cb1" checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51"
dependencies = [ dependencies = [
"glib-sys", "glib-sys",
"libc", "libc",
@ -1104,11 +1103,10 @@ dependencies = [
[[package]] [[package]]
name = "gdk" name = "gdk"
version = "0.17.1" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be1df5ea52cccd7e3a0897338b5564968274b52f5fd12601e0afa44f454c74d3" checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646"
dependencies = [ dependencies = [
"bitflags 1.3.2",
"cairo-rs", "cairo-rs",
"gdk-pixbuf", "gdk-pixbuf",
"gdk-sys", "gdk-sys",
@ -1120,11 +1118,10 @@ dependencies = [
[[package]] [[package]]
name = "gdk-pixbuf" name = "gdk-pixbuf"
version = "0.17.10" version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "695d6bc846438c5708b07007537b9274d883373dd30858ca881d7d71b5540717" checksum = "446f32b74d22c33b7b258d4af4ffde53c2bf96ca2e29abdf1a785fe59bd6c82c"
dependencies = [ dependencies = [
"bitflags 1.3.2",
"gdk-pixbuf-sys", "gdk-pixbuf-sys",
"gio", "gio",
"glib", "glib",
@ -1134,9 +1131,9 @@ dependencies = [
[[package]] [[package]]
name = "gdk-pixbuf-sys" name = "gdk-pixbuf-sys"
version = "0.17.10" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9285ec3c113c66d7d0ab5676599176f1f42f4944ca1b581852215bf5694870cb" checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7"
dependencies = [ dependencies = [
"gio-sys", "gio-sys",
"glib-sys", "glib-sys",
@ -1147,9 +1144,9 @@ dependencies = [
[[package]] [[package]]
name = "gdk-sys" name = "gdk-sys"
version = "0.17.0" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2152de9d38bc67a17b3fe49dc0823af5bf874df59ea088c5f28f31cf103de703" checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2"
dependencies = [ dependencies = [
"cairo-sys-rs", "cairo-sys-rs",
"gdk-pixbuf-sys", "gdk-pixbuf-sys",
@ -1191,11 +1188,10 @@ checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
[[package]] [[package]]
name = "gio" name = "gio"
version = "0.17.10" version = "0.18.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6973e92937cf98689b6a054a9e56c657ed4ff76de925e36fc331a15f0c5d30a" checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73"
dependencies = [ dependencies = [
"bitflags 1.3.2",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-io", "futures-io",
@ -1211,9 +1207,9 @@ dependencies = [
[[package]] [[package]]
name = "gio-sys" name = "gio-sys"
version = "0.17.10" version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ccf87c30a12c469b6d958950f6a9c09f2be20b7773f7e70d20b867fdf2628c3" checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2"
dependencies = [ dependencies = [
"glib-sys", "glib-sys",
"gobject-sys", "gobject-sys",
@ -1224,11 +1220,11 @@ dependencies = [
[[package]] [[package]]
name = "glib" name = "glib"
version = "0.17.10" version = "0.18.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fad45ba8d4d2cea612b432717e834f48031cd8853c8aaf43b2c79fec8d144b" checksum = "951bbd7fdc5c044ede9f05170f05a3ae9479239c3afdfe2d22d537a3add15c4e"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 2.4.0",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-executor", "futures-executor",
@ -1247,24 +1243,23 @@ dependencies = [
[[package]] [[package]]
name = "glib-macros" name = "glib-macros"
version = "0.17.10" version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eca5c79337338391f1ab8058d6698125034ce8ef31b72a442437fa6c8580de26" checksum = "72793962ceece3863c2965d7f10c8786323b17c7adea75a515809fa20ab799a5"
dependencies = [ dependencies = [
"anyhow",
"heck 0.4.1", "heck 0.4.1",
"proc-macro-crate", "proc-macro-crate 2.0.1",
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote 1.0.32", "quote 1.0.32",
"syn 1.0.109", "syn 2.0.28",
] ]
[[package]] [[package]]
name = "glib-sys" name = "glib-sys"
version = "0.17.10" version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d80aa6ea7bba0baac79222204aa786a6293078c210abe69ef1336911d4bdc4f0" checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898"
dependencies = [ dependencies = [
"libc", "libc",
"system-deps", "system-deps",
@ -1272,9 +1267,9 @@ dependencies = [
[[package]] [[package]]
name = "gobject-sys" name = "gobject-sys"
version = "0.17.10" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd34c3317740a6358ec04572c1bcfd3ac0b5b6529275fae255b237b314bb8062" checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44"
dependencies = [ dependencies = [
"glib-sys", "glib-sys",
"libc", "libc",
@ -1283,12 +1278,11 @@ dependencies = [
[[package]] [[package]]
name = "gtk" name = "gtk"
version = "0.17.1" version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c4222ab92b08d4d0bab90ddb6185b4e575ceeea8b8cdf00b938d7b6661d966" checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c"
dependencies = [ dependencies = [
"atk", "atk",
"bitflags 1.3.2",
"cairo-rs", "cairo-rs",
"field-offset", "field-offset",
"futures-channel", "futures-channel",
@ -1299,16 +1293,15 @@ dependencies = [
"gtk-sys", "gtk-sys",
"gtk3-macros", "gtk3-macros",
"libc", "libc",
"once_cell",
"pango", "pango",
"pkg-config", "pkg-config",
] ]
[[package]] [[package]]
name = "gtk-layer-shell" name = "gtk-layer-shell"
version = "0.6.1" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "992f5fedb31835424a5280acd162bf348995f617d26969fde8d3dfd389b3ff5f" checksum = "19fd93acba7b8ea8918fc564843a22cd1eeffe234b85a8c7d5732c611a425bb0"
dependencies = [ dependencies = [
"bitflags 2.4.0", "bitflags 2.4.0",
"gdk", "gdk",
@ -1321,9 +1314,9 @@ dependencies = [
[[package]] [[package]]
name = "gtk-layer-shell-sys" name = "gtk-layer-shell-sys"
version = "0.6.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5754bcfaadfc3529116af6ae93559b267d88647f965382153a4b8ea9372be75a" checksum = "90e46fa9aa7c926630b2483cc3d47de26a51173fc2fddb65737e5d813d4be448"
dependencies = [ dependencies = [
"gdk-sys", "gdk-sys",
"glib-sys", "glib-sys",
@ -1334,9 +1327,9 @@ dependencies = [
[[package]] [[package]]
name = "gtk-sys" name = "gtk-sys"
version = "0.17.0" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d8eb6a4b93e5a7e6980f7348d08c1cd93d31fae07cf97f20678c5ec41de3d7e" checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722"
dependencies = [ dependencies = [
"atk-sys", "atk-sys",
"cairo-sys-rs", "cairo-sys-rs",
@ -1352,16 +1345,15 @@ dependencies = [
[[package]] [[package]]
name = "gtk3-macros" name = "gtk3-macros"
version = "0.17.1" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3efb84d682c9a39c10bd9f24f5a4b9c15cc8c7edc45c19cb2ca2c4fc38b2d95e" checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e"
dependencies = [ dependencies = [
"anyhow", "proc-macro-crate 1.3.1",
"proc-macro-crate",
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote 1.0.32", "quote 1.0.32",
"syn 1.0.109", "syn 2.0.28",
] ]
[[package]] [[package]]
@ -2120,11 +2112,10 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]] [[package]]
name = "pango" name = "pango"
version = "0.17.10" version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35be456fc620e61f62dff7ff70fbd54dcbaf0a4b920c0f16de1107c47d921d48" checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4"
dependencies = [ dependencies = [
"bitflags 1.3.2",
"gio", "gio",
"glib", "glib",
"libc", "libc",
@ -2134,9 +2125,9 @@ dependencies = [
[[package]] [[package]]
name = "pango-sys" name = "pango-sys"
version = "0.17.10" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3da69f9f3850b0d8990d462f8c709561975e95f689c1cdf0fecdebde78b35195" checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5"
dependencies = [ dependencies = [
"glib-sys", "glib-sys",
"gobject-sys", "gobject-sys",
@ -2290,7 +2281,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [ dependencies = [
"once_cell", "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]] [[package]]
@ -3181,7 +3182,7 @@ dependencies = [
"serde", "serde",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",
"toml_edit", "toml_edit 0.19.14",
] ]
[[package]] [[package]]
@ -3206,6 +3207,17 @@ dependencies = [
"winnow", "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]] [[package]]
name = "tower-service" name = "tower-service"
version = "0.3.2" version = "0.3.2"
@ -4005,7 +4017,7 @@ version = "3.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d"
dependencies = [ dependencies = [
"proc-macro-crate", "proc-macro-crate 1.3.1",
"proc-macro2", "proc-macro2",
"quote 1.0.32", "quote 1.0.32",
"regex", "regex",
@ -4044,7 +4056,7 @@ version = "3.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd"
dependencies = [ dependencies = [
"proc-macro-crate", "proc-macro-crate 1.3.1",
"proc-macro2", "proc-macro2",
"quote 1.0.32", "quote 1.0.32",
"syn 1.0.109", "syn 1.0.109",

View file

@ -63,9 +63,9 @@ workspaces = ["futures-util"]
[dependencies] [dependencies]
# core # core
gtk = "0.17.0" gtk = "0.18.1"
gtk-layer-shell = "0.6.0" gtk-layer-shell = "0.8.0"
glib = "0.17.10" glib = "0.18.4"
tokio = { version = "1.35.0", features = [ tokio = { version = "1.35.0", features = [
"macros", "macros",
"rt-multi-thread", "rt-multi-thread",

View file

@ -5,9 +5,11 @@ use crate::modules::{
use crate::popup::Popup; use crate::popup::Popup;
use crate::{Config, Ironbar}; use crate::{Config, Ironbar};
use color_eyre::Result; use color_eyre::Result;
use glib::Propagation;
use gtk::gdk::Monitor; use gtk::gdk::Monitor;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, IconTheme, Orientation, Window, WindowType}; use gtk::{Application, ApplicationWindow, IconTheme, Orientation, Window, WindowType};
use gtk_layer_shell::LayerShell;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use std::time::Duration; use std::time::Duration;
@ -81,7 +83,7 @@ impl Bar {
window.connect_destroy_event(|_, _| { window.connect_destroy_event(|_, _| {
info!("Shutting down"); info!("Shutting down");
gtk::main_quit(); gtk::main_quit();
Inhibit(false) Propagation::Proceed
}); });
Bar { Bar {
@ -161,42 +163,38 @@ impl Bar {
) { ) {
let position = self.position; let position = self.position;
gtk_layer_shell::init_for_window(win); win.init_layer_shell();
gtk_layer_shell::set_monitor(win, monitor); win.set_monitor(monitor);
gtk_layer_shell::set_layer(win, gtk_layer_shell::Layer::Top); win.set_layer(gtk_layer_shell::Layer::Top);
gtk_layer_shell::set_namespace(win, env!("CARGO_PKG_NAME")); win.set_namespace(env!("CARGO_PKG_NAME"));
if exclusive_zone { 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); win.set_layer_shell_margin(gtk_layer_shell::Edge::Top, margin.top);
gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Bottom, margin.bottom); win.set_layer_shell_margin(gtk_layer_shell::Edge::Bottom, margin.bottom);
gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Left, margin.left); win.set_layer_shell_margin(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::Right, margin.right);
let bar_orientation = position.get_orientation(); let bar_orientation = position.get_orientation();
gtk_layer_shell::set_anchor( win.set_anchor(
win,
gtk_layer_shell::Edge::Top, gtk_layer_shell::Edge::Top,
position == BarPosition::Top position == BarPosition::Top
|| (bar_orientation == Orientation::Vertical && anchor_to_edges), || (bar_orientation == Orientation::Vertical && anchor_to_edges),
); );
gtk_layer_shell::set_anchor( win.set_anchor(
win,
gtk_layer_shell::Edge::Bottom, gtk_layer_shell::Edge::Bottom,
position == BarPosition::Bottom position == BarPosition::Bottom
|| (bar_orientation == Orientation::Vertical && anchor_to_edges), || (bar_orientation == Orientation::Vertical && anchor_to_edges),
); );
gtk_layer_shell::set_anchor( win.set_anchor(
win,
gtk_layer_shell::Edge::Left, gtk_layer_shell::Edge::Left,
position == BarPosition::Left position == BarPosition::Left
|| (bar_orientation == Orientation::Horizontal && anchor_to_edges), || (bar_orientation == Orientation::Horizontal && anchor_to_edges),
); );
gtk_layer_shell::set_anchor( win.set_anchor(
win,
gtk_layer_shell::Edge::Right, gtk_layer_shell::Edge::Right,
position == BarPosition::Right position == BarPosition::Right
|| (bar_orientation == Orientation::Horizontal && anchor_to_edges), || (bar_orientation == Orientation::Horizontal && anchor_to_edges),
@ -221,7 +219,7 @@ impl Bar {
win.hide(); win.hide();
hotspot_window.show(); hotspot_window.show();
}); });
Inhibit(false) Propagation::Proceed
}); });
} }
@ -232,7 +230,7 @@ impl Bar {
hotspot_win.hide(); hotspot_win.hide();
win.show(); win.show();
Inhibit(false) Propagation::Proceed
}); });
} }
} }

View file

@ -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<T> {
async_tx: mpsc::Sender<T>,
sync_rx: glib::Receiver<T>,
}
impl<T: Send + 'static> BridgeChannel<T> {
/// 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<T> {
self.async_tx.clone()
}
/// Attaches a callback to the receiver.
pub fn recv<F>(self, f: F) -> glib::SourceId
where
F: FnMut(T) -> glib::Continue + 'static,
{
self.sync_rx.attach(None, f)
}
}

View file

@ -1,10 +1,9 @@
use super::wayland::{self, ClipboardItem}; 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::map::Iter;
use indexmap::IndexMap; use indexmap::IndexMap;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use tokio::spawn;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tracing::{debug, trace}; use tracing::{debug, trace};

View file

@ -1,5 +1,5 @@
use super::{Visibility, Workspace, WorkspaceClient, WorkspaceUpdate}; 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 color_eyre::Result;
use hyprland::data::{Workspace as HWorkspace, Workspaces}; use hyprland::data::{Workspace as HWorkspace, Workspaces};
use hyprland::dispatch::{Dispatch, DispatchType, WorkspaceIdentifierWithSpecial}; use hyprland::dispatch::{Dispatch, DispatchType, WorkspaceIdentifierWithSpecial};
@ -8,7 +8,6 @@ use hyprland::prelude::*;
use hyprland::shared::{HyprDataVec, WorkspaceType}; use hyprland::shared::{HyprDataVec, WorkspaceType};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use tokio::sync::broadcast::{channel, Receiver, Sender}; use tokio::sync::broadcast::{channel, Receiver, Sender};
use tokio::task::spawn_blocking;
use tracing::{debug, error, info}; use tracing::{debug, error, info};
pub struct EventClient { pub struct EventClient {

View file

@ -1,12 +1,11 @@
use super::{Visibility, Workspace, WorkspaceClient, WorkspaceUpdate}; use super::{Visibility, Workspace, WorkspaceClient, WorkspaceUpdate};
use crate::{await_sync, send}; use crate::{await_sync, send, spawn};
use async_once::AsyncOnce; use async_once::AsyncOnce;
use color_eyre::Report; use color_eyre::Report;
use futures_util::StreamExt; use futures_util::StreamExt;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::sync::Arc; use std::sync::Arc;
use swayipc_async::{Connection, Event, EventType, Node, WorkspaceChange, WorkspaceEvent}; use swayipc_async::{Connection, Event, EventType, Node, WorkspaceChange, WorkspaceEvent};
use tokio::spawn;
use tokio::sync::broadcast::{channel, Receiver, Sender}; use tokio::sync::broadcast::{channel, Receiver, Sender};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tracing::{info, trace}; use tracing::{info, trace};

View file

@ -1,7 +1,7 @@
use super::{ use super::{
MusicClient, PlayerState, PlayerUpdate, ProgressTick, Status, Track, TICK_INTERVAL_MS, 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 color_eyre::Result;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use mpd_client::client::{Connection, ConnectionEvent, Subsystem}; use mpd_client::client::{Connection, ConnectionEvent, Subsystem};
@ -17,7 +17,6 @@ use std::path::{Path, PathBuf};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tokio::net::{TcpStream, UnixStream}; use tokio::net::{TcpStream, UnixStream};
use tokio::spawn;
use tokio::sync::broadcast; use tokio::sync::broadcast;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tokio::time::sleep; use tokio::time::sleep;

View file

@ -1,6 +1,6 @@
use super::{MusicClient, PlayerState, PlayerUpdate, Status, Track, TICK_INTERVAL_MS}; use super::{MusicClient, PlayerState, PlayerUpdate, Status, Track, TICK_INTERVAL_MS};
use crate::clients::music::ProgressTick; use crate::clients::music::ProgressTick;
use crate::{arc_mut, lock, send}; use crate::{arc_mut, lock, send, spawn_blocking};
use color_eyre::Result; use color_eyre::Result;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use mpris::{DBusError, Event, Metadata, PlaybackStatus, Player, PlayerFinder}; use mpris::{DBusError, Event, Metadata, PlaybackStatus, Player, PlayerFinder};
@ -10,7 +10,6 @@ use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use std::{cmp, string}; use std::{cmp, string};
use tokio::sync::broadcast; use tokio::sync::broadcast;
use tokio::task::spawn_blocking;
use tracing::{debug, error, trace}; use tracing::{debug, error, trace};
lazy_static! { lazy_static! {
@ -245,7 +244,7 @@ impl MusicClient for Client {
fn set_volume_percent(&self, vol: u8) -> Result<()> { fn set_volume_percent(&self, vol: u8) -> Result<()> {
if let Some(player) = Self::get_player(self) { if let Some(player) = Self::get_player(self) {
player.set_volume(vol as f64 / 100.0)?; player.set_volume(f64::from(vol) / 100.0)?;
} else { } else {
error!("Could not find player"); error!("Could not find player");
} }

View file

@ -1,4 +1,4 @@
use crate::{arc_mut, lock, send, Ironbar}; use crate::{arc_mut, lock, send, spawn, Ironbar};
use async_once::AsyncOnce; use async_once::AsyncOnce;
use color_eyre::Report; use color_eyre::Report;
use lazy_static::lazy_static; 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::tray::StatusNotifierItem;
use system_tray::message::{NotifierItemCommand, NotifierItemMessage}; use system_tray::message::{NotifierItemCommand, NotifierItemMessage};
use system_tray::StatusNotifierWatcher; use system_tray::StatusNotifierWatcher;
use tokio::spawn;
use tokio::sync::{broadcast, mpsc}; use tokio::sync::{broadcast, mpsc};
use tracing::{debug, error, trace}; use tracing::{debug, error, trace};

View file

@ -3,7 +3,7 @@ use super::wlr_foreign_toplevel::manager::ToplevelManagerState;
use super::wlr_foreign_toplevel::ToplevelEvent; use super::wlr_foreign_toplevel::ToplevelEvent;
use super::Environment; use super::Environment;
use crate::error::ERR_CHANNEL_RECV; use crate::error::ERR_CHANNEL_RECV;
use crate::send; use crate::{send, spawn_blocking};
use cfg_if::cfg_if; use cfg_if::cfg_if;
use color_eyre::Report; use color_eyre::Report;
use smithay_client_toolkit::output::{OutputInfo, OutputState}; use smithay_client_toolkit::output::{OutputInfo, OutputState};
@ -15,7 +15,6 @@ use smithay_client_toolkit::seat::SeatState;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::mpsc; use std::sync::mpsc;
use tokio::sync::broadcast; use tokio::sync::broadcast;
use tokio::task::spawn_blocking;
use tracing::{debug, error, trace}; use tracing::{debug, error, trace};
use wayland_client::globals::registry_queue_init; use wayland_client::globals::registry_queue_init;
use wayland_client::protocol::wl_seat::WlSeat; use wayland_client::protocol::wl_seat::WlSeat;

View file

@ -1,5 +1,6 @@
use crate::dynamic_value::{dynamic_string, DynamicBool}; use crate::dynamic_value::{dynamic_string, DynamicBool};
use crate::script::{Script, ScriptInput}; use crate::script::{Script, ScriptInput};
use glib::Propagation;
use gtk::gdk::ScrollDirection; use gtk::gdk::ScrollDirection;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{EventBox, Orientation, Revealer, RevealerTransitionType}; use gtk::{EventBox, Orientation, Revealer, RevealerTransitionType};
@ -75,7 +76,7 @@ impl CommonConfig {
script.run_as_oneshot(None); script.run_as_oneshot(None);
} }
Inhibit(false) Propagation::Proceed
}); });
let scroll_up_script = self.on_scroll_up.map(Script::new_polling); let scroll_up_script = self.on_scroll_up.map(Script::new_polling);
@ -93,7 +94,7 @@ impl CommonConfig {
script.run_as_oneshot(None); script.run_as_oneshot(None);
} }
Inhibit(false) Propagation::Proceed
}); });
macro_rules! install_oneshot { macro_rules! install_oneshot {
@ -101,7 +102,7 @@ impl CommonConfig {
$option.map(Script::new_polling).map(|script| { $option.map(Script::new_polling).map(|script| {
container.$method(move |_, _| { container.$method(move |_, _| {
script.run_as_oneshot(None); script.run_as_oneshot(None);
Inhibit(false) Propagation::Proceed
}); });
}) })
}; };
@ -114,7 +115,6 @@ impl CommonConfig {
let container = container.clone(); let container = container.clone();
dynamic_string(&tooltip, move |string| { dynamic_string(&tooltip, move |string| {
container.set_tooltip_text(Some(&string)); container.set_tooltip_text(Some(&string));
Continue(true)
}); });
} }
} }
@ -136,7 +136,6 @@ impl CommonConfig {
container.show_all(); container.show_all();
} }
revealer.set_reveal_child(success); revealer.set_reveal_child(success);
Continue(true)
}); });
} }

View file

@ -1,11 +1,10 @@
use crate::script::Script; use crate::script::Script;
use crate::send; use crate::{glib_recv_mpsc, spawn, try_send};
#[cfg(feature = "ipc")] #[cfg(feature = "ipc")]
use crate::Ironbar; use crate::{send_async, Ironbar};
use cfg_if::cfg_if; use cfg_if::cfg_if;
use glib::Continue;
use serde::Deserialize; use serde::Deserialize;
use tokio::spawn; use tokio::sync::mpsc;
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
#[serde(untagged)] #[serde(untagged)]
@ -18,9 +17,9 @@ pub enum DynamicBool {
} }
impl DynamicBool { impl DynamicBool {
pub fn subscribe<F>(self, f: F) pub fn subscribe<F>(self, mut f: F)
where where
F: FnMut(bool) -> Continue + 'static, F: FnMut(bool) + 'static,
{ {
let value = match self { let value = match self {
Self::Unknown(input) => { Self::Unknown(input) => {
@ -40,16 +39,16 @@ impl DynamicBool {
_ => self, _ => 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 { spawn(async move {
match value { match value {
DynamicBool::Script(script) => { DynamicBool::Script(script) => {
script script
.run(None, |_, success| { .run(None, |_, success| {
send!(tx, success); try_send!(tx, success);
}) })
.await; .await;
} }
@ -62,7 +61,7 @@ impl DynamicBool {
while let Ok(value) = rx.recv().await { while let Ok(value) = rx.recv().await {
let has_value = value.map(|s| is_truthy(&s)).unwrap_or_default(); let has_value = value.map(|s| is_truthy(&s)).unwrap_or_default();
send!(tx, has_value); send_async!(tx, has_value);
} }
} }
DynamicBool::Unknown(_) => unreachable!(), 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")] #[cfg(feature = "ipc")]
fn is_truthy(string: &str) -> bool { fn is_truthy(string: &str) -> bool {
!(string.is_empty() || string == "0" || string == "false") !(string.is_empty() || string == "0" || string == "false")

View file

@ -1,9 +1,8 @@
use crate::script::{OutputStream, Script}; use crate::script::{OutputStream, Script};
#[cfg(feature = "ipc")] #[cfg(feature = "ipc")]
use crate::Ironbar; use crate::Ironbar;
use crate::{arc_mut, lock, send}; use crate::{arc_mut, glib_recv_mpsc, lock, spawn, try_send};
use gtk::prelude::*; use tokio::sync::mpsc;
use tokio::spawn;
/// A segment of a dynamic string, /// A segment of a dynamic string,
/// containing either a static string /// containing either a static string
@ -24,17 +23,16 @@ enum DynamicStringSegment {
/// ```rs /// ```rs
/// dynamic_string(&text, move |string| { /// dynamic_string(&text, move |string| {
/// label.set_markup(&string); /// label.set_markup(&string);
/// Continue(true)
/// }); /// });
/// ``` /// ```
pub fn dynamic_string<F>(input: &str, f: F) pub fn dynamic_string<F>(input: &str, mut f: F)
where where
F: FnMut(String) -> Continue + 'static, F: FnMut(String) + 'static,
{ {
let tokens = parse_input(input); let tokens = parse_input(input);
let label_parts = arc_mut!(vec![]); 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() { for (i, segment) in tokens.into_iter().enumerate() {
match segment { match segment {
@ -57,7 +55,7 @@ where
let _: String = std::mem::replace(&mut label_parts[i], out); let _: String = std::mem::replace(&mut label_parts[i], out);
let string = label_parts.join(""); let string = label_parts.join("");
send!(tx, string); try_send!(tx, string);
} }
}) })
.await; .await;
@ -82,7 +80,7 @@ where
let _: String = std::mem::replace(&mut label_parts[i], value); let _: String = std::mem::replace(&mut label_parts[i], value);
let string = label_parts.join(""); 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 // initialize
{ {
let label_parts = lock!(label_parts).join(""); let label_parts = lock!(label_parts).join("");
send!(tx, label_parts); try_send!(tx, label_parts);
} }
} }

View file

@ -1,4 +1,6 @@
use crate::desktop_file::get_desktop_icon_name; 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 cfg_if::cfg_if;
use color_eyre::{Help, Report, Result}; use color_eyre::{Help, Report, Result};
use gtk::cairo::Surface; use gtk::cairo::Surface;
@ -7,13 +9,13 @@ use gtk::gdk_pixbuf::Pixbuf;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{IconLookupFlags, IconTheme}; use gtk::{IconLookupFlags, IconTheme};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[cfg(feature = "http")]
use tokio::sync::mpsc;
use tracing::warn; use tracing::warn;
cfg_if!( cfg_if!(
if #[cfg(feature = "http")] { if #[cfg(feature = "http")] {
use crate::send;
use gtk::gio::{Cancellable, MemoryInputStream}; use gtk::gio::{Cancellable, MemoryInputStream};
use tokio::spawn;
use tracing::error; use tracing::error;
} }
); );
@ -143,18 +145,18 @@ impl<'a> ImageProvider<'a> {
#[cfg(feature = "http")] #[cfg(feature = "http")]
if let ImageLocation::Remote(url) = &self.location { if let ImageLocation::Remote(url) = &self.location {
let url = url.clone(); let url = url.clone();
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); let (tx, mut rx) = mpsc::channel(64);
spawn(async move { spawn(async move {
let bytes = Self::get_bytes_from_http(url).await; let bytes = Self::get_bytes_from_http(url).await;
if let Ok(bytes) = bytes { if let Ok(bytes) = bytes {
send!(tx, bytes); send_async!(tx, bytes);
} }
}); });
{ {
let size = self.size; let size = self.size;
rx.attach(None, move |bytes| { glib_recv_mpsc!(rx, bytes => {
let stream = MemoryInputStream::from_bytes(&bytes); let stream = MemoryInputStream::from_bytes(&bytes);
let scale = image.scale_factor(); let scale = image.scale_factor();
@ -175,8 +177,6 @@ impl<'a> ImageProvider<'a> {
Err(err) => error!("{err:?}"), Err(err) => error!("{err:?}"),
_ => {} _ => {}
} }
Continue(false)
}); });
} }
} else { } else {

View file

@ -3,20 +3,17 @@ use std::path::Path;
use std::rc::Rc; use std::rc::Rc;
use color_eyre::{Report, Result}; use color_eyre::{Report, Result};
use glib::Continue;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::Application; use gtk::Application;
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{UnixListener, UnixStream}; use tokio::net::{UnixListener, UnixStream};
use tokio::spawn;
use tokio::sync::mpsc::{self, Receiver, Sender}; use tokio::sync::mpsc::{self, Receiver, Sender};
use tracing::{debug, error, info, warn}; use tracing::{debug, error, info, warn};
use crate::bridge_channel::BridgeChannel;
use crate::ipc::{Command, Response}; use crate::ipc::{Command, Response};
use crate::modules::PopupButton; use crate::modules::PopupButton;
use crate::style::load_css; 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; use super::Ipc;
@ -25,8 +22,7 @@ impl Ipc {
/// ///
/// Once started, the server will begin accepting connections. /// Once started, the server will begin accepting connections.
pub fn start(&self, application: &Application, ironbar: Rc<Ironbar>) { pub fn start(&self, application: &Application, ironbar: Rc<Ironbar>) {
let bridge = BridgeChannel::<Command>::new(); let (cmd_tx, mut cmd_rx) = mpsc::channel(32);
let cmd_tx = bridge.create_sender();
let (res_tx, mut res_rx) = mpsc::channel(32); let (res_tx, mut res_rx) = mpsc::channel(32);
let path = self.path.clone(); let path = self.path.clone();
@ -68,10 +64,9 @@ impl Ipc {
}); });
let application = application.clone(); let application = application.clone();
bridge.recv(move |command| { glib_recv_mpsc!(cmd_rx, command => {
let res = Self::handle_command(command, &application, ironbar.clone()); let res = Self::handle_command(command, &application, &ironbar);
try_send!(res_tx, res); 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. /// Takes an input command, runs it and returns with the appropriate response.
/// ///
/// This runs on the main thread, allowing commands to interact with GTK. /// This runs on the main thread, allowing commands to interact with GTK.
fn handle_command( fn handle_command(command: Command, application: &Application, ironbar: &Ironbar) -> Response {
command: Command,
application: &Application,
ironbar: Rc<Ironbar>,
) -> Response {
match command { match command {
Command::Inspect => { Command::Inspect => {
gtk::Window::set_interactive_debugging(true); gtk::Window::set_interactive_debugging(true);

View file

@ -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`. /// Locks a `Mutex`.
/// Panics if the `Mutex` cannot be locked. /// Panics if the `Mutex` cannot be locked.
/// ///

View file

@ -7,9 +7,9 @@ use std::path::PathBuf;
use std::process::exit; use std::process::exit;
use std::rc::Rc; use std::rc::Rc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::mpsc;
#[cfg(feature = "ipc")] #[cfg(feature = "ipc")]
use std::sync::{Arc, RwLock}; use std::sync::RwLock;
use std::sync::{mpsc, Arc};
use cfg_if::cfg_if; use cfg_if::cfg_if;
#[cfg(feature = "cli")] #[cfg(feature = "cli")]
@ -21,8 +21,8 @@ use glib::PropertySet;
use gtk::gdk::Display; use gtk::gdk::Display;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::Application; use gtk::Application;
use tokio::runtime::Handle; use tokio::runtime::{Handle, Runtime};
use tokio::task::{block_in_place, spawn_blocking}; use tokio::task::{block_in_place, JoinHandle};
use tracing::{debug, error, info, warn}; use tracing::{debug, error, info, warn};
use universal_config::ConfigLoader; use universal_config::ConfigLoader;
@ -36,7 +36,6 @@ use crate::ironvar::VariableManager;
use crate::style::load_css; use crate::style::load_css;
mod bar; mod bar;
mod bridge_channel;
#[cfg(feature = "cli")] #[cfg(feature = "cli")]
mod cli; mod cli;
mod clients; mod clients;
@ -60,13 +59,12 @@ mod style;
const GTK_APP_ID: &str = "dev.jstanger.ironbar"; const GTK_APP_ID: &str = "dev.jstanger.ironbar";
const VERSION: &str = env!("CARGO_PKG_VERSION"); const VERSION: &str = env!("CARGO_PKG_VERSION");
#[tokio::main] fn main() {
async fn main() {
let _guard = logging::install_logging(); let _guard = logging::install_logging();
cfg_if! { cfg_if! {
if #[cfg(feature = "cli")] { if #[cfg(feature = "cli")] {
run_with_args().await; run_with_args();
} else { } else {
start_ironbar(); start_ironbar();
} }
@ -74,16 +72,19 @@ async fn main() {
} }
#[cfg(feature = "cli")] #[cfg(feature = "cli")]
async fn run_with_args() { fn run_with_args() {
let args = cli::Args::parse(); let args = cli::Args::parse();
match args.command { match args.command {
Some(command) => { Some(command) => {
let ipc = ipc::Ipc::new(); let rt = create_runtime();
match ipc.send(command).await { rt.block_on(async move {
Ok(res) => cli::handle_response(res), let ipc = ipc::Ipc::new();
Err(err) => error!("{err:?}"), match ipc.send(command).await {
}; Ok(res) => cli::handle_response(res),
Err(err) => error!("{err:?}"),
};
});
} }
None => start_ironbar(), None => start_ironbar(),
} }
@ -91,6 +92,10 @@ async fn run_with_args() {
static COUNTER: AtomicUsize = AtomicUsize::new(1); static COUNTER: AtomicUsize = AtomicUsize::new(1);
lazy_static::lazy_static! {
static ref RUNTIME: Arc<Runtime> = Arc::new(create_runtime());
}
#[cfg(feature = "ipc")] #[cfg(feature = "ipc")]
lazy_static::lazy_static! { lazy_static::lazy_static! {
static ref VARIABLE_MANAGER: Arc<RwLock<VariableManager>> = arc_rw!(VariableManager::new()); static ref VARIABLE_MANAGER: Arc<RwLock<VariableManager>> = arc_rw!(VariableManager::new());
@ -184,6 +189,12 @@ impl Ironbar {
app.run_with_args(&Vec::<&str>::new()); app.run_with_args(&Vec::<&str>::new());
} }
/// Gets the current Tokio runtime.
#[must_use]
pub fn runtime() -> Arc<Runtime> {
RUNTIME.clone()
}
/// Gets a `usize` ID value that is unique to the entire Ironbar instance. /// 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. /// This is just a static `AtomicUsize` that increments every time this function is called.
pub fn unique_id() -> usize { pub fn unique_id() -> usize {
@ -323,6 +334,31 @@ fn create_bars(app: &Application, display: &Display, config: &Config) -> Result<
Ok(all_bars) 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: F) -> JoinHandle<F::Output>
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, R>(f: F) -> JoinHandle<R>
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
Ironbar::runtime().spawn_blocking(f)
}
/// Blocks on a `Future` until it resolves. /// Blocks on a `Future` until it resolves.
/// ///
/// This is not an `async` operation /// This is not an `async` operation

View file

@ -5,7 +5,8 @@ use crate::image::new_icon_button;
use crate::modules::{ use crate::modules::{
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext, 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::gdk_pixbuf::Pixbuf;
use gtk::gio::{Cancellable, MemoryInputStream}; use gtk::gio::{Cancellable, MemoryInputStream};
use gtk::prelude::*; use gtk::prelude::*;
@ -13,8 +14,7 @@ use gtk::{Button, EventBox, Image, Label, Orientation, RadioButton, Widget};
use serde::Deserialize; use serde::Deserialize;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tokio::spawn; use tokio::sync::{broadcast, mpsc};
use tokio::sync::mpsc::{Receiver, Sender};
use tracing::{debug, error}; use tracing::{debug, error};
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
@ -72,8 +72,8 @@ impl Module<Button> for ClipboardModule {
fn spawn_controller( fn spawn_controller(
&self, &self,
_info: &ModuleInfo, _info: &ModuleInfo,
tx: Sender<ModuleUpdateEvent<Self::SendMessage>>, tx: mpsc::Sender<ModuleUpdateEvent<Self::SendMessage>>,
mut rx: Receiver<Self::ReceiveMessage>, mut rx: mpsc::Receiver<Self::ReceiveMessage>,
) -> color_eyre::Result<()> { ) -> color_eyre::Result<()> {
let max_items = self.max_items; let max_items = self.max_items;
@ -129,19 +129,14 @@ impl Module<Button> for ClipboardModule {
let button = new_icon_button(&self.icon, info.icon_theme, self.icon_size); let button = new_icon_button(&self.icon, info.icon_theme, self.icon_size);
button.style_context().add_class("btn"); button.style_context().add_class("btn");
let tx = context.tx.clone();
button.connect_clicked(move |button| { button.connect_clicked(move |button| {
try_send!( try_send!(tx, ModuleUpdateEvent::TogglePopup(button.popup_id()));
context.tx,
ModuleUpdateEvent::TogglePopup(button.popup_id())
);
}); });
// we need to bind to the receiver as the channel does not open let rx = context.subscribe();
// until the popup is first opened.
context.widget_rx.attach(None, |_| Continue(true));
let popup = self let popup = self
.into_popup(context.controller_tx, context.popup_rx, info) .into_popup(context.controller_tx, rx, info)
.into_popup_parts(vec![&button]); .into_popup_parts(vec![&button]);
Ok(ModuleParts::new(button, popup)) Ok(ModuleParts::new(button, popup))
@ -149,8 +144,8 @@ impl Module<Button> for ClipboardModule {
fn into_popup( fn into_popup(
self, self,
tx: Sender<Self::ReceiveMessage>, tx: mpsc::Sender<Self::ReceiveMessage>,
rx: glib::Receiver<Self::SendMessage>, mut rx: broadcast::Receiver<Self::SendMessage>,
_info: &ModuleInfo, _info: &ModuleInfo,
) -> Option<gtk::Box> ) -> Option<gtk::Box>
where where
@ -168,7 +163,7 @@ impl Module<Button> for ClipboardModule {
{ {
let hidden_option = hidden_option.clone(); let hidden_option = hidden_option.clone();
rx.attach(None, move |event| { glib_recv!(rx, event => {
match event { match event {
ControllerEvent::Add(id, item) => { ControllerEvent::Add(id, item) => {
debug!("Adding new value with ID {}", id); debug!("Adding new value with ID {}", id);
@ -234,7 +229,7 @@ impl Module<Button> for ClipboardModule {
try_send!(tx, UIEvent::Copy(id)); try_send!(tx, UIEvent::Copy(id));
} }
Inhibit(true) Propagation::Stop
}, },
); );
} }
@ -293,8 +288,6 @@ impl Module<Button> for ClipboardModule {
hidden_option.set_active(true); hidden_option.set_active(true);
} }
} }
Continue(true)
}); });
} }

View file

@ -2,12 +2,10 @@ use std::env;
use chrono::{DateTime, Local, Locale}; use chrono::{DateTime, Local, Locale};
use color_eyre::Result; use color_eyre::Result;
use glib::Continue;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Align, Button, Calendar, Label, Orientation}; use gtk::{Align, Button, Calendar, Label, Orientation};
use serde::Deserialize; use serde::Deserialize;
use tokio::spawn; use tokio::sync::{broadcast, mpsc};
use tokio::sync::mpsc;
use tokio::time::sleep; use tokio::time::sleep;
use crate::config::CommonConfig; use crate::config::CommonConfig;
@ -15,7 +13,7 @@ use crate::gtk_helpers::IronbarGtkExt;
use crate::modules::{ use crate::modules::{
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext, Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext,
}; };
use crate::{send_async, try_send}; use crate::{glib_recv, send_async, spawn, try_send};
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
pub struct ClockModule { pub struct ClockModule {
@ -104,24 +102,22 @@ impl Module<Button> for ClockModule {
label.set_angle(info.bar_position.get_angle()); label.set_angle(info.bar_position.get_angle());
button.add(&label); button.add(&label);
let tx = context.tx.clone();
button.connect_clicked(move |button| { button.connect_clicked(move |button| {
try_send!( try_send!(tx, ModuleUpdateEvent::TogglePopup(button.popup_id()));
context.tx,
ModuleUpdateEvent::TogglePopup(button.popup_id())
);
}); });
let format = self.format.clone(); let format = self.format.clone();
let locale = Locale::try_from(self.locale.as_str()).unwrap_or(Locale::POSIX); let locale = Locale::try_from(self.locale.as_str()).unwrap_or(Locale::POSIX);
context.widget_rx.attach(None, move |date| { let mut rx = context.subscribe();
glib_recv!(rx, date => {
let date_string = format!("{}", date.format_localized(&format, locale)); let date_string = format!("{}", date.format_localized(&format, locale));
label.set_label(&date_string); label.set_label(&date_string);
Continue(true)
}); });
let popup = self let popup = self
.into_popup(context.controller_tx, context.popup_rx, info) .into_popup(context.controller_tx.clone(), context.subscribe(), info)
.into_popup_parts(vec![&button]); .into_popup_parts(vec![&button]);
Ok(ModuleParts::new(button, popup)) Ok(ModuleParts::new(button, popup))
@ -130,7 +126,7 @@ impl Module<Button> for ClockModule {
fn into_popup( fn into_popup(
self, self,
_tx: mpsc::Sender<Self::ReceiveMessage>, _tx: mpsc::Sender<Self::ReceiveMessage>,
rx: glib::Receiver<Self::SendMessage>, mut rx: broadcast::Receiver<Self::SendMessage>,
_info: &ModuleInfo, _info: &ModuleInfo,
) -> Option<gtk::Box> { ) -> Option<gtk::Box> {
let container = gtk::Box::new(Orientation::Vertical, 0); let container = gtk::Box::new(Orientation::Vertical, 0);
@ -147,10 +143,9 @@ impl Module<Button> for ClockModule {
let format = self.format_popup; let format = self.format_popup;
let locale = Locale::try_from(self.locale.as_str()).unwrap_or(Locale::POSIX); let locale = Locale::try_from(self.locale.as_str()).unwrap_or(Locale::POSIX);
rx.attach(None, move |date| { glib_recv!(rx, date => {
let date_string = format!("{}", date.format_localized(&format, locale)); let date_string = format!("{}", date.format_localized(&format, locale));
clock.set_label(&date_string); clock.set_label(&date_string);
Continue(true)
}); });
container.show_all(); container.show_all();

View file

@ -30,7 +30,6 @@ impl CustomWidget for ButtonWidget {
dynamic_string(&text, move |string| { dynamic_string(&text, move |string| {
label.set_markup(&string); label.set_markup(&string);
Continue(true)
}); });
} }

View file

@ -34,8 +34,6 @@ impl CustomWidget for ImageWidget {
dynamic_string(&self.src, move |src| { dynamic_string(&self.src, move |src| {
ImageProvider::parse(&src, &icon_theme, false, self.size) ImageProvider::parse(&src, &icon_theme, false, self.size)
.map(|image| image.load_into_image(gtk_image.clone())); .map(|image| image.load_into_image(gtk_image.clone()));
Continue(true)
}); });
} }

View file

@ -26,7 +26,6 @@ impl CustomWidget for LabelWidget {
let label = label.clone(); let label = label.clone();
dynamic_string(&self.label, move |string| { dynamic_string(&self.label, move |string| {
label.set_markup(&string); label.set_markup(&string);
Continue(true)
}); });
} }

View file

@ -16,15 +16,14 @@ use crate::modules::{
wrap_widget, Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext, wrap_widget, Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext,
}; };
use crate::script::Script; use crate::script::Script;
use crate::send_async; use crate::{send_async, spawn};
use color_eyre::{Report, Result}; use color_eyre::{Report, Result};
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Button, IconTheme, Orientation}; use gtk::{Button, IconTheme, Orientation};
use serde::Deserialize; use serde::Deserialize;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use tokio::spawn; use tokio::sync::{broadcast, mpsc};
use tokio::sync::mpsc::{Receiver, Sender};
use tracing::{debug, error}; use tracing::{debug, error};
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
@ -59,7 +58,7 @@ pub enum Widget {
#[derive(Clone)] #[derive(Clone)]
struct CustomWidgetContext<'a> { struct CustomWidgetContext<'a> {
tx: &'a Sender<ExecEvent>, tx: &'a mpsc::Sender<ExecEvent>,
bar_orientation: Orientation, bar_orientation: Orientation,
icon_theme: &'a IconTheme, icon_theme: &'a IconTheme,
popup_buttons: Rc<RefCell<Vec<Button>>>, popup_buttons: Rc<RefCell<Vec<Button>>>,
@ -159,8 +158,8 @@ impl Module<gtk::Box> for CustomModule {
fn spawn_controller( fn spawn_controller(
&self, &self,
_info: &ModuleInfo, _info: &ModuleInfo,
tx: Sender<ModuleUpdateEvent<Self::SendMessage>>, tx: mpsc::Sender<ModuleUpdateEvent<Self::SendMessage>>,
mut rx: Receiver<Self::ReceiveMessage>, mut rx: mpsc::Receiver<Self::ReceiveMessage>,
) -> Result<()> { ) -> Result<()> {
spawn(async move { spawn(async move {
while let Some(event) = rx.recv().await { while let Some(event) = rx.recv().await {
@ -213,7 +212,7 @@ impl Module<gtk::Box> for CustomModule {
}); });
let popup = self let popup = self
.into_popup(context.controller_tx, context.popup_rx, info) .into_popup(context.controller_tx.clone(), context.subscribe(), info)
.into_popup_parts_owned(popup_buttons.take()); .into_popup_parts_owned(popup_buttons.take());
Ok(ModuleParts { Ok(ModuleParts {
@ -224,8 +223,8 @@ impl Module<gtk::Box> for CustomModule {
fn into_popup( fn into_popup(
self, self,
tx: Sender<Self::ReceiveMessage>, tx: mpsc::Sender<Self::ReceiveMessage>,
_rx: glib::Receiver<Self::SendMessage>, _rx: broadcast::Receiver<Self::SendMessage>,
info: &ModuleInfo, info: &ModuleInfo,
) -> Option<gtk::Box> ) -> Option<gtk::Box>
where where

View file

@ -1,13 +1,13 @@
use gtk::prelude::*; use gtk::prelude::*;
use gtk::ProgressBar; use gtk::ProgressBar;
use serde::Deserialize; use serde::Deserialize;
use tokio::spawn; use tokio::sync::mpsc;
use tracing::error; use tracing::error;
use crate::dynamic_value::dynamic_string; use crate::dynamic_value::dynamic_string;
use crate::modules::custom::set_length; use crate::modules::custom::set_length;
use crate::script::{OutputStream, Script, ScriptInput}; use crate::script::{OutputStream, Script, ScriptInput};
use crate::{build, send}; use crate::{build, glib_recv_mpsc, spawn, try_send};
use super::{try_get_orientation, CustomWidget, CustomWidgetContext}; use super::{try_get_orientation, CustomWidget, CustomWidgetContext};
@ -47,13 +47,13 @@ impl CustomWidget for ProgressWidget {
let script = Script::from(value); let script = Script::from(value);
let progress = progress.clone(); let progress = progress.clone();
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); let (tx, mut rx) = mpsc::channel(128);
spawn(async move { spawn(async move {
script script
.run(None, move |stream, _success| match stream { .run(None, move |stream, _success| match stream {
OutputStream::Stdout(out) => match out.parse::<f64>() { OutputStream::Stdout(out) => match out.parse::<f64>() {
Ok(value) => send!(tx, value), Ok(value) => try_send!(tx, value),
Err(err) => error!("{err:?}"), Err(err) => error!("{err:?}"),
}, },
OutputStream::Stderr(err) => error!("{err:?}"), OutputStream::Stderr(err) => error!("{err:?}"),
@ -61,10 +61,7 @@ impl CustomWidget for ProgressWidget {
.await; .await;
}); });
rx.attach(None, move |value| { glib_recv_mpsc!(rx, value => progress.set_fraction(value / self.max));
progress.set_fraction(value / self.max);
Continue(true)
});
} }
if let Some(text) = self.label { if let Some(text) = self.label {
@ -73,7 +70,6 @@ impl CustomWidget for ProgressWidget {
dynamic_string(&text, move |string| { dynamic_string(&text, move |string| {
progress.set_text(Some(&string)); progress.set_text(Some(&string));
Continue(true)
}); });
} }

View file

@ -1,15 +1,16 @@
use glib::Propagation;
use std::cell::Cell; use std::cell::Cell;
use std::ops::Neg; use std::ops::Neg;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::Scale; use gtk::Scale;
use serde::Deserialize; use serde::Deserialize;
use tokio::spawn; use tokio::sync::mpsc;
use tracing::error; use tracing::error;
use crate::modules::custom::set_length; use crate::modules::custom::set_length;
use crate::script::{OutputStream, Script, ScriptInput}; use crate::script::{OutputStream, Script, ScriptInput};
use crate::{build, send, try_send}; use crate::{build, glib_recv_mpsc, spawn, try_send};
use super::{try_get_orientation, CustomWidget, CustomWidgetContext, ExecEvent}; use super::{try_get_orientation, CustomWidget, CustomWidgetContext, ExecEvent};
@ -77,7 +78,7 @@ impl CustomWidget for SliderWidget {
}; };
scale.set_value(value + delta); scale.set_value(value + delta);
Inhibit(false) Propagation::Proceed
}); });
scale.connect_change_value(move |_, _, val| { scale.connect_change_value(move |_, _, val| {
@ -97,7 +98,7 @@ impl CustomWidget for SliderWidget {
prev_value.set(val); prev_value.set(val);
} }
Inhibit(false) Propagation::Proceed
}); });
} }
@ -105,13 +106,13 @@ impl CustomWidget for SliderWidget {
let script = Script::from(value); let script = Script::from(value);
let scale = scale.clone(); let scale = scale.clone();
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); let (tx, mut rx) = mpsc::channel(128);
spawn(async move { spawn(async move {
script script
.run(None, move |stream, _success| match stream { .run(None, move |stream, _success| match stream {
OutputStream::Stdout(out) => match out.parse() { OutputStream::Stdout(out) => match out.parse() {
Ok(value) => send!(tx, value), Ok(value) => try_send!(tx, value),
Err(err) => error!("{err:?}"), Err(err) => error!("{err:?}"),
}, },
OutputStream::Stderr(err) => error!("{err:?}"), OutputStream::Stderr(err) => error!("{err:?}"),
@ -119,10 +120,7 @@ impl CustomWidget for SliderWidget {
.await; .await;
}); });
rx.attach(None, move |value| { glib_recv_mpsc!(rx, value => scale.set_value(value));
scale.set_value(value);
Continue(true)
});
} }
scale scale

View file

@ -3,13 +3,11 @@ use crate::config::{CommonConfig, TruncateMode};
use crate::gtk_helpers::IronbarGtkExt; use crate::gtk_helpers::IronbarGtkExt;
use crate::image::ImageProvider; use crate::image::ImageProvider;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
use crate::{lock, send_async, try_send}; use crate::{glib_recv, lock, send_async, spawn, try_send};
use color_eyre::Result; use color_eyre::Result;
use glib::Continue;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::Label; use gtk::Label;
use serde::Deserialize; use serde::Deserialize;
use tokio::spawn;
use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::mpsc::{Receiver, Sender};
use tracing::debug; use tracing::debug;
@ -141,7 +139,7 @@ impl Module<gtk::Box> for FocusedModule {
{ {
let icon_theme = icon_theme.clone(); let icon_theme = icon_theme.clone();
context.widget_rx.attach(None, move |data| { glib_recv!(context.subscribe(), data => {
if let Some((name, id)) = data { if let Some((name, id)) = data {
if self.show_icon { if self.show_icon {
match ImageProvider::parse(&id, &icon_theme, true, self.icon_size) match ImageProvider::parse(&id, &icon_theme, true, self.icon_size)
@ -160,8 +158,6 @@ impl Module<gtk::Box> for FocusedModule {
icon.hide(); icon.hide();
label.hide(); label.hide();
} }
Continue(true)
}); });
} }

View file

@ -1,9 +1,8 @@
use crate::config::CommonConfig; use crate::config::CommonConfig;
use crate::dynamic_value::dynamic_string; use crate::dynamic_value::dynamic_string;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
use crate::try_send; use crate::{glib_recv, try_send};
use color_eyre::Result; use color_eyre::Result;
use glib::Continue;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::Label; use gtk::Label;
use serde::Deserialize; use serde::Deserialize;
@ -42,7 +41,6 @@ impl Module<Label> for LabelModule {
) -> Result<()> { ) -> Result<()> {
dynamic_string(&self.label, move |string| { dynamic_string(&self.label, move |string| {
try_send!(tx, ModuleUpdateEvent::Update(string)); try_send!(tx, ModuleUpdateEvent::Update(string));
Continue(true)
}); });
Ok(()) Ok(())
@ -58,10 +56,7 @@ impl Module<Label> for LabelModule {
{ {
let label = label.clone(); let label = label.clone();
context.widget_rx.attach(None, move |string| { glib_recv!(context.subscribe(), string => label.set_markup(&string));
label.set_markup(&string);
Continue(true)
});
} }
Ok(ModuleParts { Ok(ModuleParts {

View file

@ -7,6 +7,7 @@ use crate::modules::launcher::{ItemEvent, LauncherUpdate};
use crate::modules::ModuleUpdateEvent; use crate::modules::ModuleUpdateEvent;
use crate::{read_lock, try_send}; use crate::{read_lock, try_send};
use color_eyre::{Report, Result}; use color_eyre::{Report, Result};
use glib::Propagation;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Button, IconTheme}; use gtk::{Button, IconTheme};
use indexmap::IndexMap; use indexmap::IndexMap;
@ -258,7 +259,7 @@ impl ItemButton {
try_send!(tx, ModuleUpdateEvent::ClosePopup); try_send!(tx, ModuleUpdateEvent::ClosePopup);
} }
Inhibit(false) Propagation::Proceed
}); });
} }
@ -273,9 +274,9 @@ impl ItemButton {
let (x, y) = ev.position(); let (x, y) = ev.position();
let close = match bar_position { let close = match bar_position {
BarPosition::Top => y + THRESHOLD < alloc.height() as f64, BarPosition::Top => y + THRESHOLD < f64::from(alloc.height()),
BarPosition::Bottom => y > THRESHOLD, BarPosition::Bottom => y > THRESHOLD,
BarPosition::Left => x + THRESHOLD < alloc.width() as f64, BarPosition::Left => x + THRESHOLD < f64::from(alloc.width()),
BarPosition::Right => x > THRESHOLD, BarPosition::Right => x > THRESHOLD,
}; };
@ -283,7 +284,7 @@ impl ItemButton {
try_send!(tx, ModuleUpdateEvent::ClosePopup); try_send!(tx, ModuleUpdateEvent::ClosePopup);
} }
Inhibit(false) Propagation::Proceed
}); });
} }

View file

@ -10,17 +10,15 @@ use crate::modules::launcher::item::AppearanceOptions;
use crate::modules::{ use crate::modules::{
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext, Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext,
}; };
use crate::{arc_mut, lock, send_async, try_send, write_lock}; use crate::{arc_mut, glib_recv, lock, send_async, spawn, try_send, write_lock};
use color_eyre::{Help, Report}; use color_eyre::{Help, Report};
use glib::Continue;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Button, Orientation}; use gtk::{Button, Orientation};
use indexmap::IndexMap; use indexmap::IndexMap;
use serde::Deserialize; use serde::Deserialize;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use std::sync::Arc; use std::sync::Arc;
use tokio::spawn; use tokio::sync::{broadcast, mpsc};
use tokio::sync::mpsc::{Receiver, Sender};
use tracing::{debug, error, trace}; use tracing::{debug, error, trace};
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
@ -92,8 +90,8 @@ impl Module<gtk::Box> for LauncherModule {
fn spawn_controller( fn spawn_controller(
&self, &self,
_info: &ModuleInfo, _info: &ModuleInfo,
tx: Sender<ModuleUpdateEvent<Self::SendMessage>>, tx: mpsc::Sender<ModuleUpdateEvent<Self::SendMessage>>,
mut rx: Receiver<Self::ReceiveMessage>, mut rx: mpsc::Receiver<Self::ReceiveMessage>,
) -> crate::Result<()> { ) -> crate::Result<()> {
let items = self let items = self
.favorites .favorites
@ -338,7 +336,9 @@ impl Module<gtk::Box> for LauncherModule {
let mut buttons = IndexMap::<String, ItemButton>::new(); let mut buttons = IndexMap::<String, ItemButton>::new();
context.widget_rx.attach(None, move |event| { let tx = context.tx.clone();
let mut rx = context.subscribe();
glib_recv!(rx, event => {
match event { match event {
LauncherUpdate::AddItem(item) => { LauncherUpdate::AddItem(item) => {
debug!("Adding item with id {}", item.app_id); debug!("Adding item with id {}", item.app_id);
@ -351,7 +351,7 @@ impl Module<gtk::Box> for LauncherModule {
appearance_options, appearance_options,
&icon_theme, &icon_theme,
bar_position, bar_position,
&context.tx, &tx,
&controller_tx, &controller_tx,
); );
@ -411,13 +411,12 @@ impl Module<gtk::Box> for LauncherModule {
} }
LauncherUpdate::Hover(_) => {} LauncherUpdate::Hover(_) => {}
}; };
Continue(true)
}); });
} }
let rx = context.subscribe();
let popup = self let popup = self
.into_popup(context.controller_tx, context.popup_rx, info) .into_popup(context.controller_tx, rx, info)
.into_popup_parts(vec![]); // since item buttons are dynamic, they pass their geometry directly .into_popup_parts(vec![]); // since item buttons are dynamic, they pass their geometry directly
Ok(ModuleParts { Ok(ModuleParts {
@ -428,8 +427,8 @@ impl Module<gtk::Box> for LauncherModule {
fn into_popup( fn into_popup(
self, self,
controller_tx: Sender<Self::ReceiveMessage>, controller_tx: mpsc::Sender<Self::ReceiveMessage>,
rx: glib::Receiver<Self::SendMessage>, mut rx: broadcast::Receiver<Self::SendMessage>,
_info: &ModuleInfo, _info: &ModuleInfo,
) -> Option<gtk::Box> { ) -> Option<gtk::Box> {
const MAX_WIDTH: i32 = 250; const MAX_WIDTH: i32 = 250;
@ -445,7 +444,7 @@ impl Module<gtk::Box> for LauncherModule {
{ {
let container = container.clone(); let container = container.clone();
rx.attach(None, move |event| { glib_recv!(rx, event => {
match event { match event {
LauncherUpdate::AddItem(item) => { LauncherUpdate::AddItem(item) => {
let app_id = item.app_id.clone(); let app_id = item.app_id.clone();
@ -532,8 +531,6 @@ impl Module<gtk::Box> for LauncherModule {
} }
_ => {} _ => {}
} }
Continue(true)
}); });
} }

View file

@ -1,4 +1,5 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt::Debug;
use std::rc::Rc; use std::rc::Rc;
use color_eyre::Result; use color_eyre::Result;
@ -6,14 +7,13 @@ use glib::IsA;
use gtk::gdk::{EventMask, Monitor}; use gtk::gdk::{EventMask, Monitor};
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Application, Button, EventBox, IconTheme, Orientation, Revealer, Widget}; use gtk::{Application, Button, EventBox, IconTheme, Orientation, Revealer, Widget};
use tokio::sync::mpsc; use tokio::sync::{broadcast, mpsc};
use tracing::debug; use tracing::debug;
use crate::bridge_channel::BridgeChannel;
use crate::config::{BarPosition, CommonConfig, TransitionType}; use crate::config::{BarPosition, CommonConfig, TransitionType};
use crate::gtk_helpers::{IronbarGtkExt, WidgetGeometry}; use crate::gtk_helpers::{IronbarGtkExt, WidgetGeometry};
use crate::popup::Popup; use crate::popup::Popup;
use crate::send; use crate::{glib_recv_mpsc, send};
#[cfg(feature = "clipboard")] #[cfg(feature = "clipboard")]
pub mod clipboard; pub mod clipboard;
@ -56,8 +56,8 @@ pub struct ModuleInfo<'a> {
pub icon_theme: &'a IconTheme, pub icon_theme: &'a IconTheme,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum ModuleUpdateEvent<T> { pub enum ModuleUpdateEvent<T: Clone> {
/// Sends an update to the module UI. /// Sends an update to the module UI.
Update(T), Update(T),
/// Toggles the open state of the popup. /// Toggles the open state of the popup.
@ -71,12 +71,25 @@ pub enum ModuleUpdateEvent<T> {
ClosePopup, ClosePopup,
} }
pub struct WidgetContext<TSend, TReceive> { pub struct WidgetContext<TSend, TReceive>
where
TSend: Clone,
{
pub id: usize, pub id: usize,
pub tx: mpsc::Sender<ModuleUpdateEvent<TSend>>, pub tx: mpsc::Sender<ModuleUpdateEvent<TSend>>,
pub update_tx: broadcast::Sender<TSend>,
pub controller_tx: mpsc::Sender<TReceive>, pub controller_tx: mpsc::Sender<TReceive>,
pub widget_rx: glib::Receiver<TSend>,
pub popup_rx: glib::Receiver<TSend>, _update_rx: broadcast::Receiver<TSend>,
}
impl<TSend, TReceive> WidgetContext<TSend, TReceive>
where
TSend: Clone,
{
pub fn subscribe(&self) -> broadcast::Receiver<TSend> {
self.update_tx.subscribe()
}
} }
pub struct ModuleParts<W: IsA<Widget>> { pub struct ModuleParts<W: IsA<Widget>> {
@ -151,18 +164,22 @@ where
info: &ModuleInfo, info: &ModuleInfo,
tx: mpsc::Sender<ModuleUpdateEvent<Self::SendMessage>>, tx: mpsc::Sender<ModuleUpdateEvent<Self::SendMessage>>,
rx: mpsc::Receiver<Self::ReceiveMessage>, rx: mpsc::Receiver<Self::ReceiveMessage>,
) -> Result<()>; ) -> Result<()>
where
<Self as Module<W>>::SendMessage: Clone;
fn into_widget( fn into_widget(
self, self,
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>, context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
info: &ModuleInfo, info: &ModuleInfo,
) -> Result<ModuleParts<W>>; ) -> Result<ModuleParts<W>>
where
<Self as Module<W>>::SendMessage: Clone;
fn into_popup( fn into_popup(
self, self,
_tx: mpsc::Sender<Self::ReceiveMessage>, _tx: mpsc::Sender<Self::ReceiveMessage>,
_rx: glib::Receiver<Self::SendMessage>, _rx: broadcast::Receiver<Self::SendMessage>,
_info: &ModuleInfo, _info: &ModuleInfo,
) -> Option<gtk::Box> ) -> Option<gtk::Box>
where where
@ -184,22 +201,21 @@ pub fn create_module<TModule, TWidget, TSend, TRec>(
where where
TModule: Module<TWidget, SendMessage = TSend, ReceiveMessage = TRec>, TModule: Module<TWidget, SendMessage = TSend, ReceiveMessage = TRec>,
TWidget: IsA<Widget>, TWidget: IsA<Widget>,
TSend: Clone + Send + 'static, TSend: Debug + Clone + Send + 'static,
{ {
let (w_tx, w_rx) = glib::MainContext::channel::<TSend>(glib::PRIORITY_DEFAULT); let (ui_tx, ui_rx) = mpsc::channel::<ModuleUpdateEvent<TSend>>(64);
let (p_tx, p_rx) = glib::MainContext::channel::<TSend>(glib::PRIORITY_DEFAULT); let (controller_tx, controller_rx) = mpsc::channel::<TRec>(64);
let channel = BridgeChannel::<ModuleUpdateEvent<TSend>>::new(); let (tx, rx) = broadcast::channel(64);
let (ui_tx, ui_rx) = mpsc::channel::<TRec>(16);
module.spawn_controller(info, channel.create_sender(), ui_rx)?; module.spawn_controller(info, ui_tx.clone(), controller_rx)?;
let context = WidgetContext { let context = WidgetContext {
id, id,
widget_rx: w_rx, tx: ui_tx,
popup_rx: p_rx, update_tx: tx.clone(),
tx: channel.create_sender(), controller_tx,
controller_tx: ui_tx, _update_rx: rx,
}; };
let module_name = TModule::name(); let module_name = TModule::name();
@ -209,27 +225,16 @@ where
module_parts.widget.add_class("widget"); module_parts.widget.add_class("widget");
module_parts.widget.add_class(module_name); module_parts.widget.add_class(module_name);
let has_popup = if let Some(popup_content) = module_parts.popup.clone() { if let Some(popup_content) = module_parts.popup.clone() {
popup_content popup_content
.container .container
.style_context() .style_context()
.add_class(&format!("popup-{module_name}")); .add_class(&format!("popup-{module_name}"));
register_popup_content(popup, id, instance_name, popup_content); register_popup_content(popup, id, instance_name, popup_content);
true }
} else {
false
};
setup_receiver( setup_receiver(tx, ui_rx, popup.clone(), module_name, id);
channel,
w_tx,
p_tx,
popup.clone(),
module_name,
id,
has_popup,
);
Ok(module_parts) Ok(module_parts)
} }
@ -250,28 +255,22 @@ fn register_popup_content(
/// Handles opening/closing popups /// Handles opening/closing popups
/// and communicating update messages between controllers and widgets/popups. /// and communicating update messages between controllers and widgets/popups.
fn setup_receiver<TSend>( fn setup_receiver<TSend>(
channel: BridgeChannel<ModuleUpdateEvent<TSend>>, tx: broadcast::Sender<TSend>,
w_tx: glib::Sender<TSend>, mut rx: mpsc::Receiver<ModuleUpdateEvent<TSend>>,
p_tx: glib::Sender<TSend>,
popup: Rc<RefCell<Popup>>, popup: Rc<RefCell<Popup>>,
name: &'static str, name: &'static str,
id: usize, id: usize,
has_popup: bool,
) where ) where
TSend: Clone + Send + 'static, TSend: Debug + Clone + Send + 'static,
{ {
// some rare cases can cause the popup to incorrectly calculate its size on first open. // some rare cases can cause the popup to incorrectly calculate its size on first open.
// we can fix that by just force re-rendering it on its first open. // we can fix that by just force re-rendering it on its first open.
let mut has_popup_opened = false; let mut has_popup_opened = false;
channel.recv(move |ev| { glib_recv_mpsc!(rx, ev => {
match ev { match ev {
ModuleUpdateEvent::Update(update) => { ModuleUpdateEvent::Update(update) => {
if has_popup { send!(tx, update);
send!(p_tx, update.clone());
}
send!(w_tx, update);
} }
ModuleUpdateEvent::TogglePopup(button_id) => { ModuleUpdateEvent::TogglePopup(button_id) => {
debug!("Toggling popup for {} [#{}]", name, id); debug!("Toggling popup for {} [#{}]", name, id);
@ -321,8 +320,6 @@ fn setup_receiver<TSend>(
popup.hide(); popup.hide();
} }
} }
Continue(true)
}); });
} }

View file

@ -4,12 +4,11 @@ use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use color_eyre::Result; use color_eyre::Result;
use glib::{Continue, PropertySet}; use glib::{Propagation, PropertySet};
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Button, IconTheme, Label, Orientation, Scale}; use gtk::{Button, IconTheme, Label, Orientation, Scale};
use regex::Regex; use regex::Regex;
use tokio::spawn; use tokio::sync::{broadcast, mpsc};
use tokio::sync::mpsc::{Receiver, Sender};
use tracing::error; use tracing::error;
use crate::clients::music::{ use crate::clients::music::{
@ -21,7 +20,7 @@ use crate::modules::PopupButton;
use crate::modules::{ use crate::modules::{
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext, Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext,
}; };
use crate::{send_async, try_send}; use crate::{glib_recv, send_async, spawn, try_send};
pub use self::config::MusicModule; pub use self::config::MusicModule;
use self::config::PlayerType; use self::config::PlayerType;
@ -91,8 +90,8 @@ impl Module<Button> for MusicModule {
fn spawn_controller( fn spawn_controller(
&self, &self,
_info: &ModuleInfo, _info: &ModuleInfo,
tx: Sender<ModuleUpdateEvent<Self::SendMessage>>, tx: mpsc::Sender<ModuleUpdateEvent<Self::SendMessage>>,
mut rx: Receiver<Self::ReceiveMessage>, mut rx: mpsc::Receiver<Self::ReceiveMessage>,
) -> Result<()> { ) -> Result<()> {
let format = self.format.clone(); let format = self.format.clone();
@ -213,11 +212,13 @@ impl Module<Button> for MusicModule {
{ {
let button = button.clone(); let button = button.clone();
let tx = context.tx.clone();
context.widget_rx.attach(None, move |event| { let tx = context.tx.clone();
let mut rx = context.subscribe();
glib_recv!(rx, event => {
let ControllerEvent::Update(mut event) = event else { let ControllerEvent::Update(mut event) = event else {
return Continue(true); continue;
}; };
if let Some(event) = event.take() { if let Some(event) = event.take() {
@ -248,13 +249,12 @@ impl Module<Button> for MusicModule {
button.hide(); button.hide();
try_send!(tx, ModuleUpdateEvent::ClosePopup); try_send!(tx, ModuleUpdateEvent::ClosePopup);
} }
Continue(true)
}); });
}; };
let rx = context.subscribe();
let popup = self let popup = self
.into_popup(context.controller_tx, context.popup_rx, info) .into_popup(context.controller_tx, rx, info)
.into_popup_parts(vec![&button]); .into_popup_parts(vec![&button]);
Ok(ModuleParts::new(button, popup)) Ok(ModuleParts::new(button, popup))
@ -262,8 +262,8 @@ impl Module<Button> for MusicModule {
fn into_popup( fn into_popup(
self, self,
tx: Sender<Self::ReceiveMessage>, tx: mpsc::Sender<Self::ReceiveMessage>,
rx: glib::Receiver<Self::SendMessage>, mut rx: broadcast::Receiver<Self::SendMessage>,
info: &ModuleInfo, info: &ModuleInfo,
) -> Option<gtk::Box> { ) -> Option<gtk::Box> {
let icon_theme = info.icon_theme; let icon_theme = info.icon_theme;
@ -355,7 +355,7 @@ impl Module<Button> for MusicModule {
let tx_vol = tx.clone(); let tx_vol = tx.clone();
volume_slider.connect_change_value(move |_, _, val| { volume_slider.connect_change_value(move |_, _, val| {
try_send!(tx_vol, PlayerCommand::Volume(val as u8)); try_send!(tx_vol, PlayerCommand::Volume(val as u8));
Inhibit(false) Propagation::Proceed
}); });
let progress_box = gtk::Box::new(Orientation::Horizontal, 5); let progress_box = gtk::Box::new(Orientation::Horizontal, 5);
@ -380,7 +380,7 @@ impl Module<Button> for MusicModule {
let drag_lock = drag_lock.clone(); let drag_lock = drag_lock.clone();
progress.connect_button_press_event(move |_, _| { progress.connect_button_press_event(move |_, _| {
drag_lock.set(true); drag_lock.set(true);
Inhibit(false) Propagation::Proceed
}); });
} }
@ -391,7 +391,7 @@ impl Module<Button> for MusicModule {
try_send!(tx, PlayerCommand::Seek(Duration::from_secs_f64(value))); try_send!(tx, PlayerCommand::Seek(Duration::from_secs_f64(value)));
drag_lock.set(false); drag_lock.set(false);
Inhibit(false) Propagation::Proceed
}); });
} }
@ -402,7 +402,7 @@ impl Module<Button> for MusicModule {
let image_size = self.cover_image_size; let image_size = self.cover_image_size;
let mut prev_cover = None; let mut prev_cover = None;
rx.attach(None, move |event| { glib_recv!(rx, event => {
match event { match event {
ControllerEvent::Update(Some(update)) => { ControllerEvent::Update(Some(update)) => {
// only update art when album changes // only update art when album changes
@ -460,7 +460,7 @@ impl Module<Button> for MusicModule {
btn_next.set_sensitive(enable_next); btn_next.set_sensitive(enable_next);
if let Some(volume) = update.status.volume_percent { if let Some(volume) = update.status.volume_percent {
volume_slider.set_value(volume as f64); volume_slider.set_value(f64::from(volume));
volume_box.show(); volume_box.show();
} else { } else {
volume_box.hide(); volume_box.hide();
@ -487,8 +487,6 @@ impl Module<Button> for MusicModule {
} }
_ => {} _ => {}
}; };
Continue(true)
}); });
} }

View file

@ -1,12 +1,11 @@
use crate::config::CommonConfig; use crate::config::CommonConfig;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
use crate::script::{OutputStream, Script, ScriptMode}; use crate::script::{OutputStream, Script, ScriptMode};
use crate::try_send; use crate::{glib_recv, spawn, try_send};
use color_eyre::{Help, Report, Result}; use color_eyre::{Help, Report, Result};
use gtk::prelude::*; use gtk::prelude::*;
use gtk::Label; use gtk::Label;
use serde::Deserialize; use serde::Deserialize;
use tokio::spawn;
use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::mpsc::{Receiver, Sender};
use tracing::error; use tracing::error;
@ -89,10 +88,7 @@ impl Module<Label> for ScriptModule {
{ {
let label = label.clone(); let label = label.clone();
context.widget_rx.attach(None, move |s| { glib_recv!(context.subscribe(), s => label.set_markup(s.as_str()));
label.set_markup(s.as_str());
Continue(true)
});
} }
Ok(ModuleParts { Ok(ModuleParts {

View file

@ -1,7 +1,7 @@
use crate::config::CommonConfig; use crate::config::CommonConfig;
use crate::gtk_helpers::IronbarGtkExt; use crate::gtk_helpers::IronbarGtkExt;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
use crate::send_async; use crate::{glib_recv, send_async, spawn};
use color_eyre::Result; use color_eyre::Result;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::Label; use gtk::Label;
@ -10,7 +10,6 @@ use serde::Deserialize;
use std::collections::HashMap; use std::collections::HashMap;
use std::time::Duration; use std::time::Duration;
use sysinfo::{ComponentExt, CpuExt, DiskExt, NetworkExt, RefreshKind, System, SystemExt}; use sysinfo::{ComponentExt, CpuExt, DiskExt, NetworkExt, RefreshKind, System, SystemExt};
use tokio::spawn;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::mpsc::{Receiver, Sender};
use tokio::time::sleep; use tokio::time::sleep;
@ -205,7 +204,7 @@ impl Module<gtk::Box> for SysInfoModule {
{ {
let formats = self.format; let formats = self.format;
context.widget_rx.attach(None, move |info| { glib_recv!(context.subscribe(), info => {
for (format, label) in formats.iter().zip(labels.clone()) { for (format, label) in formats.iter().zip(labels.clone()) {
let format_compiled = re.replace_all(format, |caps: &Captures| { let format_compiled = re.replace_all(format, |caps: &Captures| {
info.get(&caps[1]) info.get(&caps[1])
@ -215,8 +214,6 @@ impl Module<gtk::Box> for SysInfoModule {
label.set_markup(format_compiled.as_ref()); label.set_markup(format_compiled.as_ref());
} }
Continue(true)
}); });
} }

View file

@ -1,7 +1,7 @@
use crate::clients::system_tray::get_tray_event_client; use crate::clients::system_tray::get_tray_event_client;
use crate::config::CommonConfig; use crate::config::CommonConfig;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
use crate::{await_sync, try_send}; use crate::{await_sync, glib_recv, spawn, try_send};
use color_eyre::Result; use color_eyre::Result;
use glib::ffi::g_strfreev; use glib::ffi::g_strfreev;
use glib::translate::ToGlibPtr; use glib::translate::ToGlibPtr;
@ -20,7 +20,6 @@ use std::ptr;
use system_tray::message::menu::{MenuItem as MenuItemInfo, MenuType}; use system_tray::message::menu::{MenuItem as MenuItemInfo, MenuType};
use system_tray::message::tray::StatusNotifierItem; use system_tray::message::tray::StatusNotifierItem;
use system_tray::message::{NotifierItemCommand, NotifierItemMessage}; use system_tray::message::{NotifierItemCommand, NotifierItemMessage};
use tokio::spawn;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::mpsc::{Receiver, Sender};
@ -58,9 +57,9 @@ fn get_icon_theme_search_paths(icon_theme: &IconTheme) -> HashSet<String> {
/// Attempts to get a GTK `Image` component /// Attempts to get a GTK `Image` component
/// for the status notifier item's icon. /// for the status notifier item's icon.
fn get_image_from_icon_name(item: &StatusNotifierItem, icon_theme: IconTheme) -> Option<Image> { fn get_image_from_icon_name(item: &StatusNotifierItem, icon_theme: &IconTheme) -> Option<Image> {
if let Some(path) = item.icon_theme_path.as_ref() { if let Some(path) = item.icon_theme_path.as_ref() {
if !path.is_empty() && !get_icon_theme_search_paths(&icon_theme).contains(path) { if !path.is_empty() && !get_icon_theme_search_paths(icon_theme).contains(path) {
icon_theme.append_search_path(path); icon_theme.append_search_path(path);
} }
} }
@ -209,7 +208,7 @@ impl Module<MenuBar> for TrayModule {
let icon_theme = info.icon_theme.clone(); let icon_theme = info.icon_theme.clone();
// listen for UI updates // listen for UI updates
context.widget_rx.attach(None, move |update| { glib_recv!(context.subscribe(), update => {
match update { match update {
NotifierItemMessage::Update { NotifierItemMessage::Update {
item, item,
@ -221,7 +220,7 @@ impl Module<MenuBar> for TrayModule {
let menu_item = MenuItem::new(); let menu_item = MenuItem::new();
menu_item.style_context().add_class("item"); menu_item.style_context().add_class("item");
get_image_from_icon_name(&item, icon_theme.clone()) get_image_from_icon_name(&item, &icon_theme)
.or_else(|| get_image_from_pixmap(&item)) .or_else(|| get_image_from_pixmap(&item))
.map_or_else( .map_or_else(
|| { || {
@ -262,8 +261,6 @@ impl Module<MenuBar> for TrayModule {
} }
} }
}; };
Continue(true)
}); });
}; };

View file

@ -3,8 +3,7 @@ use futures_lite::stream::StreamExt;
use gtk::{prelude::*, Button}; use gtk::{prelude::*, Button};
use gtk::{Label, Orientation}; use gtk::{Label, Orientation};
use serde::Deserialize; use serde::Deserialize;
use tokio::spawn; use tokio::sync::{broadcast, mpsc};
use tokio::sync::mpsc::{Receiver, Sender};
use upower_dbus::BatteryState; use upower_dbus::BatteryState;
use zbus; use zbus;
@ -16,7 +15,7 @@ use crate::modules::PopupButton;
use crate::modules::{ use crate::modules::{
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext, Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext,
}; };
use crate::{await_sync, error, send_async, try_send}; use crate::{await_sync, error, glib_recv, send_async, spawn, try_send};
const DAY: i64 = 24 * 60 * 60; const DAY: i64 = 24 * 60 * 60;
const HOUR: i64 = 60 * 60; const HOUR: i64 = 60 * 60;
@ -62,8 +61,8 @@ impl Module<gtk::Button> for UpowerModule {
fn spawn_controller( fn spawn_controller(
&self, &self,
_info: &ModuleInfo, _info: &ModuleInfo,
tx: Sender<ModuleUpdateEvent<Self::SendMessage>>, tx: mpsc::Sender<ModuleUpdateEvent<Self::SendMessage>>,
_rx: Receiver<Self::ReceiveMessage>, _rx: mpsc::Receiver<Self::ReceiveMessage>,
) -> Result<()> { ) -> Result<()> {
spawn(async move { spawn(async move {
// await_sync due to strange "higher-ranked lifetime error" // await_sync due to strange "higher-ranked lifetime error"
@ -174,29 +173,28 @@ impl Module<gtk::Button> for UpowerModule {
container.add(&label); container.add(&label);
button.add(&container); button.add(&container);
let tx = context.tx.clone();
button.connect_clicked(move |button| { button.connect_clicked(move |button| {
try_send!( try_send!(tx, ModuleUpdateEvent::TogglePopup(button.popup_id()));
context.tx,
ModuleUpdateEvent::TogglePopup(button.popup_id())
);
}); });
label.set_angle(info.bar_position.get_angle()); label.set_angle(info.bar_position.get_angle());
let format = self.format.clone(); let format = self.format.clone();
context let mut rx = context.subscribe();
.widget_rx glib_recv!(rx, properties => {
.attach(None, move |properties: UpowerProperties| { let format = format.replace("{percentage}", &properties.percentage.to_string());
let format = format.replace("{percentage}", &properties.percentage.to_string()); let icon_name = String::from("icon:") + &properties.icon_name;
let icon_name = String::from("icon:") + &properties.icon_name;
ImageProvider::parse(&icon_name, &icon_theme, false, self.icon_size)
.map(|provider| provider.load_into_image(icon.clone()));
label.set_markup(format.as_ref());
Continue(true)
});
ImageProvider::parse(&icon_name, &icon_theme, false, self.icon_size)
.map(|provider| provider.load_into_image(icon.clone()));
label.set_markup(format.as_ref());
});
let rx = context.subscribe();
let popup = self let popup = self
.into_popup(context.controller_tx, context.popup_rx, info) .into_popup(context.controller_tx, rx, info)
.into_popup_parts(vec![&button]); .into_popup_parts(vec![&button]);
Ok(ModuleParts::new(button, popup)) Ok(ModuleParts::new(button, popup))
@ -204,8 +202,8 @@ impl Module<gtk::Button> for UpowerModule {
fn into_popup( fn into_popup(
self, self,
_tx: Sender<Self::ReceiveMessage>, _tx: mpsc::Sender<Self::ReceiveMessage>,
rx: glib::Receiver<Self::SendMessage>, mut rx: broadcast::Receiver<Self::SendMessage>,
_info: &ModuleInfo, _info: &ModuleInfo,
) -> Option<gtk::Box> ) -> Option<gtk::Box>
where where
@ -219,7 +217,7 @@ impl Module<gtk::Button> for UpowerModule {
label.add_class("upower-details"); label.add_class("upower-details");
container.add(&label); container.add(&label);
rx.attach(None, move |properties| { glib_recv!(rx, properties => {
let state = u32_to_battery_state(properties.state); let state = u32_to_battery_state(properties.state);
let format = match state { let format = match state {
Ok(BatteryState::Charging | BatteryState::PendingCharge) => { Ok(BatteryState::Charging | BatteryState::PendingCharge) => {
@ -246,7 +244,6 @@ impl Module<gtk::Button> for UpowerModule {
}; };
label.set_markup(&format); label.set_markup(&format);
Continue(true)
}); });
container.show_all(); container.show_all();

View file

@ -2,14 +2,13 @@ use crate::clients::compositor::{Compositor, Visibility, Workspace, WorkspaceUpd
use crate::config::CommonConfig; use crate::config::CommonConfig;
use crate::image::new_icon_button; use crate::image::new_icon_button;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
use crate::{send_async, try_send}; use crate::{glib_recv, send_async, spawn, try_send};
use color_eyre::{Report, Result}; use color_eyre::{Report, Result};
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Button, IconTheme}; use gtk::{Button, IconTheme};
use serde::Deserialize; use serde::Deserialize;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use tokio::spawn;
use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::mpsc::{Receiver, Sender};
use tracing::trace; use tracing::trace;
@ -205,7 +204,7 @@ impl Module<gtk::Box> for WorkspacesModule {
// since it fires for every workspace subscriber // since it fires for every workspace subscriber
let mut has_initialized = false; let mut has_initialized = false;
context.widget_rx.attach(None, move |event| { glib_recv!(context.subscribe(), event => {
match event { match event {
WorkspaceUpdate::Init(workspaces) => { WorkspaceUpdate::Init(workspaces) => {
if !has_initialized { if !has_initialized {
@ -350,8 +349,6 @@ impl Module<gtk::Box> for WorkspacesModule {
} }
WorkspaceUpdate::Update(_) => {} WorkspaceUpdate::Update(_) => {}
}; };
Continue(true)
}); });
} }

View file

@ -1,8 +1,10 @@
use glib::Propagation;
use std::collections::HashMap; use std::collections::HashMap;
use gtk::gdk::Monitor; use gtk::gdk::Monitor;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{ApplicationWindow, Orientation}; use gtk::{ApplicationWindow, Orientation};
use gtk_layer_shell::LayerShell;
use tracing::debug; use tracing::debug;
use crate::config::BarPosition; use crate::config::BarPosition;
@ -37,52 +39,38 @@ impl Popup {
.application(module_info.app) .application(module_info.app)
.build(); .build();
gtk_layer_shell::init_for_window(&win); win.init_layer_shell();
gtk_layer_shell::set_monitor(&win, module_info.monitor); win.set_monitor(module_info.monitor);
gtk_layer_shell::set_layer(&win, gtk_layer_shell::Layer::Overlay); win.set_layer(gtk_layer_shell::Layer::Overlay);
gtk_layer_shell::set_namespace(&win, env!("CARGO_PKG_NAME")); win.set_namespace(env!("CARGO_PKG_NAME"));
gtk_layer_shell::set_margin( win.set_layer_shell_margin(
&win,
gtk_layer_shell::Edge::Top, gtk_layer_shell::Edge::Top,
if pos == BarPosition::Top { gap } else { 0 }, if pos == BarPosition::Top { gap } else { 0 },
); );
gtk_layer_shell::set_margin( win.set_layer_shell_margin(
&win,
gtk_layer_shell::Edge::Bottom, gtk_layer_shell::Edge::Bottom,
if pos == BarPosition::Bottom { gap } else { 0 }, if pos == BarPosition::Bottom { gap } else { 0 },
); );
gtk_layer_shell::set_margin( win.set_layer_shell_margin(
&win,
gtk_layer_shell::Edge::Left, gtk_layer_shell::Edge::Left,
if pos == BarPosition::Left { gap } else { 0 }, if pos == BarPosition::Left { gap } else { 0 },
); );
gtk_layer_shell::set_margin( win.set_layer_shell_margin(
&win,
gtk_layer_shell::Edge::Right, gtk_layer_shell::Edge::Right,
if pos == BarPosition::Right { gap } else { 0 }, if pos == BarPosition::Right { gap } else { 0 },
); );
gtk_layer_shell::set_anchor( win.set_anchor(
&win,
gtk_layer_shell::Edge::Top, gtk_layer_shell::Edge::Top,
pos == BarPosition::Top || orientation == Orientation::Vertical, pos == BarPosition::Top || orientation == Orientation::Vertical,
); );
gtk_layer_shell::set_anchor( win.set_anchor(gtk_layer_shell::Edge::Bottom, pos == BarPosition::Bottom);
&win, win.set_anchor(
gtk_layer_shell::Edge::Bottom,
pos == BarPosition::Bottom,
);
gtk_layer_shell::set_anchor(
&win,
gtk_layer_shell::Edge::Left, gtk_layer_shell::Edge::Left,
pos == BarPosition::Left || orientation == Orientation::Horizontal, pos == BarPosition::Left || orientation == Orientation::Horizontal,
); );
gtk_layer_shell::set_anchor( win.set_anchor(gtk_layer_shell::Edge::Right, pos == BarPosition::Right);
&win,
gtk_layer_shell::Edge::Right,
pos == BarPosition::Right,
);
win.connect_leave_notify_event(move |win, ev| { win.connect_leave_notify_event(move |win, ev| {
const THRESHOLD: f64 = 3.0; const THRESHOLD: f64 = 3.0;
@ -111,7 +99,7 @@ impl Popup {
win.hide(); win.hide();
} }
Inhibit(false) Propagation::Proceed
}); });
Self { Self {
@ -229,6 +217,6 @@ impl Popup {
gtk_layer_shell::Edge::Top gtk_layer_shell::Edge::Top
}; };
gtk_layer_shell::set_margin(&self.window, edge, offset as i32); self.window.set_layer_shell_margin(edge, offset as i32);
} }
} }

View file

@ -1,6 +1,5 @@
use crate::send; use crate::{glib_recv_mpsc, spawn, try_send};
use color_eyre::{Help, Report}; use color_eyre::{Help, Report};
use glib::Continue;
use gtk::ffi::GTK_STYLE_PROVIDER_PRIORITY_USER; use gtk::ffi::GTK_STYLE_PROVIDER_PRIORITY_USER;
use gtk::prelude::CssProviderExt; use gtk::prelude::CssProviderExt;
use gtk::{gdk, gio, CssProvider, StyleContext}; use gtk::{gdk, gio, CssProvider, StyleContext};
@ -8,7 +7,7 @@ use notify::event::ModifyKind;
use notify::{recommended_watcher, Event, EventKind, RecursiveMode, Result, Watcher}; use notify::{recommended_watcher, Event, EventKind, RecursiveMode, Result, Watcher};
use std::path::PathBuf; use std::path::PathBuf;
use std::time::Duration; use std::time::Duration;
use tokio::spawn; use tokio::sync::mpsc;
use tokio::time::sleep; use tokio::time::sleep;
use tracing::{debug, error, info}; use tracing::{debug, error, info};
@ -36,7 +35,7 @@ pub fn load_css(style_path: PathBuf) {
GTK_STYLE_PROVIDER_PRIORITY_USER as u32, GTK_STYLE_PROVIDER_PRIORITY_USER as u32,
); );
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); let (tx, mut rx) = mpsc::channel(8);
spawn(async move { spawn(async move {
let style_path2 = style_path.clone(); let style_path2 = style_path.clone();
@ -49,7 +48,7 @@ pub fn load_css(style_path: PathBuf) {
.map(|p| p == &style_path2) .map(|p| p == &style_path2)
.unwrap_or_default() .unwrap_or_default()
{ {
send!(tx, style_path2.clone()); try_send!(tx, style_path2.clone());
} }
} }
Err(e) => error!("Error occurred when watching stylesheet: {:?}", e), Err(e) => error!("Error occurred when watching stylesheet: {:?}", e),
@ -70,19 +69,14 @@ pub fn load_css(style_path: PathBuf) {
} }
}); });
{ glib_recv_mpsc!(rx, path => {
rx.attach(None, move |path| { info!("Reloading CSS");
info!("Reloading CSS"); if let Err(err) = provider.load_from_file(&gio::File::for_path(path)) {
if let Err(err) = provider error!("{:?}", Report::new(err)
.load_from_file(&gio::File::for_path(path)) { .wrap_err("Failed to load CSS")
error!("{:?}", Report::new(err) .suggestion("Check the CSS file for errors")
.wrap_err("Failed to load CSS") .suggestion("GTK CSS uses a subset of the full CSS spec and many properties are not available. Ensure you are not using any unsupported property.")
.suggestion("Check the CSS file for errors") );
.suggestion("GTK CSS uses a subset of the full CSS spec and many properties are not available. Ensure you are not using any unsupported property.") }
); });
}
Continue(true)
});
}
} }