237 lines
8.7 KiB
Rust
237 lines
8.7 KiB
Rust
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
use cosmic_comp_config::output::comp::{OutputConfig, OutputState, TransformDef};
|
|
use smithay::{output::Output, utils::Point};
|
|
use tracing::{error, warn};
|
|
|
|
use crate::{
|
|
state::State,
|
|
utils::prelude::OutputExt,
|
|
wayland::protocols::output_configuration::{
|
|
delegate_output_configuration, ModeConfiguration, OutputConfiguration,
|
|
OutputConfigurationHandler, OutputConfigurationState,
|
|
},
|
|
};
|
|
|
|
use std::cell::RefCell;
|
|
|
|
impl OutputConfigurationHandler for State {
|
|
fn output_configuration_state(&mut self) -> &mut OutputConfigurationState<Self> {
|
|
&mut self.common.output_configuration_state
|
|
}
|
|
|
|
fn test_configuration(&mut self, conf: Vec<(Output, OutputConfiguration)>) -> bool {
|
|
self.output_configuration(true, conf)
|
|
}
|
|
fn apply_configuration(&mut self, conf: Vec<(Output, OutputConfiguration)>) -> bool {
|
|
self.output_configuration(false, conf)
|
|
}
|
|
|
|
fn request_xwayland_primary(&mut self, primary_output: Option<Output>) {
|
|
for output in self.common.output_configuration_state.outputs() {
|
|
output.config_mut().xwayland_primary =
|
|
primary_output.as_ref().is_some_and(|o| *o == output);
|
|
}
|
|
self.common.update_xwayland_primary_output();
|
|
self.common
|
|
.config
|
|
.write_outputs(self.common.output_configuration_state.outputs());
|
|
}
|
|
}
|
|
|
|
impl State {
|
|
fn output_configuration(
|
|
&mut self,
|
|
test_only: bool,
|
|
mut conf: Vec<(Output, OutputConfiguration)>,
|
|
) -> bool {
|
|
if conf
|
|
.iter()
|
|
.all(|(_, conf)| matches!(conf, OutputConfiguration::Disabled))
|
|
{
|
|
return false; // we don't allow the user to accidentally disable all their outputs
|
|
}
|
|
|
|
// sanitize negative positions
|
|
{
|
|
let (offset_x, offset_y) = conf.iter().fold((0, 0), |mut offset, (_, conf)| {
|
|
if let OutputConfiguration::Enabled {
|
|
position: Some(position),
|
|
..
|
|
} = conf
|
|
{
|
|
if position.x.is_negative() {
|
|
offset.0 = offset.0.max(position.x.abs());
|
|
}
|
|
if position.y.is_negative() {
|
|
offset.1 = offset.1.max(position.y.abs());
|
|
}
|
|
}
|
|
offset
|
|
});
|
|
|
|
if offset_x > 0 || offset_y > 0 {
|
|
for (output, conf) in conf.iter_mut() {
|
|
if let OutputConfiguration::Enabled { position, .. } = conf {
|
|
let current_config = output
|
|
.user_data()
|
|
.get::<RefCell<OutputConfig>>()
|
|
.unwrap()
|
|
.borrow();
|
|
|
|
*position = Some(
|
|
position.unwrap_or(Point::from((
|
|
current_config.position.0 as i32,
|
|
current_config.position.1 as i32,
|
|
))) + Point::from((offset_x, offset_y)),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut backups = Vec::new();
|
|
for (output, conf) in &conf {
|
|
{
|
|
let mut current_config = output
|
|
.user_data()
|
|
.get::<RefCell<OutputConfig>>()
|
|
.unwrap()
|
|
.borrow_mut();
|
|
backups.push((output, current_config.clone()));
|
|
|
|
if let OutputConfiguration::Enabled {
|
|
mirroring,
|
|
mode,
|
|
scale,
|
|
transform,
|
|
position,
|
|
adaptive_sync,
|
|
} = conf
|
|
{
|
|
match mode {
|
|
Some(ModeConfiguration::Mode(mode)) => {
|
|
current_config.mode =
|
|
((mode.size.w, mode.size.h), Some(mode.refresh as u32));
|
|
}
|
|
Some(ModeConfiguration::Custom { size, refresh }) => {
|
|
current_config.mode = ((size.w, size.h), refresh.map(|x| x as u32));
|
|
}
|
|
_ => {}
|
|
}
|
|
if let Some(scale) = scale {
|
|
current_config.scale = *scale;
|
|
}
|
|
if let Some(transform) = transform {
|
|
current_config.transform = match transform {
|
|
smithay::utils::Transform::Normal => TransformDef::Normal,
|
|
smithay::utils::Transform::_90 => TransformDef::_90,
|
|
smithay::utils::Transform::_180 => TransformDef::_180,
|
|
smithay::utils::Transform::_270 => TransformDef::_270,
|
|
smithay::utils::Transform::Flipped => TransformDef::Flipped,
|
|
smithay::utils::Transform::Flipped90 => TransformDef::Flipped90,
|
|
smithay::utils::Transform::Flipped180 => TransformDef::Flipped180,
|
|
smithay::utils::Transform::Flipped270 => TransformDef::Flipped270,
|
|
}
|
|
}
|
|
if let Some(position) = position {
|
|
current_config.position = (position.x as u32, position.y as u32);
|
|
}
|
|
if let Some(vrr) = adaptive_sync {
|
|
current_config.vrr = *vrr;
|
|
}
|
|
if let Some(mirror) = mirroring {
|
|
current_config.enabled = OutputState::Mirroring(mirror.name());
|
|
} else {
|
|
current_config.enabled = OutputState::Enabled;
|
|
}
|
|
} else {
|
|
current_config.enabled = OutputState::Disabled;
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut backend = self.backend.lock();
|
|
let res = backend.apply_config_for_outputs(
|
|
test_only,
|
|
&self.common.event_loop_handle,
|
|
self.common.config.dynamic_conf.screen_filter(),
|
|
self.common.shell.clone(),
|
|
&mut self.common.workspace_state.update(),
|
|
&self.common.xdg_activation_state,
|
|
self.common.startup_done.clone(),
|
|
&self.common.clock,
|
|
);
|
|
if let Err(err) = res {
|
|
warn!("Failed to apply config. Resetting: {:?}", err);
|
|
for (output, backup) in backups {
|
|
{
|
|
let mut current_config = output
|
|
.user_data()
|
|
.get::<RefCell<OutputConfig>>()
|
|
.unwrap()
|
|
.borrow_mut();
|
|
*current_config = backup;
|
|
}
|
|
}
|
|
if !test_only {
|
|
if let Err(err) = backend.apply_config_for_outputs(
|
|
false,
|
|
&self.common.event_loop_handle,
|
|
self.common.config.dynamic_conf.screen_filter(),
|
|
self.common.shell.clone(),
|
|
&mut self.common.workspace_state.update(),
|
|
&self.common.xdg_activation_state,
|
|
self.common.startup_done.clone(),
|
|
&self.common.clock,
|
|
) {
|
|
error!("Failed to reset output config: {:?}", err);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
self.common.refresh();
|
|
|
|
for output in conf
|
|
.iter()
|
|
.filter(|(_, c)| {
|
|
matches!(
|
|
c,
|
|
OutputConfiguration::Enabled {
|
|
mirroring: None,
|
|
..
|
|
}
|
|
)
|
|
})
|
|
.map(|(o, _)| o)
|
|
{
|
|
self.common.output_configuration_state.enable_head(output);
|
|
}
|
|
for output in conf
|
|
.iter()
|
|
.filter(|(_, c)| {
|
|
matches!(
|
|
c,
|
|
OutputConfiguration::Disabled
|
|
| OutputConfiguration::Enabled {
|
|
mirroring: Some(_),
|
|
..
|
|
}
|
|
)
|
|
})
|
|
.map(|(o, _)| o)
|
|
{
|
|
self.common.output_configuration_state.disable_head(output);
|
|
}
|
|
self.common
|
|
.config
|
|
.write_outputs(self.common.output_configuration_state.outputs());
|
|
self.common.event_loop_handle.insert_idle(move |state| {
|
|
state.common.output_configuration_state.update();
|
|
});
|
|
|
|
true
|
|
}
|
|
}
|
|
|
|
delegate_output_configuration!(State);
|