From d0f738bd85eacb208cb6c0b674a47d83049aa13b Mon Sep 17 00:00:00 2001 From: Val Packett Date: Wed, 1 Jan 2025 03:07:47 -0300 Subject: [PATCH] dbus: DisplayConfig: implement apply_monitors_config This enables gnome-control-center to apply display configuration changes. Only temporarily, persistence is ignored currently. --- src/dbus/mod.rs | 15 +++- src/dbus/mutter_display_config.rs | 113 +++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 4 deletions(-) diff --git a/src/dbus/mod.rs b/src/dbus/mod.rs index 347b16fd0..6e5ee0002 100644 --- a/src/dbus/mod.rs +++ b/src/dbus/mod.rs @@ -50,7 +50,20 @@ impl DBusServers { } if is_session_instance || config.debug.dbus_interfaces_in_non_session_instances { - let display_config = DisplayConfig::new(backend.ipc_outputs()); + let (to_niri, from_display_config) = calloop::channel::channel(); + let display_config = DisplayConfig::new(to_niri, backend.ipc_outputs()); + niri.event_loop + .insert_source(from_display_config, move |event, _, state| match event { + calloop::channel::Event::Msg(messages) => { + for (output, actions) in messages.into_iter() { + for action in actions.into_iter() { + state.apply_transient_output_config(&output, action); + } + } + } + calloop::channel::Event::Closed => (), + }) + .unwrap(); dbus.conn_display_config = try_start(display_config); let screen_saver = ScreenSaver::new(niri.is_fdo_idle_inhibited.clone()); diff --git a/src/dbus/mutter_display_config.rs b/src/dbus/mutter_display_config.rs index 13f78dc88..7d6d73c45 100644 --- a/src/dbus/mutter_display_config.rs +++ b/src/dbus/mutter_display_config.rs @@ -1,7 +1,8 @@ use std::collections::HashMap; +use std::str::FromStr; use std::sync::{Arc, Mutex}; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use zbus::fdo::RequestNameFlags; use zbus::object_server::SignalEmitter; use zbus::zvariant::{self, OwnedValue, Type}; @@ -10,8 +11,12 @@ use zbus::{fdo, interface}; use super::Start; use crate::backend::IpcOutputMap; use crate::utils::is_laptop_panel; +use niri_ipc::{ + ConfiguredMode, ConfiguredPosition, ModeToSet, OutputAction, PositionToSet, ScaleToSet, +}; pub struct DisplayConfig { + to_niri: calloop::channel::Sender>>, ipc_outputs: Arc>, } @@ -81,6 +86,17 @@ pub struct LogicalMonitor { properties: HashMap, } +// ApplyMonitorsConfig +#[derive(Deserialize, Type)] +pub struct LogicalMonitorConfiguration { + x: i32, + y: i32, + scale: f64, + transform: u32, + _is_primary: bool, + monitors: Vec<(String, String, HashMap)>, +} + #[interface(name = "org.gnome.Mutter.DisplayConfig")] impl DisplayConfig { async fn get_resources( @@ -313,11 +329,102 @@ impl DisplayConfig { #[zbus(signal)] pub async fn monitors_changed(ctxt: &SignalEmitter<'_>) -> zbus::Result<()>; + + async fn apply_monitors_config( + &self, + _serial: u32, + method: u32, + logical_monitor_configs: Vec, + _properties: HashMap, + ) -> fdo::Result<()> { + let current_conf = self.ipc_outputs.lock().unwrap(); + let mut messages = HashMap::new(); + for requested_config in logical_monitor_configs.into_iter() { + for (connector, mode, _props) in requested_config.monitors { + if current_conf + .values() + .find(|o| o.name == connector) + .is_none() + { + return Err(zbus::fdo::Error::Failed(format!( + "Connector '{}' not found", + connector + ))); + } + let msgs = vec![ + OutputAction::Mode { + mode: ModeToSet::Specific(ConfiguredMode::from_str(&mode).map_err( + |e| { + zbus::fdo::Error::Failed(format!( + "Could not parse mode '{}': {}", + mode, e + )) + }, + )?), + }, + OutputAction::Position { + position: PositionToSet::Specific(ConfiguredPosition { + x: requested_config.x, + y: requested_config.y, + }), + }, + OutputAction::Scale { + scale: ScaleToSet::Specific(requested_config.scale), + }, + OutputAction::Transform { + transform: match requested_config.transform { + 0 => niri_ipc::Transform::Normal, + 1 => niri_ipc::Transform::_90, + 2 => niri_ipc::Transform::_180, + 3 => niri_ipc::Transform::_270, + 4 => niri_ipc::Transform::Flipped, + 5 => niri_ipc::Transform::Flipped90, + 6 => niri_ipc::Transform::Flipped180, + 7 => niri_ipc::Transform::Flipped270, + x => { + return Err(zbus::fdo::Error::Failed(format!( + "Unknown transform {}", + x + ))) + } + }, + }, + OutputAction::On, + ]; + messages.insert(connector, msgs); + } + } + if messages.is_empty() { + return Err(zbus::fdo::Error::Failed( + "At least one output must be enabled".to_owned(), + )); + } + if method == 0 { + // 0 means "verify", so don't actually apply here + return Ok(()); + } + for (_, output) in current_conf.iter() { + if !messages.contains_key(&output.name) { + messages.insert(output.name.clone(), vec![OutputAction::Off]); + } + } + if let Err(err) = self.to_niri.send(messages) { + warn!("error sending message to niri: {err:?}"); + return Err(fdo::Error::Failed("internal error".to_owned())); + } + Ok(()) + } } impl DisplayConfig { - pub fn new(ipc_outputs: Arc>) -> Self { - Self { ipc_outputs } + pub fn new( + to_niri: calloop::channel::Sender>>, + ipc_outputs: Arc>, + ) -> Self { + Self { + to_niri, + ipc_outputs, + } } }