From eece44a59c493cc7c084e9a0ad3d6e45e25f34a7 Mon Sep 17 00:00:00 2001 From: kossLAN Date: Mon, 25 May 2026 22:57:29 -0400 Subject: [PATCH] add config options for waking dpms on mouse and keyboard interaction --- jay-config/src/_private/client.rs | 8 ++++++++ jay-config/src/_private/ipc.rs | 6 ++++++ jay-config/src/lib.rs | 14 +++++++++++++ src/compositor.rs | 3 +++ src/config/handler.rs | 14 +++++++++++++ src/ifs/jay_compositor.rs | 2 +- src/state.rs | 21 +++++++++++++++++++- src/tasks/input_device.rs | 25 ++++++++++++++++++++++-- toml-config/src/config.rs | 2 ++ toml-config/src/config/parsers/config.rs | 6 ++++++ toml-config/src/config/parsers/idle.rs | 16 +++++++++++++-- toml-config/src/lib.rs | 8 +++++--- 12 files changed, 116 insertions(+), 9 deletions(-) diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index 8ef87476..b48c6227 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -1327,6 +1327,14 @@ impl ConfigClient { self.send(&ClientMessage::SetIdle { timeout }) } + pub fn set_key_press_enables_dpms(&self, enabled: bool) { + self.send(&ClientMessage::SetKeyPressEnablesDpms { enabled }) + } + + pub fn set_mouse_move_enables_dpms(&self, enabled: bool) { + self.send(&ClientMessage::SetMouseMoveEnablesDpms { enabled }) + } + pub fn set_idle_grace_period(&self, period: Duration) { self.send(&ClientMessage::SetIdleGracePeriod { period }) } diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index acb5ad81..0a2b9491 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -475,6 +475,12 @@ pub enum ClientMessage<'a> { SetIdle { timeout: Duration, }, + SetKeyPressEnablesDpms { + enabled: bool, + }, + SetMouseMoveEnablesDpms { + enabled: bool, + }, MoveToOutput { workspace: WorkspaceSource, connector: Connector, diff --git a/jay-config/src/lib.rs b/jay-config/src/lib.rs index e25710f9..dcc4e346 100644 --- a/jay-config/src/lib.rs +++ b/jay-config/src/lib.rs @@ -252,6 +252,20 @@ pub fn set_idle(timeout: Option) { get!().set_idle(timeout.unwrap_or_default()) } +/// Configures whether a key press turns monitors back on after `jay dpms off`. +/// +/// The default is `false`. +pub fn set_key_press_enables_dpms(enabled: bool) { + get!().set_key_press_enables_dpms(enabled) +} + +/// Configures whether mouse movement turns monitors back on after `jay dpms off`. +/// +/// The default is `false`. +pub fn set_mouse_move_enables_dpms(enabled: bool) { + get!().set_mouse_move_enables_dpms(enabled) +} + /// Configures the idle grace period. /// /// The grace period starts after the idle timeout expires. During the grace period, the diff --git a/src/compositor.rs b/src/compositor.rs index 45d2a018..e197353d 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -279,11 +279,14 @@ fn start_compositor2( change: Default::default(), timeout: Cell::new(Duration::from_secs(10 * 60)), grace_period: Cell::new(Duration::from_secs(5)), + key_press_enables_dpms: Cell::new(false), + mouse_move_enables_dpms: Cell::new(false), timeout_changed: Default::default(), inhibitors: Default::default(), inhibitors_changed: Default::default(), inhibited_idle_notifications: Default::default(), backend_idle: Cell::new(true), + dpms_off_by_command: Cell::new(false), in_grace_period: Cell::new(false), }, run_args, diff --git a/src/config/handler.rs b/src/config/handler.rs index 526c1cde..90d65b9b 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -1134,6 +1134,14 @@ impl ConfigProxyHandler { self.state.idle.set_timeout(&self.state, timeout); } + fn handle_set_key_press_enables_dpms(&self, enabled: bool) { + self.state.idle.key_press_enables_dpms.set(enabled); + } + + fn handle_set_mouse_move_enables_dpms(&self, enabled: bool) { + self.state.idle.mouse_move_enables_dpms.set(enabled); + } + fn handle_set_idle_grace_period(&self, period: Duration) { self.state.idle.set_grace_period(&self.state, period); } @@ -3129,6 +3137,12 @@ impl ConfigProxyHandler { .handle_get_input_device_devnode(device) .wrn("get_input_device_devnode")?, ClientMessage::SetIdle { timeout } => self.handle_set_idle(timeout), + ClientMessage::SetKeyPressEnablesDpms { enabled } => { + self.handle_set_key_press_enables_dpms(enabled) + } + ClientMessage::SetMouseMoveEnablesDpms { enabled } => { + self.handle_set_mouse_move_enables_dpms(enabled) + } ClientMessage::MoveToOutput { workspace, connector, diff --git a/src/ifs/jay_compositor.rs b/src/ifs/jay_compositor.rs index 4abf04c3..4ccc45db 100644 --- a/src/ifs/jay_compositor.rs +++ b/src/ifs/jay_compositor.rs @@ -547,7 +547,7 @@ impl JayCompositorRequestHandler for JayCompositor { fn set_dpms(&self, req: SetDpms, _slf: &Rc) -> Result<(), Self::Error> { self.client .state - .set_connectors_active(req.active != 0) + .set_dpms_active(req.active != 0) .map_err(JayCompositorError::SetDpms)?; Ok(()) } diff --git a/src/state.rs b/src/state.rs index 9349bbc7..1d2f7a08 100644 --- a/src/state.rs +++ b/src/state.rs @@ -342,10 +342,13 @@ pub struct IdleState { pub change: AsyncEvent, pub timeout: Cell, pub grace_period: Cell, + pub key_press_enables_dpms: Cell, + pub mouse_move_enables_dpms: Cell, pub timeout_changed: Cell, pub inhibitors: CopyHashMap>, pub inhibitors_changed: Cell, pub backend_idle: Cell, + pub dpms_off_by_command: Cell, pub inhibited_idle_notifications: CopyHashMap<(ClientId, ExtIdleNotificationV1Id), Rc>, pub in_grace_period: Cell, @@ -975,7 +978,14 @@ impl State { } } - pub fn input_occurred(&self) { + pub fn input_occurred(self: &Rc, key_press: bool, mouse_move: bool) { + if self.idle.dpms_off_by_command.get() { + let enable_dpms = key_press && self.idle.key_press_enables_dpms.get() + || mouse_move && self.idle.mouse_move_enables_dpms.get(); + if enable_dpms && let Err(e) = self.set_dpms_active(true) { + log::error!("Could not enable DPMS after input: {}", ErrorFmt(e)); + } + } if !self.idle.input.replace(true) { self.idle.change.trigger(); } @@ -1420,6 +1430,15 @@ impl State { Ok(()) } + pub fn set_dpms_active( + self: &Rc, + active: bool, + ) -> Result<(), BackendConnectorTransactionError> { + self.set_connectors_active(active)?; + self.idle.dpms_off_by_command.set(!active); + Ok(()) + } + pub fn root_visible(&self) -> bool { !self.idle.backend_idle.get() } diff --git a/src/tasks/input_device.rs b/src/tasks/input_device.rs index 61550def..55afc5f9 100644 --- a/src/tasks/input_device.rs +++ b/src/tasks/input_device.rs @@ -1,6 +1,6 @@ use { crate::{ - backend::{InputDevice, InputDeviceCapability}, + backend::{InputDevice, InputDeviceCapability, InputEvent, KeyState}, ifs::wl_seat::PX_PER_SCROLL, state::{DeviceHandlerData, InputDeviceData, State}, tasks::udev_utils::{UdevProps, udev_props}, @@ -80,13 +80,21 @@ impl DeviceHandler { } if let Some(seat) = self.data.seat.get() { let mut any_events = false; + let mut key_press = false; + let mut mouse_move = false; while let Some(event) = self.dev.event() { + let (is_key_press, is_mouse_move) = dpms_wake_triggers_for(&event); + key_press |= is_key_press; + mouse_move |= is_mouse_move; + if is_key_press || is_mouse_move { + self.state.input_occurred(is_key_press, is_mouse_move); + } seat.event(&self.data, event); any_events = true; } if any_events { seat.mark_last_active(); - self.state.input_occurred(); + self.state.input_occurred(key_press, mouse_move); } } else { while self.dev.event().is_some() { @@ -105,3 +113,16 @@ impl DeviceHandler { self.data.set_seat(&self.state, None); } } + +fn dpms_wake_triggers_for(event: &InputEvent) -> (bool, bool) { + match event { + InputEvent::Key { + state: KeyState::Pressed, + .. + } => (true, false), + InputEvent::ConnectorPosition { .. } + | InputEvent::Motion { .. } + | InputEvent::MotionAbsolute { .. } => (false, true), + _ => (false, false), + } +} diff --git a/toml-config/src/config.rs b/toml-config/src/config.rs index ba71c585..75c24bf2 100644 --- a/toml-config/src/config.rs +++ b/toml-config/src/config.rs @@ -560,6 +560,8 @@ pub struct Config { pub inputs: Vec, pub idle: Option, pub grace_period: Option, + pub key_press_enables_dpms: Option, + pub mouse_move_enables_dpms: Option, pub explicit_sync_enabled: Option, pub focus_follows_mouse: bool, pub window_management_key: Option, diff --git a/toml-config/src/config/parsers/config.rs b/toml-config/src/config/parsers/config.rs index b9d34e74..45654007 100644 --- a/toml-config/src/config/parsers/config.rs +++ b/toml-config/src/config/parsers/config.rs @@ -367,11 +367,15 @@ impl Parser for ConfigParser<'_> { } let mut idle = None; let mut grace_period = None; + let mut key_press_enables_dpms = None; + let mut mouse_move_enables_dpms = None; if let Some(value) = idle_val { match value.parse(&mut IdleParser(self.0)) { Ok(v) => { idle = v.timeout; grace_period = v.grace_period; + key_press_enables_dpms = v.key_press_enables_dpms; + mouse_move_enables_dpms = v.mouse_move_enables_dpms; } Err(e) => { log::warn!("Could not parse the idle timeout: {}", self.0.error(e)); @@ -581,6 +585,8 @@ impl Parser for ConfigParser<'_> { inputs, idle, grace_period, + key_press_enables_dpms, + mouse_move_enables_dpms, focus_follows_mouse: focus_follows_mouse.despan().unwrap_or(true), window_management_key, vrr, diff --git a/toml-config/src/config/parsers/idle.rs b/toml-config/src/config/parsers/idle.rs index 57d03b36..5da15f8b 100644 --- a/toml-config/src/config/parsers/idle.rs +++ b/toml-config/src/config/parsers/idle.rs @@ -2,7 +2,7 @@ use { crate::{ config::{ context::Context, - extractor::{Extractor, ExtractorError, n64, opt, val}, + extractor::{Extractor, ExtractorError, bol, n64, opt, recover, val}, parser::{DataType, ParseResult, Parser, UnexpectedDataType}, }, toml::{ @@ -28,6 +28,8 @@ pub struct IdleParser<'a>(pub &'a Context<'a>); pub struct Idle { pub timeout: Option, pub grace_period: Option, + pub key_press_enables_dpms: Option, + pub mouse_move_enables_dpms: Option, } impl Parser for IdleParser<'_> { @@ -41,10 +43,18 @@ impl Parser for IdleParser<'_> { table: &IndexMap, Spanned>, ) -> ParseResult { let mut ext = Extractor::new(self.0, span, table); - let (minutes, seconds, grace_period_val) = ext.extract(( + let ( + minutes, + seconds, + grace_period_val, + key_press_enables_dpms, + mouse_move_enables_dpms, + ) = ext.extract(( opt(n64("minutes")), opt(n64("seconds")), opt(val("grace-period")), + recover(opt(bol("key-press-enables-dpms"))), + recover(opt(bol("mouse-move-enables-dpms"))), ))?; let mut timeout = None; if minutes.is_some() || seconds.is_some() { @@ -57,6 +67,8 @@ impl Parser for IdleParser<'_> { Ok(Idle { timeout, grace_period, + key_press_enables_dpms: key_press_enables_dpms.despan(), + mouse_move_enables_dpms: mouse_move_enables_dpms.despan(), }) } } diff --git a/toml-config/src/lib.rs b/toml-config/src/lib.rs index 391bcee9..d39941d3 100644 --- a/toml-config/src/lib.rs +++ b/toml-config/src/lib.rs @@ -40,9 +40,9 @@ use { on_devices_enumerated, on_idle, on_unload, quit, reload, set_autotile, set_color_management_enabled, set_corner_radius, set_default_workspace_capture, set_explicit_sync_enabled, set_float_above_fullscreen, set_floating_titles, set_idle, - set_idle_grace_period, set_middle_click_paste_enabled, set_show_bar, - set_show_float_pin_icon, set_show_titles, set_tab_title_align, set_ui_drag_enabled, - set_ui_drag_threshold, + set_idle_grace_period, set_key_press_enables_dpms, set_middle_click_paste_enabled, + set_mouse_move_enables_dpms, set_show_bar, set_show_float_pin_icon, set_show_titles, + set_tab_title_align, set_ui_drag_enabled, set_ui_drag_threshold, status::{set_i3bar_separator, set_status, set_status_command, unset_status_command}, switch_to_vt, tasks::{self, JoinHandle}, @@ -1657,6 +1657,8 @@ fn load_config(initial_load: bool, auto_reload: bool, persistent: &Rc