Skip to content

Commit

Permalink
dbus: DisplayConfig: implement apply_monitors_config
Browse files Browse the repository at this point in the history
This enables gnome-control-center to apply display configuration
changes. Only temporarily, persistence is ignored currently.
  • Loading branch information
valpackett committed Jan 4, 2025
1 parent 77efc71 commit d0f738b
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 4 deletions.
15 changes: 14 additions & 1 deletion src/dbus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
113 changes: 110 additions & 3 deletions src/dbus/mutter_display_config.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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<HashMap<String, Vec<OutputAction>>>,
ipc_outputs: Arc<Mutex<IpcOutputMap>>,
}

Expand Down Expand Up @@ -81,6 +86,17 @@ pub struct LogicalMonitor {
properties: HashMap<String, OwnedValue>,
}

// ApplyMonitorsConfig
#[derive(Deserialize, Type)]
pub struct LogicalMonitorConfiguration {
x: i32,
y: i32,
scale: f64,
transform: u32,
_is_primary: bool,
monitors: Vec<(String, String, HashMap<String, OwnedValue>)>,
}

#[interface(name = "org.gnome.Mutter.DisplayConfig")]
impl DisplayConfig {
async fn get_resources(
Expand Down Expand Up @@ -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<LogicalMonitorConfiguration>,
_properties: HashMap<String, OwnedValue>,
) -> 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<Mutex<IpcOutputMap>>) -> Self {
Self { ipc_outputs }
pub fn new(
to_niri: calloop::channel::Sender<HashMap<String, Vec<OutputAction>>>,
ipc_outputs: Arc<Mutex<IpcOutputMap>>,
) -> Self {
Self {
to_niri,
ipc_outputs,
}
}
}

Expand Down

0 comments on commit d0f738b

Please sign in to comment.