mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2025-07-01 10:41:03 +02:00
Merge pull request #480 from JakeStanger/feat/swaync
feat: swaync notifications module
This commit is contained in:
commit
26677cd038
15 changed files with 592 additions and 3 deletions
|
@ -19,6 +19,7 @@ default = [
|
||||||
"ipc",
|
"ipc",
|
||||||
"launcher",
|
"launcher",
|
||||||
"music+all",
|
"music+all",
|
||||||
|
"notifications",
|
||||||
"sys_info",
|
"sys_info",
|
||||||
"tray",
|
"tray",
|
||||||
"upower",
|
"upower",
|
||||||
|
@ -57,6 +58,8 @@ music = ["regex"]
|
||||||
"music+mpris" = ["music", "mpris"]
|
"music+mpris" = ["music", "mpris"]
|
||||||
"music+mpd" = ["music", "mpd-utils"]
|
"music+mpd" = ["music", "mpd-utils"]
|
||||||
|
|
||||||
|
notifications = ["zbus"]
|
||||||
|
|
||||||
sys_info = ["sysinfo", "regex"]
|
sys_info = ["sysinfo", "regex"]
|
||||||
|
|
||||||
tray = ["system-tray"]
|
tray = ["system-tray"]
|
||||||
|
@ -133,7 +136,6 @@ system-tray = { version = "0.1.5", optional = true }
|
||||||
# upower
|
# upower
|
||||||
upower_dbus = { version = "0.3.2", optional = true }
|
upower_dbus = { version = "0.3.2", optional = true }
|
||||||
futures-lite = { version = "2.2.0", optional = true }
|
futures-lite = { version = "2.2.0", optional = true }
|
||||||
zbus = { version = "3.15.2", optional = true }
|
|
||||||
|
|
||||||
# volume
|
# volume
|
||||||
libpulse-binding = { version = "2.28.1", optional = true }
|
libpulse-binding = { version = "2.28.1", optional = true }
|
||||||
|
@ -148,3 +150,4 @@ futures-util = { version = "0.3.30", optional = true }
|
||||||
regex = { version = "1.10.3", default-features = false, features = [
|
regex = { version = "1.10.3", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
], optional = true } # music, sys_info
|
], optional = true } # music, sys_info
|
||||||
|
zbus = { version = "3.15.2", optional = true } # notifications, upower
|
|
@ -30,6 +30,7 @@
|
||||||
- [Label](label)
|
- [Label](label)
|
||||||
- [Launcher](launcher)
|
- [Launcher](launcher)
|
||||||
- [Music](music)
|
- [Music](music)
|
||||||
|
- [Notifications](notifications)
|
||||||
- [Script](script)
|
- [Script](script)
|
||||||
- [Sys_Info](sys-info)
|
- [Sys_Info](sys-info)
|
||||||
- [Tray](tray)
|
- [Tray](tray)
|
||||||
|
|
116
docs/modules/Notifications.md
Normal file
116
docs/modules/Notifications.md
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
Displays information about the current SwayNC state such as notification count and DnD.
|
||||||
|
Clicking the widget opens the SwayNC panel.
|
||||||
|
|
||||||
|
[!Notifications widget in its closed state showing 3 notifications](https://f.jstanger.dev/github/ironbar/notifications.png)
|
||||||
|
|
||||||
|
> ![NOTE]
|
||||||
|
> This widget requires the [SwayNC](https://github.com/ErikReider/SwayNotificationCenter)
|
||||||
|
> daemon to be running to use.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
> Type: `notifications`
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
|---------------------|-----------|---------|--------------------------------------------------------------------------------------------------------|
|
||||||
|
| `show_count` | `boolean` | `true` | Whether to show the current notification count. |
|
||||||
|
| `icons.closed_none` | `string` | `` | Icon to show when the panel is closed, with no notifications. |
|
||||||
|
| `icons.closed_some` | `string` | `` | Icon to show when the panel is closed, with notifications. |
|
||||||
|
| `icons.closed_dnd` | `string` | `` | Icon to show when the panel is closed, with DnD enabled. Takes higher priority than count-based icons. |
|
||||||
|
| `icons.open_none` | `string` | `` | Icon to show when the panel is open, with no notifications. |
|
||||||
|
| `icons.open_some` | `string` | `` | Icon to show when the panel is open, with notifications. |
|
||||||
|
| `icons.open_dnd` | `string` | `` | Icon to show when the panel is open, with DnD enabled. Takes higher priority than count-based icons. |
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>JSON</summary>
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"end": [
|
||||||
|
{
|
||||||
|
"type": "notifications",
|
||||||
|
"show_count": true,
|
||||||
|
"icons": {
|
||||||
|
"closed_none": "",
|
||||||
|
"closed_some": "",
|
||||||
|
"closed_dnd": "",
|
||||||
|
"open_none": "",
|
||||||
|
"open_some": "",
|
||||||
|
"open_dnd": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>TOML</summary>
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[end]]
|
||||||
|
type = "notifications"
|
||||||
|
show_count = true
|
||||||
|
|
||||||
|
[[end.icons]]
|
||||||
|
closed_none = ""
|
||||||
|
closed_some = ""
|
||||||
|
closed_dnd = ""
|
||||||
|
open_none = ""
|
||||||
|
open_some = ""
|
||||||
|
open_dnd = ""
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>YAML</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
end:
|
||||||
|
- type: notifications
|
||||||
|
show_count: true
|
||||||
|
icons:
|
||||||
|
closed_none:
|
||||||
|
closed_some:
|
||||||
|
closed_dnd:
|
||||||
|
open_none:
|
||||||
|
open_some:
|
||||||
|
open_dnd:
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Corn</summary>
|
||||||
|
|
||||||
|
```corn
|
||||||
|
{
|
||||||
|
end = [
|
||||||
|
{
|
||||||
|
type = "notifications"
|
||||||
|
show_count = true
|
||||||
|
|
||||||
|
icons.closed_none = ""
|
||||||
|
icons.closed_some = ""
|
||||||
|
icons.closed_dnd = ""
|
||||||
|
icons.open_none = ""
|
||||||
|
icons.open_some = ""
|
||||||
|
icons.open_dnd = ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Styling
|
||||||
|
|
||||||
|
| Selector | Description |
|
||||||
|
|-------------------------|---------------------------------------|
|
||||||
|
| `.notifications` | Notifications widget button |
|
||||||
|
| `.notifications .count` | Notifications count indicator overlay |
|
||||||
|
|
||||||
|
For more information on styling, please see the [styling guide](styling-guide).
|
|
@ -33,6 +33,18 @@ let {
|
||||||
$mpd_local = { type = "music" player_type = "mpd" music_dir = "/home/jake/Music" truncate.mode = "end" truncate.max_length = 100 }
|
$mpd_local = { type = "music" player_type = "mpd" music_dir = "/home/jake/Music" truncate.mode = "end" truncate.max_length = 100 }
|
||||||
$mpd_server = { type = "music" player_type = "mpd" host = "chloe:6600" truncate = "end" }
|
$mpd_server = { type = "music" player_type = "mpd" host = "chloe:6600" truncate = "end" }
|
||||||
|
|
||||||
|
$notifications = {
|
||||||
|
type = "notifications"
|
||||||
|
show_count = true
|
||||||
|
|
||||||
|
icons.closed_none = ""
|
||||||
|
icons.closed_some = ""
|
||||||
|
icons.closed_dnd = ""
|
||||||
|
icons.open_none = ""
|
||||||
|
icons.open_some = ""
|
||||||
|
icons.open_dnd = ""
|
||||||
|
}
|
||||||
|
|
||||||
$sys_info = {
|
$sys_info = {
|
||||||
type = "sys_info"
|
type = "sys_info"
|
||||||
|
|
||||||
|
@ -110,7 +122,7 @@ let {
|
||||||
// -- end custom --
|
// -- end custom --
|
||||||
|
|
||||||
$left = [ $workspaces $launcher $label ]
|
$left = [ $workspaces $launcher $label ]
|
||||||
$right = [ $mpd_local $mpd_server $phone_battery $sys_info $volume $clipboard $power_menu $clock ]
|
$right = [ $mpd_local $mpd_server $phone_battery $sys_info $volume $clipboard $power_menu $clock $notifications ]
|
||||||
}
|
}
|
||||||
in {
|
in {
|
||||||
anchor_to_edges = true
|
anchor_to_edges = true
|
||||||
|
|
|
@ -141,6 +141,18 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "clock"
|
"type": "clock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "notifications",
|
||||||
|
"show_count": true,
|
||||||
|
"icons": {
|
||||||
|
"closed_none": "",
|
||||||
|
"closed_some": "",
|
||||||
|
"closed_dnd": "",
|
||||||
|
"open_none": "",
|
||||||
|
"open_some": "",
|
||||||
|
"open_dnd": ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,3 +131,15 @@ label = "Uptime: {{30000:uptime -p | cut -d ' ' -f2-}}"
|
||||||
[[end]]
|
[[end]]
|
||||||
type = "clock"
|
type = "clock"
|
||||||
|
|
||||||
|
[[end]]
|
||||||
|
type = "notifications"
|
||||||
|
show_count = true
|
||||||
|
|
||||||
|
[end.icons]
|
||||||
|
closed_none = ""
|
||||||
|
closed_some = ""
|
||||||
|
closed_dnd = ""
|
||||||
|
open_none = ""
|
||||||
|
open_some = ""
|
||||||
|
open_dnd = ""
|
||||||
|
|
||||||
|
|
|
@ -92,4 +92,13 @@ end:
|
||||||
label: 'Uptime: {{30000:uptime -p | cut -d '' '' -f2-}}'
|
label: 'Uptime: {{30000:uptime -p | cut -d '' '' -f2-}}'
|
||||||
tooltip: 'Up: {{30000:uptime -p | cut -d '' '' -f2-}}'
|
tooltip: 'Up: {{30000:uptime -p | cut -d '' '' -f2-}}'
|
||||||
- type: clock
|
- type: clock
|
||||||
|
- type: notifications
|
||||||
|
show_count: true
|
||||||
|
icons:
|
||||||
|
closed_none:
|
||||||
|
closed_some:
|
||||||
|
closed_dnd:
|
||||||
|
open_none:
|
||||||
|
open_some:
|
||||||
|
open_dnd:
|
||||||
|
|
||||||
|
|
|
@ -150,6 +150,20 @@ scale trough {
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* notifications */
|
||||||
|
|
||||||
|
.notifications .count {
|
||||||
|
font-size: 0.6rem;
|
||||||
|
background-color: @color_text;
|
||||||
|
color: @color_bg;
|
||||||
|
border-radius: 100%;
|
||||||
|
margin-right: 3px;
|
||||||
|
margin-top: 3px;
|
||||||
|
padding-left: 4px;
|
||||||
|
padding-right: 4px;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
/* -- script -- */
|
/* -- script -- */
|
||||||
|
|
||||||
.script {
|
.script {
|
||||||
|
|
|
@ -385,6 +385,8 @@ fn add_modules(
|
||||||
ModuleConfig::Launcher(mut module) => add_module!(module, id),
|
ModuleConfig::Launcher(mut module) => add_module!(module, id),
|
||||||
#[cfg(feature = "music")]
|
#[cfg(feature = "music")]
|
||||||
ModuleConfig::Music(mut module) => add_module!(module, id),
|
ModuleConfig::Music(mut module) => add_module!(module, id),
|
||||||
|
#[cfg(feature = "notifications")]
|
||||||
|
ModuleConfig::Notifications(mut module) => add_module!(module, id),
|
||||||
ModuleConfig::Script(mut module) => add_module!(module, id),
|
ModuleConfig::Script(mut module) => add_module!(module, id),
|
||||||
#[cfg(feature = "sys_info")]
|
#[cfg(feature = "sys_info")]
|
||||||
ModuleConfig::SysInfo(mut module) => add_module!(module, id),
|
ModuleConfig::SysInfo(mut module) => add_module!(module, id),
|
||||||
|
|
|
@ -6,6 +6,8 @@ pub mod clipboard;
|
||||||
pub mod compositor;
|
pub mod compositor;
|
||||||
#[cfg(feature = "music")]
|
#[cfg(feature = "music")]
|
||||||
pub mod music;
|
pub mod music;
|
||||||
|
#[cfg(feature = "notifications")]
|
||||||
|
pub mod swaync;
|
||||||
#[cfg(feature = "tray")]
|
#[cfg(feature = "tray")]
|
||||||
pub mod system_tray;
|
pub mod system_tray;
|
||||||
#[cfg(feature = "upower")]
|
#[cfg(feature = "upower")]
|
||||||
|
@ -25,6 +27,8 @@ pub struct Clients {
|
||||||
clipboard: Option<Arc<clipboard::Client>>,
|
clipboard: Option<Arc<clipboard::Client>>,
|
||||||
#[cfg(feature = "music")]
|
#[cfg(feature = "music")]
|
||||||
music: std::collections::HashMap<music::ClientType, Arc<dyn music::MusicClient>>,
|
music: std::collections::HashMap<music::ClientType, Arc<dyn music::MusicClient>>,
|
||||||
|
#[cfg(feature = "notifications")]
|
||||||
|
notifications: Option<Arc<swaync::Client>>,
|
||||||
#[cfg(feature = "tray")]
|
#[cfg(feature = "tray")]
|
||||||
tray: Option<Arc<system_tray::TrayEventReceiver>>,
|
tray: Option<Arc<system_tray::TrayEventReceiver>>,
|
||||||
#[cfg(feature = "upower")]
|
#[cfg(feature = "upower")]
|
||||||
|
@ -71,6 +75,15 @@ impl Clients {
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "notifications")]
|
||||||
|
pub fn notifications(&mut self) -> Arc<swaync::Client> {
|
||||||
|
self.notifications
|
||||||
|
.get_or_insert_with(|| {
|
||||||
|
Arc::new(crate::await_sync(async { swaync::Client::new().await }))
|
||||||
|
})
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "tray")]
|
#[cfg(feature = "tray")]
|
||||||
pub fn tray(&mut self) -> Arc<system_tray::TrayEventReceiver> {
|
pub fn tray(&mut self) -> Arc<system_tray::TrayEventReceiver> {
|
||||||
self.tray
|
self.tray
|
||||||
|
@ -122,7 +135,7 @@ macro_rules! register_client {
|
||||||
where
|
where
|
||||||
TSend: Clone,
|
TSend: Clone,
|
||||||
{
|
{
|
||||||
fn provide(&self) -> Arc<$ty> {
|
fn provide(&self) -> std::sync::Arc<$ty> {
|
||||||
self.ironbar.clients.borrow_mut().$method()
|
self.ironbar.clients.borrow_mut().$method()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
111
src/clients/swaync/dbus.rs
Normal file
111
src/clients/swaync/dbus.rs
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
//! # D-Bus interface proxy for: `org.erikreider.swaync.cc`
|
||||||
|
//!
|
||||||
|
//! This code was generated by `zbus-xmlgen` `4.0.1` from D-Bus introspection data.
|
||||||
|
//! Source: `Interface '/org/erikreider/swaync/cc' from service 'org.erikreider.swaync.cc' on session bus`.
|
||||||
|
//!
|
||||||
|
//! You may prefer to adapt it, instead of using it verbatim.
|
||||||
|
//!
|
||||||
|
//! More information can be found in the [Writing a client proxy] section of the zbus
|
||||||
|
//! documentation.
|
||||||
|
//!
|
||||||
|
//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the
|
||||||
|
//! following zbus API can be used:
|
||||||
|
//!
|
||||||
|
//! * [`zbus::fdo::PropertiesProxy`]
|
||||||
|
//! * [`zbus::fdo::IntrospectableProxy`]
|
||||||
|
//! * [`zbus::fdo::PeerProxy`]
|
||||||
|
//!
|
||||||
|
//! Consequently `zbus-xmlgen` did not generate code for the above interfaces.
|
||||||
|
//!
|
||||||
|
//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html
|
||||||
|
//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces,
|
||||||
|
|
||||||
|
#[zbus::dbus_proxy(
|
||||||
|
interface = "org.erikreider.swaync.cc",
|
||||||
|
default_service = "org.erikreider.swaync.cc",
|
||||||
|
default_path = "/org/erikreider/swaync/cc"
|
||||||
|
)]
|
||||||
|
trait SwayNc {
|
||||||
|
/// AddInhibitor method
|
||||||
|
fn add_inhibitor(&self, application_id: &str) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// ChangeConfigValue method
|
||||||
|
fn change_config_value(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
value: &zbus::zvariant::Value<'_>,
|
||||||
|
write_to_file: bool,
|
||||||
|
path: &str,
|
||||||
|
) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// ClearInhibitors method
|
||||||
|
fn clear_inhibitors(&self) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// CloseAllNotifications method
|
||||||
|
fn close_all_notifications(&self) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// CloseNotification method
|
||||||
|
fn close_notification(&self, id: u32) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// GetDnd method
|
||||||
|
fn get_dnd(&self) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// GetSubscribeData method
|
||||||
|
fn get_subscribe_data(&self) -> zbus::Result<(bool, bool, u32, bool)>;
|
||||||
|
|
||||||
|
/// GetVisibility method
|
||||||
|
fn get_visibility(&self) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// HideLatestNotifications method
|
||||||
|
fn hide_latest_notifications(&self, close: bool) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// IsInhibited method
|
||||||
|
fn is_inhibited(&self) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// NotificationCount method
|
||||||
|
fn notification_count(&self) -> zbus::Result<u32>;
|
||||||
|
|
||||||
|
/// NumberOfInhibitors method
|
||||||
|
fn number_of_inhibitors(&self) -> zbus::Result<u32>;
|
||||||
|
|
||||||
|
/// ReloadConfig method
|
||||||
|
fn reload_config(&self) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// ReloadCss method
|
||||||
|
fn reload_css(&self) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// RemoveInhibitor method
|
||||||
|
fn remove_inhibitor(&self, application_id: &str) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// SetDnd method
|
||||||
|
fn set_dnd(&self, state: bool) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// SetVisibility method
|
||||||
|
fn set_visibility(&self, visibility: bool) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// ToggleDnd method
|
||||||
|
fn toggle_dnd(&self) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// ToggleVisibility method
|
||||||
|
fn toggle_visibility(&self) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// Subscribe signal
|
||||||
|
#[dbus_proxy(signal)]
|
||||||
|
fn subscribe(&self, count: u32, dnd: bool, cc_open: bool) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// SubscribeV2 signal
|
||||||
|
#[dbus_proxy(signal)]
|
||||||
|
fn subscribe_v2(
|
||||||
|
&self,
|
||||||
|
count: u32,
|
||||||
|
dnd: bool,
|
||||||
|
cc_open: bool,
|
||||||
|
inhibited: bool,
|
||||||
|
) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// Inhibited property
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn inhibited(&self) -> zbus::Result<bool>;
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn set_inhibited(&self, value: bool) -> zbus::Result<()>;
|
||||||
|
}
|
88
src/clients/swaync/mod.rs
Normal file
88
src/clients/swaync/mod.rs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
mod dbus;
|
||||||
|
|
||||||
|
use crate::{register_client, send, spawn};
|
||||||
|
use color_eyre::{Report, Result};
|
||||||
|
use dbus::SwayNcProxy;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
use tracing::{debug, error};
|
||||||
|
use zbus::export::ordered_stream::OrderedStreamExt;
|
||||||
|
use zbus::zvariant::Type;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Type, Deserialize)]
|
||||||
|
pub struct Event {
|
||||||
|
pub count: u32,
|
||||||
|
pub dnd: bool,
|
||||||
|
pub cc_open: bool,
|
||||||
|
pub inhibited: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSubscribeData = (bool, bool, u32, bool);
|
||||||
|
|
||||||
|
/// Converts the data returned from
|
||||||
|
/// `get_subscribe_data` into an event for convenience.
|
||||||
|
impl From<GetSubscribeData> for Event {
|
||||||
|
fn from((dnd, cc_open, count, inhibited): (bool, bool, u32, bool)) -> Self {
|
||||||
|
Self {
|
||||||
|
dnd,
|
||||||
|
cc_open,
|
||||||
|
count,
|
||||||
|
inhibited,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Client {
|
||||||
|
proxy: SwayNcProxy<'static>,
|
||||||
|
tx: broadcast::Sender<Event>,
|
||||||
|
_rx: broadcast::Receiver<Event>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
pub async fn new() -> Self {
|
||||||
|
let dbus = Box::pin(zbus::Connection::session())
|
||||||
|
.await
|
||||||
|
.expect("failed to create connection to system bus");
|
||||||
|
|
||||||
|
let proxy = SwayNcProxy::new(&dbus).await.unwrap();
|
||||||
|
let (tx, rx) = broadcast::channel(8);
|
||||||
|
|
||||||
|
let mut stream = proxy.receive_subscribe_v2().await.unwrap();
|
||||||
|
|
||||||
|
{
|
||||||
|
let tx = tx.clone();
|
||||||
|
|
||||||
|
spawn(async move {
|
||||||
|
while let Some(ev) = stream.next().await {
|
||||||
|
let ev = ev.body::<Event>().expect("to deserialize");
|
||||||
|
debug!("Received event: {ev:?}");
|
||||||
|
send!(tx, ev);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Self { proxy, tx, _rx: rx }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subscribe(&self) -> broadcast::Receiver<Event> {
|
||||||
|
self.tx.subscribe()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn state(&self) -> Result<Event> {
|
||||||
|
debug!("Getting subscribe data (current state)");
|
||||||
|
match self.proxy.get_subscribe_data().await {
|
||||||
|
Ok(data) => Ok(data.into()),
|
||||||
|
Err(err) => Err(Report::new(err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn toggle_visibility(&self) {
|
||||||
|
debug!("Toggling visibility");
|
||||||
|
if let Err(err) = self.proxy.toggle_visibility().await {
|
||||||
|
error!("{err:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register_client!(Client, notifications);
|
|
@ -14,6 +14,8 @@ use crate::modules::label::LabelModule;
|
||||||
use crate::modules::launcher::LauncherModule;
|
use crate::modules::launcher::LauncherModule;
|
||||||
#[cfg(feature = "music")]
|
#[cfg(feature = "music")]
|
||||||
use crate::modules::music::MusicModule;
|
use crate::modules::music::MusicModule;
|
||||||
|
#[cfg(feature = "notifications")]
|
||||||
|
use crate::modules::notifications::NotificationsModule;
|
||||||
use crate::modules::script::ScriptModule;
|
use crate::modules::script::ScriptModule;
|
||||||
#[cfg(feature = "sys_info")]
|
#[cfg(feature = "sys_info")]
|
||||||
use crate::modules::sysinfo::SysInfoModule;
|
use crate::modules::sysinfo::SysInfoModule;
|
||||||
|
@ -47,6 +49,8 @@ pub enum ModuleConfig {
|
||||||
Launcher(Box<LauncherModule>),
|
Launcher(Box<LauncherModule>),
|
||||||
#[cfg(feature = "music")]
|
#[cfg(feature = "music")]
|
||||||
Music(Box<MusicModule>),
|
Music(Box<MusicModule>),
|
||||||
|
#[cfg(feature = "notifications")]
|
||||||
|
Notifications(Box<NotificationsModule>),
|
||||||
Script(Box<ScriptModule>),
|
Script(Box<ScriptModule>),
|
||||||
#[cfg(feature = "sys_info")]
|
#[cfg(feature = "sys_info")]
|
||||||
SysInfo(Box<SysInfoModule>),
|
SysInfo(Box<SysInfoModule>),
|
||||||
|
|
|
@ -34,6 +34,8 @@ pub mod label;
|
||||||
pub mod launcher;
|
pub mod launcher;
|
||||||
#[cfg(feature = "music")]
|
#[cfg(feature = "music")]
|
||||||
pub mod music;
|
pub mod music;
|
||||||
|
#[cfg(feature = "notifications")]
|
||||||
|
pub mod notifications;
|
||||||
pub mod script;
|
pub mod script;
|
||||||
#[cfg(feature = "sys_info")]
|
#[cfg(feature = "sys_info")]
|
||||||
pub mod sysinfo;
|
pub mod sysinfo;
|
||||||
|
|
190
src/modules/notifications.rs
Normal file
190
src/modules/notifications.rs
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
use crate::clients::swaync;
|
||||||
|
use crate::config::CommonConfig;
|
||||||
|
use crate::gtk_helpers::IronbarGtkExt;
|
||||||
|
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
||||||
|
use crate::{glib_recv, send_async, spawn, try_send};
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use gtk::{Align, Button, Label, Overlay};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use tokio::sync::mpsc::Receiver;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct NotificationsModule {
|
||||||
|
#[serde(default = "crate::config::default_true")]
|
||||||
|
show_count: bool,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
icons: Icons,
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub common: Option<CommonConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
struct Icons {
|
||||||
|
#[serde(default = "default_icon_closed_none")]
|
||||||
|
closed_none: String,
|
||||||
|
#[serde(default = "default_icon_closed_some")]
|
||||||
|
closed_some: String,
|
||||||
|
#[serde(default = "default_icon_closed_dnd")]
|
||||||
|
closed_dnd: String,
|
||||||
|
#[serde(default = "default_icon_open_none")]
|
||||||
|
open_none: String,
|
||||||
|
#[serde(default = "default_icon_open_some")]
|
||||||
|
open_some: String,
|
||||||
|
#[serde(default = "default_icon_open_dnd")]
|
||||||
|
open_dnd: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Icons {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
closed_none: default_icon_closed_none(),
|
||||||
|
closed_some: default_icon_closed_some(),
|
||||||
|
closed_dnd: default_icon_closed_dnd(),
|
||||||
|
open_none: default_icon_open_none(),
|
||||||
|
open_some: default_icon_open_some(),
|
||||||
|
open_dnd: default_icon_open_dnd(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_icon_closed_none() -> String {
|
||||||
|
String::from("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_icon_closed_some() -> String {
|
||||||
|
String::from("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_icon_closed_dnd() -> String {
|
||||||
|
String::from("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_icon_open_none() -> String {
|
||||||
|
String::from("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_icon_open_some() -> String {
|
||||||
|
String::from("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_icon_open_dnd() -> String {
|
||||||
|
String::from("")
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Icons {
|
||||||
|
fn icon(&self, value: &swaync::Event) -> &str {
|
||||||
|
match (value.cc_open, value.count > 0, value.dnd) {
|
||||||
|
(true, _, true) => &self.open_dnd,
|
||||||
|
(true, true, false) => &self.open_some,
|
||||||
|
(true, false, false) => &self.open_none,
|
||||||
|
(false, _, true) => &self.closed_dnd,
|
||||||
|
(false, true, false) => &self.closed_some,
|
||||||
|
(false, false, false) => &self.closed_none,
|
||||||
|
}
|
||||||
|
.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum UiEvent {
|
||||||
|
ToggleVisibility,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Module<Overlay> for NotificationsModule {
|
||||||
|
type SendMessage = swaync::Event;
|
||||||
|
type ReceiveMessage = UiEvent;
|
||||||
|
|
||||||
|
fn name() -> &'static str {
|
||||||
|
"notifications"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_controller(
|
||||||
|
&self,
|
||||||
|
_info: &ModuleInfo,
|
||||||
|
context: &WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
||||||
|
mut rx: Receiver<Self::ReceiveMessage>,
|
||||||
|
) -> color_eyre::Result<()>
|
||||||
|
where
|
||||||
|
<Self as Module<Overlay>>::SendMessage: Clone,
|
||||||
|
{
|
||||||
|
let client = context.client::<swaync::Client>();
|
||||||
|
|
||||||
|
{
|
||||||
|
let client = client.clone();
|
||||||
|
let mut rx = client.subscribe();
|
||||||
|
let tx = context.tx.clone();
|
||||||
|
|
||||||
|
spawn(async move {
|
||||||
|
let initial_state = client.state().await;
|
||||||
|
|
||||||
|
match initial_state {
|
||||||
|
Ok(ev) => send_async!(tx, ModuleUpdateEvent::Update(ev)),
|
||||||
|
Err(err) => error!("{err:?}"),
|
||||||
|
};
|
||||||
|
|
||||||
|
while let Ok(ev) = rx.recv().await {
|
||||||
|
send_async!(tx, ModuleUpdateEvent::Update(ev));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
spawn(async move {
|
||||||
|
while let Some(event) = rx.recv().await {
|
||||||
|
match event {
|
||||||
|
UiEvent::ToggleVisibility => client.toggle_visibility().await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_widget(
|
||||||
|
self,
|
||||||
|
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
||||||
|
_info: &ModuleInfo,
|
||||||
|
) -> color_eyre::Result<ModuleParts<Overlay>>
|
||||||
|
where
|
||||||
|
<Self as Module<Overlay>>::SendMessage: Clone,
|
||||||
|
{
|
||||||
|
let overlay = Overlay::new();
|
||||||
|
let button = Button::with_label(&self.icons.closed_none);
|
||||||
|
overlay.add(&button);
|
||||||
|
|
||||||
|
let label = Label::builder()
|
||||||
|
.label("0")
|
||||||
|
.halign(Align::End)
|
||||||
|
.valign(Align::Start)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
if self.show_count {
|
||||||
|
label.add_class("count");
|
||||||
|
overlay.add_overlay(&label);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ctx = context.controller_tx.clone();
|
||||||
|
button.connect_clicked(move |_| {
|
||||||
|
try_send!(ctx, UiEvent::ToggleVisibility);
|
||||||
|
});
|
||||||
|
|
||||||
|
{
|
||||||
|
let button = button.clone();
|
||||||
|
|
||||||
|
glib_recv!(context.subscribe(), ev => {
|
||||||
|
let icon = self.icons.icon(&ev);
|
||||||
|
button.set_label(icon);
|
||||||
|
|
||||||
|
label.set_label(&ev.count.to_string());
|
||||||
|
label.set_visible(self.show_count && ev.count > 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ModuleParts {
|
||||||
|
widget: overlay,
|
||||||
|
popup: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue