1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-08-16 22:31:03 +02:00

refactor: replace channel macros with ext trait methods

This commit is contained in:
Jake Stanger 2025-05-18 15:17:09 +01:00
parent d5744f597c
commit f929aef2d9
No known key found for this signature in database
GPG key ID: C51FC8F9CB0BEA61
50 changed files with 658 additions and 476 deletions

155
src/channels.rs Normal file
View file

@ -0,0 +1,155 @@
use crate::modules::ModuleUpdateEvent;
use crate::spawn;
use smithay_client_toolkit::reexports::calloop;
use std::fmt::Debug;
use tokio::sync::{broadcast, mpsc};
pub trait SyncSenderExt<T> {
/// Asynchronously sends a message on the channel,
/// panicking if it cannot be sent.
///
/// This should be used in cases where sending should *never* fail,
/// or where failing indicates a serious bug.
fn send_expect(&self, message: T);
}
impl<T> SyncSenderExt<T> for std::sync::mpsc::Sender<T> {
#[inline]
fn send_expect(&self, message: T) {
self.send(message).expect(crate::error::ERR_CHANNEL_SEND);
}
}
impl<T> SyncSenderExt<T> for calloop::channel::Sender<T> {
#[inline]
fn send_expect(&self, message: T) {
self.send(message).expect(crate::error::ERR_CHANNEL_SEND);
}
}
impl<T: Debug> SyncSenderExt<T> for broadcast::Sender<T> {
#[inline]
fn send_expect(&self, message: T) {
self.send(message).expect(crate::error::ERR_CHANNEL_SEND);
}
}
pub trait AsyncSenderExt<T>: Sync + Send + Sized + Clone {
/// Asynchronously sends a message on the channel,
/// panicking if it cannot be sent.
///
/// This should be used in cases where sending should *never* fail,
/// or where failing indicates a serious bug.
fn send_expect(&self, message: T) -> impl Future<Output = ()> + Send;
/// Asynchronously sends a message on the channel,
/// spawning a task to allow it to be sent in the background,
/// and panicking if it cannot be sent.
///
/// Note that this function will return *before* the message is sent.
///
/// This should be used in cases where sending should *never* fail,
/// or where failing indicates a serious bug.
#[inline]
fn send_spawn(&self, message: T)
where
Self: 'static,
T: Send + 'static,
{
let tx = self.clone();
spawn(async move { tx.send_expect(message).await });
}
/// Shorthand for [`AsyncSenderExt::send_expect`]
/// when sending a [`ModuleUpdateEvent::Update`].
#[inline]
async fn send_update<U: Clone>(&self, update: U)
where
Self: AsyncSenderExt<ModuleUpdateEvent<U>>,
{
self.send_expect(ModuleUpdateEvent::Update(update)).await;
}
/// Shorthand for [`AsyncSenderExt::send_spawn`]
/// when sending a [`ModuleUpdateEvent::Update`].
#[inline]
fn send_update_spawn<U>(&self, update: U)
where
Self: AsyncSenderExt<ModuleUpdateEvent<U>> + 'static,
U: Clone + Send + 'static,
{
self.send_spawn(ModuleUpdateEvent::Update(update));
}
}
impl<T: Send> AsyncSenderExt<T> for mpsc::Sender<T> {
#[inline]
async fn send_expect(&self, message: T) {
self.send(message)
.await
.expect(crate::error::ERR_CHANNEL_SEND);
}
}
pub trait MpscReceiverExt<T> {
/// Spawns a `GLib` future on the local thread, and calls `rx.recv()`
/// in a loop, passing the message to `f`.
///
/// This allows use of `GObjects` and futures in the same context.
fn recv_glib<F>(self, f: F)
where
F: FnMut(T) + 'static;
}
impl<T: 'static> MpscReceiverExt<T> for mpsc::Receiver<T> {
fn recv_glib<F>(mut self, mut f: F)
where
F: FnMut(T) + 'static,
{
glib::spawn_future_local(async move {
while let Some(val) = self.recv().await {
f(val);
}
});
}
}
pub trait BroadcastReceiverExt<T>
where
T: Debug + Clone + 'static,
{
/// Spawns a `GLib` future on the local thread, and calls `rx.recv()`
/// in a loop, passing the message to `f`.
///
/// This allows use of `GObjects` and futures in the same context.
fn recv_glib<F>(self, f: F)
where
F: FnMut(T) + 'static;
}
impl<T> BroadcastReceiverExt<T> for broadcast::Receiver<T>
where
T: Debug + Clone + 'static,
{
fn recv_glib<F>(mut self, mut f: F)
where
F: FnMut(T) + 'static,
{
glib::spawn_future_local(async move {
loop {
match self.recv().await {
Ok(val) => f(val),
Err(broadcast::error::RecvError::Lagged(count)) => {
tracing::warn!(
"Channel lagged behind by {count}, this may result in unexpected or broken behaviour"
);
}
Err(err) => {
tracing::error!("{err:?}");
break;
}
}
}
});
}
}

View file

@ -1,5 +1,6 @@
use super::wayland::{self, ClipboardItem}; use super::wayland::{self, ClipboardItem};
use crate::{arc_mut, lock, register_client, spawn, try_send}; use crate::channels::AsyncSenderExt;
use crate::{arc_mut, lock, register_client, spawn};
use indexmap::IndexMap; use indexmap::IndexMap;
use indexmap::map::Iter; use indexmap::map::Iter;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -46,7 +47,7 @@ impl Client {
let senders = lock!(senders); let senders = lock!(senders);
let iter = senders.iter(); let iter = senders.iter();
for (tx, _) in iter { for (tx, _) in iter {
try_send!(tx, ClipboardEvent::Add(item.clone())); tx.send_spawn(ClipboardEvent::Add(item.clone()));
} }
lock!(cache).insert(item, senders.len()); lock!(cache).insert(item, senders.len());
@ -74,16 +75,17 @@ impl Client {
let removed_id = lock!(cache) let removed_id = lock!(cache)
.remove_ref_first() .remove_ref_first()
.expect("Clipboard cache unexpectedly empty"); .expect("Clipboard cache unexpectedly empty");
try_send!(tx, ClipboardEvent::Remove(removed_id));
tx.send_spawn(ClipboardEvent::Remove(removed_id));
} }
try_send!(tx, ClipboardEvent::Add(item.clone())); tx.send_spawn(ClipboardEvent::Add(item.clone()));
} }
}, },
|existing_id| { |existing_id| {
let senders = lock!(senders); let senders = lock!(senders);
let iter = senders.iter(); let iter = senders.iter();
for (tx, _) in iter { for (tx, _) in iter {
try_send!(tx, ClipboardEvent::Activate(existing_id)); tx.send_spawn(ClipboardEvent::Activate(existing_id));
} }
}, },
); );
@ -106,7 +108,7 @@ impl Client {
let iter = cache.iter(); let iter = cache.iter();
for (_, (item, _)) in iter { for (_, (item, _)) in iter {
try_send!(tx, ClipboardEvent::Add(item.clone())); tx.send_spawn(ClipboardEvent::Add(item.clone()));
} }
} }
@ -130,7 +132,7 @@ impl Client {
let senders = lock!(self.senders); let senders = lock!(self.senders);
let iter = senders.iter(); let iter = senders.iter();
for (tx, _) in iter { for (tx, _) in iter {
try_send!(tx, ClipboardEvent::Activate(id)); tx.send_spawn(ClipboardEvent::Activate(id));
} }
} }
@ -140,7 +142,7 @@ impl Client {
let senders = lock!(self.senders); let senders = lock!(self.senders);
let iter = senders.iter(); let iter = senders.iter();
for (tx, _) in iter { for (tx, _) in iter {
try_send!(tx, ClipboardEvent::Remove(id)); tx.send_spawn(ClipboardEvent::Remove(id));
} }
} }
} }

View file

@ -3,7 +3,8 @@ use super::{BindModeClient, BindModeUpdate};
#[cfg(feature = "keyboard+hyprland")] #[cfg(feature = "keyboard+hyprland")]
use super::{KeyboardLayoutClient, KeyboardLayoutUpdate}; use super::{KeyboardLayoutClient, KeyboardLayoutUpdate};
use super::{Visibility, Workspace}; use super::{Visibility, Workspace};
use crate::{arc_mut, lock, send, spawn_blocking}; use crate::channels::SyncSenderExt;
use crate::{arc_mut, lock, spawn_blocking};
use color_eyre::Result; use color_eyre::Result;
use hyprland::ctl::switch_xkb_layout; use hyprland::ctl::switch_xkb_layout;
use hyprland::data::{Devices, Workspace as HWorkspace, Workspaces}; use hyprland::data::{Devices, Workspace as HWorkspace, Workspaces};
@ -121,7 +122,7 @@ impl Client {
match workspace { match workspace {
Ok(Some(workspace)) => { Ok(Some(workspace)) => {
send!(tx, WorkspaceUpdate::Add(workspace)); tx.send_expect(WorkspaceUpdate::Add(workspace));
} }
Err(e) => error!("Failed to get workspace: {e:#}"), Err(e) => error!("Failed to get workspace: {e:#}"),
_ => {} _ => {}
@ -230,13 +231,10 @@ impl Client {
let _lock = lock!(lock); let _lock = lock!(lock);
debug!("Received workspace rename: {data:?}"); debug!("Received workspace rename: {data:?}");
send!( tx.send_expect(WorkspaceUpdate::Rename {
tx, id: data.id as i64,
WorkspaceUpdate::Rename { name: data.name,
id: data.id as i64, });
name: data.name
}
);
}); });
} }
@ -247,7 +245,7 @@ impl Client {
event_listener.add_workspace_deleted_handler(move |data| { event_listener.add_workspace_deleted_handler(move |data| {
let _lock = lock!(lock); let _lock = lock!(lock);
debug!("Received workspace destroy: {data:?}"); debug!("Received workspace destroy: {data:?}");
send!(tx, WorkspaceUpdate::Remove(data.id as i64)); tx.send_expect(WorkspaceUpdate::Remove(data.id as i64));
}); });
} }
@ -271,13 +269,10 @@ impl Client {
error!("Unable to locate client"); error!("Unable to locate client");
}, },
|c| { |c| {
send!( tx.send_expect(WorkspaceUpdate::Urgent {
tx, id: c.workspace.id as i64,
WorkspaceUpdate::Urgent { urgent: true,
id: c.workspace.id as i64, });
urgent: true,
}
);
}, },
); );
}); });
@ -333,8 +328,7 @@ impl Client {
}; };
debug!("Received layout: {layout:?}"); debug!("Received layout: {layout:?}");
tx.send_expect(KeyboardLayoutUpdate(layout));
send!(tx, KeyboardLayoutUpdate(layout));
}); });
} }
@ -351,13 +345,10 @@ impl Client {
let _lock = lock!(lock); let _lock = lock!(lock);
debug!("Received bind mode: {bind_mode:?}"); debug!("Received bind mode: {bind_mode:?}");
send!( tx.send_expect(BindModeUpdate {
tx, name: bind_mode,
BindModeUpdate { pango_markup: false,
name: bind_mode, });
pango_markup: false,
}
);
}); });
} }
@ -369,21 +360,15 @@ impl Client {
workspace: Workspace, workspace: Workspace,
tx: &Sender<WorkspaceUpdate>, tx: &Sender<WorkspaceUpdate>,
) { ) {
send!( tx.send_expect(WorkspaceUpdate::Focus {
tx, old: prev_workspace.take(),
WorkspaceUpdate::Focus { new: workspace.clone(),
old: prev_workspace.take(), });
new: workspace.clone(),
}
);
send!( tx.send_expect(WorkspaceUpdate::Urgent {
tx, id: workspace.id,
WorkspaceUpdate::Urgent { urgent: false,
id: workspace.id, });
urgent: false,
}
);
prev_workspace.replace(workspace); prev_workspace.replace(workspace);
} }
@ -439,7 +424,9 @@ impl super::WorkspaceClient for Client {
}) })
.collect(); .collect();
send!(self.workspace.tx, WorkspaceUpdate::Init(workspaces)); self.workspace
.tx
.send_expect(WorkspaceUpdate::Init(workspaces));
} }
Err(e) => { Err(e) => {
error!("Failed to get workspaces: {e:#}"); error!("Failed to get workspaces: {e:#}");
@ -486,7 +473,9 @@ impl KeyboardLayoutClient for Client {
.map(|k| k.active_keymap.clone()) .map(|k| k.active_keymap.clone())
}) { }) {
Ok(Some(layout)) => { Ok(Some(layout)) => {
send!(self.keyboard_layout.tx, KeyboardLayoutUpdate(layout)); self.keyboard_layout
.tx
.send_expect(KeyboardLayoutUpdate(layout));
} }
Ok(None) => error!("Failed to get current keyboard layout hyprland"), Ok(None) => error!("Failed to get current keyboard layout hyprland"),
Err(err) => error!("Failed to get devices: {err:#?}"), Err(err) => error!("Failed to get devices: {err:#?}"),

View file

@ -1,4 +1,4 @@
use crate::{clients::compositor::Visibility, send, spawn}; use crate::{clients::compositor::Visibility, spawn};
use color_eyre::Report; use color_eyre::Report;
use tracing::{error, warn}; use tracing::{error, warn};
@ -7,6 +7,7 @@ use tokio::sync::broadcast;
use super::{Workspace as IronWorkspace, WorkspaceClient, WorkspaceUpdate}; use super::{Workspace as IronWorkspace, WorkspaceClient, WorkspaceUpdate};
mod connection; mod connection;
use crate::channels::SyncSenderExt;
use connection::{Action, Connection, Event, Request, WorkspaceReferenceArg}; use connection::{Action, Connection, Event, Request, WorkspaceReferenceArg};
#[derive(Debug)] #[derive(Debug)]
@ -151,7 +152,7 @@ impl Client {
}; };
for event in events { for event in events {
send!(tx, event); tx.send_expect(event);
} }
} }

View file

