aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVal Packett <val@packett.cool>2025-01-01 03:07:47 -0300
committerIvan Molodetskikh <yalterz@gmail.com>2025-01-17 11:16:10 +0300
commit890bbff0077cddaa08d7d6c05c797c41b0b4c09e (patch)
tree15f3f825bcda7571763f699a8a55787e594ec31c /src
parentb853d5b1247fddfaeac0dbdfb9304c69c6de463a (diff)
downloadniri-890bbff0077cddaa08d7d6c05c797c41b0b4c09e.tar.gz
niri-890bbff0077cddaa08d7d6c05c797c41b0b4c09e.tar.bz2
niri-890bbff0077cddaa08d7d6c05c797c41b0b4c09e.zip
dbus: DisplayConfig: implement apply_monitors_config
This enables gnome-control-center to apply display configuration changes. Only temporarily, persistence is ignored currently.
Diffstat (limited to 'src')
-rw-r--r--src/dbus/mod.rs20
-rw-r--r--src/dbus/mutter_display_config.rs106
-rw-r--r--src/niri.rs137
3 files changed, 192 insertions, 71 deletions
diff --git a/src/dbus/mod.rs b/src/dbus/mod.rs
index 347b16fd..f69dc1d9 100644
--- a/src/dbus/mod.rs
+++ b/src/dbus/mod.rs
@@ -50,7 +50,25 @@ 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(new_conf) => {
+ for (name, conf) in new_conf {
+ state.modify_output_config(&name, move |output| {
+ if let Some(new_output) = conf {
+ *output = new_output;
+ } else {
+ output.off = true;
+ }
+ });
+ }
+ state.reload_output_config();
+ }
+ 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 2be37b5e..b8ab08f9 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 smithay::utils::Size;
use zbus::fdo::RequestNameFlags;
use zbus::object_server::SignalEmitter;
@@ -14,6 +15,7 @@ use crate::utils::is_laptop_panel;
use crate::utils::scale::supported_scales;
pub struct DisplayConfig {
+ to_niri: calloop::channel::Sender<HashMap<String, Option<niri_config::Output>>>,
ipc_outputs: Arc<Mutex<IpcOutputMap>>,
}
@@ -46,6 +48,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_current_state(
@@ -158,6 +171,87 @@ impl DisplayConfig {
Ok((0, monitors, logical_monitors, properties))
}
+ 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 new_conf = HashMap::new();
+ for requested_config in logical_monitor_configs {
+ if requested_config.monitors.len() > 1 {
+ return Err(zbus::fdo::Error::Failed(
+ "Mirroring is not yet supported".to_owned(),
+ ));
+ }
+ for (connector, mode, _props) in requested_config.monitors {
+ if !current_conf.values().any(|o| o.name == connector) {
+ return Err(zbus::fdo::Error::Failed(format!(
+ "Connector '{}' not found",
+ connector
+ )));
+ }
+ new_conf.insert(
+ connector.clone(),
+ Some(niri_config::Output {
+ off: false,
+ name: connector,
+ scale: Some(niri_config::FloatOrInt(requested_config.scale)),
+ 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
+ )))
+ }
+ },
+ position: Some(niri_config::Position {
+ x: requested_config.x,
+ y: requested_config.y,
+ }),
+ mode: Some(niri_ipc::ConfiguredMode::from_str(&mode).map_err(|e| {
+ zbus::fdo::Error::Failed(format!(
+ "Could not parse mode '{}': {}",
+ mode, e
+ ))
+ })?),
+ // FIXME: VRR
+ ..Default::default()
+ }),
+ );
+ }
+ }
+ if new_conf.is_empty() {
+ return Err(zbus::fdo::Error::Failed(
+ "At least one output must be enabled".to_owned(),
+ ));
+ }
+ for output in current_conf.values() {
+ if !new_conf.contains_key(&output.name) {
+ new_conf.insert(output.name.clone(), None);
+ }
+ }
+ if method == 0 {
+ // 0 means "verify", so don't actually apply here
+ return Ok(());
+ }
+ if let Err(err) = self.to_niri.send(new_conf) {
+ warn!("error sending message to niri: {err:?}");
+ return Err(fdo::Error::Failed("internal error".to_owned()));
+ }
+ Ok(())
+ }
+
#[zbus(signal)]
pub async fn monitors_changed(ctxt: &SignalEmitter<'_>) -> zbus::Result<()>;
@@ -188,8 +282,14 @@ impl DisplayConfig {
}
impl DisplayConfig {
- pub fn new(ipc_outputs: Arc<Mutex<IpcOutputMap>>) -> Self {
- Self { ipc_outputs }
+ pub fn new(
+ to_niri: calloop::channel::Sender<HashMap<String, Option<niri_config::Output>>>,
+ ipc_outputs: Arc<Mutex<IpcOutputMap>>,
+ ) -> Self {
+ Self {
+ to_niri,
+ ipc_outputs,
+ }
}
}
diff --git a/src/niri.rs b/src/niri.rs
index ef4e8e6c..16758ab5 100644
--- a/src/niri.rs
+++ b/src/niri.rs
@@ -1343,82 +1343,85 @@ impl State {
self.niri.output_management_state.on_config_changed(config);
}
- pub fn apply_transient_output_config(&mut self, name: &str, action: niri_ipc::OutputAction) {
+ pub fn modify_output_config<F>(&mut self, name: &str, fun: F)
+ where
+ F: FnOnce(&mut niri_config::Output),
+ {
+ // Try hard to find the output config section corresponding to the output set by the
+ // user. Since if we add a new section and some existing section also matches the
+ // output, then our new section won't do anything.
+ let temp;
+ let match_name = if let Some(output) = self.niri.output_by_name_match(name) {
+ output.user_data().get::<OutputName>().unwrap()
+ } else if let Some(output_name) = self
+ .backend
+ .tty_checked()
+ .and_then(|tty| tty.disconnected_connector_name_by_name_match(name))
{
- // Try hard to find the output config section corresponding to the output set by the
- // user. Since if we add a new section and some existing section also matches the
- // output, then our new section won't do anything.
- let temp;
- let match_name = if let Some(output) = self.niri.output_by_name_match(name) {
- output.user_data().get::<OutputName>().unwrap()
- } else if let Some(output_name) = self
- .backend
- .tty_checked()
- .and_then(|tty| tty.disconnected_connector_name_by_name_match(name))
- {
- temp = output_name;
- &temp
- } else {
- // Even if name is "make model serial", matching will work fine this way.
- temp = OutputName {
- connector: name.to_owned(),
- make: None,
- model: None,
- serial: None,
- };
- &temp
+ temp = output_name;
+ &temp
+ } else {
+ // Even if name is "make model serial", matching will work fine this way.
+ temp = OutputName {
+ connector: name.to_owned(),
+ make: None,
+ model: None,
+ serial: None,
};
+ &temp
+ };
- let mut config = self.niri.config.borrow_mut();
- let config = if let Some(config) = config.outputs.find_mut(match_name) {
- config
- } else {
- config.outputs.0.push(niri_config::Output {
- // Save name as set by the user.
- name: String::from(name),
- ..Default::default()
- });
- config.outputs.0.last_mut().unwrap()
- };
+ let mut config = self.niri.config.borrow_mut();
+ let config = if let Some(config) = config.outputs.find_mut(match_name) {
+ config
+ } else {
+ config.outputs.0.push(niri_config::Output {
+ // Save name as set by the user.
+ name: String::from(name),
+ ..Default::default()
+ });
+ config.outputs.0.last_mut().unwrap()
+ };
- match action {
- niri_ipc::OutputAction::Off => config.off = true,
- niri_ipc::OutputAction::On => config.off = false,
- niri_ipc::OutputAction::Mode { mode } => {
- config.mode = match mode {
- niri_ipc::ModeToSet::Automatic => None,
- niri_ipc::ModeToSet::Specific(mode) => Some(mode),
- }
+ fun(config);
+ }
+
+ pub fn apply_transient_output_config(&mut self, name: &str, action: niri_ipc::OutputAction) {
+ self.modify_output_config(name, move |config| match action {
+ niri_ipc::OutputAction::Off => config.off = true,
+ niri_ipc::OutputAction::On => config.off = false,
+ niri_ipc::OutputAction::Mode { mode } => {
+ config.mode = match mode {
+ niri_ipc::ModeToSet::Automatic => None,
+ niri_ipc::ModeToSet::Specific(mode) => Some(mode),
}
- niri_ipc::OutputAction::Scale { scale } => {
- config.scale = match scale {
- niri_ipc::ScaleToSet::Automatic => None,
- niri_ipc::ScaleToSet::Specific(scale) => Some(FloatOrInt(scale)),
- }
+ }
+ niri_ipc::OutputAction::Scale { scale } => {
+ config.scale = match scale {
+ niri_ipc::ScaleToSet::Automatic => None,
+ niri_ipc::ScaleToSet::Specific(scale) => Some(FloatOrInt(scale)),
}
- niri_ipc::OutputAction::Transform { transform } => config.transform = transform,
- niri_ipc::OutputAction::Position { position } => {
- config.position = match position {
- niri_ipc::PositionToSet::Automatic => None,
- niri_ipc::PositionToSet::Specific(position) => {
- Some(niri_config::Position {
- x: position.x,
- y: position.y,
- })
- }
- }
+ }
+ niri_ipc::OutputAction::Transform { transform } => config.transform = transform,
+ niri_ipc::OutputAction::Position { position } => {
+ config.position = match position {
+ niri_ipc::PositionToSet::Automatic => None,
+ niri_ipc::PositionToSet::Specific(position) => Some(niri_config::Position {
+ x: position.x,
+ y: position.y,
+ }),
}
- niri_ipc::OutputAction::Vrr { vrr } => {
- config.variable_refresh_rate = if vrr.vrr {
- Some(niri_config::Vrr {
- on_demand: vrr.on_demand,
- })
- } else {
- None
- }
+ }
+ niri_ipc::OutputAction::Vrr { vrr } => {
+ config.variable_refresh_rate = if vrr.vrr {
+ Some(niri_config::Vrr {
+ on_demand: vrr.on_demand,
+ })
+ } else {
+ None
}
}
- }
+ });
self.reload_output_config();
}