From c788cadd45d5eb96d64bad90e414a512f62c48e9 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Fri, 16 Aug 2024 20:49:59 -0700 Subject: [PATCH] WIP DPMS with `wlr-output-power-management-unstable-v1` protocol --- Cargo.lock | 1 + Cargo.toml | 1 + src/backend/kms/drm_helpers.rs | 14 +++ src/backend/kms/mod.rs | 2 +- src/backend/kms/surface/mod.rs | 4 +- src/state.rs | 2 + src/wayland/handlers/mod.rs | 1 + src/wayland/handlers/output_power.rs | 27 +++++ src/wayland/protocols/mod.rs | 1 + src/wayland/protocols/output_power.rs | 151 ++++++++++++++++++++++++++ 10 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 src/wayland/handlers/output_power.rs create mode 100644 src/wayland/protocols/output_power.rs diff --git a/Cargo.lock b/Cargo.lock index 5e9b2e2e..4b856eb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -828,6 +828,7 @@ dependencies = [ "cosmic-config", "cosmic-protocols", "cosmic-settings-config", + "drm-ffi 0.8.0", "edid-rs", "egui", "egui_plot", diff --git a/Cargo.toml b/Cargo.toml index c221e8e2..386dcc7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,7 @@ zbus = "4.4.0" profiling = { version = "1.0" } rustix = { version = "0.38.32", features = ["process"] } smallvec = "1.13.2" +drm-ffi = "0.8.0" [dependencies.id_tree] branch = "feature/copy_clone" diff --git a/src/backend/kms/drm_helpers.rs b/src/backend/kms/drm_helpers.rs index 5c9f53f4..c074587d 100644 --- a/src/backend/kms/drm_helpers.rs +++ b/src/backend/kms/drm_helpers.rs @@ -421,3 +421,17 @@ pub fn set_max_bpc(dev: &impl ControlDevice, conn: connector::Handle, bpc: u32) _ => unreachable!(), }) } + +pub fn set_dpms(dev: &impl ControlDevice, conn: connector::Handle, on: bool) -> Result<()> { + dev.set_property( + conn, + get_prop(dev, conn, "DPMS")?, + property::Value::UnsignedRange(if on { + drm_ffi::DRM_MODE_DPMS_ON.into() + } else { + drm_ffi::DRM_MODE_DPMS_OFF.into() + }) + .into(), + )?; + Ok(()) +} diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 279cacb7..d8961ea3 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -39,7 +39,7 @@ use std::{ }; mod device; -mod drm_helpers; +pub(crate) mod drm_helpers; pub mod render; mod socket; mod surface; diff --git a/src/backend/kms/surface/mod.rs b/src/backend/kms/surface/mod.rs index 8b866dad..f0b21451 100644 --- a/src/backend/kms/surface/mod.rs +++ b/src/backend/kms/surface/mod.rs @@ -102,9 +102,9 @@ static NVIDIA_LOGO: &'static [u8] = include_bytes!("../../../../resources/icons/ #[derive(Debug)] pub struct Surface { - pub(super) connector: connector::Handle, + pub(crate) connector: connector::Handle, pub(super) crtc: crtc::Handle, - pub(super) output: Output, + pub(crate) output: Output, known_nodes: HashSet, active: Arc, diff --git a/src/state.rs b/src/state.rs index 0776d71e..dbbe47b3 100644 --- a/src/state.rs +++ b/src/state.rs @@ -15,6 +15,7 @@ use crate::{ drm::WlDrmState, image_source::ImageSourceState, output_configuration::OutputConfigurationState, + output_power::OutputPowerState, screencopy::ScreencopyState, toplevel_info::ToplevelInfoState, toplevel_management::{ManagementCapabilities, ToplevelManagementState}, @@ -490,6 +491,7 @@ impl State { let keyboard_shortcuts_inhibit_state = KeyboardShortcutsInhibitState::new::(dh); let output_state = OutputManagerState::new_with_xdg_output::(dh); let output_configuration_state = OutputConfigurationState::new(dh, client_is_privileged); + OutputPowerState::new::(dh, client_is_privileged); let presentation_state = PresentationState::new::(dh, clock.id() as u32); let primary_selection_state = PrimarySelectionState::new::(dh); let image_source_state = ImageSourceState::new::(dh, client_is_privileged); diff --git a/src/wayland/handlers/mod.rs b/src/wayland/handlers/mod.rs index b25ec2bb..d9f4a29b 100644 --- a/src/wayland/handlers/mod.rs +++ b/src/wayland/handlers/mod.rs @@ -18,6 +18,7 @@ pub mod keyboard_shortcuts_inhibit; pub mod layer_shell; pub mod output; pub mod output_configuration; +pub mod output_power; pub mod pointer_constraints; pub mod pointer_gestures; pub mod presentation; diff --git a/src/wayland/handlers/output_power.rs b/src/wayland/handlers/output_power.rs new file mode 100644 index 00000000..1f9f0e71 --- /dev/null +++ b/src/wayland/handlers/output_power.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use smithay::{output::Output, reexports::wayland_server::protocol::wl_output::WlOutput}; + +use crate::{ + backend::kms::drm_helpers, + state::{BackendData, State}, + wayland::protocols::output_power::{delegate_output_power, OutputPowerHandler}, +}; + +impl OutputPowerHandler for State { + fn set_dpms(&mut self, wl_output: &WlOutput, on: bool) { + if let Some(output) = Output::from_resource(wl_output) { + if let BackendData::Kms(ref mut kms_state) = &mut self.backend { + for (_, device) in &mut kms_state.drm_devices { + for (_, surface) in &mut device.surfaces { + if surface.output == output { + drm_helpers::set_dpms(&device.drm, surface.connector, on); + } + } + } + } + } + } +} + +delegate_output_power!(State); diff --git a/src/wayland/protocols/mod.rs b/src/wayland/protocols/mod.rs index c35b0b89..b98de33b 100644 --- a/src/wayland/protocols/mod.rs +++ b/src/wayland/protocols/mod.rs @@ -3,6 +3,7 @@ pub mod drm; pub mod image_source; pub mod output_configuration; +pub mod output_power; pub mod screencopy; pub mod toplevel_info; pub mod toplevel_management; diff --git a/src/wayland/protocols/output_power.rs b/src/wayland/protocols/output_power.rs new file mode 100644 index 00000000..a74a656b --- /dev/null +++ b/src/wayland/protocols/output_power.rs @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use smithay::reexports::{ + wayland_protocols_wlr::output_power_management::v1::server::{ + zwlr_output_power_manager_v1::{self, ZwlrOutputPowerManagerV1}, + zwlr_output_power_v1::{self, ZwlrOutputPowerV1}, + }, + wayland_server::{ + backend::GlobalId, protocol::wl_output::WlOutput, Client, DataInit, Dispatch, + DisplayHandle, GlobalDispatch, New, Resource, Weak, + }, +}; +use wayland_backend::protocol::WEnum; + +pub trait OutputPowerHandler { + fn set_dpms(&mut self, output: &WlOutput, on: bool); +} + +pub struct OutputPowerState { + global: GlobalId, +} + +impl OutputPowerState { + pub fn new(dh: &DisplayHandle, client_filter: F) -> OutputPowerState + where + D: GlobalDispatch + 'static, + F: for<'a> Fn(&'a Client) -> bool + Clone + Send + Sync + 'static, + { + let global = dh.create_global::( + 1, + OutputPowerManagerGlobalData { + filter: Box::new(client_filter.clone()), + }, + ); + + OutputPowerState { global } + } + + pub fn send_dpms(&self, _on: bool) { + // TODO + } +} + +pub struct OutputPowerManagerGlobalData { + filter: Box Fn(&'a Client) -> bool + Send + Sync>, +} + +pub struct OutputPowerData { + output: Weak, +} + +impl GlobalDispatch + for OutputPowerState +where + D: GlobalDispatch + + Dispatch + + 'static, +{ + fn bind( + _state: &mut D, + _dh: &DisplayHandle, + _client: &Client, + resource: New, + _global_data: &OutputPowerManagerGlobalData, + data_init: &mut DataInit<'_, D>, + ) { + data_init.init(resource, ()); + } + + fn can_view(client: Client, global_data: &OutputPowerManagerGlobalData) -> bool { + (global_data.filter)(&client) + } +} + +impl Dispatch for OutputPowerState +where + D: GlobalDispatch + + Dispatch + + Dispatch + + 'static, +{ + fn request( + _state: &mut D, + _client: &Client, + _obj: &ZwlrOutputPowerManagerV1, + request: zwlr_output_power_manager_v1::Request, + _data: &(), + _dh: &DisplayHandle, + data_init: &mut DataInit<'_, D>, + ) { + match request { + zwlr_output_power_manager_v1::Request::GetOutputPower { id, output } => { + data_init.init( + id, + OutputPowerData { + output: output.downgrade(), + }, + ); + } + zwlr_output_power_manager_v1::Request::Destroy => {} + _ => unreachable!(), + } + } +} + +impl Dispatch for OutputPowerState +where + D: Dispatch + OutputPowerHandler + 'static, +{ + fn request( + state: &mut D, + _client: &Client, + _obj: &ZwlrOutputPowerV1, + request: zwlr_output_power_v1::Request, + data: &OutputPowerData, + _dh: &DisplayHandle, + _data_init: &mut DataInit<'_, D>, + ) { + match request { + zwlr_output_power_v1::Request::SetMode { mode } => { + if let Ok(output) = data.output.upgrade() { + let on = match mode { + WEnum::Value(zwlr_output_power_v1::Mode::On) => true, + WEnum::Value(zwlr_output_power_v1::Mode::Off) => false, + _ => { + return; + } + }; + state.set_dpms(&output, on); + } + } + zwlr_output_power_v1::Request::Destroy => {} + _ => unreachable!(), + } + } +} + +macro_rules! delegate_output_power { + ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { + smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + smithay::reexports::wayland_protocols_wlr::output_power_management::v1::server::zwlr_output_power_manager_v1::ZwlrOutputPowerManagerV1: $crate::wayland::protocols::output_power::OutputPowerManagerGlobalData + ] => $crate::wayland::protocols::output_power::OutputPowerState); + smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + smithay::reexports::wayland_protocols_wlr::output_power_management::v1::server::zwlr_output_power_manager_v1::ZwlrOutputPowerManagerV1: () + ] => $crate::wayland::protocols::output_power::OutputPowerState); + smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + smithay::reexports::wayland_protocols_wlr::output_power_management::v1::server::zwlr_output_power_v1::ZwlrOutputPowerV1: $crate::wayland::protocols::output_power::OutputPowerData + ] => $crate::wayland::protocols::output_power::OutputPowerState); + }; +} +pub(crate) use delegate_output_power;