@ -1,6 +1,7 @@
use super::{Visibility, Workspace}; use super::{Visibility, Workspace};
use crate::channels::SyncSenderExt;
use crate::clients::sway::Client; use crate::clients::sway::Client;
use crate::{await_sync, error, send, spawn}; use crate::{await_sync, error, spawn};
use color_eyre::Report; use color_eyre::Report;
use swayipc_async::{InputChange, InputEvent, Node, WorkspaceChange, WorkspaceEvent}; use swayipc_async::{InputChange, InputEvent, Node, WorkspaceChange, WorkspaceEvent};
use tokio::sync::broadcast::{Receiver, channel}; use tokio::sync::broadcast::{Receiver, channel};
@ -8,7 +9,7 @@ use tokio::sync::broadcast::{Receiver, channel};
#[cfg(feature = "workspaces")] #[cfg(feature = "workspaces")]
use super::WorkspaceUpdate; use super::WorkspaceUpdate;
#[cfg(feature = "workspaces")] #[cfg(feature = "workspaces+sway")]
impl super::WorkspaceClient for Client { impl super::WorkspaceClient for Client {
fn focus(&self, id: i64) { fn focus(&self, id: i64) {
let client = self.connection().clone(); let client = self.connection().clone();
@ -49,13 +50,13 @@ impl super::WorkspaceClient for Client {
let event = let event =
WorkspaceUpdate::Init(workspaces.into_iter().map(Workspace::from).collect()); WorkspaceUpdate::Init(workspaces.into_iter().map(Workspace::from).collect());
send!(tx, event); tx.send_expect(event);
drop(client); drop(client);
self.add_listener::<WorkspaceEvent>(move |event| { self.add_listener::<WorkspaceEvent>(move |event| {
let update = WorkspaceUpdate::from(event.clone()); let update = WorkspaceUpdate::from(event.clone());
send!(tx, update); tx.send_expect(update);
}) })
.await .await
.expect("to add listener"); .expect("to add listener");
@ -198,7 +199,7 @@ impl KeyboardLayoutClient for Client {
let inputs = client.get_inputs().await.expect("to get inputs"); let inputs = client.get_inputs().await.expect("to get inputs");
if let Some(layout) = inputs.into_iter().find_map(|i| i.xkb_active_layout_name) { if let Some(layout) = inputs.into_iter().find_map(|i| i.xkb_active_layout_name) {
send!(tx, KeyboardLayoutUpdate(layout)); tx.send_expect(KeyboardLayoutUpdate(layout));
} else { } else {
error!("Failed to get keyboard layout from Sway!"); error!("Failed to get keyboard layout from Sway!");
} }
@ -207,7 +208,7 @@ impl KeyboardLayoutClient for Client {
self.add_listener::<InputEvent>(move |event| { self.add_listener::<InputEvent>(move |event| {
if let Ok(layout) = KeyboardLayoutUpdate::try_from(event.clone()) { if let Ok(layout) = KeyboardLayoutUpdate::try_from(event.clone()) {
send!(tx, layout); tx.send_expect(layout);
} }
}) })
.await .await
@ -255,13 +256,10 @@ impl BindModeClient for Client {
mode.change.clone() mode.change.clone()
}; };
send!( tx.send_expect(BindModeUpdate {
tx, name,
BindModeUpdate { pango_markup: mode.pango_markup,
name, });
pango_markup: mode.pango_markup,
}
);
}) })
.await .await
})?; })?;

View file

@ -1,4 +1,5 @@
use crate::{Ironbar, arc_rw, read_lock, send, spawn, write_lock}; use crate::channels::SyncSenderExt;
use crate::{Ironbar, arc_rw, read_lock, spawn, write_lock};
use color_eyre::{Report, Result}; use color_eyre::{Report, Result};
use colpetto::event::{AsRawEvent, DeviceEvent, KeyState, KeyboardEvent}; use colpetto::event::{AsRawEvent, DeviceEvent, KeyState, KeyboardEvent};
use colpetto::{DeviceCapability, Libinput}; use colpetto::{DeviceCapability, Libinput};
@ -179,7 +180,7 @@ impl Client {
device_path.display() device_path.display()
); );
write_lock!(self.known_devices).push(device_path.to_path_buf()); write_lock!(self.known_devices).push(device_path.to_path_buf());
send!(self.tx, Event::Device); self.tx.send_expect(Event::Device);
} }
} }
} }
@ -208,7 +209,7 @@ impl Client {
let data = KeyData { device_path, key }; let data = KeyData { device_path, key };
if let Ok(event) = data.try_into() { if let Ok(event) = data.try_into() {
send!(tx, event); tx.send_expect(event);
} }
}); });
} }

View file

@ -1,7 +1,8 @@
use super::{ use super::{
MusicClient, PlayerState, PlayerUpdate, ProgressTick, Status, TICK_INTERVAL_MS, Track, MusicClient, PlayerState, PlayerUpdate, ProgressTick, Status, TICK_INTERVAL_MS, Track,
}; };
use crate::{Ironbar, await_sync, send, spawn}; use crate::channels::SyncSenderExt;
use crate::{Ironbar, await_sync, spawn};
use color_eyre::Report; use color_eyre::Report;
use color_eyre::Result; use color_eyre::Result;
use mpd_client::client::{ConnectionEvent, Subsystem}; use mpd_client::client::{ConnectionEvent, Subsystem};
@ -97,7 +98,7 @@ impl Client {
let status = Status::from(status); let status = Status::from(status);
let update = PlayerUpdate::Update(Box::new(track), status); let update = PlayerUpdate::Update(Box::new(track), status);
send!(tx, update); tx.send_expect(update);
} }
Ok(()) Ok(())
@ -113,7 +114,7 @@ impl Client {
elapsed: status.elapsed, elapsed: status.elapsed,
}); });
send!(tx, update); tx.send_expect(update);
} }
} }
} }

View file

@ -1,6 +1,7 @@
use super::{MusicClient, PlayerState, PlayerUpdate, Status, TICK_INTERVAL_MS, Track}; use super::{MusicClient, PlayerState, PlayerUpdate, Status, TICK_INTERVAL_MS, Track};
use crate::channels::SyncSenderExt;
use crate::clients::music::ProgressTick; use crate::clients::music::ProgressTick;
use crate::{arc_mut, lock, send, spawn_blocking}; use crate::{arc_mut, lock, spawn_blocking};
use color_eyre::Result; use color_eyre::Result;
use mpris::{DBusError, Event, Metadata, PlaybackStatus, Player, PlayerFinder}; use mpris::{DBusError, Event, Metadata, PlaybackStatus, Player, PlayerFinder};
use std::cmp; use std::cmp;
@ -137,7 +138,7 @@ impl Client {
let mut players_locked = lock!(players); let mut players_locked = lock!(players);
players_locked.remove(identity); players_locked.remove(identity);
if players_locked.is_empty() { if players_locked.is_empty() {
send!(tx, PlayerUpdate::Update(Box::new(None), Status::default())); tx.send_expect(PlayerUpdate::Update(Box::new(None), Status::default()));
} }
}; };
@ -212,7 +213,7 @@ impl Client {
let track = Track::from(metadata); let track = Track::from(metadata);
let player_update = PlayerUpdate::Update(Box::new(Some(track)), status); let player_update = PlayerUpdate::Update(Box::new(Some(track)), status);
send!(tx, player_update); tx.send_expect(player_update);
Ok(()) Ok(())
} }
@ -242,7 +243,7 @@ impl Client {
duration: metadata.length(), duration: metadata.length(),
}); });
send!(tx, update); tx.send_expect(update);
} }
} }
} }
@ -327,7 +328,9 @@ impl MusicClient for Client {
state: PlayerState::Stopped, state: PlayerState::Stopped,
volume_percent: None, volume_percent: None,
}; };
send!(self.tx, PlayerUpdate::Update(Box::new(None), status));
self.tx
.send_expect(PlayerUpdate::Update(Box::new(None), status));
} }
rx rx

View file

@ -1,6 +1,7 @@
mod dbus; mod dbus;
use crate::{register_fallible_client, send, spawn}; use crate::channels::SyncSenderExt;
use crate::{register_fallible_client, spawn};
use color_eyre::{Report, Result}; use color_eyre::{Report, Result};
use dbus::SwayNcProxy; use dbus::SwayNcProxy;
use serde::Deserialize; use serde::Deserialize;
@ -60,7 +61,7 @@ impl Client {
.deserialize::<Event>() .deserialize::<Event>()
.expect("to deserialize"); .expect("to deserialize");
debug!("Received event: {ev:?}"); debug!("Received event: {ev:?}");
send!(tx, ev); tx.send_expect(ev);
} }
}); });
} }

View file

