a lightweight, interval-based utility to combat digital strain through "Ma" (intentional pauses) for the eyes and body.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: idle detection

+77
+51
src/idle/linux.rs
··· 1 + use anyhow::Result; 2 + use std::time::Duration; 3 + use zbus::{blocking::Connection, blocking::Proxy}; 4 + 5 + use super::IdleDetector; 6 + 7 + pub struct LinuxIdleDetector { 8 + proxy: Option<Proxy<'static>>, 9 + } 10 + 11 + impl LinuxIdleDetector { 12 + pub fn new() -> Self { 13 + let proxy = Self::connect(); 14 + Self { proxy } 15 + } 16 + 17 + fn connect() -> Option<Proxy<'static>> { 18 + // Try org.freedesktop.ScreenSaver (works on both X11 and Wayland via logind/mutter) 19 + let conn = Connection::session().ok()?; 20 + let proxy = Proxy::new_blocking( 21 + &conn, 22 + "org.freedesktop.ScreenSaver", 23 + "/org/freedesktop/ScreenSaver", 24 + "org.freedesktop.ScreenSaver", 25 + ) 26 + .ok()?; 27 + Some(proxy) 28 + } 29 + } 30 + 31 + impl IdleDetector for LinuxIdleDetector { 32 + fn idle_duration(&mut self) -> Result<Option<Duration>> { 33 + let proxy = match &self.proxy { 34 + Some(p) => p, 35 + None => { 36 + // Retry connection once. 37 + self.proxy = Self::connect(); 38 + match &self.proxy { 39 + Some(p) => p, 40 + None => return Ok(None), 41 + } 42 + } 43 + }; 44 + 45 + // GetSessionIdleTime returns milliseconds. 46 + match proxy.call::<(), u32>("GetSessionIdleTime", &()) { 47 + Ok(ms) => Ok(Some(Duration::from_millis(ms as u64))), 48 + Err(_) => Ok(None), 49 + } 50 + } 51 + }
+26
src/idle/windows.rs
··· 1 + use anyhow::Result; 2 + use std::time::Duration; 3 + use windows::Win32::UI::Input::KeyboardAndMouse::{GetLastInputInfo, LASTINPUTINFO}; 4 + use windows::Win32::Foundation::GetLastError; 5 + 6 + use super::IdleDetector; 7 + 8 + pub struct WindowsIdleDetector; 9 + 10 + impl IdleDetector for WindowsIdleDetector { 11 + fn idle_duration(&mut self) -> Result<Option<Duration>> { 12 + let mut info = LASTINPUTINFO { 13 + cbSize: std::mem::size_of::<LASTINPUTINFO>() as u32, 14 + dwTime: 0, 15 + }; 16 + let ok = unsafe { GetLastInputInfo(&mut info) }; 17 + if !ok.as_bool() { 18 + return Ok(None); 19 + } 20 + 21 + // GetTickCount wraps at u32::MAX (~49.7 days); handle wrap-around. 22 + let tick_now = unsafe { windows::Win32::System::SystemInformation::GetTickCount() }; 23 + let elapsed_ms = tick_now.wrapping_sub(info.dwTime) as u64; 24 + Ok(Some(Duration::from_millis(elapsed_ms))) 25 + } 26 + }