mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-23 02:05:33 +07:00
Add power-off-monitors bind
Implements https://github.com/YaLTeR/niri/issues/24
This commit is contained in:
@@ -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; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user