use { crate::{ backend::transaction::{BackendConnectorTransactionError, ConnectorTransaction}, client::ClientId, ifs::{ ext_idle_notification_v1::ExtIdleNotificationV1, wl_surface::zwp_idle_inhibitor_v1::{ IdleInhibitorId, ZwpIdleInhibitorV1, }, }, utils::{ asyncevent::AsyncEvent, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, hash_map_ext::HashMapExt, }, wire::ExtIdleNotificationV1Id, }, std::{cell::Cell, rc::Rc, time::Duration}, }; use super::State; pub struct IdleState { pub input: Cell, 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, } impl IdleState { pub fn set_timeout(&self, state: &State, timeout: Duration) { self.timeout.set(timeout); self.timeout_changed(state); } pub fn set_grace_period(&self, state: &State, grace_period: Duration) { self.grace_period.set(grace_period); self.timeout_changed(state); } fn timeout_changed(&self, _state: &State) { self.timeout_changed.set(true); self.change.trigger(); } pub fn add_inhibitor(&self, state: &State, inhibitor: &Rc) { self.inhibitors.set(inhibitor.inhibit_id, inhibitor.clone()); self.inhibitors_changed(state); } pub fn remove_inhibitor(&self, state: &State, inhibitor: &ZwpIdleInhibitorV1) { self.inhibitors.remove(&inhibitor.inhibit_id); self.inhibitors_changed(state); if self.inhibitors.is_empty() { self.resume_inhibited_notifications(); } } fn inhibitors_changed(&self, _state: &State) { self.inhibitors_changed.set(true); self.change.trigger(); } fn resume_inhibited_notifications(&self) { for notification in self.inhibited_idle_notifications.lock().drain_values() { notification.resume.trigger(); } } pub fn add_inhibited_notification(&self, n: &Rc) { self.inhibited_idle_notifications .set((n.client.id, n.id), n.clone()); } pub fn remove_inhibited_notification(&self, n: &ExtIdleNotificationV1) { self.inhibited_idle_notifications .remove(&(n.client.id, n.id)); } } impl State { 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(); } } pub fn set_backend_idle(&self, idle: bool) { if self.idle.backend_idle.replace(idle) != idle { self.root.update_visible(self); } } pub fn set_connectors_active( self: &Rc, active: bool, ) -> Result<(), BackendConnectorTransactionError> { let mut tran = ConnectorTransaction::new(self); for connector in self.connectors.lock().values() { let mut state = connector.state.borrow().clone(); state.active = active; tran.add(&connector.connector, state)?; } tran.prepare()?.apply()?.commit(); self.set_backend_idle(!active); 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() } }