Add Ctl Socket, iterate on Idling daemon, update sysinfo crate, start on run menu

This commit is contained in:
jmb05
2026-10-30 16:49:21 +01:00
parent a6b601e3bd
commit 80b5cafb8c
17 changed files with 504 additions and 260 deletions

View File

@@ -215,14 +215,19 @@ impl BackendThread<BrightnessMessage, Sender<BrightnessMessage>> for BrightnessM
}
});
thread::spawn(move || {
let mut sleep_dur = 500;
loop {
thread::sleep(Duration::from_millis(10_000));
if sleep_dur < 500 {
sleep_dur += 1;
}
thread::sleep(Duration::from_millis(sleep_dur));
let mut messages = vec![];
for (monitor, data) in
&mut instance.lock().expect("Couldn't get lock").display_cache
{
if data.update() {
sleep_dur = 50;
messages.push(BrightnessMessage::Value(monitor.clone(), data.value, false));
}
}

62
src/backend/commands.rs Normal file
View File

@@ -0,0 +1,62 @@
use crate::LOGGER;
use crate::backend::{BackendThread, SimpleBackendThread};
use crate::ctl::{ControlCommand, SOCKET_PATH};
use crossbeam_channel::{Receiver, Sender};
use jmb_logger_rs::*;
use std::io::Read;
use std::net::Shutdown;
use std::os::unix::net::UnixListener;
use std::sync::{Arc, Mutex};
pub struct ControlSocket {
subscribers: Vec<Sender<ControlCommand>>,
}
impl SimpleBackendThread<ControlCommand> for ControlSocket {
fn subscribers(&self) -> &Vec<Sender<ControlCommand>> {
&self.subscribers
}
fn init(registration_rx: Receiver<Sender<ControlCommand>>) {
let instance = Arc::new(Mutex::new(Self {
subscribers: Vec::new(),
}));
let instance_clone = instance.clone();
std::thread::spawn(move || {
while let Ok(sender) = registration_rx.recv() {
instance_clone.lock().unwrap().subscribers.push(sender);
}
});
std::thread::spawn(move || {
if std::fs::metadata(SOCKET_PATH).is_ok() {
if let Err(e) = std::fs::remove_file(SOCKET_PATH) {
error!(&LOGGER, "Error deleting old socket file: {e}");
return;
}
}
let socket = UnixListener::bind(SOCKET_PATH);
let Ok(socket) = socket else {
error!(&LOGGER, "Error creating socket {}", socket.err().unwrap());
return;
};
info!(&LOGGER, "Successfully started control socket");
loop {
if let Ok((mut stream, _addr)) = socket.accept() {
info!(&LOGGER, "Socket accepted connection");
let mut read = String::new();
let res = stream.read_to_string(&mut read);
if let Err(e) = res {
error!(&LOGGER, "Error reading: {e}");
let _ = stream.shutdown(Shutdown::Both);
continue;
}
if let Some(command) = ControlCommand::from_str(&read) {
instance.lock().unwrap().publish(command);
}
}
}
});
}
}

View File

@@ -6,7 +6,7 @@ use nmrs::raw::zbus::interface;
use std::collections::HashMap;
use std::error::Error;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::time::{Duration, Instant};
use tokio::sync::watch;
use tokio::time::sleep;
use wayland_client::globals::registry_queue_init;
@@ -26,7 +26,7 @@ const LEGACY_OBJECT_PATH: &str = "/ScreenSaver";
struct WlState {
idle: Option<bool>,
tx: watch::Sender<IdleStateInner>,
tx: watch::Sender<bool>,
}
delegate_noop!(WlState: ignore WlSeat);
@@ -53,14 +53,18 @@ impl Dispatch<ExtIdleNotificationV1, ()> for WlState {
_: &QueueHandle<Self>,
) {
match event {
Event::Idled => state.idle = Some(true),
Event::Resumed => state.idle = Some(false),
Event::Idled => {
println!("Idled: {:?}", Instant::now());
state.idle = Some(true);
}
Event::Resumed => {
println!("Resumed: {:?}", Instant::now());
state.idle = Some(false);
}
_ => {}
}
if let Some(idle) = state.idle {
state.tx.send_modify(|state| {
state.wayland_idle = idle;
});
state.tx.send_modify(|state| *state = idle);
}
}
}
@@ -84,20 +88,6 @@ struct Inhibitor {
_reason: String,
}
#[derive(Clone, Copy, Debug)]
struct IdleStateInner {
dimmed: bool,
wayland_idle: bool,
dbus_inhibited: bool,
caffeine: bool,
}
impl IdleStateInner {
fn should_dim(&self) -> bool {
self.wayland_idle && !self.dbus_inhibited
}
}
#[derive(Debug, Clone)]
pub enum IdleState {
Init(Sender<IdleMessage>),
@@ -107,20 +97,6 @@ pub enum IdleState {
Active,
}
impl From<IdleStateInner> for IdleState {
fn from(value: IdleStateInner) -> Self {
if value.should_dim() {
IdleState::Dimmed
} else if value.caffeine {
IdleState::Caffeine
} else if value.dbus_inhibited {
IdleState::Inhibited
} else {
IdleState::Active
}
}
}
#[derive(Debug, Clone)]
pub enum IdleMessage {
Inhibit,
@@ -170,15 +146,10 @@ impl BackendThread<IdleState, Sender<IdleMessage>> for IdleDaemon {
}
}
});
let (state_tx, state_rx) = watch::channel(IdleStateInner {
dimmed: false,
wayland_idle: false,
dbus_inhibited: false,
caffeine: false,
});
let tx_clone = state_tx.clone();
let (wl_tx, wl_rx) = watch::channel(false);
let (dbus_tx, dbus_rx) = watch::channel((false, false));
std::thread::spawn(move || {
if let Err(err) = Self::start_idle_notifier(tx_clone) {
if let Err(err) = Self::start_idle_notifier(wl_tx) {
error!(&LOGGER, "Idle Daemon unexpectedly quit, reason: {err}");
}
});
@@ -216,10 +187,10 @@ impl BackendThread<IdleState, Sender<IdleMessage>> for IdleDaemon {
.expect("Failed to create Tokio runtime");
runtime.block_on(async move {
let res = IdleDaemon::start_dbus(state_tx).await;
let res = IdleDaemon::start_dbus(dbus_tx).await;
match res {
Ok((_, _)) => {
Self::run_idle_timer(instance, state_rx, Duration::from_secs(10)).await;
Self::run_idle_timer(instance, wl_rx, dbus_rx).await;
}
Err(err) => error!(&LOGGER, "Error running D-Bus Screensaver: {err}"),
}
@@ -228,41 +199,77 @@ impl BackendThread<IdleState, Sender<IdleMessage>> for IdleDaemon {
}
}
#[derive(Clone, Copy, Debug)]
enum IdleStage {
Dim,
TurnOff,
Sleep,
}
impl IdleStage {
fn next(self) -> Self {
match self {
IdleStage::Dim => IdleStage::TurnOff,
IdleStage::TurnOff => IdleStage::Sleep,
IdleStage::Sleep => IdleStage::Sleep,
}
}
fn timeout(self) -> Duration {
match self {
Self::Dim => Duration::from_secs(20),
Self::TurnOff => Duration::from_secs(20),
Self::Sleep => Duration::from_secs(20),
}
}
}
impl IdleDaemon {
async fn run_idle_timer(
instance: Arc<Mutex<Self>>,
mut state_rx: watch::Receiver<IdleStateInner>,
timeout: Duration,
_instance: Arc<Mutex<Self>>,
mut wl_rx: watch::Receiver<bool>,
mut dbus_rx: watch::Receiver<(bool, bool)>,
) {
loop {
let mut state = *state_rx.borrow_and_update();
instance.lock().unwrap().publish(state.into());
if !state.should_dim() {
if state_rx.changed().await.is_err() {
return;
}
continue;
if let Err(e) = wl_rx.changed().await {
warn!(&LOGGER, "Error getting Wayland idle state {e}");
return;
}
tokio::select! {
_ = sleep(timeout) => {
state.dimmed = true;
instance.lock().unwrap().publish(state.into());
}
result = state_rx.changed() => {
if result.is_err() {
return;
let wl_idle = *wl_rx.borrow_and_update();
let dbus_state = *dbus_rx.borrow_and_update();
println!("Wayland idle state changed {wl_idle} (DBus state: {dbus_state:?})");
if wl_idle && !dbus_state.0 {
println!("Going into idle cycle");
let mut stage = IdleStage::Dim;
loop {
tokio::select! {
_ = sleep(stage.timeout()) => {
stage = stage.next();
println!("Reached next stage {stage:?}");
}
res = wl_rx.changed() => {
if let Err(e) = res {
warn!(&LOGGER, "Error getting Wayland idle state {e}");
return;
}
println!("WL state change");
break;
}
res = dbus_rx.changed() => {
if let Err(e) = res {
warn!(&LOGGER, "Error getting D-Bus idle state {e}");
return;
}
println!("D-Bus state change");
break;
}
}
state.dimmed = false;
instance.lock().unwrap().publish(state.into());
}
}
}
}
fn start_idle_notifier(tx: watch::Sender<IdleStateInner>) -> Result<(), Box<dyn Error>> {
fn start_idle_notifier(tx: watch::Sender<bool>) -> Result<(), Box<dyn Error>> {
let conn = Connection::connect_to_env()?;
let (globals, mut event_queue) = registry_queue_init::<WlState>(&conn)?;
let queue_handle = event_queue.handle();
@@ -281,7 +288,7 @@ impl IdleDaemon {
}
async fn start_dbus(
tx: watch::Sender<IdleStateInner>,
tx: watch::Sender<(bool, bool)>,
) -> zbus::Result<(ScreenSaver, zbus::connection::Connection)> {
let service = ScreenSaver::new(tx);
@@ -345,11 +352,11 @@ struct ScreenSaver {
struct Inner {
next_cookie: u32,
inhibitors: HashMap<u32, Inhibitor>,
tx: watch::Sender<IdleStateInner>,
tx: watch::Sender<(bool, bool)>,
}
impl ScreenSaver {
fn new(tx: watch::Sender<IdleStateInner>) -> Self {
fn new(tx: watch::Sender<(bool, bool)>) -> Self {
Self {
inner: Arc::new(Mutex::new(Inner {
next_cookie: 1,
@@ -405,8 +412,8 @@ impl ScreenSaver {
);
inner.tx.send_modify(|state| {
state.dbus_inhibited = !inner.inhibitors.is_empty();
state.caffeine = caffeine_flag;
state.0 = !inner.inhibitors.is_empty();
state.1 = caffeine_flag;
});
Ok(cookie)
}
@@ -436,8 +443,8 @@ impl ScreenSaver {
}
}
inner.tx.send_modify(|state| {
state.dbus_inhibited = !inner.inhibitors.is_empty();
state.caffeine = caffeine_flag;
state.0 = !inner.inhibitors.is_empty();
state.1 = caffeine_flag;
});
Ok(())
}

View File

@@ -16,8 +16,8 @@ const POLL_INTERVAL: u64 = 10_000;
#[derive(Clone, Debug)]
pub struct Stats {
pub cpu: u8,
pub ram: u8,
pub cpu: Option<u8>,
pub ram: Option<u8>,
pub disks: Vec<(String, u8)>,
pub gpus: Vec<(String, u8)>,
pub battery_charge: u8,
@@ -28,8 +28,8 @@ pub struct Stats {
pub struct StatsBackend {
subscribers: Vec<Sender<Stats>>,
system: System,
disks: Disks,
system: Option<System>,
disks: Option<Disks>,
}
impl BackendThread<Stats, ()> for StatsBackend {
@@ -47,8 +47,8 @@ impl BackendThread<Stats, ()> for StatsBackend {
thread::spawn(move || {
let mut instance = Self {
subscribers: Vec::new(),
system: System::new(),
disks: Disks::new(),
system: System::new().ok(),
disks: Disks::new().ok(),
};
let manager = battery::Manager::new();
let smi = AllSmi::new();
@@ -58,24 +58,29 @@ impl BackendThread<Stats, ()> for StatsBackend {
instance.subscribers.push(tx);
}
instance.system.refresh_cpu_usage();
instance.system.refresh_memory();
instance.disks.refresh(true);
let mut cpu = None;
let mut ram = None;
if let Some(system) = instance.system.as_mut() {
system.refresh_cpu_usage();
system.refresh_memory();
let cpu = instance.system.global_cpu_usage() as u8;
let ram =
(instance.system.used_memory() * 100 / instance.system.total_memory()) as u8;
cpu = Some(system.global_cpu_usage() as u8);
ram = Some((system.used_memory() * 100 / system.total_memory()) as u8);
}
let disk_stats: Vec<(String, u8)> = instance
.disks
.iter()
.map(|disk| {
let mount_point = disk.mount_point().display().to_string();
let used_space = disk.total_space() - disk.available_space();
let percentage = (used_space * 100 / disk.total_space()) as u8;
(mount_point, percentage)
})
.collect();
let mut disk_stats = Vec::new();
if let Some(disks) = instance.disks.as_mut() {
disks.refresh(true);
disk_stats = disks
.iter()
.map(|disk| {
let mount_point = disk.mount_point().display().to_string();
let used_space = disk.total_space() - disk.available_space();
let percentage = (used_space * 100 / disk.total_space()) as u8;
(mount_point, percentage)
})
.collect();
}
let mut gpus: Vec<(String, u8)> = Vec::new();
if let Ok(smi) = &smi {