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/device/legacy.rs b/src/backend/drm/device/legacy.rs index dc1a23fa5561..fa0564614ae4 100644 --- a/src/backend/drm/device/legacy.rs +++ b/src/backend/drm/device/legacy.rs @@ -202,9 +202,9 @@ where conn, *handle, if enabled { - 0 /*DRM_MODE_DPMS_ON*/ + drm_ffi::DRM_MODE_DPMS_ON.into() } else { - 3 /*DRM_MODE_DPMS_OFF*/ + drm_ffi::DRM_MODE_DPMS_OFF.into() }, ) .map_err(|source| { 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 027bdc8e11d9..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; @@ -18,7 +18,6 @@ use tracing::{debug, info, info_span, instrument, trace}; #[derive(Debug, PartialEq, Eq, Clone)] pub struct State { - pub active: bool, pub mode: Mode, pub connectors: HashSet, } @@ -74,9 +73,6 @@ impl State { // we need to be sure, we require a mode to always be set without relying on the compiler. // So we cheat, because it works and is easier to handle later. Ok(State { - // On legacy there is not (reliable) way to read-back the dpms state. - // So we just always assume it is off. - active: false, mode: current_mode.unwrap_or_else(|| unsafe { std::mem::zeroed() }), connectors: current_connectors, }) @@ -90,6 +86,7 @@ pub struct LegacyDrmSurface { crtc: crtc::Handle, state: RwLock, pending: RwLock, + dpms: Mutex, pub(super) span: tracing::Span, } @@ -107,7 +104,6 @@ impl LegacyDrmSurface { let state = State::current_state(&*fd, crtc)?; let pending = State { - active: true, mode, connectors: connectors.iter().copied().collect(), }; @@ -119,6 +115,7 @@ impl LegacyDrmSurface { crtc, state: RwLock::new(state), pending: RwLock::new(pending), + dpms: Mutex::new(true), span, }; @@ -238,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); @@ -336,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, @@ -457,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>(