Add Ctl Socket, iterate on Idling daemon, update sysinfo crate, start on run menu
This commit is contained in:
@@ -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
62
src/backend/commands.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user