@ -1,7 +1,7 @@
mod sink; mod sink;
mod sink_input; mod sink_input;
use crate::{APP_ID, arc_mut, lock, register_client, send, spawn_blocking}; use crate::{APP_ID, arc_mut, lock, register_client, spawn_blocking};
use libpulse_binding::callbacks::ListResult; use libpulse_binding::callbacks::ListResult;
use libpulse_binding::context::introspect::{Introspector, ServerInfo}; use libpulse_binding::context::introspect::{Introspector, ServerInfo};
use libpulse_binding::context::subscribe::{Facility, InterestMaskSet, Operation}; use libpulse_binding::context::subscribe::{Facility, InterestMaskSet, Operation};
@ -14,6 +14,7 @@ use std::sync::{Arc, Mutex};
use tokio::sync::broadcast; use tokio::sync::broadcast;
use tracing::{debug, error, info, trace, warn}; use tracing::{debug, error, info, trace, warn};
use crate::channels::SyncSenderExt;
pub use sink::Sink; pub use sink::Sink;
pub use sink_input::SinkInput; pub use sink_input::SinkInput;
@ -271,7 +272,7 @@ fn set_default_sink(
{ {
sink.active = true; sink.active = true;
debug!("Set sink active: {}", sink.name); debug!("Set sink active: {}", sink.name);
send!(tx, Event::UpdateSink(sink.clone())); tx.send_expect(Event::UpdateSink(sink.clone()));
} else { } else {
warn!("Couldn't find sink: {}", default_sink_name); warn!("Couldn't find sink: {}", default_sink_name);
} }

View file

@ -1,5 +1,6 @@
use super::{ArcMutVec, Client, ConnectionState, Event, percent_to_volume, volume_to_percent}; use super::{ArcMutVec, Client, ConnectionState, Event, percent_to_volume, volume_to_percent};
use crate::{lock, send}; use crate::channels::SyncSenderExt;
use crate::lock;
use libpulse_binding::callbacks::ListResult; use libpulse_binding::callbacks::ListResult;
use libpulse_binding::context::Context; use libpulse_binding::context::Context;
use libpulse_binding::context::introspect::SinkInfo; use libpulse_binding::context::introspect::SinkInfo;
@ -62,7 +63,7 @@ impl Client {
let ListResult::Item(info) = info else { let ListResult::Item(info) = info else {
return; return;
}; };
send!(tx, info.volume); tx.send_expect(info.volume);
}); });
let new_volume = percent_to_volume(volume_percent); let new_volume = percent_to_volume(volume_percent);
@ -129,7 +130,7 @@ pub fn add(info: ListResult<&SinkInfo>, sinks: &ArcMutVec<Sink>, tx: &broadcast:
trace!("adding {info:?}"); trace!("adding {info:?}");
lock!(sinks).push(info.into()); lock!(sinks).push(info.into());
send!(tx, Event::AddSink(info.into())); tx.send_expect(Event::AddSink(info.into()));
} }
fn update( fn update(
@ -170,7 +171,7 @@ fn update(
} }
} }
send!(tx, Event::UpdateSink(sink)); tx.send_expect(Event::UpdateSink(sink));
} }
fn remove(index: u32, sinks: &ArcMutVec<Sink>, tx: &broadcast::Sender<Event>) { fn remove(index: u32, sinks: &ArcMutVec<Sink>, tx: &broadcast::Sender<Event>) {
@ -180,6 +181,6 @@ fn remove(index: u32, sinks: &ArcMutVec<Sink>, tx: &broadcast::Sender<Event>) {
if let Some(pos) = sinks.iter().position(|s| s.index == index) { if let Some(pos) = sinks.iter().position(|s| s.index == index) {
let info = sinks.remove(pos); let info = sinks.remove(pos);
send!(tx, Event::RemoveSink(info.name)); tx.send_expect(Event::RemoveSink(info.name));
} }
} }

View file

@ -1,5 +1,6 @@
use super::{ArcMutVec, Client, ConnectionState, Event, percent_to_volume, volume_to_percent}; use super::{ArcMutVec, Client, ConnectionState, Event, percent_to_volume, volume_to_percent};
use crate::{lock, send}; use crate::channels::SyncSenderExt;
use crate::lock;
use libpulse_binding::callbacks::ListResult; use libpulse_binding::callbacks::ListResult;
use libpulse_binding::context::Context; use libpulse_binding::context::Context;
use libpulse_binding::context::introspect::SinkInputInfo; use libpulse_binding::context::introspect::SinkInputInfo;
@ -49,7 +50,7 @@ impl Client {
let ListResult::Item(info) = info else { let ListResult::Item(info) = info else {
return; return;
}; };
send!(tx, info.volume); tx.send_expect(info.volume);
}); });
let new_volume = percent_to_volume(volume_percent); let new_volume = percent_to_volume(volume_percent);
@ -118,7 +119,7 @@ pub fn add(
trace!("adding {info:?}"); trace!("adding {info:?}");
lock!(inputs).push(info.into()); lock!(inputs).push(info.into());
send!(tx, Event::AddInput(info.into())); tx.send_expect(Event::AddInput(info.into()));
} }
fn update( fn update(
@ -142,7 +143,7 @@ fn update(
inputs[pos] = info.into(); inputs[pos] = info.into();
} }
send!(tx, Event::UpdateInput(info.into())); tx.send_expect(Event::UpdateInput(info.into()));
} }
fn remove(index: u32, inputs: &ArcMutVec<SinkInput>, tx: &broadcast::Sender<Event>) { fn remove(index: u32, inputs: &ArcMutVec<SinkInput>, tx: &broadcast::Sender<Event>) {
@ -152,6 +153,6 @@ fn remove(index: u32, inputs: &ArcMutVec<SinkInput>, tx: &broadcast::Sender<Even
if let Some(pos) = inputs.iter().position(|s| s.index == index) { if let Some(pos) = inputs.iter().position(|s| s.index == index) {
let info = inputs.remove(pos); let info = inputs.remove(pos);
send!(tx, Event::RemoveInput(info.index)); tx.send_expect(Event::RemoveInput(info.index));
} }
} }

View file

@ -3,10 +3,11 @@ mod wl_output;
mod wl_seat; mod wl_seat;
use crate::error::{ERR_CHANNEL_RECV, ExitCode}; use crate::error::{ERR_CHANNEL_RECV, ExitCode};
use crate::{arc_mut, lock, register_client, send, spawn, spawn_blocking}; use crate::{arc_mut, lock, register_client, spawn, spawn_blocking};
use std::process::exit; use std::process::exit;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use crate::channels::SyncSenderExt;
use calloop_channel::Event::Msg; use calloop_channel::Event::Msg;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use color_eyre::{Help, Report}; use color_eyre::{Help, Report};
@ -151,11 +152,11 @@ impl Client {
spawn(async move { spawn(async move {
while let Some(event) = event_rx.recv().await { while let Some(event) = event_rx.recv().await {
match event { match event {
Event::Output(event) => send!(output_tx, event), Event::Output(event) => output_tx.send_expect(event),
#[cfg(any(feature = "focused", feature = "launcher"))] #[cfg(any(feature = "focused", feature = "launcher"))]
Event::Toplevel(event) => send!(toplevel_tx, event), Event::Toplevel(event) => toplevel_tx.send_expect(event),
#[cfg(feature = "clipboard")] #[cfg(feature = "clipboard")]
Event::Clipboard(item) => send!(clipboard_tx, item), Event::Clipboard(item) => clipboard_tx.send_expect(item),
}; };
} }
}); });
@ -176,7 +177,7 @@ impl Client {
/// Sends a request to the environment event loop, /// Sends a request to the environment event loop,
/// and returns the response. /// and returns the response.
fn send_request(&self, request: Request) -> Response { fn send_request(&self, request: Request) -> Response {
send!(self.tx, request); self.tx.send_expect(request);
lock!(self.rx).recv().expect(ERR_CHANNEL_RECV) lock!(self.rx).recv().expect(ERR_CHANNEL_RECV)
} }
@ -333,12 +334,12 @@ impl Environment {
match event { match event {
Msg(Request::Roundtrip) => { Msg(Request::Roundtrip) => {
debug!("received roundtrip request"); debug!("received roundtrip request");
send!(env.response_tx, Response::Ok); env.response_tx.send_expect(Response::Ok);
} }
#[cfg(feature = "ipc")] #[cfg(feature = "ipc")]
Msg(Request::OutputInfoAll) => { Msg(Request::OutputInfoAll) => {
let infos = env.output_info_all(); let infos = env.output_info_all();
send!(env.response_tx, Response::OutputInfoAll(infos)); env.response_tx.send_expect(Response::OutputInfoAll(infos));
} }
#[cfg(any(feature = "focused", feature = "launcher"))] #[cfg(any(feature = "focused", feature = "launcher"))]
Msg(Request::ToplevelInfoAll) => { Msg(Request::ToplevelInfoAll) => {
@ -347,7 +348,9 @@ impl Environment {
.iter() .iter()
.filter_map(ToplevelHandle::info) .filter_map(ToplevelHandle::info)
.collect(); .collect();
send!(env.response_tx, Response::ToplevelInfoAll(infos));
env.response_tx
.send_expect(Response::ToplevelInfoAll(infos));
} }
#[cfg(feature = "launcher")] #[cfg(feature = "launcher")]
Msg(Request::ToplevelFocus(id)) => { Msg(Request::ToplevelFocus(id)) => {
@ -361,7 +364,7 @@ impl Environment {
handle.focus(&seat); handle.focus(&seat);
} }
send!(env.response_tx, Response::Ok); env.response_tx.send_expect(Response::Ok);
} }
#[cfg(feature = "launcher")] #[cfg(feature = "launcher")]
Msg(Request::ToplevelMinimize(id)) => { Msg(Request::ToplevelMinimize(id)) => {
@ -374,17 +377,17 @@ impl Environment {
handle.minimize(); handle.minimize();
} }
send!(env.response_tx, Response::Ok); env.response_tx.send_expect(Response::Ok);
} }
#[cfg(feature = "clipboard")] #[cfg(feature = "clipboard")]
Msg(Request::CopyToClipboard(item)) => { Msg(Request::CopyToClipboard(item)) => {
env.copy_to_clipboard(item); env.copy_to_clipboard(item);
send!(env.response_tx, Response::Ok); env.response_tx.send_expect(Response::Ok);
} }
#[cfg(feature = "clipboard")] #[cfg(feature = "clipboard")]
Msg(Request::ClipboardItem) => { Msg(Request::ClipboardItem) => {
let item = lock!(env.clipboard).clone(); let item = lock!(env.clipboard).clone();
send!(env.response_tx, Response::ClipboardItem(item)); env.response_tx.send_expect(Response::ClipboardItem(item));
} }
calloop_channel::Event::Closed => error!("request channel unexpectedly closed"), calloop_channel::Event::Closed => error!("request channel unexpectedly closed"),
} }

View file

@ -1,5 +1,5 @@
use super::{Client, Environment, Event}; use super::{Client, Environment, Event};
use crate::try_send; use crate::channels::AsyncSenderExt;
use smithay_client_toolkit::output::{OutputHandler, OutputInfo, OutputState}; use smithay_client_toolkit::output::{OutputHandler, OutputInfo, OutputState};
use tokio::sync::broadcast; use tokio::sync::broadcast;
use tracing::{debug, error}; use tracing::{debug, error};
@ -63,13 +63,10 @@ impl OutputHandler for Environment {
fn new_output(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, output: WlOutput) { fn new_output(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, output: WlOutput) {
debug!("Handler received new output"); debug!("Handler received new output");
if let Some(info) = self.output_state.info(&output) { if let Some(info) = self.output_state.info(&output) {
try_send!( self.event_tx.send_spawn(Event::Output(OutputEvent {
self.event_tx, output: info,
Event::Output(OutputEvent { event_type: OutputEventType::New,
output: info, }));
event_type: OutputEventType::New
})
);
} else { } else {
error!("Output is missing information!"); error!("Output is missing information!");
} }
@ -78,13 +75,10 @@ impl OutputHandler for Environment {
fn update_output(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, output: WlOutput) { fn update_output(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, output: WlOutput) {
debug!("Handle received output update"); debug!("Handle received output update");
if let Some(info) = self.output_state.info(&output) { if let Some(info) = self.output_state.info(&output) {
try_send!( self.event_tx.send_spawn(Event::Output(OutputEvent {
self.event_tx, output: info,
Event::Output(OutputEvent { event_type: OutputEventType::Update,
output: info, }));
event_type: OutputEventType::Update
})
);
} else { } else {
error!("Output is missing information!"); error!("Output is missing information!");
} }
@ -93,13 +87,10 @@ impl OutputHandler for Environment {
fn output_destroyed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, output: WlOutput) { fn output_destroyed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, output: WlOutput) {
debug!("Handle received output destruction"); debug!("Handle received output destruction");
if let Some(info) = self.output_state.info(&output) { if let Some(info) = self.output_state.info(&output) {
try_send!( self.event_tx.send_spawn(Event::Output(OutputEvent {
self.event_tx, output: info,
Event::Output(OutputEvent { event_type: OutputEventType::Destroyed,
output: info, }));
event_type: OutputEventType::Destroyed
})
);
} else { } else {
error!("Output is missing information!"); error!("Output is missing information!");
} }

View file

@ -7,7 +7,8 @@ use self::device::{DataControlDeviceDataExt, DataControlDeviceHandler};
use self::offer::{DataControlDeviceOffer, DataControlOfferHandler}; use self::offer::{DataControlDeviceOffer, DataControlOfferHandler};
use self::source::DataControlSourceHandler; use self::source::DataControlSourceHandler;
use super::{Client, Environment, Event, Request, Response}; use super::{Client, Environment, Event, Request, Response};
use crate::{Ironbar, lock, spawn, try_send}; use crate::channels::AsyncSenderExt;
use crate::{Ironbar, lock, spawn};
use color_eyre::Result; use color_eyre::Result;
use device::DataControlDevice; use device::DataControlDevice;
use glib::Bytes; use glib::Bytes;
@ -219,14 +220,12 @@ impl DataControlDeviceHandler for Environment {
let Some(mime_type) = MimeType::parse_multiple(&mime_types) else { let Some(mime_type) = MimeType::parse_multiple(&mime_types) else {
lock!(self.clipboard).take(); lock!(self.clipboard).take();
// send an event so the clipboard module is aware it's changed // send an event so the clipboard module is aware it's changed
try_send!( self.event_tx.send_spawn(Event::Clipboard(ClipboardItem {
self.event_tx, id: usize::MAX,
Event::Clipboard(ClipboardItem { mime_type: String::new().into(),
id: usize::MAX, value: Arc::new(ClipboardValue::Other),
mime_type: String::new().into(), }));
value: Arc::new(ClipboardValue::Other)
})
);
return; return;
}; };
@ -239,7 +238,7 @@ impl DataControlDeviceHandler for Environment {
match Self::read_file(&mime_type, &mut read_pipe).await { match Self::read_file(&mime_type, &mut read_pipe).await {
Ok(item) => { Ok(item) => {
lock!(clipboard).replace(item.clone()); lock!(clipboard).replace(item.clone());
try_send!(tx, Event::Clipboard(item)); tx.send_spawn(Event::Clipboard(item));
} }
Err(err) => error!("{err:?}"), Err(err) => error!("{err:?}"),
} }

View file

@ -4,11 +4,11 @@ pub mod manager;
use self::handle::ToplevelHandleHandler; use self::handle::ToplevelHandleHandler;
use self::manager::ToplevelManagerHandler; use self::manager::ToplevelManagerHandler;
use super::{Client, Environment, Event, Request, Response}; use super::{Client, Environment, Event, Request, Response};
use crate::try_send;
use tokio::sync::broadcast; use tokio::sync::broadcast;
use tracing::{debug, error, trace}; use tracing::{debug, error, trace};
use wayland_client::{Connection, QueueHandle}; use wayland_client::{Connection, QueueHandle};
use crate::channels::AsyncSenderExt;
pub use handle::{ToplevelHandle, ToplevelInfo}; pub use handle::{ToplevelHandle, ToplevelInfo};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -71,7 +71,8 @@ impl ToplevelHandleHandler for Environment {
trace!("Adding new handle: {info:?}"); trace!("Adding new handle: {info:?}");
self.handles.push(handle.clone()); self.handles.push(handle.clone());
if let Some(info) = handle.info() { if let Some(info) = handle.info() {
try_send!(self.event_tx, Event::Toplevel(ToplevelEvent::New(info))); self.event_tx
.send_spawn(Event::Toplevel(ToplevelEvent::New(info)));
} }
} }
None => { None => {
@ -92,7 +93,8 @@ impl ToplevelHandleHandler for Environment {
Some(info) => { Some(info) => {
trace!("Updating handle: {info:?}"); trace!("Updating handle: {info:?}");
if let Some(info) = handle.info() { if let Some(info) = handle.info() {
try_send!(self.event_tx, Event::Toplevel(ToplevelEvent::Update(info))); self.event_tx
.send_spawn(Event::Toplevel(ToplevelEvent::Update(info)));
} }
} }
None => { None => {
@ -111,7 +113,8 @@ impl ToplevelHandleHandler for Environment {
self.handles.retain(|h| h != &handle); self.handles.retain(|h| h != &handle);
if let Some(info) = handle.info() { if let Some(info) = handle.info() {
try_send!(self.event_tx, Event::Toplevel(ToplevelEvent::Remove(info))); self.event_tx
.send_spawn(Event::Toplevel(ToplevelEvent::Remove(info)));
} }
} }
} }

View file

@ -1,7 +1,8 @@
use crate::script::Script;
#[cfg(feature = "ipc")] #[cfg(feature = "ipc")]
use crate::{Ironbar, send_async}; use crate::Ironbar;
use crate::{glib_recv_mpsc, spawn, try_send}; use crate::channels::{AsyncSenderExt, MpscReceiverExt};
use crate::script::Script;
use crate::spawn;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use serde::Deserialize; use serde::Deserialize;
use tokio::sync::mpsc; use tokio::sync::mpsc;
@ -18,7 +19,7 @@ pub enum DynamicBool {
} }
impl DynamicBool { impl DynamicBool {
pub fn subscribe<F>(self, mut f: F) pub fn subscribe<F>(self, f: F)
where where
F: FnMut(bool) + 'static, F: FnMut(bool) + 'static,
{ {
@ -42,14 +43,14 @@ impl DynamicBool {
let (tx, rx) = mpsc::channel(32); let (tx, rx) = mpsc::channel(32);
glib_recv_mpsc!(rx, val => f(val)); rx.recv_glib(f);
spawn(async move { spawn(async move {
match value { match value {
DynamicBool::Script(script) => { DynamicBool::Script(script) => {
script script
.run(None, |_, success| { .run(None, |_, success| {
try_send!(tx, success); tx.send_spawn(success);
}) })
.await; .await;
} }
@ -62,7 +63,7 @@ impl DynamicBool {
while let Ok(value) = rx.recv().await { while let Ok(value) = rx.recv().await {
let has_value = value.is_some_and(|s| is_truthy(&s)); let has_value = value.is_some_and(|s| is_truthy(&s));
send_async!(tx, has_value); tx.send_expect(has_value).await;
} }
} }
DynamicBool::Unknown(_) => unreachable!(), DynamicBool::Unknown(_) => unreachable!(),

View file

@ -1,7 +1,8 @@
#[cfg(feature = "ipc")] #[cfg(feature = "ipc")]
use crate::Ironbar; use crate::Ironbar;
use crate::channels::{AsyncSenderExt, MpscReceiverExt};
use crate::script::{OutputStream, Script}; use crate::script::{OutputStream, Script};
use crate::{arc_mut, glib_recv_mpsc, lock, spawn, try_send}; use crate::{arc_mut, lock, spawn};
use tokio::sync::mpsc; use tokio::sync::mpsc;
/// A segment of a dynamic string, /// A segment of a dynamic string,
@ -25,7 +26,7 @@ enum DynamicStringSegment {
/// label.set_label_escaped(&string); /// label.set_label_escaped(&string);
/// }); /// });
/// ``` /// ```
pub fn dynamic_string<F>(input: &str, mut f: F) pub fn dynamic_string<F>(input: &str, f: F)
where where
F: FnMut(String) + 'static, F: FnMut(String) + 'static,
{ {
@ -55,7 +56,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("");
try_send!(tx, string); tx.send_spawn(string);
} }
}) })
.await; .await;
@ -80,7 +81,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("");
try_send!(tx, string); tx.send_spawn(string);
} }
} }
}); });
@ -88,12 +89,12 @@ where
} }
} }
glib_recv_mpsc!(rx , val => f(val)); rx.recv_glib(f);
// initialize // initialize
if is_static { if is_static {
let label_parts = lock!(label_parts).join(""); let label_parts = lock!(label_parts).join("");
try_send!(tx, label_parts); tx.send_spawn(label_parts);
} }
} }

View file

@ -1,6 +1,7 @@
use crate::channels::{AsyncSenderExt, MpscReceiverExt};
use crate::desktop_file::get_desktop_icon_name; use crate::desktop_file::get_desktop_icon_name;
#[cfg(feature = "http")] #[cfg(feature = "http")]
use crate::{glib_recv_mpsc, send_async, spawn}; use crate::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;
@ -151,14 +152,14 @@ impl<'a> ImageProvider<'a> {
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_async!(tx, bytes); tx.send_expect(bytes).await;
} }
}); });
{ {
let size = self.size; let size = self.size;
let image = image.clone(); let image = image.clone();
glib_recv_mpsc!(rx, bytes => { rx.recv_glib(move |bytes| {
let stream = MemoryInputStream::from_bytes(&bytes); let stream = MemoryInputStream::from_bytes(&bytes);
let scale = image.scale_factor(); let scale = image.scale_factor();
@ -173,8 +174,7 @@ impl<'a> ImageProvider<'a> {
); );
// Different error types makes this a bit awkward // Different error types makes this a bit awkward
match pixbuf.map(|pixbuf| Self::create_and_load_surface(&pixbuf, &image)) match pixbuf.map(|pixbuf| Self::create_and_load_surface(&pixbuf, &image)) {
{
Ok(Err(err)) => error!("{err:?}"), Ok(Err(err)) => error!("{err:?}"),
Err(err) => error!("{err:?}"), Err(err) => error!("{err:?}"),
_ => {} _ => {}

View file

@ -13,11 +13,11 @@ use tokio::net::{UnixListener, UnixStream};
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 super::Ipc;
use crate::channels::{AsyncSenderExt, MpscReceiverExt};
use crate::ipc::{Command, Response}; use crate::ipc::{Command, Response};
use crate::style::load_css; use crate::style::load_css;
use crate::{Ironbar, glib_recv_mpsc, send_async, spawn, try_send}; use crate::{Ironbar, spawn};
use super::Ipc;
impl Ipc { impl Ipc {
/// Starts the IPC server on its socket. /// Starts the IPC server on its socket.
@ -66,9 +66,9 @@ impl Ipc {
}); });
let application = application.clone(); let application = application.clone();
glib_recv_mpsc!(cmd_rx, command => { cmd_rx.recv_glib(move |command| {
let res = Self::handle_command(command, &application, &ironbar); let res = Self::handle_command(command, &application, &ironbar);
try_send!(res_tx, res); res_tx.send_spawn(res);
}); });
} }
@ -91,7 +91,7 @@ impl Ipc {
debug!("Received command: {command:?}"); debug!("Received command: {command:?}");
send_async!(cmd_tx, command); cmd_tx.send_expect(command).await;
let res = res_rx let res = res_rx
.recv() .recv()
.await .await

View file

@ -1,6 +1,7 @@
#![doc = include_str!("../docs/Ironvars.md")] #![doc = include_str!("../docs/Ironvars.md")]
use crate::{arc_rw, read_lock, send, write_lock}; use crate::channels::SyncSenderExt;
use crate::{arc_rw, read_lock, write_lock};
use color_eyre::{Report, Result}; use color_eyre::{Report, Result};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
@ -74,14 +75,10 @@ impl VariableManager {
impl Namespace for VariableManager { impl Namespace for VariableManager {
fn get(&self, key: &str) -> Option<String> { fn get(&self, key: &str) -> Option<String> {
if key.contains('.') { if key.contains('.') {
let Some((ns, key)) = key.split_once('.') else { let (ns, key) = key.split_once('.')?;
return None;
};
let namespaces = read_lock!(self.namespaces); let namespaces = read_lock!(self.namespaces);
let Some(ns) = namespaces.get(ns) else { let ns = namespaces.get(ns)?;
return None;
};
ns.get(key).map(|v| v.to_owned()) ns.get(key).map(|v| v.to_owned())
} else { } else {
@ -161,14 +158,14 @@ impl IronVar {
/// The change is broadcast to all receivers. /// The change is broadcast to all receivers.
fn set(&mut self, value: Option<String>) { fn set(&mut self, value: Option<String>) {
self.value.clone_from(&value); self.value.clone_from(&value);
send!(self.tx, value); self.tx.send_expect(value);
} }
/// Subscribes to the variable. /// Subscribes to the variable.
/// The latest value is immediately sent to all receivers. /// The latest value is immediately sent to all receivers.
fn subscribe(&self) -> broadcast::Receiver<Option<String>> { fn subscribe(&self) -> broadcast::Receiver<Option<String>> {
let rx = self.tx.subscribe(); let rx = self.tx.subscribe();
send!(self.tx, self.value.clone()); self.tx.send_expect(self.value.clone());
rx rx
} }
} }

View file

@ -35,6 +35,7 @@ macro_rules! module_impl {
/// send_async!(tx, "my message"); /// send_async!(tx, "my message");
/// ``` /// ```
#[macro_export] #[macro_export]
#[deprecated(since = "0.17.0", note = "Use `AsyncSenderExt::send_expect` instead")]
macro_rules! send_async { macro_rules! send_async {
($tx:expr, $msg:expr) => { ($tx:expr, $msg:expr) => {
$tx.send($msg).await.expect($crate::error::ERR_CHANNEL_SEND) $tx.send($msg).await.expect($crate::error::ERR_CHANNEL_SEND)
@ -50,6 +51,7 @@ macro_rules! send_async {
/// send!(tx, "my message"); /// send!(tx, "my message");
/// ``` /// ```
#[macro_export] #[macro_export]
#[deprecated(since = "0.17.0", note = "Use `SyncSenderExt::send_expect` instead")]
macro_rules! send { macro_rules! send {
($tx:expr, $msg:expr) => { ($tx:expr, $msg:expr) => {
$tx.send($msg).expect($crate::error::ERR_CHANNEL_SEND) $tx.send($msg).expect($crate::error::ERR_CHANNEL_SEND)
@ -65,6 +67,7 @@ macro_rules! send {
/// try_send!(tx, "my message"); /// try_send!(tx, "my message");
/// ``` /// ```
#[macro_export] #[macro_export]
#[deprecated(since = "0.17.0", note = "Use `AsyncSenderExt::send_spawn` instead")]
macro_rules! try_send { macro_rules! try_send {
($tx:expr, $msg:expr) => { ($tx:expr, $msg:expr) => {
$tx.try_send($msg).expect($crate::error::ERR_CHANNEL_SEND) $tx.try_send($msg).expect($crate::error::ERR_CHANNEL_SEND)
@ -85,6 +88,7 @@ macro_rules! try_send {
/// module_update!(tx, "my event"); /// module_update!(tx, "my event");
/// ``` /// ```
#[macro_export] #[macro_export]
#[deprecated(since = "0.17.0", note = "Use `AsyncSenderExt::send_update` instead")]
macro_rules! module_update { macro_rules! module_update {
($tx:expr, $msg:expr) => { ($tx:expr, $msg:expr) => {
send_async!($tx, $crate::modules::ModuleUpdateEvent::Update($msg)) send_async!($tx, $crate::modules::ModuleUpdateEvent::Update($msg))
@ -105,6 +109,10 @@ macro_rules! module_update {
/// glib_recv(rx, msg => println!("{msg}")); /// glib_recv(rx, msg => println!("{msg}"));
/// ``` /// ```
#[macro_export] #[macro_export]
#[deprecated(
since = "0.17.0",
note = "Use `BroadcastReceiverExt::recv_glib` instead"
)]
macro_rules! glib_recv { macro_rules! glib_recv {
($rx:expr, $func:ident) => { glib_recv!($rx, ev => $func(ev)) }; ($rx:expr, $func:ident) => { glib_recv!($rx, ev => $func(ev)) };
@ -143,6 +151,7 @@ macro_rules! glib_recv {
/// glib_recv_mpsc(rx, msg => println!("{msg}")); /// glib_recv_mpsc(rx, msg => println!("{msg}"));
/// ``` /// ```
#[macro_export] #[macro_export]
#[deprecated(since = "0.17.0", note = "Use `MpscReceiverExt::recv_glib` instead")]
macro_rules! glib_recv_mpsc { macro_rules! glib_recv_mpsc {
($rx:expr, $func:ident) => { glib_recv_mpsc!($rx, ev => $func(ev)) }; ($rx:expr, $func:ident) => { glib_recv_mpsc!($rx, ev => $func(ev)) };

View file

@ -25,6 +25,7 @@ use tracing::{debug, error, info, warn};
use universal_config::ConfigLoader; use universal_config::ConfigLoader;
use crate::bar::{Bar, create_bar}; use crate::bar::{Bar, create_bar};
use crate::channels::SyncSenderExt;
use crate::clients::Clients; use crate::clients::Clients;
use crate::clients::wayland::OutputEventType; use crate::clients::wayland::OutputEventType;
use crate::config::{Config, MonitorConfig}; use crate::config::{Config, MonitorConfig};
@ -34,6 +35,7 @@ use crate::ironvar::{VariableManager, WritableNamespace};
use crate::style::load_css; use crate::style::load_css;
mod bar; mod bar;
mod channels;
#[cfg(feature = "cli")] #[cfg(feature = "cli")]
mod cli; mod cli;
mod clients; mod clients;
@ -202,7 +204,7 @@ impl Ironbar {
.expect("Error setting Ctrl-C handler"); .expect("Error setting Ctrl-C handler");
let hold = app.hold(); let hold = app.hold();
send!(activate_tx, hold); activate_tx.send_expect(hold);
}); });
{ {

View file

@ -1,8 +1,9 @@
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::clients::compositor::BindModeUpdate; use crate::clients::compositor::BindModeUpdate;
use crate::config::{CommonConfig, LayoutConfig, TruncateMode}; use crate::config::{CommonConfig, LayoutConfig, TruncateMode};
use crate::gtk_helpers::IronbarLabelExt; use crate::gtk_helpers::IronbarLabelExt;
use crate::modules::{Module, ModuleInfo, ModuleParts, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleParts, WidgetContext};
use crate::{glib_recv, module_impl, module_update, send_async, spawn}; use crate::{module_impl, spawn};
use color_eyre::Result; use color_eyre::Result;
use gtk::Label; use gtk::Label;
use gtk::prelude::*; use gtk::prelude::*;
@ -49,7 +50,7 @@ impl Module<Label> for Bindmode {
let mut rx = client.subscribe()?; let mut rx = client.subscribe()?;
spawn(async move { spawn(async move {
while let Ok(ev) = rx.recv().await { while let Ok(ev) = rx.recv().await {
module_update!(tx, ev); tx.send_update(ev).await;
} }
}); });
@ -80,7 +81,7 @@ impl Module<Label> for Bindmode {
label.set_label_escaped(&mode.name); label.set_label_escaped(&mode.name);
}; };
glib_recv!(context.subscribe(), on_mode); context.subscribe().recv_glib(on_mode);
} }
Ok(ModuleParts { Ok(ModuleParts {

View file

@ -1,6 +1,7 @@
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::config::CommonConfig; use crate::config::CommonConfig;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleParts, WidgetContext};
use crate::{glib_recv, module_impl, spawn, try_send}; use crate::{module_impl, spawn};
use cairo::{Format, ImageSurface}; use cairo::{Format, ImageSurface};
use glib::Propagation; use glib::Propagation;
use glib::translate::ToGlibPtr; use glib::translate::ToGlibPtr;
@ -87,7 +88,7 @@ impl Module<gtk::Box> for CairoModule {
debug!("{event:?}"); debug!("{event:?}");
if event.paths.first().is_some_and(|p| p == &path) { if event.paths.first().is_some_and(|p| p == &path) {
try_send!(tx, ModuleUpdateEvent::Update(())); tx.send_update_spawn(());
} }
} }
Err(e) => error!("Error occurred when watching stylesheet: {:?}", e), Err(e) => error!("Error occurred when watching stylesheet: {:?}", e),
@ -191,22 +192,20 @@ impl Module<gtk::Box> for CairoModule {
} }
}); });
glib_recv!(context.subscribe(), _ev => { context.subscribe().recv_glib(move |_ev| {
let res = fs::read_to_string(&self.path) let res = fs::read_to_string(&self.path)
.map(|s| s.replace("function draw", format!("function __draw_{id}").as_str())); .map(|s| s.replace("function draw", format!("function __draw_{id}").as_str()));
match res { match res {
Ok(script) => { Ok(script) => match lua.load(&script).exec() {
match lua.load(&script).exec() { Ok(()) => {}
Ok(()) => {}, Err(Error::SyntaxError { message, .. }) => {
Err(Error::SyntaxError { message, ..}) => { let message = message.split_once("]:").expect("to exist").1;
let message = message.split_once("]:").expect("to exist").1; error!("[lua syntax error] {}:{message}", self.path.display());
error!("[lua syntax error] {}:{message}", self.path.display());
},
Err(err) => error!("lua error: {err:?}")
} }
Err(err) => error!("lua error: {err:?}"),
}, },
Err(err) => error!("{err:?}") Err(err) => error!("{err:?}"),
} }
}); });

View file

@ -1,3 +1,4 @@
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::clients::clipboard::{self, ClipboardEvent}; use crate::clients::clipboard::{self, ClipboardEvent};
use crate::clients::wayland::{ClipboardItem, ClipboardValue}; use crate::clients::wayland::{ClipboardItem, ClipboardValue};
use crate::config::{CommonConfig, LayoutConfig, TruncateMode}; use crate::config::{CommonConfig, LayoutConfig, TruncateMode};
@ -7,7 +8,7 @@ use crate::image::IconButton;
use crate::modules::{ use crate::modules::{
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext, Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext,
}; };
use crate::{glib_recv, module_impl, spawn, try_send}; use crate::{module_impl, spawn};
use glib::Propagation; use glib::Propagation;
use gtk::gdk_pixbuf::Pixbuf; use gtk::gdk_pixbuf::Pixbuf;
use gtk::gio::{Cancellable, MemoryInputStream}; use gtk::gio::{Cancellable, MemoryInputStream};
@ -109,18 +110,16 @@ impl Module<Button> for ClipboardModule {
match event { match event {
ClipboardEvent::Add(item) => { ClipboardEvent::Add(item) => {
let msg = match item.value.as_ref() { let msg = match item.value.as_ref() {
ClipboardValue::Other => { ClipboardValue::Other => ControllerEvent::Deactivate,
ModuleUpdateEvent::Update(ControllerEvent::Deactivate) _ => ControllerEvent::Add(item.id, item),
}
_ => ModuleUpdateEvent::Update(ControllerEvent::Add(item.id, item)),
}; };
try_send!(tx, msg); tx.send_update_spawn(msg);
} }
ClipboardEvent::Remove(id) => { ClipboardEvent::Remove(id) => {
try_send!(tx, ModuleUpdateEvent::Update(ControllerEvent::Remove(id))); tx.send_update_spawn(ControllerEvent::Remove(id));
} }
ClipboardEvent::Activate(id) => { ClipboardEvent::Activate(id) => {
try_send!(tx, ModuleUpdateEvent::Update(ControllerEvent::Activate(id))); tx.send_update_spawn(ControllerEvent::Activate(id));
} }
} }
} }
@ -156,7 +155,7 @@ impl Module<Button> for ClipboardModule {
let tx = context.tx.clone(); let tx = context.tx.clone();
button.connect_clicked(move |button| { button.connect_clicked(move |button| {
try_send!(tx, ModuleUpdateEvent::TogglePopup(button.popup_id())); tx.send_spawn(ModuleUpdateEvent::TogglePopup(button.popup_id()));
}); });
let rx = context.subscribe(); let rx = context.subscribe();
@ -189,7 +188,7 @@ impl Module<Button> for ClipboardModule {
{ {
let hidden_option = hidden_option.clone(); let hidden_option = hidden_option.clone();
glib_recv!(rx, event => { rx.recv_glib(move |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);
@ -231,7 +230,7 @@ impl Module<Button> for ClipboardModule {
button.style_context().add_class("image"); button.style_context().add_class("image");
button button
}, }
Err(err) => { Err(err) => {
error!("{err:?}"); error!("{err:?}");
return; return;
@ -260,7 +259,7 @@ impl Module<Button> for ClipboardModule {
.expect("Failed to get id from button name"); .expect("Failed to get id from button name");
debug!("Copying item with id: {id}"); debug!("Copying item with id: {id}");
try_send!(tx, UIEvent::Copy(id)); tx.send_spawn(UIEvent::Copy(id));
} }
Propagation::Stop Propagation::Stop
@ -282,7 +281,7 @@ impl Module<Button> for ClipboardModule {
.expect("Failed to get id from button name"); .expect("Failed to get id from button name");
debug!("Removing item with id: {id}"); debug!("Removing item with id: {id}");
try_send!(tx, UIEvent::Remove(id)); tx.send_spawn(UIEvent::Remove(id));
entries.remove(&row); entries.remove(&row);
}); });

View file

@ -8,12 +8,13 @@ use serde::Deserialize;
use tokio::sync::{broadcast, mpsc}; use tokio::sync::{broadcast, mpsc};
use tokio::time::sleep; use tokio::time::sleep;
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::config::{CommonConfig, LayoutConfig}; use crate::config::{CommonConfig, LayoutConfig};
use crate::gtk_helpers::IronbarGtkExt; 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::{glib_recv, module_impl, send_async, spawn, try_send}; use crate::{module_impl, spawn};
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
@ -107,7 +108,7 @@ impl Module<Button> for ClockModule {
spawn(async move { spawn(async move {
loop { loop {
let date = Local::now(); let date = Local::now();
send_async!(tx, ModuleUpdateEvent::Update(date)); tx.send_update(date).await;
sleep(tokio::time::Duration::from_millis(500)).await; sleep(tokio::time::Duration::from_millis(500)).await;
} }
}); });
@ -131,14 +132,14 @@ impl Module<Button> for ClockModule {
let tx = context.tx.clone(); let tx = context.tx.clone();
button.connect_clicked(move |button| { button.connect_clicked(move |button| {
try_send!(tx, ModuleUpdateEvent::TogglePopup(button.popup_id())); tx.send_spawn(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);
let rx = context.subscribe(); let rx = context.subscribe();
glib_recv!(rx, date => { rx.recv_glib(move |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);
}); });
@ -179,7 +180,7 @@ 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);
glib_recv!(rx, date => { rx.recv_glib(move |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);
}); });

View file

@ -3,11 +3,12 @@ use gtk::{Button, Label};
use serde::Deserialize; use serde::Deserialize;
use super::{CustomWidget, CustomWidgetContext, ExecEvent, WidgetConfig}; use super::{CustomWidget, CustomWidgetContext, ExecEvent, WidgetConfig};
use crate::build;
use crate::channels::AsyncSenderExt;
use crate::config::LayoutConfig; use crate::config::LayoutConfig;
use crate::dynamic_value::dynamic_string; use crate::dynamic_value::dynamic_string;
use crate::gtk_helpers::IronbarLabelExt; use crate::gtk_helpers::IronbarLabelExt;
use crate::modules::PopupButton; use crate::modules::PopupButton;
use crate::{build, try_send};
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
@ -81,14 +82,11 @@ impl CustomWidget for ButtonWidget {
let tx = context.tx.clone(); let tx = context.tx.clone();
button.connect_clicked(move |button| { button.connect_clicked(move |button| {
try_send!( tx.send_spawn(ExecEvent {
tx, cmd: exec.clone(),
ExecEvent { args: None,
cmd: exec.clone(), id: button.try_popup_id().unwrap_or(usize::MAX), // may not be a popup button
args: None, });
id: button.try_popup_id().unwrap_or(usize::MAX), // may not be a popup button
}
);
}); });
} }

View file

@ -9,6 +9,7 @@ use self::r#box::BoxWidget;
use self::image::ImageWidget; use self::image::ImageWidget;
use self::label::LabelWidget; use self::label::LabelWidget;
use self::slider::SliderWidget; use self::slider::SliderWidget;
use crate::channels::AsyncSenderExt;
use crate::config::{CommonConfig, ModuleConfig}; use crate::config::{CommonConfig, ModuleConfig};
use crate::modules::custom::button::ButtonWidget; use crate::modules::custom::button::ButtonWidget;
use crate::modules::custom::progress::ProgressWidget; use crate::modules::custom::progress::ProgressWidget;
@ -17,7 +18,7 @@ use crate::modules::{
ModuleUpdateEvent, PopupButton, PopupModuleFactory, WidgetContext, wrap_widget, ModuleUpdateEvent, PopupButton, PopupModuleFactory, WidgetContext, wrap_widget,
}; };
use crate::script::Script; use crate::script::Script;
use crate::{module_impl, send_async, spawn}; use crate::{module_impl, spawn};
use color_eyre::Result; use color_eyre::Result;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Button, IconTheme, Orientation}; use gtk::{Button, IconTheme, Orientation};
@ -205,11 +206,12 @@ impl Module<gtk::Box> for CustomModule {
let args = event.args.unwrap_or_default(); let args = event.args.unwrap_or_default();
script.run_as_oneshot(Some(&args)); script.run_as_oneshot(Some(&args));
} else if event.cmd == "popup:toggle" { } else if event.cmd == "popup:toggle" {
send_async!(tx, ModuleUpdateEvent::TogglePopup(event.id)); tx.send_expect(ModuleUpdateEvent::TogglePopup(event.id))
.await;
} else if event.cmd == "popup:open" { } else if event.cmd == "popup:open" {
send_async!(tx, ModuleUpdateEvent::OpenPopup(event.id)); tx.send_expect(ModuleUpdateEvent::OpenPopup(event.id)).await;
} else if event.cmd == "popup:close" { } else if event.cmd == "popup:close" {
send_async!(tx, ModuleUpdateEvent::ClosePopup); tx.send_expect(ModuleUpdateEvent::ClosePopup).await;
} else { } else {
error!("Received invalid command: '{}'", event.cmd); error!("Received invalid command: '{}'", event.cmd);
} }

View file

@ -4,13 +4,13 @@ use serde::Deserialize;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tracing::error; use tracing::error;
use super::{CustomWidget, CustomWidgetContext};
use crate::channels::{AsyncSenderExt, MpscReceiverExt};
use crate::config::ModuleOrientation; use crate::config::ModuleOrientation;
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, glib_recv_mpsc, spawn, try_send}; use crate::{build, spawn};
use super::{CustomWidget, CustomWidgetContext};
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
@ -87,7 +87,7 @@ impl CustomWidget for ProgressWidget {
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) => try_send!(tx, value), Ok(value) => tx.send_spawn(value),
Err(err) => error!("{err:?}"), Err(err) => error!("{err:?}"),
}, },
OutputStream::Stderr(err) => error!("{err:?}"), OutputStream::Stderr(err) => error!("{err:?}"),
@ -95,7 +95,7 @@ impl CustomWidget for ProgressWidget {
.await; .await;
}); });
glib_recv_mpsc!(rx, value => progress.set_fraction(value / self.max)); rx.recv_glib(move |value| progress.set_fraction(value / self.max));
} }
if let Some(text) = self.label { if let Some(text) = self.label {

View file

@ -8,12 +8,12 @@ use serde::Deserialize;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tracing::error; use tracing::error;
use super::{CustomWidget, CustomWidgetContext, ExecEvent};
use crate::channels::{AsyncSenderExt, MpscReceiverExt};
use crate::config::ModuleOrientation; use crate::config::ModuleOrientation;
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, glib_recv_mpsc, spawn, try_send}; use crate::{build, spawn};
use super::{CustomWidget, CustomWidgetContext, ExecEvent};
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
@ -134,14 +134,11 @@ impl CustomWidget for SliderWidget {
let val = val.clamp(min, max); let val = val.clamp(min, max);
if val != prev_value.get() { if val != prev_value.get() {
try_send!( tx.send_spawn(ExecEvent {
tx, cmd: on_change.clone(),
ExecEvent { args: Some(vec![val.to_string()]),
cmd: on_change.clone(), id: usize::MAX, // ignored
args: Some(vec![val.to_string()]), });
id: usize::MAX // ignored
}
);
prev_value.set(val); prev_value.set(val);
} }
@ -160,7 +157,7 @@ impl CustomWidget for SliderWidget {
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) => try_send!(tx, value), Ok(value) => tx.send_spawn(value),
Err(err) => error!("{err:?}"), Err(err) => error!("{err:?}"),
}, },
OutputStream::Stderr(err) => error!("{err:?}"), OutputStream::Stderr(err) => error!("{err:?}"),
@ -168,7 +165,7 @@ impl CustomWidget for SliderWidget {
.await; .await;
}); });
glib_recv_mpsc!(rx, value => scale.set_value(value)); rx.recv_glib(move |value| scale.set_value(value));
} }
scale scale

View file

@ -1,10 +1,11 @@
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::clients::wayland::{self, ToplevelEvent}; use crate::clients::wayland::{self, ToplevelEvent};
use crate::config::{CommonConfig, LayoutConfig, TruncateMode}; use crate::config::{CommonConfig, LayoutConfig, TruncateMode};
use crate::gtk_helpers::IronbarGtkExt; use crate::gtk_helpers::IronbarGtkExt;
use crate::gtk_helpers::IronbarLabelExt; use crate::gtk_helpers::IronbarLabelExt;
use crate::image::ImageProvider; use crate::image::ImageProvider;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleParts, WidgetContext};
use crate::{glib_recv, module_impl, send_async, spawn, try_send}; use crate::{module_impl, spawn};
use color_eyre::Result; use color_eyre::Result;
use gtk::Label; use gtk::Label;
use gtk::prelude::*; use gtk::prelude::*;
@ -90,10 +91,8 @@ impl Module<gtk::Box> for FocusedModule {
if let Some(focused) = focused { if let Some(focused) = focused {
current = Some(focused.id); current = Some(focused.id);
try_send!( tx.send_update(Some((focused.title.clone(), focused.app_id)))
tx, .await;
ModuleUpdateEvent::Update(Some((focused.title.clone(), focused.app_id)))
);
}; };
while let Ok(event) = wlrx.recv().await { while let Ok(event) = wlrx.recv().await {
@ -104,24 +103,19 @@ impl Module<gtk::Box> for FocusedModule {
current = Some(info.id); current = Some(info.id);
send_async!( tx.send_update(Some((info.title.clone(), info.app_id)))
tx, .await;
ModuleUpdateEvent::Update(Some((
info.title.clone(),
info.app_id.clone()
)))
);
} else if info.id == current.unwrap_or_default() { } else if info.id == current.unwrap_or_default() {
debug!("Clearing focus"); debug!("Clearing focus");
current = None; current = None;
send_async!(tx, ModuleUpdateEvent::Update(None)); tx.send_update(None).await;
} }
} }
ToplevelEvent::Remove(info) => { ToplevelEvent::Remove(info) => {
if info.focused { if info.focused {
debug!("Clearing focus"); debug!("Clearing focus");
current = None; current = None;
send_async!(tx, ModuleUpdateEvent::Update(None)); tx.send_update(None).await;
} }
} }
ToplevelEvent::New(_) => {} ToplevelEvent::New(_) => {}
@ -162,10 +156,9 @@ impl Module<gtk::Box> for FocusedModule {
let icon_overrides = info.icon_overrides.clone(); let icon_overrides = info.icon_overrides.clone();
let icon_theme = info.icon_theme.clone(); let icon_theme = info.icon_theme.clone();
glib_recv!(context.subscribe(), data => { context.subscribe().recv_glib(move |data| {
if let Some((name, mut id)) = data { if let Some((name, mut id)) = data {
if self.show_icon { if self.show_icon {
if let Some(icon) = icon_overrides.get(&id) { if let Some(icon) = icon_overrides.get(&id) {
id = icon.clone(); id = icon.clone();
} }

View file

@ -8,12 +8,13 @@ use tokio::sync::mpsc;
use tracing::{debug, trace}; use tracing::{debug, trace};
use super::{Module, ModuleInfo, ModuleParts, WidgetContext}; use super::{Module, ModuleInfo, ModuleParts, WidgetContext};
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::clients::compositor::{self, KeyboardLayoutUpdate}; use crate::clients::compositor::{self, KeyboardLayoutUpdate};
use crate::clients::libinput::{Event, Key, KeyEvent}; use crate::clients::libinput::{Event, Key, KeyEvent};
use crate::config::{CommonConfig, LayoutConfig}; use crate::config::{CommonConfig, LayoutConfig};
use crate::gtk_helpers::IronbarGtkExt; use crate::gtk_helpers::IronbarGtkExt;
use crate::image::{IconButton, IconLabel}; use crate::image::{IconButton, IconLabel};
use crate::{glib_recv, module_impl, module_update, send_async, spawn, try_send}; use crate::{module_impl, spawn};
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
@ -196,11 +197,11 @@ impl Module<gtk::Box> for KeyboardModule {
key, key,
state: client.get_state(key), state: client.get_state(key),
}; };
module_update!(tx, KeyboardUpdate::Key(event)); tx.send_update(KeyboardUpdate::Key(event)).await;
} }
} }
Event::Key(ev) => { Event::Key(ev) => {
module_update!(tx, KeyboardUpdate::Key(ev)); tx.send_update(KeyboardUpdate::Key(ev)).await;
} }
} }
} }
@ -219,7 +220,7 @@ impl Module<gtk::Box> for KeyboardModule {
match srx.recv().await { match srx.recv().await {
Ok(payload) => { Ok(payload) => {
debug!("Received update: {payload:?}"); debug!("Received update: {payload:?}");
module_update!(tx, KeyboardUpdate::Layout(payload)); tx.send_update(KeyboardUpdate::Layout(payload)).await;
} }
Err(tokio::sync::broadcast::error::RecvError::Lagged(count)) => { Err(tokio::sync::broadcast::error::RecvError::Lagged(count)) => {
tracing::warn!( tracing::warn!(
@ -297,7 +298,7 @@ impl Module<gtk::Box> for KeyboardModule {
{ {
let tx = context.controller_tx.clone(); let tx = context.controller_tx.clone();
layout_button.connect_clicked(move |_| { layout_button.connect_clicked(move |_| {
try_send!(tx, ()); tx.send_spawn(());
}); });
} }
@ -334,7 +335,7 @@ impl Module<gtk::Box> for KeyboardModule {
} }
}; };
glib_recv!(context.subscribe(), handle_event); context.subscribe().recv_glib(handle_event);
Ok(ModuleParts::new(container, None)) Ok(ModuleParts::new(container, None))
} }
} }

View file

@ -1,8 +1,9 @@
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::config::{CommonConfig, LayoutConfig, TruncateMode}; use crate::config::{CommonConfig, LayoutConfig, TruncateMode};
use crate::dynamic_value::dynamic_string; use crate::dynamic_value::dynamic_string;
use crate::gtk_helpers::IronbarLabelExt; use crate::gtk_helpers::IronbarLabelExt;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; use crate::module_impl;
use crate::{glib_recv, module_impl, try_send}; use crate::modules::{Module, ModuleInfo, ModuleParts, WidgetContext};
use color_eyre::Result; use color_eyre::Result;
use gtk::Label; use gtk::Label;
use serde::Deserialize; use serde::Deserialize;
@ -57,7 +58,7 @@ impl Module<Label> for LabelModule {
) -> Result<()> { ) -> Result<()> {
let tx = context.tx.clone(); let tx = context.tx.clone();
dynamic_string(&self.label, move |string| { dynamic_string(&self.label, move |string| {
try_send!(tx, ModuleUpdateEvent::Update(string)); tx.send_update_spawn(string);
}); });
Ok(()) Ok(())
@ -80,7 +81,9 @@ impl Module<Label> for LabelModule {
{ {
let label = label.clone(); let label = label.clone();
glib_recv!(context.subscribe(), string => label.set_label_escaped(&string)); context
.subscribe()
.recv_glib(move |string| label.set_label_escaped(&string));
} }
Ok(ModuleParts { Ok(ModuleParts {

View file

@ -1,11 +1,12 @@
use super::open_state::OpenState; use super::open_state::OpenState;
use crate::channels::AsyncSenderExt;
use crate::clients::wayland::ToplevelInfo; use crate::clients::wayland::ToplevelInfo;
use crate::config::{BarPosition, TruncateMode}; use crate::config::{BarPosition, TruncateMode};
use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt}; use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt};
use crate::image::ImageProvider; use crate::image::ImageProvider;
use crate::modules::ModuleUpdateEvent; use crate::modules::ModuleUpdateEvent;
use crate::modules::launcher::{ItemEvent, LauncherUpdate}; use crate::modules::launcher::{ItemEvent, LauncherUpdate};
use crate::{read_lock, try_send}; use crate::read_lock;
use glib::Propagation; use glib::Propagation;
use gtk::gdk::{BUTTON_MIDDLE, BUTTON_PRIMARY}; use gtk::gdk::{BUTTON_MIDDLE, BUTTON_PRIMARY};
use gtk::prelude::*; use gtk::prelude::*;
@ -226,15 +227,15 @@ impl ItemButton {
let menu_state = read_lock!(menu_state); let menu_state = read_lock!(menu_state);
if style_context.has_class("focused") && menu_state.num_windows == 1 { if style_context.has_class("focused") && menu_state.num_windows == 1 {
try_send!(tx, ItemEvent::MinimizeItem(app_id.clone())); tx.send_spawn(ItemEvent::MinimizeItem(app_id.clone()));
} else { } else {
try_send!(tx, ItemEvent::FocusItem(app_id.clone())); tx.send_spawn(ItemEvent::FocusItem(app_id.clone()));
} }
} else { } else {
try_send!(tx, ItemEvent::OpenItem(app_id.clone())); tx.send_spawn(ItemEvent::OpenItem(app_id.clone()));
} }
} else if event.button() == BUTTON_MIDDLE { } else if event.button() == BUTTON_MIDDLE {
try_send!(tx, ItemEvent::OpenItem(app_id.clone())); tx.send_spawn(ItemEvent::OpenItem(app_id.clone()));
} }
Propagation::Proceed Propagation::Proceed
@ -250,17 +251,12 @@ impl ItemButton {
let menu_state = read_lock!(menu_state); let menu_state = read_lock!(menu_state);
if menu_state.num_windows > 1 { if menu_state.num_windows > 1 {
try_send!( tx.send_update_spawn(LauncherUpdate::Hover(app_id.clone()));
tx, tx.send_spawn(ModuleUpdateEvent::OpenPopupAt(
ModuleUpdateEvent::Update(LauncherUpdate::Hover(app_id.clone(),)) button.geometry(bar_position.orientation()),
); ));
try_send!(
tx,
ModuleUpdateEvent::OpenPopupAt(button.geometry(bar_position.orientation()))
);
} else { } else {
try_send!(tx, ModuleUpdateEvent::ClosePopup); tx.send_spawn(ModuleUpdateEvent::ClosePopup);
} }
Propagation::Proceed Propagation::Proceed
@ -285,7 +281,7 @@ impl ItemButton {
}; };
if close { if close {
try_send!(tx, ModuleUpdateEvent::ClosePopup); tx.send_spawn(ModuleUpdateEvent::ClosePopup);
} }
Propagation::Proceed Propagation::Proceed

View file

@ -5,13 +5,14 @@ mod pagination;
use self::item::{AppearanceOptions, Item, ItemButton, Window}; use self::item::{AppearanceOptions, Item, ItemButton, Window};
use self::open_state::OpenState; use self::open_state::OpenState;
use super::{Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext}; use super::{Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext};
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::clients::wayland::{self, ToplevelEvent}; use crate::clients::wayland::{self, ToplevelEvent};
use crate::config::{CommonConfig, EllipsizeMode, LayoutConfig, TruncateMode}; use crate::config::{CommonConfig, EllipsizeMode, LayoutConfig, TruncateMode};
use crate::desktop_file::find_desktop_file; use crate::desktop_file::find_desktop_file;
use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt}; use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt};
use crate::modules::launcher::item::ImageTextButton; use crate::modules::launcher::item::ImageTextButton;
use crate::modules::launcher::pagination::{IconContext, Pagination}; use crate::modules::launcher::pagination::{IconContext, Pagination};
use crate::{arc_mut, glib_recv, lock, module_impl, send_async, spawn, try_send, write_lock}; use crate::{arc_mut, lock, module_impl, spawn, write_lock};
use color_eyre::{Help, Report}; use color_eyre::{Help, Report};
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Button, Orientation}; use gtk::{Button, Orientation};
@ -271,10 +272,7 @@ impl Module<gtk::Box> for LauncherModule {
let items = lock!(items); let items = lock!(items);
let items = items.iter(); let items = items.iter();
for (_, item) in items { for (_, item) in items {
try_send!( tx.send_update_spawn(LauncherUpdate::AddItem(item.clone()));
tx,
ModuleUpdateEvent::Update(LauncherUpdate::AddItem(item.clone()))
);
} }
} }
@ -410,7 +408,7 @@ impl Module<gtk::Box> for LauncherModule {
}, },
); );
} else { } else {
send_async!(tx, ModuleUpdateEvent::ClosePopup); tx.send_expect(ModuleUpdateEvent::ClosePopup).await;
let minimize_window = matches!(event, ItemEvent::MinimizeItem(_)); let minimize_window = matches!(event, ItemEvent::MinimizeItem(_));
@ -494,7 +492,7 @@ impl Module<gtk::Box> for LauncherModule {
let tx = context.tx.clone(); let tx = context.tx.clone();
let rx = context.subscribe(); let rx = context.subscribe();
let mut handle_event = move |event: LauncherUpdate| { let handle_event = move |event: LauncherUpdate| {
// all widgets show by default // all widgets show by default
// so check if pagination should be shown // so check if pagination should be shown
// to ensure correct state on init. // to ensure correct state on init.
@ -598,7 +596,7 @@ impl Module<gtk::Box> for LauncherModule {
}; };
}; };
glib_recv!(rx, handle_event); rx.recv_glib(handle_event);
} }
let rx = context.subscribe(); let rx = context.subscribe();
@ -632,7 +630,7 @@ impl Module<gtk::Box> for LauncherModule {
{ {
let container = container.clone(); let container = container.clone();
glib_recv!(rx, event => { rx.recv_glib(move |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();
@ -651,7 +649,7 @@ impl Module<gtk::Box> for LauncherModule {
{ {
let tx = controller_tx.clone(); let tx = controller_tx.clone();
button.connect_clicked(move |_| { button.connect_clicked(move |_| {
try_send!(tx, ItemEvent::FocusWindow(win.id)); tx.send_spawn(ItemEvent::FocusWindow(win.id));
}); });
} }
@ -677,7 +675,7 @@ impl Module<gtk::Box> for LauncherModule {
{ {
let tx = controller_tx.clone(); let tx = controller_tx.clone();
button.connect_clicked(move |_button| { button.connect_clicked(move |_button| {
try_send!(tx, ItemEvent::FocusWindow(win.id)); tx.send_spawn(ItemEvent::FocusWindow(win.id));
}); });
} }

View file

@ -11,11 +11,12 @@ use gtk::{Application, Button, EventBox, IconTheme, Orientation, Revealer, Widge
use tokio::sync::{broadcast, mpsc}; use tokio::sync::{broadcast, mpsc};
use tracing::debug; use tracing::debug;
use crate::Ironbar;
use crate::channels::{MpscReceiverExt, SyncSenderExt};
use crate::clients::{ClientResult, ProvidesClient, ProvidesFallibleClient}; use crate::clients::{ClientResult, ProvidesClient, ProvidesFallibleClient};
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::{Ironbar, glib_recv_mpsc, send};
#[cfg(feature = "bindmode")] #[cfg(feature = "bindmode")]
pub mod bindmode; pub mod bindmode;
@ -391,37 +392,41 @@ impl ModuleFactory for BarModuleFactory {
TSend: Debug + Clone + Send + 'static, TSend: Debug + Clone + Send + 'static,
{ {
let popup = self.popup.clone(); let popup = self.popup.clone();
glib_recv_mpsc!(rx, ev => { rx.recv_glib(move |ev| match ev {
match ev { ModuleUpdateEvent::Update(update) => {
ModuleUpdateEvent::Update(update) => { tx.send_expect(update);
send!(tx, update); }
} ModuleUpdateEvent::TogglePopup(button_id) if !disable_popup => {
ModuleUpdateEvent::TogglePopup(button_id) if !disable_popup => { debug!(
debug!("Toggling popup for {} [#{}] (button id: {button_id})", name, id); "Toggling popup for {} [#{}] (button id: {button_id})",
if popup.visible() && popup.current_widget().unwrap_or_default() == id { name, id
popup.hide(); );
} else { if popup.visible() && popup.current_widget().unwrap_or_default() == id {
popup.show(id, button_id);
}
}
ModuleUpdateEvent::OpenPopup(button_id) if !disable_popup => {
debug!("Opening popup for {} [#{}] (button id: {button_id})", name, id);
popup.hide(); popup.hide();
} else {
popup.show(id, button_id); popup.show(id, button_id);
} }
#[cfg(feature = "launcher")]
ModuleUpdateEvent::OpenPopupAt(geometry) if !disable_popup => {
debug!("Opening popup for {} [#{}]", name, id);
popup.hide();
popup.show_at(id, geometry);
}
ModuleUpdateEvent::ClosePopup if !disable_popup => {
debug!("Closing popup for {} [#{}]", name, id);
popup.hide();
},
_ => {}
} }
ModuleUpdateEvent::OpenPopup(button_id) if !disable_popup => {
debug!(
"Opening popup for {} [#{}] (button id: {button_id})",
name, id
);
popup.hide();
popup.show(id, button_id);
}
#[cfg(feature = "launcher")]
ModuleUpdateEvent::OpenPopupAt(geometry) if !disable_popup => {
debug!("Opening popup for {} [#{}]", name, id);
popup.hide();
popup.show_at(id, geometry);
}
ModuleUpdateEvent::ClosePopup if !disable_popup => {
debug!("Closing popup for {} [#{}]", name, id);
popup.hide();
}
_ => {}
}); });
} }
@ -464,37 +469,42 @@ impl ModuleFactory for PopupModuleFactory {
{ {
let popup = self.popup.clone(); let popup = self.popup.clone();
let button_id = self.button_id; let button_id = self.button_id;
glib_recv_mpsc!(rx, ev => {
match ev { rx.recv_glib(move |ev| match ev {
ModuleUpdateEvent::Update(update) => { ModuleUpdateEvent::Update(update) => {
send!(tx, update); tx.send_expect(update);
} }
ModuleUpdateEvent::TogglePopup(_) if !disable_popup => { ModuleUpdateEvent::TogglePopup(_) if !disable_popup => {
debug!("Toggling popup for {} [#{}] (button id: {button_id})", name, id); debug!(
if popup.visible() && popup.current_widget().unwrap_or_default() == id { "Toggling popup for {} [#{}] (button id: {button_id})",
popup.hide(); name, id
} else { );
popup.show(id, button_id); if popup.visible() && popup.current_widget().unwrap_or_default() == id {
}
}
ModuleUpdateEvent::OpenPopup(_) if !disable_popup => {
debug!("Opening popup for {} [#{}] (button id: {button_id})", name, id);
popup.hide(); popup.hide();
} else {
popup.show(id, button_id); popup.show(id, button_id);
} }
#[cfg(feature = "launcher")]
ModuleUpdateEvent::OpenPopupAt(geometry) if !disable_popup => {
debug!("Opening popup for {} [#{}]", name, id);
popup.hide();
popup.show_at(id, geometry);
}
ModuleUpdateEvent::ClosePopup if !disable_popup => {
debug!("Closing popup for {} [#{}]", name, id);
popup.hide();
},
_ => {}
} }
ModuleUpdateEvent::OpenPopup(_) if !disable_popup => {
debug!(
"Opening popup for {} [#{}] (button id: {button_id})",
name, id
);
popup.hide();
popup.show(id, button_id);
}
#[cfg(feature = "launcher")]
ModuleUpdateEvent::OpenPopupAt(geometry) if !disable_popup => {
debug!("Opening popup for {} [#{}]", name, id);
popup.hide();
popup.show_at(id, geometry);
}
ModuleUpdateEvent::ClosePopup if !disable_popup => {
debug!("Closing popup for {} [#{}]", name, id);
popup.hide();
}
_ => {}
}); });
} }

View file

@ -12,6 +12,9 @@ use regex::Regex;
use tokio::sync::{broadcast, mpsc}; use tokio::sync::{broadcast, mpsc};
use tracing::error; use tracing::error;
pub use self::config::MusicModule;
use self::config::PlayerType;
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::clients::Clients; use crate::clients::Clients;
use crate::clients::music::{ use crate::clients::music::{
self, MusicClient, PlayerState, PlayerUpdate, ProgressTick, Status, Track, self, MusicClient, PlayerState, PlayerUpdate, ProgressTick, Status, Track,
@ -22,10 +25,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::{glib_recv, module_impl, send_async, spawn, try_send}; use crate::{module_impl, spawn};
pub use self::config::MusicModule;
use self::config::PlayerType;
mod config; mod config;
@ -136,24 +136,14 @@ impl Module<Button> for MusicModule {
display_string, display_string,
}; };
send_async!( tx.send_update(ControllerEvent::Update(Some(update))).await;
tx,
ModuleUpdateEvent::Update(ControllerEvent::Update(Some(
update
)))
);
} }
None => send_async!( None => tx.send_update(ControllerEvent::Update(None)).await,
tx,
ModuleUpdateEvent::Update(ControllerEvent::Update(None))
),
}, },
PlayerUpdate::ProgressTick(progress_tick) => send_async!( PlayerUpdate::ProgressTick(progress_tick) => {
tx, tx.send_update(ControllerEvent::UpdateProgress(progress_tick))
ModuleUpdateEvent::Update(ControllerEvent::UpdateProgress( .await
progress_tick }
))
),
} }
} }
} }
@ -221,7 +211,7 @@ impl Module<Button> for MusicModule {
let tx = context.tx.clone(); let tx = context.tx.clone();
button.connect_clicked(move |button| { button.connect_clicked(move |button| {
try_send!(tx, ModuleUpdateEvent::TogglePopup(button.popup_id())); tx.send_spawn(ModuleUpdateEvent::TogglePopup(button.popup_id()));
}); });
} }
@ -231,9 +221,9 @@ impl Module<Button> for MusicModule {
let tx = context.tx.clone(); let tx = context.tx.clone();
let rx = context.subscribe(); let rx = context.subscribe();
glib_recv!(rx, event => { rx.recv_glib(move |event| {
let ControllerEvent::Update(mut event) = event else { let ControllerEvent::Update(mut event) = event else {
continue; return;
}; };
if let Some(event) = event.take() { if let Some(event) = event.take() {
@ -262,7 +252,7 @@ impl Module<Button> for MusicModule {
} }
} else { } else {
button.hide(); button.hide();
try_send!(tx, ModuleUpdateEvent::ClosePopup); tx.send_spawn(ModuleUpdateEvent::ClosePopup);
} }
}); });
}; };
@ -350,27 +340,27 @@ impl Module<Button> for MusicModule {
let tx_prev = tx.clone(); let tx_prev = tx.clone();
btn_prev.connect_clicked(move |_| { btn_prev.connect_clicked(move |_| {
try_send!(tx_prev, PlayerCommand::Previous); tx_prev.send_spawn(PlayerCommand::Previous);
}); });
let tx_play = tx.clone(); let tx_play = tx.clone();
btn_play.connect_clicked(move |_| { btn_play.connect_clicked(move |_| {
try_send!(tx_play, PlayerCommand::Play); tx_play.send_spawn(PlayerCommand::Play);
}); });
let tx_pause = tx.clone(); let tx_pause = tx.clone();
btn_pause.connect_clicked(move |_| { btn_pause.connect_clicked(move |_| {
try_send!(tx_pause, PlayerCommand::Pause); tx_pause.send_spawn(PlayerCommand::Pause);
}); });
let tx_next = tx.clone(); let tx_next = tx.clone();
btn_next.connect_clicked(move |_| { btn_next.connect_clicked(move |_| {
try_send!(tx_next, PlayerCommand::Next); tx_next.send_spawn(PlayerCommand::Next);
}); });
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)); tx_vol.send_spawn(PlayerCommand::Volume(val as u8));
Propagation::Proceed Propagation::Proceed
}); });
@ -404,7 +394,7 @@ impl Module<Button> for MusicModule {
let drag_lock = drag_lock.clone(); let drag_lock = drag_lock.clone();
progress.connect_button_release_event(move |scale, _| { progress.connect_button_release_event(move |scale, _| {
let value = scale.value(); let value = scale.value();
try_send!(tx, PlayerCommand::Seek(Duration::from_secs_f64(value))); tx.send_spawn(PlayerCommand::Seek(Duration::from_secs_f64(value)));
drag_lock.set(false); drag_lock.set(false);
Propagation::Proceed Propagation::Proceed
@ -418,7 +408,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;
glib_recv!(rx, event => { rx.recv_glib(move |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

View file

@ -6,12 +6,13 @@ use gtk::{Box as GtkBox, Image};
use serde::Deserialize; use serde::Deserialize;
use tokio::sync::mpsc::Receiver; use tokio::sync::mpsc::Receiver;
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::clients::networkmanager::{Client, ClientState}; use crate::clients::networkmanager::{Client, ClientState};
use crate::config::CommonConfig; use crate::config::CommonConfig;
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, WidgetContext};
use crate::{glib_recv, module_impl, send_async, spawn}; use crate::{module_impl, spawn};
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
@ -41,11 +42,11 @@ impl Module<GtkBox> for NetworkManagerModule {
) -> Result<()> { ) -> Result<()> {
let client = context.try_client::<Client>()?; let client = context.try_client::<Client>()?;
let mut client_signal = client.subscribe().to_stream(); let mut client_signal = client.subscribe().to_stream();
let widget_transmitter = context.tx.clone(); let tx = context.tx.clone();
spawn(async move { spawn(async move {
while let Some(state) = client_signal.next().await { while let Some(state) = client_signal.next().await {
send_async!(widget_transmitter, ModuleUpdateEvent::Update(state)); tx.send_update(state).await;
} }
}); });
@ -68,8 +69,7 @@ impl Module<GtkBox> for NetworkManagerModule {
ImageProvider::parse(initial_icon_name, &icon_theme, false, self.icon_size) ImageProvider::parse(initial_icon_name, &icon_theme, false, self.icon_size)
.map(|provider| provider.load_into_image(&icon)); .map(|provider| provider.load_into_image(&icon));
let widget_receiver = context.subscribe(); context.subscribe().recv_glib(move |state| {
glib_recv!(widget_receiver, state => {
let icon_name = match state { let icon_name = match state {
ClientState::WiredConnected => "network-wired-symbolic", ClientState::WiredConnected => "network-wired-symbolic",
ClientState::WifiConnected => "network-wireless-symbolic", ClientState::WifiConnected => "network-wireless-symbolic",

View file

@ -1,8 +1,9 @@
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::clients::swaync; use crate::clients::swaync;
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, WidgetContext};
use crate::{glib_recv, module_impl, send_async, spawn, try_send}; use crate::{module_impl, spawn};
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Align, Button, Label, Overlay}; use gtk::{Align, Button, Label, Overlay};
use serde::Deserialize; use serde::Deserialize;
@ -153,12 +154,12 @@ impl Module<Overlay> for NotificationsModule {
let initial_state = client.state().await; let initial_state = client.state().await;
match initial_state { match initial_state {
Ok(ev) => send_async!(tx, ModuleUpdateEvent::Update(ev)), Ok(ev) => tx.send_update(ev).await,
Err(err) => error!("{err:?}"), Err(err) => error!("{err:?}"),
}; };
while let Ok(ev) = rx.recv().await { while let Ok(ev) = rx.recv().await {
send_async!(tx, ModuleUpdateEvent::Update(ev)); tx.send_update(ev).await;
} }
}); });
} }
@ -200,13 +201,13 @@ impl Module<Overlay> for NotificationsModule {
let ctx = context.controller_tx.clone(); let ctx = context.controller_tx.clone();
button.connect_clicked(move |_| { button.connect_clicked(move |_| {
try_send!(ctx, UiEvent::ToggleVisibility); ctx.send_spawn(UiEvent::ToggleVisibility);
}); });
{ {
let button = button.clone(); let button = button.clone();
glib_recv!(context.subscribe(), ev => { context.subscribe().recv_glib(move |ev| {
let icon = self.icons.icon(ev); let icon = self.icons.icon(ev);
button.set_label(icon); button.set_label(icon);

View file

@ -1,8 +1,9 @@
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::config::{CommonConfig, LayoutConfig}; use crate::config::{CommonConfig, LayoutConfig};
use crate::gtk_helpers::IronbarLabelExt; use crate::gtk_helpers::IronbarLabelExt;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleParts, WidgetContext};
use crate::script::{OutputStream, Script, ScriptMode}; use crate::script::{OutputStream, Script, ScriptMode};
use crate::{glib_recv, module_impl, spawn, try_send}; use crate::{module_impl, spawn};
use color_eyre::{Help, Report, Result}; use color_eyre::{Help, Report, Result};
use gtk::Label; use gtk::Label;
use serde::Deserialize; use serde::Deserialize;
@ -83,7 +84,7 @@ impl Module<Label> for ScriptModule {
spawn(async move { spawn(async move {
script.run(None, move |out, _| match out { script.run(None, move |out, _| match out {
OutputStream::Stdout(stdout) => { OutputStream::Stdout(stdout) => {
try_send!(tx, ModuleUpdateEvent::Update(stdout)); tx.send_update_spawn(stdout);
}, },
OutputStream::Stderr(stderr) => { OutputStream::Stderr(stderr) => {
error!("{:?}", Report::msg(stderr) error!("{:?}", Report::msg(stderr)
@ -111,7 +112,9 @@ impl Module<Label> for ScriptModule {
{ {
let label = label.clone(); let label = label.clone();
glib_recv!(context.subscribe(), s => label.set_label_escaped(&s)); context
.subscribe()
.recv_glib(move |s| label.set_label_escaped(&s));
} }
Ok(ModuleParts { Ok(ModuleParts {

View file

@ -2,12 +2,13 @@ mod parser;
mod renderer; mod renderer;
mod token; mod token;
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::clients::sysinfo::TokenType; use crate::clients::sysinfo::TokenType;
use crate::config::{CommonConfig, LayoutConfig, ModuleOrientation}; use crate::config::{CommonConfig, LayoutConfig, ModuleOrientation};
use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt}; use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt};
use crate::modules::sysinfo::token::Part; use crate::modules::sysinfo::token::Part;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleParts, WidgetContext};
use crate::{clients, glib_recv, module_impl, send_async, spawn, try_send}; use crate::{clients, module_impl, spawn};
use color_eyre::Result; use color_eyre::Result;
use gtk::Label; use gtk::Label;
use gtk::prelude::*; use gtk::prelude::*;
@ -216,7 +217,7 @@ impl Module<gtk::Box> for SysInfoModule {
for (i, token_set) in format_tokens.iter().enumerate() { for (i, token_set) in format_tokens.iter().enumerate() {
let rendered = Part::render_all(token_set, &client, interval); let rendered = Part::render_all(token_set, &client, interval);
try_send!(context.tx, ModuleUpdateEvent::Update((i, rendered))); context.tx.send_update_spawn((i, rendered));
} }
let (refresh_tx, mut refresh_rx) = mpsc::channel(16); let (refresh_tx, mut refresh_rx) = mpsc::channel(16);
@ -226,7 +227,7 @@ impl Module<gtk::Box> for SysInfoModule {
let tx = refresh_tx.clone(); let tx = refresh_tx.clone();
spawn(async move { spawn(async move {
loop { loop {
send_async!(tx, $refresh_type); tx.send_expect($refresh_type).await;
sleep(Duration::from_secs(interval.$func())).await; sleep(Duration::from_secs(interval.$func())).await;
} }
}); });
@ -266,7 +267,7 @@ impl Module<gtk::Box> for SysInfoModule {
if is_affected { if is_affected {
let rendered = Part::render_all(token_set, &client, interval); let rendered = Part::render_all(token_set, &client, interval);
send_async!(tx, ModuleUpdateEvent::Update((i, rendered))); tx.send_update((i, rendered)).await;
} }
} }
} }
@ -302,7 +303,7 @@ impl Module<gtk::Box> for SysInfoModule {
labels.push(label); labels.push(label);
} }
glib_recv!(context.subscribe(), data => { context.subscribe().recv_glib(move |data| {
let label = &labels[data.0]; let label = &labels[data.0];
label.set_label_escaped(&data.1); label.set_label_escaped(&data.1);
}); });

View file

@ -1,10 +1,11 @@
mod icon; mod icon;
mod interface; mod interface;
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::clients::tray; use crate::clients::tray;
use crate::config::{CommonConfig, ModuleOrientation}; use crate::config::{CommonConfig, ModuleOrientation};
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleParts, WidgetContext};
use crate::{glib_recv, lock, module_impl, send_async, spawn}; use crate::{lock, module_impl, spawn};
use color_eyre::{Report, Result}; use color_eyre::{Report, Result};
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{IconTheme, Orientation}; use gtk::{IconTheme, Orientation};
@ -74,21 +75,16 @@ impl Module<gtk::Box> for TrayModule {
// listen to tray updates // listen to tray updates
spawn(async move { spawn(async move {
for (key, (item, menu)) in initial_items { for (key, (item, menu)) in initial_items {
send_async!( tx.send_update(Event::Add(key.clone(), item.into())).await;
tx,
ModuleUpdateEvent::Update(Event::Add(key.clone(), item.into()))
);
if let Some(menu) = menu.clone() { if let Some(menu) = menu.clone() {
send_async!( tx.send_update(Event::Update(key, UpdateEvent::Menu(menu)))
tx, .await;
ModuleUpdateEvent::Update(Event::Update(key, UpdateEvent::Menu(menu)))
);
} }
} }
while let Ok(message) = tray_rx.recv().await { while let Ok(message) = tray_rx.recv().await {
send_async!(tx, ModuleUpdateEvent::Update(message)); tx.send_update(message).await;
} }
}); });
@ -127,9 +123,16 @@ impl Module<gtk::Box> for TrayModule {
let icon_theme = info.icon_theme.clone(); let icon_theme = info.icon_theme.clone();
// listen for UI updates // listen for UI updates
glib_recv!(context.subscribe(), update => context.subscribe().recv_glib(move |update| {
on_update(update, &container, &mut menus, &icon_theme, self.icon_size, self.prefer_theme_icons) on_update(
); update,
&container,
&mut menus,
&icon_theme,
self.icon_size,
self.prefer_theme_icons,
);
});
}; };
Ok(ModuleParts { Ok(ModuleParts {

View file

@ -7,6 +7,7 @@ use tokio::sync::{broadcast, mpsc};
use zbus; use zbus;
use zbus::fdo::PropertiesProxy; use zbus::fdo::PropertiesProxy;
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::clients::upower::BatteryState; use crate::clients::upower::BatteryState;
use crate::config::{CommonConfig, LayoutConfig}; use crate::config::{CommonConfig, LayoutConfig};
use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt}; use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt};
@ -15,7 +16,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::{glib_recv, module_impl, send_async, spawn, try_send}; use crate::{module_impl, spawn};
const DAY: i64 = 24 * 60 * 60; const DAY: i64 = 24 * 60 * 60;
const HOUR: i64 = 60 * 60; const HOUR: i64 = 60 * 60;
@ -116,7 +117,7 @@ impl Module<Button> for UpowerModule {
time_to_empty, time_to_empty,
}; };
send_async!(tx, ModuleUpdateEvent::Update(properties.clone())); tx.send_update(properties.clone()).await;
while let Some(signal) = prop_changed_stream.next().await { while let Some(signal) = prop_changed_stream.next().await {
let args = signal.args().expect("Invalid signal arguments"); let args = signal.args().expect("Invalid signal arguments");
@ -156,7 +157,7 @@ impl Module<Button> for UpowerModule {
} }
} }
send_async!(tx, ModuleUpdateEvent::Update(properties.clone())); tx.send_update(properties.clone()).await;
} }
Result::<()>::Ok(()) Result::<()>::Ok(())
@ -195,22 +196,23 @@ impl Module<Button> for UpowerModule {
let tx = context.tx.clone(); let tx = context.tx.clone();
button.connect_clicked(move |button| { button.connect_clicked(move |button| {
try_send!(tx, ModuleUpdateEvent::TogglePopup(button.popup_id())); tx.send_spawn(ModuleUpdateEvent::TogglePopup(button.popup_id()));
}); });
let format = self.format.clone(); let format = self.format.clone();
let rx = context.subscribe(); let rx = context.subscribe();
glib_recv!(rx, properties => { rx.recv_glib(move |properties| {
let state = properties.state; let state = properties.state;
let is_charging = state == BatteryState::Charging || state == BatteryState::PendingCharge; let is_charging =
state == BatteryState::Charging || state == BatteryState::PendingCharge;
let time_remaining = if is_charging { let time_remaining = if is_charging {
seconds_to_string(properties.time_to_full) seconds_to_string(properties.time_to_full)
} } else {
else {
seconds_to_string(properties.time_to_empty) seconds_to_string(properties.time_to_empty)
}; };
let format = format.replace("{percentage}", &properties.percentage.to_string()) let format = format
.replace("{percentage}", &properties.percentage.to_string())
.replace("{time_remaining}", &time_remaining) .replace("{time_remaining}", &time_remaining)
.replace("{state}", battery_state_to_string(state)); .replace("{state}", battery_state_to_string(state));
@ -218,7 +220,7 @@ impl Module<Button> for UpowerModule {
icon_name.push_str(&properties.icon_name); icon_name.push_str(&properties.icon_name);
ImageProvider::parse(&icon_name, &icon_theme, false, self.icon_size) ImageProvider::parse(&icon_name, &icon_theme, false, self.icon_size)
.map(|provider| provider.load_into_image(&icon)); .map(|provider| provider.load_into_image(&icon));
label.set_label_escaped(&format); label.set_label_escaped(&format);
}); });
@ -249,7 +251,7 @@ impl Module<Button> for UpowerModule {
label.add_class("upower-details"); label.add_class("upower-details");
container.add(&label); container.add(&label);
glib_recv!(rx, properties => { rx.recv_glib(move |properties| {
let state = properties.state; let state = properties.state;
let format = match state { let format = match state {
BatteryState::Charging | BatteryState::PendingCharge => { BatteryState::Charging | BatteryState::PendingCharge => {

View file

@ -1,10 +1,11 @@
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::clients::volume::{self, Event}; use crate::clients::volume::{self, Event};
use crate::config::{CommonConfig, LayoutConfig}; use crate::config::{CommonConfig, LayoutConfig};
use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt}; use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt};
use crate::modules::{ use crate::modules::{
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext, Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext,
}; };
use crate::{glib_recv, lock, module_impl, send_async, spawn, try_send}; use crate::{lock, module_impl, spawn};
use glib::Propagation; use glib::Propagation;
use gtk::pango::EllipsizeMode; use gtk::pango::EllipsizeMode;
use gtk::prelude::*; use gtk::prelude::*;
@ -171,20 +172,17 @@ impl Module<Button> for VolumeModule {
trace!("initial inputs: {inputs:?}"); trace!("initial inputs: {inputs:?}");
for sink in sinks { for sink in sinks {
send_async!(tx, ModuleUpdateEvent::Update(Event::AddSink(sink))); tx.send_update(Event::AddSink(sink)).await;
} }
for input in inputs { for input in inputs {
send_async!( tx.send_update(Event::AddInput(input)).await;
tx,
ModuleUpdateEvent::Update(Event::AddInput(input.clone()))
);
} }
// recv loop // recv loop
while let Ok(event) = rx.recv().await { while let Ok(event) = rx.recv().await {
trace!("received event: {event:?}"); trace!("received event: {event:?}");
send_async!(tx, ModuleUpdateEvent::Update(event)); tx.send_update(event).await;
} }
}); });
} }
@ -226,7 +224,7 @@ impl Module<Button> for VolumeModule {
let tx = context.tx.clone(); let tx = context.tx.clone();
button.connect_clicked(move |button| { button.connect_clicked(move |button| {
try_send!(tx, ModuleUpdateEvent::TogglePopup(button.popup_id())); tx.send_spawn(ModuleUpdateEvent::TogglePopup(button.popup_id()));
}); });
} }
@ -236,18 +234,23 @@ impl Module<Button> for VolumeModule {
let format = self.format.clone(); let format = self.format.clone();
glib_recv!(rx, event => { rx.recv_glib(move |event| match event {
match event { Event::AddSink(sink) | Event::UpdateSink(sink) if sink.active => {
Event::AddSink(sink) | Event::UpdateSink(sink) if sink.active => { let label = format
let label = format .replace(
.replace("{icon}", if sink.muted { &icons.muted } else { icons.volume_icon(sink.volume) }) "{icon}",
.replace("{percentage}", &sink.volume.to_string()) if sink.muted {
.replace("{name}", &sink.description); &icons.muted
} else {
icons.volume_icon(sink.volume)
},
)
.replace("{percentage}", &sink.volume.to_string())
.replace("{name}", &sink.description);
button_label.set_label_escaped(&label); button_label.set_label_escaped(&label);
},
_ => {}
} }
_ => {}
}); });
} }
@ -302,7 +305,7 @@ impl Module<Button> for VolumeModule {
let tx = tx.clone(); let tx = tx.clone();
sink_selector.connect_changed(move |selector| { sink_selector.connect_changed(move |selector| {
if let Some(name) = selector.active_id() { if let Some(name) = selector.active_id() {
try_send!(tx, Update::SinkChange(name.into())); tx.send_spawn(Update::SinkChange(name.into()));
} }
}); });
} }
@ -329,7 +332,7 @@ impl Module<Button> for VolumeModule {
if let Some(sink) = selector.active_id() { if let Some(sink) = selector.active_id() {
// GTK will send values outside min/max range // GTK will send values outside min/max range
let val = scale.value().clamp(0.0, self.max_volume); let val = scale.value().clamp(0.0, self.max_volume);
try_send!(tx, Update::SinkVolume(sink.into(), val)); tx.send_spawn(Update::SinkVolume(sink.into(), val));
} }
Propagation::Proceed Propagation::Proceed
@ -347,7 +350,7 @@ impl Module<Button> for VolumeModule {
btn_mute.connect_toggled(move |btn| { btn_mute.connect_toggled(move |btn| {
if let Some(sink) = selector.active_id() { if let Some(sink) = selector.active_id() {
let muted = btn.is_active(); let muted = btn.is_active();
try_send!(tx, Update::SinkMute(sink.into(), muted)); tx.send_spawn(Update::SinkMute(sink.into(), muted));
} }
}); });
} }
@ -361,7 +364,7 @@ impl Module<Button> for VolumeModule {
let mut sinks = vec![]; let mut sinks = vec![];
glib_recv!(rx, event => { rx.recv_glib(move |event| {
match event { match event {
Event::AddSink(info) => { Event::AddSink(info) => {
sink_selector.append(Some(&info.name), &info.description); sink_selector.append(Some(&info.name), &info.description);
@ -371,7 +374,11 @@ impl Module<Button> for VolumeModule {
slider.set_value(info.volume); slider.set_value(info.volume);
btn_mute.set_active(info.muted); btn_mute.set_active(info.muted);
btn_mute.set_label(if info.muted { &self.icons.muted } else { self.icons.volume_icon(info.volume) }); btn_mute.set_label(if info.muted {
&self.icons.muted
} else {
self.icons.volume_icon(info.volume)
});
} }
sinks.push(info); sinks.push(info);
@ -383,7 +390,11 @@ impl Module<Button> for VolumeModule {
slider.set_value(info.volume); slider.set_value(info.volume);
btn_mute.set_active(info.muted); btn_mute.set_active(info.muted);
btn_mute.set_label(if info.muted { &self.icons.muted } else { self.icons.volume_icon(info.volume) }); btn_mute.set_label(if info.muted {
&self.icons.muted
} else {
self.icons.volume_icon(info.volume)
});
} }
} }
} }
@ -413,7 +424,7 @@ impl Module<Button> for VolumeModule {
slider.connect_button_release_event(move |scale, _| { slider.connect_button_release_event(move |scale, _| {
// GTK will send values outside min/max range // GTK will send values outside min/max range
let val = scale.value().clamp(0.0, self.max_volume); let val = scale.value().clamp(0.0, self.max_volume);
try_send!(tx, Update::InputVolume(index, val)); tx.send_spawn(Update::InputVolume(index, val));
Propagation::Proceed Propagation::Proceed
}); });
@ -423,13 +434,17 @@ impl Module<Button> for VolumeModule {
btn_mute.add_class("btn-mute"); btn_mute.add_class("btn-mute");
btn_mute.set_active(info.muted); btn_mute.set_active(info.muted);
btn_mute.set_label(if info.muted { &self.icons.muted } else { self.icons.volume_icon(info.volume) }); btn_mute.set_label(if info.muted {
&self.icons.muted
} else {
self.icons.volume_icon(info.volume)
});
{ {
let tx = tx.clone(); let tx = tx.clone();
btn_mute.connect_toggled(move |btn| { btn_mute.connect_toggled(move |btn| {
let muted = btn.is_active(); let muted = btn.is_active();
try_send!(tx, Update::InputMute(index, muted)); tx.send_spawn(Update::InputMute(index, muted));
}); });
} }
@ -440,19 +455,26 @@ impl Module<Button> for VolumeModule {
input_container.add(&item_container); input_container.add(&item_container);
inputs.insert(info.index, InputUi { inputs.insert(
container: item_container, info.index,
label, InputUi {
slider, container: item_container,
btn_mute label,
}); slider,
btn_mute,
},
);
} }
Event::UpdateInput(info) => { Event::UpdateInput(info) => {
if let Some(ui) = inputs.get(&info.index) { if let Some(ui) = inputs.get(&info.index) {
ui.label.set_label(&info.name); ui.label.set_label(&info.name);
ui.slider.set_value(info.volume); ui.slider.set_value(info.volume);
ui.slider.set_sensitive(info.can_set_volume); ui.slider.set_sensitive(info.can_set_volume);
ui.btn_mute.set_label(if info.muted { &self.icons.muted } else { self.icons.volume_icon(info.volume) }); ui.btn_mute.set_label(if info.muted {
&self.icons.muted
} else {
self.icons.volume_icon(info.volume)
});
} }
} }
Event::RemoveInput(index) => { Event::RemoveInput(index) => {

View file

@ -1,8 +1,8 @@
use super::open_state::OpenState; use super::open_state::OpenState;
use crate::channels::AsyncSenderExt;
use crate::gtk_helpers::IronbarGtkExt; use crate::gtk_helpers::IronbarGtkExt;
use crate::image::IconButton; use crate::image::IconButton;
use crate::modules::workspaces::WorkspaceItemContext; use crate::modules::workspaces::WorkspaceItemContext;
use crate::try_send;
use gtk::Button as GtkButton; use gtk::Button as GtkButton;
use gtk::prelude::*; use gtk::prelude::*;
@ -23,7 +23,7 @@ impl Button {
let tx = context.tx.clone(); let tx = context.tx.clone();
button.connect_clicked(move |_item| { button.connect_clicked(move |_item| {
try_send!(tx, id); tx.send_spawn(id);
}); });
let btn = Self { let btn = Self {

View file

@ -3,12 +3,13 @@ mod button_map;
mod open_state; mod open_state;
use self::button::Button; use self::button::Button;
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
use crate::clients::compositor::{Workspace, WorkspaceClient, WorkspaceUpdate}; use crate::clients::compositor::{Workspace, WorkspaceClient, WorkspaceUpdate};
use crate::config::{CommonConfig, LayoutConfig}; use crate::config::{CommonConfig, LayoutConfig};
use crate::modules::workspaces::button_map::{ButtonMap, Identifier}; use crate::modules::workspaces::button_map::{ButtonMap, Identifier};
use crate::modules::workspaces::open_state::OpenState; use crate::modules::workspaces::open_state::OpenState;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleParts, WidgetContext};
use crate::{glib_recv, module_impl, send_async, spawn}; use crate::{module_impl, spawn};
use color_eyre::{Report, Result}; use color_eyre::{Report, Result};
use gtk::IconTheme; use gtk::IconTheme;
use gtk::prelude::*; use gtk::prelude::*;
@ -208,7 +209,7 @@ impl Module<gtk::Box> for WorkspacesModule {
while let Ok(payload) = srx.recv().await { while let Ok(payload) = srx.recv().await {
debug!("Received update: {payload:?}"); debug!("Received update: {payload:?}");
send_async!(tx, ModuleUpdateEvent::Update(payload)); tx.send_update(payload).await;
} }
}); });
@ -317,7 +318,7 @@ impl Module<gtk::Box> for WorkspacesModule {
} }
let name_map = self.name_map; let name_map = self.name_map;
let mut handle_event = move |event: WorkspaceUpdate| match event { let handle_event = move |event: WorkspaceUpdate| match event {
WorkspaceUpdate::Init(workspaces) => { WorkspaceUpdate::Init(workspaces) => {
if has_initialized { if has_initialized {
return; return;
@ -403,7 +404,7 @@ impl Module<gtk::Box> for WorkspacesModule {
WorkspaceUpdate::Unknown => warn!("received unknown type workspace event"), WorkspaceUpdate::Unknown => warn!("received unknown type workspace event"),
}; };
glib_recv!(context.subscribe(), handle_event); context.subscribe().recv_glib(handle_event);
} }
Ok(ModuleParts { Ok(ModuleParts {

View file

@ -3,11 +3,12 @@ use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use crate::channels::BroadcastReceiverExt;
use crate::clients::wayland::{OutputEvent, OutputEventType}; use crate::clients::wayland::{OutputEvent, OutputEventType};
use crate::config::BarPosition; use crate::config::BarPosition;
use crate::gtk_helpers::{IronbarGtkExt, WidgetGeometry}; use crate::gtk_helpers::{IronbarGtkExt, WidgetGeometry};
use crate::modules::{ModuleInfo, ModulePopupParts, PopupButton}; use crate::modules::{ModuleInfo, ModulePopupParts, PopupButton};
use crate::{Ironbar, glib_recv, rc_mut}; use crate::{Ironbar, rc_mut};
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{ApplicationWindow, Button, Orientation}; use gtk::{ApplicationWindow, Button, Orientation};
use gtk_layer_shell::LayerShell; use gtk_layer_shell::LayerShell;
@ -124,10 +125,8 @@ impl Popup {
} }
}; };
glib_recv!( let rx = ironbar.clients.borrow_mut().wayland().subscribe_outputs();
ironbar.clients.borrow_mut().wayland().subscribe_outputs(), rx.recv_glib(on_output_event);
on_output_event
);
} }
Self { Self {

View file

@ -1,4 +1,5 @@
use crate::{send_async, spawn}; use crate::channels::AsyncSenderExt;
use crate::spawn;
use color_eyre::eyre::WrapErr; use color_eyre::eyre::WrapErr;
use color_eyre::{Report, Result}; use color_eyre::{Report, Result};
use serde::Deserialize; use serde::Deserialize;
@ -302,11 +303,11 @@ impl Script {
_ = handle.wait() => break, _ = handle.wait() => break,
Ok(Some(line)) = stdout_lines.next_line() => { Ok(Some(line)) = stdout_lines.next_line() => {
debug!("sending stdout line: '{line}'"); debug!("sending stdout line: '{line}'");
send_async!(tx, OutputStream::Stdout(line)); tx.send_expect(OutputStream::Stdout(line)).await;
} }
Ok(Some(line)) = stderr_lines.next_line() => { Ok(Some(line)) = stderr_lines.next_line() => {
debug!("sending stderr line: '{line}'"); debug!("sending stderr line: '{line}'");
send_async!(tx, OutputStream::Stderr(line)); tx.send_expect(OutputStream::Stderr(line)).await;
} }
} }
} }

View file

@ -1,4 +1,5 @@
use crate::{glib_recv_mpsc, spawn, try_send}; use crate::channels::{AsyncSenderExt, MpscReceiverExt};
use crate::spawn;
use color_eyre::{Help, Report}; use color_eyre::{Help, Report};
use gtk::ffi::GTK_STYLE_PROVIDER_PRIORITY_USER; use gtk::ffi::GTK_STYLE_PROVIDER_PRIORITY_USER;
use gtk::prelude::*; use gtk::prelude::*;
@ -51,7 +52,7 @@ pub fn load_css(style_path: PathBuf, application: Application) {
Ok(event) if matches!(event.kind, EventKind::Modify(ModifyKind::Data(_))) => { Ok(event) if matches!(event.kind, EventKind::Modify(ModifyKind::Data(_))) => {
debug!("{event:?}"); debug!("{event:?}");
if event.paths.first().is_some_and(|p| p == &style_path2) { if event.paths.first().is_some_and(|p| p == &style_path2) {
try_send!(tx, style_path2.clone()); tx.send_spawn(style_path2.clone());
} }
} }
Err(e) => error!("Error occurred when watching stylesheet: {:?}", e), Err(e) => error!("Error occurred when watching stylesheet: {:?}", e),
@ -72,7 +73,7 @@ pub fn load_css(style_path: PathBuf, application: Application) {
} }
}); });
glib_recv_mpsc!(rx, path => { rx.recv_glib(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.load_from_file(&gio::File::for_path(path)) {
error!("{:?}", Report::new(err) error!("{:?}", Report::new(err)