aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/mod.rs7
-rw-r--r--src/backend/tty.rs48
-rw-r--r--src/config.rs1
-rw-r--r--src/input.rs10
-rw-r--r--src/niri.rs29
5 files changed, 93 insertions, 2 deletions
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;