mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-21 02:01:55 +07:00
dbus: DisplayConfig: implement apply_monitors_config
This enables gnome-control-center to apply display configuration changes. Only temporarily, persistence is ignored currently.
This commit is contained in:
committed by
Ivan Molodetskikh
parent
b853d5b124
commit
890bbff007
+19
-1
@@ -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());
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+73
-70
@@ -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),
|
||||
}
|
||||
}
|
||||
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::Vrr { vrr } => {
|
||||
config.variable_refresh_rate = if vrr.vrr {
|
||||
Some(niri_config::Vrr {
|
||||
on_demand: vrr.on_demand,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
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::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
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
self.reload_output_config();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user