feat: get rid of libnotify

This commit is contained in:
Vladimir Rubin 2025-04-27 02:36:32 +03:00
parent d27e81774f
commit 9e816928af
Signed by: vavakado
GPG key ID: CAB744727F36B524
6 changed files with 256 additions and 1093 deletions

1078
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -4,9 +4,7 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
notify-rust = "4.11.7" libloading = "0.8.6"
serde = { version = "1.0", features = ["derive"] }
toml = "0.8"
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time","io-util","net","signal" ] } tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time","io-util","net","signal" ] }
[profile.release] [profile.release]

View file

@ -32,6 +32,7 @@
devShells.default = pkgs.mkShell rec { devShells.default = pkgs.mkShell rec {
buildInputs = [ buildInputs = [
rust rust
pkgs.libnotify
]; ];
shellHook = '' shellHook = ''
@ -49,8 +50,12 @@
lockFile = ./Cargo.lock; lockFile = ./Cargo.lock;
}; };
postFixup = ''
patchelf $out/bin/hinoirisetr --add-rpath ${pkgs.libnotify}/lib
'';
nativeBuildInputs = [ ]; nativeBuildInputs = [ ];
buildInputs = [ ]; buildInputs = [ pkgs.libnotify ];
}; };
} }
); );

View file

@ -1,18 +1,18 @@
//! hinoirisetr library //! hinoirisetr library
//! Contains core logic for computing temperature and gamma and applying settings. //! Contains core logic for computing temperature and gamma and applying settings.
use std::fs; use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::Command;
use serde::Deserialize;
use time::Time; use time::Time;
pub mod log; pub mod log;
pub mod time; pub mod time;
pub mod notify;
#[derive(Debug, Deserialize, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[serde(default)]
pub struct Config { pub struct Config {
pub temp_day: u16, pub temp_day: u16,
pub temp_night: u16, pub temp_night: u16,
@ -32,7 +32,7 @@ impl Default for Config {
fn default() -> Self { fn default() -> Self {
Self { Self {
temp_day: 6500, temp_day: 6500,
temp_night: 2700, temp_night: 2500,
gamma_day: 100, gamma_day: 100,
gamma_night: 95, gamma_night: 95,
sunset_start: 19, sunset_start: 19,
@ -49,8 +49,57 @@ impl Config {
path: P, path: P,
) -> Result<Self, Box<dyn std::error::Error>> { ) -> Result<Self, Box<dyn std::error::Error>> {
trace!("Config::load({path:?})"); trace!("Config::load({path:?})");
let content = fs::read_to_string(path)?; let mut config = Self::default(); // Start with default values
let config = toml::from_str(&content)?;
let config_file = File::open(path)?;
let reader = BufReader::new(config_file);
let mut current_section = String::new();
for line in reader
.lines()
.map_while(Result::ok)
.map(|l| String::from(l.trim()))
.filter(|l| !l.is_empty())
.filter(|l| !l.starts_with('#'))
{
trace!("line: {line}");
if line.starts_with('[') && line.contains(']') {
current_section = line[1..line.find(']').unwrap()].to_string();
trace!("current_section: {current_section}");
} else if let Some((key, value)) = line.split_once('=') {
trace!("key: {key}, value: {value}");
let key_trimmed_string = key.trim().replace('"', "");
let key_trimmed = key_trimmed_string.as_str();
let value = value.trim().replace('"', "");
match current_section.as_str() {
"" => {
if key_trimmed == "notification_timeout" {
config.notification_timeout = value.parse()?;
}
}
"gamma" => match key_trimmed {
"day" => config.gamma_day = value.parse()?,
"night" => config.gamma_night = value.parse()?,
_ => {}
},
"temp" => match key_trimmed {
"day" => config.temp_day = value.parse()?,
"night" => config.temp_night = value.parse()?,
_ => {}
},
"time" => match key_trimmed {
"sunset_start" => config.sunset_start = value.parse()?,
"sunset_end" => config.sunset_end = value.parse()?,
"sunrise_start" => config.sunrise_start = value.parse()?,
"sunrise_end" => config.sunrise_end = value.parse()?,
_ => {}
},
_ => {}
}
}
}
Ok(config) Ok(config)
} }
} }

View file

@ -4,9 +4,9 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, OnceLock}; use std::sync::{Arc, OnceLock};
use std::time::Duration; use std::time::Duration;
use hinoirisetr::notify::Notification;
use hinoirisetr::time::Time; use hinoirisetr::time::Time;
use hinoirisetr::{Config, apply_settings, compute_settings, debug, error, info, trace, warn}; use hinoirisetr::{Config, apply_settings, compute_settings, debug, error, info, trace, warn};
use notify_rust::Notification;
use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::net::UnixListener; use tokio::net::UnixListener;
use tokio::signal::unix::{SignalKind, signal}; use tokio::signal::unix::{SignalKind, signal};
@ -17,9 +17,21 @@ const SOCKET_PATH: &str = "/tmp/hinoirisetr.sock";
static CONFIG: OnceLock<Arc<RwLock<Config>>> = OnceLock::new(); static CONFIG: OnceLock<Arc<RwLock<Config>>> = OnceLock::new();
enum NotifyState {
Enabled(Arc<Notification>),
Disabled,
}
async fn socket_server(disabled: Arc<AtomicBool>, notify: Arc<Notify>) { async fn socket_server(disabled: Arc<AtomicBool>, notify: Arc<Notify>) {
let listener = UnixListener::bind(SOCKET_PATH).expect("Failed to bind socket"); let listener = UnixListener::bind(SOCKET_PATH).expect("Failed to bind socket");
trace!("socket server bound"); trace!("socket server bound");
let notification: NotifyState = match unsafe { hinoirisetr::notify::Notification::new() } {
Some(not) => NotifyState::Enabled(Arc::new(not)),
None => {
info!("libnotify not found, disabling 'status_notify' command");
NotifyState::Disabled
}
};
loop { loop {
let (stream, _) = listener.accept().await.unwrap(); let (stream, _) = listener.accept().await.unwrap();
@ -92,18 +104,31 @@ async fn socket_server(disabled: Arc<AtomicBool>, notify: Arc<Notify>) {
format!("temp: {cur_temp}K, gamma: {cur_gamma}%") format!("temp: {cur_temp}K, gamma: {cur_gamma}%")
}; };
match Notification::new() match notification {
.summary("Sunsetting") NotifyState::Enabled(ref not) => {
.body(body.as_str()) trace!("notify notification enabled");
.timeout(notify_rust::Timeout::Milliseconds( let timeout = config_handle().read().await.notification_timeout;
config_guard().await.notification_timeout, unsafe {
)) not.init("hinoirisetr");
.show_async() let local_notification =
.await not.notify_notification_new("Sunsetting", body.as_str(), "");
{ not.notify_notification_set_timeout(
Ok(_) => {} local_notification,
Err(err) => { timeout as i32,
error!("Erorr occured while sending a notification: {err}")
);
not.notify_notification_show(
not.notify_notification_new(
"Sunsetting",
body.as_str(),
"notification-icon",
),
std::ptr::null_mut(),
);
}
}
NotifyState::Disabled => {
trace!("notify notification disabled");
} }
} }
} }
@ -122,7 +147,7 @@ async fn main() {
}, },
Err(_) => { Err(_) => {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
hinoirisetr::log::set_log_level(hinoirisetr::log::LogLevel::Trace); hinoirisetr::log::set_log_level(hinoirisetr::log::LogLevel::Debug);
} else { } else {
hinoirisetr::log::set_log_level(hinoirisetr::log::LogLevel::Info); hinoirisetr::log::set_log_level(hinoirisetr::log::LogLevel::Info);
} }

144
src/notify.rs Normal file
View file

@ -0,0 +1,144 @@
#![allow(clippy::missing_safety_doc)]
use std::ffi::{CString, c_char, c_void};
use libloading::os::unix::Symbol as UnixSymbol;
use libloading::{Library, Symbol};
use crate::trace;
pub struct Notification {
_lib: Library, // << keep the library alive
notify_init: UnixSymbol<NotifyInit>,
notify_uninit: UnixSymbol<NotifyUninit>,
notify_notification_new: UnixSymbol<NotifyNotificationNew>,
notify_notification_show: UnixSymbol<NotifyNotificationShow>,
notify_notification_set_timeout: UnixSymbol<NotifyNotificationSetTimeout>,
}
type NotifyInit = unsafe extern "C" fn(*const i8) -> bool;
type NotifyUninit = unsafe extern "C" fn();
type NotifyNotificationNew = unsafe extern "C" fn(
summary: *const c_char,
body: *const c_char,
icon: *const c_char,
) -> *mut std::os::raw::c_void;
type NotifyNotificationShow = unsafe extern "C" fn(
*mut c_void, // NotifyNotification*
*mut *mut c_void, // GError**
) -> i32;
type NotifyNotificationSetTimeout = unsafe extern "C" fn(
notification: *mut std::os::raw::c_void, // NotifyNotification*
timeout: i32, // gint
);
impl Notification {
/// Try to load `libnotify.so.4` at runtime.
pub unsafe fn new() -> Option<Self> {
unsafe {
let (
lib,
notify_init,
notify_uninit,
notify_notification_new,
notify_notification_show,
notify_notification_set_timeout,
) = {
// 1) Load the library
let lib = Library::new("libnotify.so.4").ok()?;
// 2) Lookup each symbol into its own local
let init_sym: Symbol<NotifyInit> = lib.get(b"notify_init\0").ok()?;
let uninit_sym: Symbol<NotifyUninit> = lib.get(b"notify_uninit\0").ok()?;
let notify_notification_new_sym: Symbol<NotifyNotificationNew> =
lib.get(b"notify_notification_new\0").ok()?;
let notify_notification_show_sym: Symbol<NotifyNotificationShow> =
lib.get(b"notify_notification_show\0").ok()?;
let notify_notification_set_timeout_sym: Symbol<NotifyNotificationSetTimeout> =
lib.get(b"notify_notification_set_timeout\0").ok()?;
// 3) Transmute their lifetimes to 'static now that we own `lib` forever
let notify_init = init_sym.into_raw();
let notify_uninit = uninit_sym.into_raw();
let notify_notification_new = notify_notification_new_sym.into_raw();
let notify_notification_show = notify_notification_show_sym.into_raw();
let notify_notification_set_timeout =
notify_notification_set_timeout_sym.into_raw();
(
lib,
notify_init,
notify_uninit,
notify_notification_new,
notify_notification_show,
notify_notification_set_timeout,
)
};
// 3) Now that the borrows are done, move `lib` and the symbols into your struct
Some(Self {
_lib: lib,
notify_init,
notify_uninit,
notify_notification_new,
notify_notification_show,
notify_notification_set_timeout,
})
}
}
pub unsafe fn init(&self, app_name: &str) -> bool {
unsafe {
trace!("Notify::init({app_name:?})");
let cstr = CString::new(app_name).unwrap();
(self.notify_init)(cstr.as_ptr())
}
}
pub unsafe fn uninit(&self) {
unsafe {
trace!("Notify::uninit()");
(self.notify_uninit)();
}
}
pub unsafe fn notify_notification_set_timeout(
&self,
notification: *mut std::os::raw::c_void,
timeout: i32,
) {
trace!("Notify::notify_notification_set_timeout({notification:?}, {timeout})");
unsafe {
(self.notify_notification_set_timeout)(notification, timeout);
}
}
pub unsafe fn notify_notification_new(
&self,
summary: &str,
body: &str,
icon: &str,
) -> *mut std::os::raw::c_void {
unsafe {
trace!("Notify::notify_notification_new({summary:?}, {body:?}, {icon:?})");
let summary = CString::new(summary).unwrap();
let body = CString::new(body).unwrap();
let icon = CString::new(icon).unwrap();
(self.notify_notification_new)(summary.as_ptr(), body.as_ptr(), icon.as_ptr())
}
}
pub unsafe fn notify_notification_show(
&self,
notification: *mut std::os::raw::c_void,
error: *mut *mut c_void,
) {
unsafe {
trace!("Notify::notify_notification_show({notification:?}, {error:?})");
(self.notify_notification_show)(notification, error);
}
}
}