forked from Shinonome/dots-hyprland
stuff
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
- This folder is for programs that have a proper folder structure that should be compiled then copied to .config/eww/scripts, if you wish.
|
||||
- If you don't mind using prebuilt binaries, you can ignore this folder
|
||||
Generated
+1926
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "notify-receive"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.69"
|
||||
chrono = "0.4.23"
|
||||
gtk = "0.17.1"
|
||||
mpsc = "0.1.0"
|
||||
nohash-hasher = "0.2.0"
|
||||
serde = {version = "1.0.152", features = ["derive"]}
|
||||
serde_json = "1.0.93"
|
||||
tokio = { version = "1.26.0", features = ["rt-multi-thread", "macros", "sync", "time"] }
|
||||
unix-named-pipe = "0.2.0"
|
||||
zbus = "3.10.0"
|
||||
@@ -0,0 +1,144 @@
|
||||
use super::NotificationArc;
|
||||
use chrono::Local;
|
||||
use std::process::Command;
|
||||
use std::path::Path;
|
||||
use gtk::{prelude::IconThemeExt, IconLookupFlags, IconTheme};
|
||||
use std::collections::HashMap;
|
||||
use zbus::zvariant::Value;
|
||||
use zbus::{dbus_interface, Connection};
|
||||
|
||||
use crate::Notification;
|
||||
|
||||
const CAPABILITIES: [&str; 6] = [
|
||||
"icons",
|
||||
"actions",
|
||||
"body",
|
||||
"body-images",
|
||||
"action-icons",
|
||||
"persistence",
|
||||
];
|
||||
|
||||
const SERVER_INFORMATION: (&str, &str, &str, &str) =
|
||||
("notify-receive", "neoney.dev", "0.0.1", "1.0");
|
||||
|
||||
const NAME: &str = "org.freedesktop.Notifications";
|
||||
const PATH: &str = "/org/freedesktop/Notifications";
|
||||
|
||||
struct NotificationServer {
|
||||
notifications: NotificationArc,
|
||||
biggest_id: u32,
|
||||
}
|
||||
|
||||
impl NotificationServer {
|
||||
fn new(notifications: NotificationArc) -> Self {
|
||||
NotificationServer {
|
||||
notifications,
|
||||
biggest_id: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[dbus_interface(name = "org.freedesktop.Notifications")]
|
||||
impl NotificationServer {
|
||||
async fn notify(
|
||||
&mut self,
|
||||
app_name: String,
|
||||
replaces_id: u32,
|
||||
app_icon: String,
|
||||
summary: String,
|
||||
body: String,
|
||||
actions: Vec<String>,
|
||||
hints: HashMap<String, Value<'_>>,
|
||||
expire_timeout: i32,
|
||||
) -> u32 {
|
||||
gtk::init().expect("Failed to initialize GTK");
|
||||
|
||||
let dt = Local::now();
|
||||
let time = dt.time().format("%H:%M").to_string();
|
||||
|
||||
let changed_id = if replaces_id != 0 {
|
||||
replaces_id
|
||||
} else {
|
||||
self.biggest_id += 1;
|
||||
self.biggest_id
|
||||
};
|
||||
|
||||
let icon = match app_icon.as_str() {
|
||||
"" => "".to_string(),
|
||||
app_icon => {
|
||||
let icon_theme = IconTheme::default().expect("Failed to find icon theme.");
|
||||
let icon = icon_theme.lookup_icon(app_icon, 256, IconLookupFlags::empty());
|
||||
|
||||
icon.map_or(app_icon.to_string(), |icon| {
|
||||
icon.filename().unwrap().to_string_lossy().to_string()
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let payload = Notification {
|
||||
id: changed_id,
|
||||
app_name,
|
||||
summary,
|
||||
time,
|
||||
body,
|
||||
actions,
|
||||
urgency: hints
|
||||
.get("urgency")
|
||||
.and_then(|v| v.clone().downcast())
|
||||
.unwrap_or_default(),
|
||||
icon,
|
||||
timeout: expire_timeout,
|
||||
visible: true,
|
||||
};
|
||||
|
||||
let mut notifs = self.notifications.lock().await;
|
||||
|
||||
notifs.insert(changed_id, payload);
|
||||
|
||||
let path = Path::new("./scripts/notification-on-receive.sh");
|
||||
if path.exists() {
|
||||
Command::new(path)
|
||||
.spawn()
|
||||
.expect("Failed to execute command");
|
||||
}
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string(¬ifs.values().collect::<Vec<_>>()).unwrap()
|
||||
);
|
||||
|
||||
changed_id
|
||||
}
|
||||
|
||||
async fn close(&mut self, id: u32) {
|
||||
let mut notifs = self.notifications.lock().await;
|
||||
|
||||
if let Some(notif) = notifs.get_mut(&id) {
|
||||
notif.visible = false;
|
||||
};
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string(¬ifs.values().collect::<Vec<_>>()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
fn get_capabilities(&self) -> &[&str] {
|
||||
&CAPABILITIES
|
||||
}
|
||||
fn get_server_information(&self) -> (&str, &str, &str, &str) {
|
||||
SERVER_INFORMATION
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn async_run(notifications: NotificationArc) -> zbus::Result<()> {
|
||||
let connection = Connection::session().await?;
|
||||
let server = NotificationServer::new(notifications);
|
||||
|
||||
connection.object_server().at(PATH, server).await?;
|
||||
|
||||
connection.request_name(NAME).await?;
|
||||
|
||||
loop {
|
||||
std::future::pending::<()>().await;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
mod dbus;
|
||||
|
||||
use std::fs;
|
||||
use std::time::Duration;
|
||||
use std::{collections::HashMap, hash::BuildHasherDefault, path::Path, sync::Arc};
|
||||
|
||||
use std::io::Read;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use anyhow::Result;
|
||||
use nohash_hasher::NoHashHasher;
|
||||
use zbus::export::serde::Serialize;
|
||||
|
||||
use crate::dbus::async_run;
|
||||
|
||||
type NotificationArc =
|
||||
Arc<Mutex<HashMap<u32, Notification, BuildHasherDefault<NoHashHasher<u32>>>>>;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Notifications<'a> {
|
||||
pub notifications: Vec<&'a Notification>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
pub struct Notification {
|
||||
pub id: u32,
|
||||
pub app_name: String,
|
||||
pub summary: String,
|
||||
pub body: String,
|
||||
pub time: String,
|
||||
pub icon: String,
|
||||
pub timeout: i32,
|
||||
pub urgency: u8,
|
||||
pub actions: Vec<String>,
|
||||
pub visible: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Action {
|
||||
Close,
|
||||
Clear,
|
||||
Notify(Notification),
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let notifications: NotificationArc = Arc::new(Mutex::new(HashMap::with_hasher(
|
||||
BuildHasherDefault::default(),
|
||||
)));
|
||||
|
||||
fs::remove_file(Path::new("/var/run/user/1000/notify-receive.pipe")).ok();
|
||||
unix_named_pipe::create("/var/run/user/1000/notify-receive.pipe", Some(0o644))?;
|
||||
|
||||
let mut reader = unix_named_pipe::open_read("/var/run/user/1000/notify-receive.pipe")?;
|
||||
|
||||
let pipe_notifications = notifications.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(Duration::from_millis(100));
|
||||
|
||||
loop {
|
||||
let mut contents = String::new();
|
||||
if reader.read_to_string(&mut contents).is_ok() && !contents.is_empty() {
|
||||
let mut notifs = pipe_notifications.lock().await;
|
||||
for line in contents.lines() {
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Ok(id) = line.parse::<u32>() {
|
||||
if let Some(notif) = notifs.get_mut(&id) {
|
||||
notif.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string(¬ifs.values().collect::<Vec<_>>()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
interval.tick().await;
|
||||
}
|
||||
});
|
||||
|
||||
async_run(notifications.clone()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user