Add power-off-monitors bind

Implements https://github.com/YaLTeR/niri/issues/24
This commit is contained in:
Ivan Molodetskikh
2023-10-09 18:37:43 +04:00
parent d91d5d674c
commit 189d1bd97b
6 changed files with 94 additions and 2 deletions
+1
View File
@@ -209,6 +209,7 @@ binds {
Print { screenshot; } Print { screenshot; }
Mod+Shift+E { quit; } Mod+Shift+E { quit; }
Mod+Shift+P { power-off-monitors; }
Mod+Shift+Ctrl+T { toggle-debug-tint; } Mod+Shift+Ctrl+T { toggle-debug-tint; }
} }
+7
View File
@@ -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 { pub fn tty(&mut self) -> &mut Tty {
if let Self::Tty(v) = self { if let Self::Tty(v) = self {
v v
+46 -2
View File
@@ -26,7 +26,7 @@ use smithay::output::{Mode, Output, OutputModeSource, PhysicalProperties, Subpix
use smithay::reexports::calloop::timer::{Timer, TimeoutAction}; use smithay::reexports::calloop::timer::{Timer, TimeoutAction};
use smithay::reexports::calloop::{Dispatcher, LoopHandle, RegistrationToken}; use smithay::reexports::calloop::{Dispatcher, LoopHandle, RegistrationToken};
use smithay::reexports::drm::control::{ 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::input::Libinput;
use smithay::reexports::nix::fcntl::OFlag; use smithay::reexports::nix::fcntl::OFlag;
@@ -612,7 +612,12 @@ impl Tty {
assert!(res.is_none(), "crtc must not have already existed"); assert!(res.is_none(), "crtc must not have already existed");
niri.add_output(output.clone(), Some(refresh_interval(*mode))); 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(()) Ok(())
} }
@@ -956,6 +961,45 @@ impl Tty {
device.drm.is_active() 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 { fn refresh_interval(mode: DrmMode) -> Duration {
+1
View File
@@ -235,6 +235,7 @@ pub enum Action {
#[knuffel(skip)] #[knuffel(skip)]
ChangeVt(i32), ChangeVt(i32),
Suspend, Suspend,
PowerOffMonitors,
ToggleDebugTint, ToggleDebugTint,
Spawn(#[knuffel(arguments)] Vec<String>), Spawn(#[knuffel(arguments)] Vec<String>),
Screenshot, Screenshot,
+10
View File
@@ -103,6 +103,13 @@ impl State {
// here. // here.
self.niri.layout.advance_animations(get_monotonic_time()); 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(); let comp_mod = self.backend.mod_key();
match event { match event {
@@ -139,6 +146,9 @@ impl State {
Action::Suspend => { Action::Suspend => {
self.backend.suspend(); self.backend.suspend();
} }
Action::PowerOffMonitors => {
self.niri.deactivate_monitors(&self.backend);
}
Action::ToggleDebugTint => { Action::ToggleDebugTint => {
self.backend.toggle_debug_tint(); self.backend.toggle_debug_tint();
} }
+29
View File
@@ -107,6 +107,9 @@ pub struct Niri {
pub output_state: HashMap<Output, OutputState>, pub output_state: HashMap<Output, OutputState>,
pub output_by_name: HashMap<String, Output>, pub output_by_name: HashMap<String, Output>,
// When false, we're idling with monitors powered off.
pub monitors_active: bool,
// Smithay state. // Smithay state.
pub compositor_state: CompositorState, pub compositor_state: CompositorState,
pub xdg_shell_state: XdgShellState, pub xdg_shell_state: XdgShellState,
@@ -688,6 +691,7 @@ impl Niri {
output_state: HashMap::new(), output_state: HashMap::new(),
output_by_name: HashMap::new(), output_by_name: HashMap::new(),
unmapped_windows: HashMap::new(), unmapped_windows: HashMap::new(),
monitors_active: true,
compositor_state, compositor_state,
xdg_shell_state, xdg_shell_state,
@@ -845,6 +849,26 @@ impl Niri {
self.queue_redraw(output); 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>)> { pub fn output_under(&self, pos: Point<f64, Logical>) -> Option<(&Output, Point<f64, Logical>)> {
let output = self.global_space.output_under(pos).next()?; let output = self.global_space.output_under(pos).next()?;
let pos_within_output = pos let pos_within_output = pos
@@ -1242,6 +1266,11 @@ impl Niri {
RedrawState::Queued(_) | RedrawState::WaitingForEstimatedVBlankAndQueued(_) RedrawState::Queued(_) | RedrawState::WaitingForEstimatedVBlankAndQueued(_)
)); ));
if !self.monitors_active {
state.redraw_state = RedrawState::Idle;
return;
}
if !backend.is_active() { if !backend.is_active() {
state.redraw_state = RedrawState::Idle; state.redraw_state = RedrawState::Idle;
return; return;