From abf158d3b96b712b0fee02e9eb80a74be310396b Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 9 Oct 2024 20:25:52 -0700 Subject: [PATCH] Add a `disable` method to `DrmSurface` and `DrmCompositor` This sets DPMS state to off. Submitting a new frame will re-enable the output. --- src/backend/drm/compositor/mod.rs | 18 ++++++++++++++++++ src/backend/drm/surface/atomic.rs | 4 ++++ src/backend/drm/surface/legacy.rs | 28 +++++++++++++++++++++++++++- src/backend/drm/surface/mod.rs | 11 +++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/backend/drm/compositor/mod.rs b/src/backend/drm/compositor/mod.rs index 8b22b00ee817..c1febf4a6806 100644 --- a/src/backend/drm/compositor/mod.rs +++ b/src/backend/drm/compositor/mod.rs @@ -4216,6 +4216,24 @@ where Err(Some(RenderingReason::ScanoutFailed)) } } + + /// Disable the surface, setting DPMS state to off, and clear + /// pending frame. + /// + /// Calling [`queue_frame`][Self::queue_frame] will re-enable. + pub fn disable(&mut self) -> Result<(), DrmError> { + self.surface.disable()?; + + self.current_frame + .planes + .iter_mut() + .for_each(|(_, state)| *state = Default::default()); + self.pending_frame = None; + self.queued_frame = None; + self.next_frame = None; + + Ok(()) + } } #[inline] diff --git a/src/backend/drm/surface/atomic.rs b/src/backend/drm/surface/atomic.rs index 5e7c1cc9fd51..bbc501b0162f 100644 --- a/src/backend/drm/surface/atomic.rs +++ b/src/backend/drm/surface/atomic.rs @@ -1081,6 +1081,10 @@ impl AtomicDrmSurface { pub(crate) fn device_fd(&self) -> &DrmDeviceFd { self.fd.device_fd() } + + pub fn disable(&self) -> Result<(), Error> { + self.clear_state() + } } struct TestBuffer { diff --git a/src/backend/drm/surface/legacy.rs b/src/backend/drm/surface/legacy.rs index ecaa78fae3d5..657df61d02ad 100644 --- a/src/backend/drm/surface/legacy.rs +++ b/src/backend/drm/surface/legacy.rs @@ -3,7 +3,7 @@ use drm::control::{connector, crtc, encoder, framebuffer, Device as ControlDevic use std::collections::HashSet; use std::sync::{ atomic::{AtomicBool, Ordering}, - Arc, RwLock, + Arc, Mutex, RwLock, }; use crate::backend::drm::error::AccessError; @@ -86,6 +86,7 @@ pub struct LegacyDrmSurface { crtc: crtc::Handle, state: RwLock, pending: RwLock, + dpms: Mutex, pub(super) span: tracing::Span, } @@ -114,6 +115,7 @@ impl LegacyDrmSurface { crtc, state: RwLock::new(state), pending: RwLock::new(pending), + dpms: Mutex::new(true), span, }; @@ -233,6 +235,13 @@ impl LegacyDrmSurface { let mut current = self.state.write().unwrap(); let pending = self.pending.read().unwrap(); + let mut dpms = self.dpms.lock().unwrap(); + + if !*dpms { + let connectors = current.connectors.intersection(&pending.connectors); + set_connector_state(&*self.fd, connectors.copied(), true)?; + *dpms = true; + } { let removed = current.connectors.difference(&pending.connectors); @@ -331,6 +340,13 @@ impl LegacyDrmSurface { return Err(Error::DeviceInactive); } + let current = self.state.read().unwrap(); + let mut dpms = self.dpms.lock().unwrap(); + if !*dpms { + set_connector_state(&*self.fd, current.connectors.iter().copied(), true)?; + *dpms = true; + } + ControlDevice::page_flip( &*self.fd, self.crtc, @@ -452,6 +468,16 @@ impl LegacyDrmSurface { pub(crate) fn device_fd(&self) -> &DrmDeviceFd { self.fd.device_fd() } + + pub fn disable(&self) -> Result<(), Error> { + let current = self.state.read().unwrap(); + let mut dpms = self.dpms.lock().unwrap(); + if *dpms { + set_connector_state(&*self.fd, current.connectors.iter().copied(), false)?; + *dpms = false; + } + Ok(()) + } } impl Drop for LegacyDrmSurface { diff --git a/src/backend/drm/surface/mod.rs b/src/backend/drm/surface/mod.rs index 04438058a377..5a926495be11 100644 --- a/src/backend/drm/surface/mod.rs +++ b/src/backend/drm/surface/mod.rs @@ -441,6 +441,17 @@ impl DrmSurface { DrmSurfaceInternal::Legacy(surf) => &surf.span, } } + + /// Disable the output, setting DPMS state to off. + /// + /// The surface will be re-enabled on the next [`page_flip`][Self::page_flip] or + /// [`commit`][Self::commit]. + pub fn disable(&self) -> Result<(), Error> { + match &*self.internal { + DrmSurfaceInternal::Atomic(surf) => surf.disable(), + DrmSurfaceInternal::Legacy(surf) => surf.disable(), + } + } } fn ensure_legacy_planes<'a>(