diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2023-10-09 18:37:43 +0400 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2023-10-09 18:37:43 +0400 |
| commit | 189d1bd97b7aa241b5ad651145ca4d3ef483f505 (patch) | |
| tree | dc48204a57c51c6ec57de7d174d60b972b16e39c | |
| parent | d91d5d674c5b456d25f2e865b28d99aa57f4db4f (diff) | |
| download | niri-189d1bd97b7aa241b5ad651145ca4d3ef483f505.tar.gz niri-189d1bd97b7aa241b5ad651145ca4d3ef483f505.tar.bz2 niri-189d1bd97b7aa241b5ad651145ca4d3ef483f505.zip | |
Add power-off-monitors bind
Implements https://github.com/YaLTeR/niri/issues/24
| -rw-r--r-- | resources/default-config.kdl | 1 | ||||
| -rw-r--r-- | src/backend/mod.rs | 7 | ||||
| -rw-r--r-- | src/backend/tty.rs | 48 | ||||
| -rw-r--r-- | src/config.rs | 1 | ||||
| -rw-r--r-- | src/input.rs | 10 | ||||
| -rw-r--r-- | src/niri.rs | 29 |
6 files changed, 94 insertions, 2 deletions
diff --git a/resources/default-config.kdl b/resources/default-config.kdl index 7282faf6..aab1d77d 100644 --- a/resources/default-config.kdl +++ b/resources/default-config.kdl @@ -209,6 +209,7 @@ binds { Print { screenshot; } Mod+Shift+E { quit; } + Mod+Shift+P { power-off-monitors; } Mod+Shift+Ctrl+T { toggle-debug-tint; } } diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 0c27abd6..64b1b5da 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -109,6 +109,13 @@ impl Backend { } } + pub fn set_monitors_active(&self, active: bool) { + match self { + Backend::Tty(tty) => tty.set_monitors_active(active), + Backend::Winit(_) => (), + } + } + pub fn tty(&mut self) -> &mut Tty { if let Self::Tty(v) = self { v diff --git a/src/backend/tty.rs b/src/backend/tty.rs index f6a40500..403f31ab 100644 --- a/src/backend/tty.rs +++ b/src/backend/tty.rs @@ -26,7 +26,7 @@ use smithay::output::{Mode, Output, OutputModeSource, PhysicalProperties, Subpix use smithay::reexports::calloop::timer::{Timer, TimeoutAction}; use smithay::reexports::calloop::{Dispatcher, LoopHandle, RegistrationToken}; use smithay::reexports::drm::control::{ - connector, crtc, Mode as DrmMode, ModeFlags, ModeTypeFlags, + connector, crtc, Mode as DrmMode, ModeFlags, ModeTypeFlags, Device, property, }; use smithay::reexports::input::Libinput; use smithay::reexports::nix::fcntl::OFlag; @@ -612,7 +612,12 @@ impl Tty { assert!(res.is_none(), "crtc must not have already existed"); niri.add_output(output.clone(), Some(refresh_interval(*mode))); - niri.queue_redraw(output); + + // Power on all monitors if necessary and queue a redraw on the new one. + niri.event_loop.insert_idle(move |state| { + state.niri.activate_monitors(&state.backend); + state.niri.queue_redraw(output); + }); Ok(()) } @@ -956,6 +961,45 @@ impl Tty { device.drm.is_active() } + + pub fn set_monitors_active(&self, active: bool) { + let Some(device) = &self.output_device else { + return; + }; + + for crtc in device.surfaces.keys() { + set_crtc_active(&device.drm, *crtc, active); + } + } +} + +fn find_drm_property(drm: &DrmDevice, crtc: crtc::Handle, name: &str) -> Option<property::Handle> { + let props = match drm.get_properties(crtc) { + Ok(props) => props, + Err(err) => { + warn!("error getting CRTC properties: {err:?}"); + return None; + } + }; + + let (handles, _) = props.as_props_and_values(); + handles.iter().find_map(|handle| { + let info = drm.get_property(*handle).ok()?; + let n = info.name().to_str().ok()?; + + (n == name).then_some(*handle) + }) +} + +fn set_crtc_active(drm: &DrmDevice, crtc: crtc::Handle, active: bool) { + let Some(prop) = find_drm_property(drm, crtc, "ACTIVE") else { + return; + }; + + let value = property::Value::Boolean(active); + if let Err(err) = drm.set_property(crtc, prop, value.into()) { + warn!("error setting CRTC property: {err:?}"); + } } fn refresh_interval(mode: DrmMode) -> Duration { diff --git a/src/config.rs b/src/config.rs index 6f695c34..4ab9c36e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -235,6 +235,7 @@ pub enum Action { #[knuffel(skip)] ChangeVt(i32), Suspend, + PowerOffMonitors, ToggleDebugTint, Spawn(#[knuffel(arguments)] Vec<String>), Screenshot, diff --git a/src/input.rs b/src/input.rs index 955c63f4..c1e335dd 100644 --- a/src/input.rs +++ b/src/input.rs @@ -103,6 +103,13 @@ impl State { // here. self.niri.layout.advance_animations(get_monotonic_time()); + // Power on monitors if they were off. + // HACK: ignore key releases so that the power-off-monitors bind can work. + if !matches!(&event, InputEvent::Keyboard { event } if event.state() == KeyState::Released) + { + self.niri.activate_monitors(&self.backend); + } + let comp_mod = self.backend.mod_key(); match event { @@ -139,6 +146,9 @@ impl State { Action::Suspend => { self.backend.suspend(); } + Action::PowerOffMonitors => { + self.niri.deactivate_monitors(&self.backend); + } Action::ToggleDebugTint => { self.backend.toggle_debug_tint(); } diff --git a/src/niri.rs b/src/niri.rs index 328f4aae..f47f97ae 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -107,6 +107,9 @@ pub struct Niri { pub output_state: HashMap<Output, OutputState>, pub output_by_name: HashMap<String, Output>, + // When false, we're idling with monitors powered off. + pub monitors_active: bool, + // Smithay state. pub compositor_state: CompositorState, pub xdg_shell_state: XdgShellState, @@ -688,6 +691,7 @@ impl Niri { output_state: HashMap::new(), output_by_name: HashMap::new(), unmapped_windows: HashMap::new(), + monitors_active: true, compositor_state, xdg_shell_state, @@ -845,6 +849,26 @@ impl Niri { self.queue_redraw(output); } + pub fn deactivate_monitors(&mut self, backend: &Backend) { + if !self.monitors_active { + return; + } + + self.monitors_active = false; + backend.set_monitors_active(false); + } + + pub fn activate_monitors(&mut self, backend: &Backend) { + if self.monitors_active { + return; + } + + self.monitors_active = true; + backend.set_monitors_active(true); + + self.queue_redraw_all(); + } + pub fn output_under(&self, pos: Point<f64, Logical>) -> Option<(&Output, Point<f64, Logical>)> { let output = self.global_space.output_under(pos).next()?; let pos_within_output = pos @@ -1242,6 +1266,11 @@ impl Niri { RedrawState::Queued(_) | RedrawState::WaitingForEstimatedVBlankAndQueued(_) )); + if !self.monitors_active { + state.redraw_state = RedrawState::Idle; + return; + } + if !backend.is_active() { state.redraw_state = RedrawState::Idle; return; |
