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

View file

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

View file

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

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 crate::{arc_mut, lock, try_send};
use crate::{arc_mut, lock, spawn, try_send};
use indexmap::map::Iter;
use indexmap::IndexMap;
use lazy_static::lazy_static;
use std::sync::{Arc, Mutex};
use tokio::spawn;
use tokio::sync::mpsc;
use tracing::{debug, trace};

View file

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
use super::{MusicClient, PlayerState, PlayerUpdate, Status, Track, TICK_INTERVAL_MS};
use crate::clients::music::ProgressTick;
use crate::{arc_mut, lock, send};
use crate::{arc_mut, lock, send, spawn_blocking};
use color_eyre::Result;
use lazy_static::lazy_static;
use mpris::{DBusError, Event, Metadata, PlaybackStatus, Player, PlayerFinder};
@ -10,7 +10,6 @@ use std::thread::sleep;
use std::time::Duration;
use std::{cmp, string};
use tokio::sync::broadcast;
use tokio::task::spawn_blocking;
use tracing::{debug, error, trace};
lazy_static! {
@ -245,7 +244,7 @@ impl MusicClient for Client {
fn set_volume_percent(&self, vol: u8) -> Result<()> {
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 {
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 color_eyre::Report;
use lazy_static::lazy_static;
@ -8,7 +8,6 @@ use system_tray::message::menu::TrayMenu;
use system_tray::message::tray::StatusNotifierItem;
use system_tray::message::{NotifierItemCommand, NotifierItemMessage};
use system_tray::StatusNotifierWatcher;
use tokio::spawn;
use tokio::sync::{broadcast, mpsc};
use tracing::{debug, error, trace};

View file

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

View file

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

View file

@ -1,11 +1,10 @@
use crate::script::Script;
use crate::send;
use crate::{glib_recv_mpsc, spawn, try_send};
#[cfg(feature = "ipc")]
use crate::Ironbar;
use crate::{send_async, Ironbar};
use cfg_if::cfg_if;
use glib::Continue;
use serde::Deserialize;
use tokio::spawn;
use tokio::sync::mpsc;
#[derive(Debug, Deserialize, Clone)]
#[serde(untagged)]
@ -18,9 +17,9 @@ pub enum DynamicBool {
}
impl DynamicBool {
pub fn subscribe<F>(self, f: F)
pub fn subscribe<F>(self, mut f: F)
where
F: FnMut(bool) -> Continue + 'static,
F: FnMut(bool) + 'static,
{
let value = match self {
Self::Unknown(input) => {
@ -40,16 +39,16 @@ impl DynamicBool {
_ => self,
};
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
let (tx, mut rx) = mpsc::channel(32);
rx.attach(None, f);
glib_recv_mpsc!(rx, val => f(val));
spawn(async move {
match value {
DynamicBool::Script(script) => {
script
.run(None, |_, success| {
send!(tx, success);
try_send!(tx, success);
})
.await;
}
@ -62,7 +61,7 @@ impl DynamicBool {
while let Ok(value) = rx.recv().await {
let has_value = value.map(|s| is_truthy(&s)).unwrap_or_default();
send!(tx, has_value);
send_async!(tx, has_value);
}
}
DynamicBool::Unknown(_) => unreachable!(),
@ -71,7 +70,10 @@ impl DynamicBool {
}
}
/// Check if a string ironvar is 'truthy'
/// Check if a string ironvar is 'truthy',
/// i.e should be evaluated to true.
///
/// This loosely follows the common JavaScript cases.
#[cfg(feature = "ipc")]
fn is_truthy(string: &str) -> bool {
!(string.is_empty() || string == "0" || string == "false")

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -34,8 +34,6 @@ impl CustomWidget for ImageWidget {
dynamic_string(&self.src, move |src| {
ImageProvider::parse(&src, &icon_theme, false, self.size)
.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();
dynamic_string(&self.label, move |string| {
label.set_markup(&string);
Continue(true)
});
}

View file

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

View file

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

View file

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

View file

@ -3,13 +3,11 @@ use crate::config::{CommonConfig, TruncateMode};
use crate::gtk_helpers::IronbarGtkExt;
use crate::image::ImageProvider;
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 glib::Continue;
use gtk::prelude::*;
use gtk::Label;
use serde::Deserialize;
use tokio::spawn;
use tokio::sync::mpsc::{Receiver, Sender};
use tracing::debug;
@ -141,7 +139,7 @@ impl Module<gtk::Box> for FocusedModule {
{
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 self.show_icon {
match ImageProvider::parse(&id, &icon_theme, true, self.icon_size)
@ -160,8 +158,6 @@ impl Module<gtk::Box> for FocusedModule {
icon.hide();
label.hide();
}
Continue(true)
});
}

View file

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

View file

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

View file

@ -10,17 +10,15 @@ use crate::modules::launcher::item::AppearanceOptions;
use crate::modules::{
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 glib::Continue;
use gtk::prelude::*;
use gtk::{Button, Orientation};
use indexmap::IndexMap;
use serde::Deserialize;
use std::process::{Command, Stdio};
use std::sync::Arc;
use tokio::spawn;
use tokio::sync::mpsc::{Receiver, Sender};
use tokio::sync::{broadcast, mpsc};
use tracing::{debug, error, trace};
#[derive(Debug, Deserialize, Clone)]
@ -92,8 +90,8 @@ impl Module<gtk::Box> for LauncherModule {
fn spawn_controller(
&self,
_info: &ModuleInfo,
tx: Sender<ModuleUpdateEvent<Self::SendMessage>>,
mut rx: Receiver<Self::ReceiveMessage>,
tx: mpsc::Sender<ModuleUpdateEvent<Self::SendMessage>>,
mut rx: mpsc::Receiver<Self::ReceiveMessage>,
) -> crate::Result<()> {
let items = self
.favorites
@ -338,7 +336,9 @@ impl Module<gtk::Box> for LauncherModule {
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 {
LauncherUpdate::AddItem(item) => {
debug!("Adding item with id {}", item.app_id);
@ -351,7 +351,7 @@ impl Module<gtk::Box> for LauncherModule {
appearance_options,
&icon_theme,
bar_position,
&context.tx,
&tx,
&controller_tx,
);
@ -411,13 +411,12 @@ impl Module<gtk::Box> for LauncherModule {
}
LauncherUpdate::Hover(_) => {}
};
Continue(true)
});
}
let rx = context.subscribe();
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
Ok(ModuleParts {
@ -428,8 +427,8 @@ impl Module<gtk::Box> for LauncherModule {
fn into_popup(
self,
controller_tx: Sender<Self::ReceiveMessage>,
rx: glib::Receiver<Self::SendMessage>,
controller_tx: mpsc::Sender<Self::ReceiveMessage>,
mut rx: broadcast::Receiver<Self::SendMessage>,
_info: &ModuleInfo,
) -> Option<gtk::Box> {
const MAX_WIDTH: i32 = 250;
@ -445,7 +444,7 @@ impl Module<gtk::Box> for LauncherModule {
{
let container = container.clone();
rx.attach(None, move |event| {
glib_recv!(rx, event => {
match event {
LauncherUpdate::AddItem(item) => {
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::fmt::Debug;
use std::rc::Rc;
use color_eyre::Result;
@ -6,14 +7,13 @@ use glib::IsA;
use gtk::gdk::{EventMask, Monitor};
use gtk::prelude::*;
use gtk::{Application, Button, EventBox, IconTheme, Orientation, Revealer, Widget};
use tokio::sync::mpsc;
use tokio::sync::{broadcast, mpsc};
use tracing::debug;
use crate::bridge_channel::BridgeChannel;
use crate::config::{BarPosition, CommonConfig, TransitionType};
use crate::gtk_helpers::{IronbarGtkExt, WidgetGeometry};
use crate::popup::Popup;
use crate::send;
use crate::{glib_recv_mpsc, send};
#[cfg(feature = "clipboard")]
pub mod clipboard;
@ -56,8 +56,8 @@ pub struct ModuleInfo<'a> {
pub icon_theme: &'a IconTheme,
}
#[derive(Debug)]
pub enum ModuleUpdateEvent<T> {
#[derive(Debug, Clone)]
pub enum ModuleUpdateEvent<T: Clone> {
/// Sends an update to the module UI.
Update(T),
/// Toggles the open state of the popup.
@ -71,12 +71,25 @@ pub enum ModuleUpdateEvent<T> {
ClosePopup,
}
pub struct WidgetContext<TSend, TReceive> {
pub struct WidgetContext<TSend, TReceive>
where
TSend: Clone,
{
pub id: usize,
pub tx: mpsc::Sender<ModuleUpdateEvent<TSend>>,
pub update_tx: broadcast::Sender<TSend>,
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>> {
@ -151,18 +164,22 @@ where
info: &ModuleInfo,
tx: mpsc::Sender<ModuleUpdateEvent<Self::SendMessage>>,
rx: mpsc::Receiver<Self::ReceiveMessage>,
) -> Result<()>;
) -> Result<()>
where
<Self as Module<W>>::SendMessage: Clone;
fn into_widget(
self,
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
info: &ModuleInfo,
) -> Result<ModuleParts<W>>;
) -> Result<ModuleParts<W>>
where
<Self as Module<W>>::SendMessage: Clone;
fn into_popup(
self,
_tx: mpsc::Sender<Self::ReceiveMessage>,
_rx: glib::Receiver<Self::SendMessage>,
_rx: broadcast::Receiver<Self::SendMessage>,
_info: &ModuleInfo,
) -> Option<gtk::Box>
where
@ -184,22 +201,21 @@ pub fn create_module<TModule, TWidget, TSend, TRec>(
where
TModule: Module<TWidget, SendMessage = TSend, ReceiveMessage = TRec>,
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 (p_tx, p_rx) = glib::MainContext::channel::<TSend>(glib::PRIORITY_DEFAULT);
let (ui_tx, ui_rx) = mpsc::channel::<ModuleUpdateEvent<TSend>>(64);
let (controller_tx, controller_rx) = mpsc::channel::<TRec>(64);
let channel = BridgeChannel::<ModuleUpdateEvent<TSend>>::new();
let (ui_tx, ui_rx) = mpsc::channel::<TRec>(16);
let (tx, rx) = broadcast::channel(64);
module.spawn_controller(info, channel.create_sender(), ui_rx)?;
module.spawn_controller(info, ui_tx.clone(), controller_rx)?;
let context = WidgetContext {
id,
widget_rx: w_rx,
popup_rx: p_rx,
tx: channel.create_sender(),
controller_tx: ui_tx,
tx: ui_tx,
update_tx: tx.clone(),
controller_tx,
_update_rx: rx,
};
let module_name = TModule::name();
@ -209,27 +225,16 @@ where
module_parts.widget.add_class("widget");
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
.container
.style_context()
.add_class(&format!("popup-{module_name}"));
register_popup_content(popup, id, instance_name, popup_content);
true
} else {
false
};
}
setup_receiver(
channel,
w_tx,
p_tx,
popup.clone(),
module_name,
id,
has_popup,
);
setup_receiver(tx, ui_rx, popup.clone(), module_name, id);
Ok(module_parts)
}
@ -250,28 +255,22 @@ fn register_popup_content(
/// Handles opening/closing popups
/// and communicating update messages between controllers and widgets/popups.
fn setup_receiver<TSend>(
channel: BridgeChannel<ModuleUpdateEvent<TSend>>,
w_tx: glib::Sender<TSend>,
p_tx: glib::Sender<TSend>,
tx: broadcast::Sender<TSend>,
mut rx: mpsc::Receiver<ModuleUpdateEvent<TSend>>,
popup: Rc<RefCell<Popup>>,
name: &'static str,
id: usize,
has_popup: bool,
) 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.
// we can fix that by just force re-rendering it on its first open.
let mut has_popup_opened = false;
channel.recv(move |ev| {
glib_recv_mpsc!(rx, ev => {
match ev {
ModuleUpdateEvent::Update(update) => {
if has_popup {
send!(p_tx, update.clone());
}
send!(w_tx, update);
send!(tx, update);
}
ModuleUpdateEvent::TogglePopup(button_id) => {
debug!("Toggling popup for {} [#{}]", name, id);
@ -321,8 +320,6 @@ fn setup_receiver<TSend>(
popup.hide();
}
}
Continue(true)
});
}

View file

@ -4,12 +4,11 @@ use std::sync::Arc;
use std::time::Duration;
use color_eyre::Result;
use glib::{Continue, PropertySet};
use glib::{Propagation, PropertySet};
use gtk::prelude::*;
use gtk::{Button, IconTheme, Label, Orientation, Scale};
use regex::Regex;
use tokio::spawn;
use tokio::sync::mpsc::{Receiver, Sender};
use tokio::sync::{broadcast, mpsc};
use tracing::error;
use crate::clients::music::{
@ -21,7 +20,7 @@ use crate::modules::PopupButton;
use crate::modules::{
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;
use self::config::PlayerType;
@ -91,8 +90,8 @@ impl Module<Button> for MusicModule {
fn spawn_controller(
&self,
_info: &ModuleInfo,
tx: Sender<ModuleUpdateEvent<Self::SendMessage>>,
mut rx: Receiver<Self::ReceiveMessage>,
tx: mpsc::Sender<ModuleUpdateEvent<Self::SendMessage>>,
mut rx: mpsc::Receiver<Self::ReceiveMessage>,
) -> Result<()> {
let format = self.format.clone();
@ -213,11 +212,13 @@ impl Module<Button> for MusicModule {
{
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 {
return Continue(true);
continue;
};
if let Some(event) = event.take() {
@ -248,13 +249,12 @@ impl Module<Button> for MusicModule {
button.hide();
try_send!(tx, ModuleUpdateEvent::ClosePopup);
}
Continue(true)
});
};
let rx = context.subscribe();
let popup = self
.into_popup(context.controller_tx, context.popup_rx, info)
.into_popup(context.controller_tx, rx, info)
.into_popup_parts(vec![&button]);
Ok(ModuleParts::new(button, popup))
@ -262,8 +262,8 @@ impl Module<Button> for MusicModule {
fn into_popup(
self,
tx: Sender<Self::ReceiveMessage>,
rx: glib::Receiver<Self::SendMessage>,
tx: mpsc::Sender<Self::ReceiveMessage>,
mut rx: broadcast::Receiver<Self::SendMessage>,
info: &ModuleInfo,
) -> Option<gtk::Box> {
let icon_theme = info.icon_theme;
@ -355,7 +355,7 @@ impl Module<Button> for MusicModule {
let tx_vol = tx.clone();
volume_slider.connect_change_value(move |_, _, val| {
try_send!(tx_vol, PlayerCommand::Volume(val as u8));
Inhibit(false)
Propagation::Proceed
});
let progress_box = gtk::Box::new(Orientation::Horizontal, 5);
@ -380,7 +380,7 @@ impl Module<Button> for MusicModule {
let drag_lock = drag_lock.clone();
progress.connect_button_press_event(move |_, _| {
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)));
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 mut prev_cover = None;
rx.attach(None, move |event| {
glib_recv!(rx, event => {
match event {
ControllerEvent::Update(Some(update)) => {
// only update art when album changes
@ -460,7 +460,7 @@ impl Module<Button> for MusicModule {
btn_next.set_sensitive(enable_next);
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();
} else {
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::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
use crate::script::{OutputStream, Script, ScriptMode};
use crate::try_send;
use crate::{glib_recv, spawn, try_send};
use color_eyre::{Help, Report, Result};
use gtk::prelude::*;
use gtk::Label;
use serde::Deserialize;
use tokio::spawn;
use tokio::sync::mpsc::{Receiver, Sender};
use tracing::error;
@ -89,10 +88,7 @@ impl Module<Label> for ScriptModule {
{
let label = label.clone();
context.widget_rx.attach(None, move |s| {
label.set_markup(s.as_str());
Continue(true)
});
glib_recv!(context.subscribe(), s => label.set_markup(s.as_str()));
}
Ok(ModuleParts {

View file

@ -1,7 +1,7 @@
use crate::config::CommonConfig;
use crate::gtk_helpers::IronbarGtkExt;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
use crate::send_async;
use crate::{glib_recv, send_async, spawn};
use color_eyre::Result;
use gtk::prelude::*;
use gtk::Label;
@ -10,7 +10,6 @@ use serde::Deserialize;
use std::collections::HashMap;
use std::time::Duration;
use sysinfo::{ComponentExt, CpuExt, DiskExt, NetworkExt, RefreshKind, System, SystemExt};
use tokio::spawn;
use tokio::sync::mpsc;
use tokio::sync::mpsc::{Receiver, Sender};
use tokio::time::sleep;
@ -205,7 +204,7 @@ impl Module<gtk::Box> for SysInfoModule {
{
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()) {
let format_compiled = re.replace_all(format, |caps: &Captures| {
info.get(&caps[1])
@ -215,8 +214,6 @@ impl Module<gtk::Box> for SysInfoModule {
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::config::CommonConfig;
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 glib::ffi::g_strfreev;
use glib::translate::ToGlibPtr;
@ -20,7 +20,6 @@ use std::ptr;
use system_tray::message::menu::{MenuItem as MenuItemInfo, MenuType};
use system_tray::message::tray::StatusNotifierItem;
use system_tray::message::{NotifierItemCommand, NotifierItemMessage};
use tokio::spawn;
use tokio::sync::mpsc;
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
/// 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 !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);
}
}
@ -209,7 +208,7 @@ impl Module<MenuBar> for TrayModule {
let icon_theme = info.icon_theme.clone();
// listen for UI updates
context.widget_rx.attach(None, move |update| {
glib_recv!(context.subscribe(), update => {
match update {
NotifierItemMessage::Update {
item,
@ -221,7 +220,7 @@ impl Module<MenuBar> for TrayModule {
let menu_item = MenuItem::new();
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))
.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::{Label, Orientation};
use serde::Deserialize;
use tokio::spawn;
use tokio::sync::mpsc::{Receiver, Sender};
use tokio::sync::{broadcast, mpsc};
use upower_dbus::BatteryState;
use zbus;
@ -16,7 +15,7 @@ use crate::modules::PopupButton;
use crate::modules::{
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 HOUR: i64 = 60 * 60;
@ -62,8 +61,8 @@ impl Module<gtk::Button> for UpowerModule {
fn spawn_controller(
&self,
_info: &ModuleInfo,
tx: Sender<ModuleUpdateEvent<Self::SendMessage>>,
_rx: Receiver<Self::ReceiveMessage>,
tx: mpsc::Sender<ModuleUpdateEvent<Self::SendMessage>>,
_rx: mpsc::Receiver<Self::ReceiveMessage>,
) -> Result<()> {
spawn(async move {
// await_sync due to strange "higher-ranked lifetime error"
@ -174,29 +173,28 @@ impl Module<gtk::Button> for UpowerModule {
container.add(&label);
button.add(&container);
let tx = context.tx.clone();
button.connect_clicked(move |button| {
try_send!(
context.tx,
ModuleUpdateEvent::TogglePopup(button.popup_id())
);
try_send!(tx, ModuleUpdateEvent::TogglePopup(button.popup_id()));
});
label.set_angle(info.bar_position.get_angle());
let format = self.format.clone();
context
.widget_rx
.attach(None, move |properties: UpowerProperties| {
let format = format.replace("{percentage}", &properties.percentage.to_string());
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)
});
let mut rx = context.subscribe();
glib_recv!(rx, properties => {
let format = format.replace("{percentage}", &properties.percentage.to_string());
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());
});
let rx = context.subscribe();
let popup = self
.into_popup(context.controller_tx, context.popup_rx, info)
.into_popup(context.controller_tx, rx, info)
.into_popup_parts(vec![&button]);
Ok(ModuleParts::new(button, popup))
@ -204,8 +202,8 @@ impl Module<gtk::Button> for UpowerModule {
fn into_popup(
self,
_tx: Sender<Self::ReceiveMessage>,
rx: glib::Receiver<Self::SendMessage>,
_tx: mpsc::Sender<Self::ReceiveMessage>,
mut rx: broadcast::Receiver<Self::SendMessage>,
_info: &ModuleInfo,
) -> Option<gtk::Box>
where
@ -219,7 +217,7 @@ impl Module<gtk::Button> for UpowerModule {
label.add_class("upower-details");
container.add(&label);
rx.attach(None, move |properties| {
glib_recv!(rx, properties => {
let state = u32_to_battery_state(properties.state);
let format = match state {
Ok(BatteryState::Charging | BatteryState::PendingCharge) => {
@ -246,7 +244,6 @@ impl Module<gtk::Button> for UpowerModule {
};
label.set_markup(&format);
Continue(true)
});
container.show_all();

View file

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

View file

@ -1,8 +1,10 @@
use glib::Propagation;
use std::collections::HashMap;
use gtk::gdk::Monitor;
use gtk::prelude::*;
use gtk::{ApplicationWindow, Orientation};
use gtk_layer_shell::LayerShell;
use tracing::debug;
use crate::config::BarPosition;
@ -37,52 +39,38 @@ impl Popup {
.application(module_info.app)
.build();
gtk_layer_shell::init_for_window(&win);
gtk_layer_shell::set_monitor(&win, module_info.monitor);
gtk_layer_shell::set_layer(&win, gtk_layer_shell::Layer::Overlay);
gtk_layer_shell::set_namespace(&win, env!("CARGO_PKG_NAME"));
win.init_layer_shell();
win.set_monitor(module_info.monitor);
win.set_layer(gtk_layer_shell::Layer::Overlay);
win.set_namespace(env!("CARGO_PKG_NAME"));
gtk_layer_shell::set_margin(
&win,
win.set_layer_shell_margin(
gtk_layer_shell::Edge::Top,
if pos == BarPosition::Top { gap } else { 0 },
);
gtk_layer_shell::set_margin(
&win,
win.set_layer_shell_margin(
gtk_layer_shell::Edge::Bottom,
if pos == BarPosition::Bottom { gap } else { 0 },
);
gtk_layer_shell::set_margin(
&win,
win.set_layer_shell_margin(
gtk_layer_shell::Edge::Left,
if pos == BarPosition::Left { gap } else { 0 },
);
gtk_layer_shell::set_margin(
&win,
win.set_layer_shell_margin(
gtk_layer_shell::Edge::Right,
if pos == BarPosition::Right { gap } else { 0 },
);
gtk_layer_shell::set_anchor(
&win,
win.set_anchor(
gtk_layer_shell::Edge::Top,
pos == BarPosition::Top || orientation == Orientation::Vertical,
);
gtk_layer_shell::set_anchor(
&win,
gtk_layer_shell::Edge::Bottom,
pos == BarPosition::Bottom,
);
gtk_layer_shell::set_anchor(
&win,
win.set_anchor(gtk_layer_shell::Edge::Bottom, pos == BarPosition::Bottom);
win.set_anchor(
gtk_layer_shell::Edge::Left,
pos == BarPosition::Left || orientation == Orientation::Horizontal,
);
gtk_layer_shell::set_anchor(
&win,
gtk_layer_shell::Edge::Right,
pos == BarPosition::Right,
);
win.set_anchor(gtk_layer_shell::Edge::Right, pos == BarPosition::Right);
win.connect_leave_notify_event(move |win, ev| {
const THRESHOLD: f64 = 3.0;
@ -111,7 +99,7 @@ impl Popup {
win.hide();
}
Inhibit(false)
Propagation::Proceed
});
Self {
@ -229,6 +217,6 @@ impl Popup {
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 glib::Continue;
use gtk::ffi::GTK_STYLE_PROVIDER_PRIORITY_USER;
use gtk::prelude::CssProviderExt;
use gtk::{gdk, gio, CssProvider, StyleContext};
@ -8,7 +7,7 @@ use notify::event::ModifyKind;
use notify::{recommended_watcher, Event, EventKind, RecursiveMode, Result, Watcher};
use std::path::PathBuf;
use std::time::Duration;
use tokio::spawn;
use tokio::sync::mpsc;
use tokio::time::sleep;
use tracing::{debug, error, info};
@ -36,7 +35,7 @@ pub fn load_css(style_path: PathBuf) {
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 {
let style_path2 = style_path.clone();
@ -49,7 +48,7 @@ pub fn load_css(style_path: PathBuf) {
.map(|p| p == &style_path2)
.unwrap_or_default()
{
send!(tx, style_path2.clone());
try_send!(tx, style_path2.clone());
}
}
Err(e) => error!("Error occurred when watching stylesheet: {:?}", e),
@ -70,19 +69,14 @@ pub fn load_css(style_path: PathBuf) {
}
});
{
rx.attach(None, move |path| {
info!("Reloading CSS");
if let Err(err) = provider
.load_from_file(&gio::File::for_path(path)) {
error!("{:?}", Report::new(err)
.wrap_err("Failed to load CSS")
.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)
});
}
glib_recv_mpsc!(rx, path => {
info!("Reloading CSS");
if let Err(err) = provider.load_from_file(&gio::File::for_path(path)) {
error!("{:?}", Report::new(err)
.wrap_err("Failed to load CSS")
.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.")
);
}
});
}