output-configuration: Test all outputs at once

This commit is contained in:
Victoria Brekenfeld 2024-06-07 19:10:13 +02:00 committed by Victoria Brekenfeld
parent aae16c49dc
commit f481112cf9
7 changed files with 188 additions and 144 deletions

View file

@ -65,6 +65,15 @@ pub fn init_backend_auto(
.unwrap()
.seats
.add_seat(initial_seat);
{
{
let (lock, cvar) = &*state.common.startup_done;
let mut startup = lock.lock().unwrap();
*startup = true;
cvar.notify_all();
}
}
}
res
}

View file

@ -107,15 +107,15 @@ impl WinitState {
Ok(())
}
pub fn apply_config_for_output(
pub fn apply_config_for_outputs(
&mut self,
output: &Output,
test_only: bool,
) -> Result<(), anyhow::Error> {
) -> Result<Vec<Output>, anyhow::Error> {
// TODO: if we ever have multiple winit outputs, don't ignore config.enabled
// reset size
let size = self.backend.window_size();
let mut config = output
let mut config = self
.output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
@ -126,7 +126,7 @@ impl WinitState {
}
Err(anyhow::anyhow!("Cannot set window size"))
} else {
Ok(())
Ok(vec![self.output.clone()])
}
}
}
@ -229,17 +229,15 @@ pub fn init_backend(
.add_heads(std::iter::once(&output));
{
state.common.add_output(&output);
let mut shell = state.common.shell.write().unwrap();
state.common.config.read_outputs(
&mut state.common.output_configuration_state,
&mut state.backend,
&mut *shell,
&state.common.shell,
&state.common.event_loop_handle,
&mut state.common.workspace_state.update(),
&state.common.xdg_activation_state,
state.common.startup_done.clone(),
);
std::mem::drop(shell);
state.common.refresh();
}
state.launch_xwayland(None);

View file

@ -167,32 +167,29 @@ impl X11State {
}
}
pub fn apply_config_for_output(
pub fn apply_config_for_outputs(
&mut self,
output: &Output,
test_only: bool,
) -> Result<(), anyhow::Error> {
// TODO: if we ever have multiple winit outputs, don't ignore config.enabled
// reset size
let size = self
.surfaces
.iter()
.find(|s| s.output == *output)
.unwrap()
.window
.size();
let mut config = output
) -> Result<Vec<Output>, anyhow::Error> {
// TODO: if we ever have multiple winit outputs, don't juse use the first and don't ignore OutputState
let surface = self.surfaces.first().unwrap();
let size = surface.window.size();
let mut config = surface
.output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut();
// reset size
if config.mode.0 != (size.w as i32, size.h as i32) {
if !test_only {
config.mode = ((size.w as i32, size.h as i32), None);
}
Err(anyhow::anyhow!("Cannot set window size"))
} else {
Ok(())
Ok(vec![surface.output.clone()])
}
}
}
@ -366,17 +363,15 @@ pub fn init_backend(
.add_heads(std::iter::once(&output));
{
state.common.add_output(&output);
let mut shell = state.common.shell.write().unwrap();
state.common.config.read_outputs(
&mut state.common.output_configuration_state,
&mut state.backend,
&mut *shell,
&state.common.shell,
&state.common.event_loop_handle,
&mut state.common.workspace_state.update(),
&state.common.xdg_activation_state,
state.common.startup_done.clone(),
);
std::mem::drop(shell);
state.common.refresh();
}
state.launch_xwayland(None);

View file

@ -23,7 +23,13 @@ pub use smithay::{
},
utils::{Logical, Physical, Point, Size, Transform},
};
use std::{cell::RefCell, collections::HashMap, fs::OpenOptions, path::PathBuf};
use std::{
cell::RefCell,
collections::HashMap,
fs::OpenOptions,
path::PathBuf,
sync::{Arc, Condvar, Mutex, RwLock},
};
use tracing::{debug, error, info, warn};
mod input_config;
@ -312,10 +318,11 @@ impl Config {
&mut self,
output_state: &mut OutputConfigurationState<State>,
backend: &mut BackendData,
shell: &mut Shell,
loop_handle: &LoopHandle<'_, State>,
shell: &Arc<RwLock<Shell>>,
loop_handle: &LoopHandle<'static, State>,
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
xdg_activation_state: &XdgActivationState,
startup_done: Arc<(Mutex<bool>, Condvar)>,
) {
let outputs = output_state.outputs().collect::<Vec<_>>();
let mut infos = outputs
@ -325,7 +332,6 @@ impl Config {
.collect::<Vec<_>>();
infos.sort();
if let Some(configs) = self.dynamic_conf.outputs().config.get(&infos).cloned() {
let mut reset = false;
let known_good_configs = outputs
.iter()
.map(|output| {
@ -338,6 +344,7 @@ impl Config {
})
.collect::<Vec<_>>();
let mut found_outputs = Vec::new();
for (name, output_config) in infos.iter().map(|o| &o.connector).zip(configs.into_iter())
{
let output = outputs.iter().find(|o| &o.name() == name).unwrap().clone();
@ -347,31 +354,19 @@ impl Config {
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut() = output_config;
if let Err(err) = backend.apply_config_for_output(
&output,
false,
shell,
loop_handle,
workspace_state,
xdg_activation_state,
) {
warn!(
?err,
"Failed to set new config for output {}.",
output.name(),
);
reset = true;
break;
} else {
if enabled == OutputState::Enabled {
output_state.enable_head(&output);
} else {
output_state.disable_head(&output);
}
}
found_outputs.push((output.clone(), enabled));
}
if reset {
if let Err(err) = backend.apply_config_for_outputs(
false,
loop_handle,
shell.clone(),
workspace_state,
xdg_activation_state,
startup_done.clone(),
) {
warn!(?err, "Failed to set new config.");
found_outputs.clear();
for (output, output_config) in outputs
.clone()
.into_iter()
@ -383,16 +378,20 @@ impl Config {
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut() = output_config;
if let Err(err) = backend.apply_config_for_output(
&output,
false,
shell,
loop_handle,
workspace_state,
xdg_activation_state,
) {
error!(?err, "Failed to reset config for output {}.", output.name());
} else {
found_outputs.push((output.clone(), enabled));
}
if let Err(err) = backend.apply_config_for_outputs(
false,
loop_handle,
shell.clone(),
workspace_state,
xdg_activation_state,
startup_done,
) {
error!(?err, "Failed to reset config.");
} else {
for (output, enabled) in found_outputs {
if enabled == OutputState::Enabled {
output_state.enable_head(&output);
} else {
@ -400,26 +399,30 @@ impl Config {
}
}
}
} else {
for (output, enabled) in found_outputs {
if enabled == OutputState::Enabled {
output_state.enable_head(&output);
} else {
output_state.disable_head(&output);
}
}
}
output_state.update();
self.write_outputs(output_state.outputs());
} else {
for output in outputs {
if let Err(err) = backend.apply_config_for_output(
&output,
false,
shell,
loop_handle,
workspace_state,
xdg_activation_state,
) {
warn!(
?err,
"Failed to set new config for output {}.",
output.name(),
);
} else {
if let Err(err) = backend.apply_config_for_outputs(
false,
loop_handle,
shell.clone(),
workspace_state,
xdg_activation_state,
startup_done,
) {
warn!(?err, "Failed to set new config.",);
} else {
for output in outputs {
if output
.user_data()
.get::<RefCell<OutputConfig>>()

View file

@ -106,7 +106,7 @@ use std::{
collections::{HashSet, VecDeque},
ffi::OsString,
process::Child,
sync::{Arc, Once, RwLock},
sync::{Arc, Condvar, Mutex, Once, RwLock},
time::{Duration, Instant},
};
@ -179,6 +179,7 @@ pub struct Common {
pub shell: Arc<RwLock<Shell>>,
pub clock: Clock<Monotonic>,
pub startup_done: Arc<(Mutex<bool>, Condvar)>,
pub should_stop: bool,
pub local_offset: time::UtcOffset,
pub gesture_state: Option<GestureState>,
@ -262,30 +263,26 @@ impl BackendData {
}
}
pub fn apply_config_for_output(
pub fn apply_config_for_outputs(
&mut self,
output: &Output,
test_only: bool,
shell: &mut Shell,
loop_handle: &LoopHandle<'_, State>,
loop_handle: &LoopHandle<'static, State>,
shell: Arc<RwLock<Shell>>,
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
xdg_activation_state: &XdgActivationState,
startup_done: Arc<(Mutex<bool>, Condvar)>,
) -> Result<(), anyhow::Error> {
let result = match self {
BackendData::Kms(ref mut state) => state.apply_config_for_output(
output,
shell,
test_only,
loop_handle,
workspace_state,
xdg_activation_state,
),
BackendData::Winit(ref mut state) => state.apply_config_for_output(output, test_only),
BackendData::X11(ref mut state) => state.apply_config_for_output(output, test_only),
BackendData::Kms(ref mut state) => {
state.apply_config_for_outputs(test_only, loop_handle, shell.clone(), startup_done)
}
BackendData::Winit(ref mut state) => state.apply_config_for_outputs(test_only),
BackendData::X11(ref mut state) => state.apply_config_for_outputs(test_only),
_ => unreachable!("No backend set when applying output config"),
};
}?;
if result.is_ok() {
let mut shell = shell.write().unwrap();
for output in result {
// apply to Output
let final_config = output
.user_data()
@ -318,10 +315,29 @@ impl BackendData {
_ => None,
});
layer_map_for_output(output).arrange();
match final_config.enabled {
OutputState::Enabled => {
shell
.workspaces
.add_output(&output, workspace_state, xdg_activation_state)
}
_ => {
let shell = &mut *shell;
shell.workspaces.remove_output(
&output,
shell.seats.iter(),
workspace_state,
xdg_activation_state,
)
}
}
layer_map_for_output(&output).arrange();
self.schedule_render(loop_handle, &output);
}
result
Ok(())
}
pub fn schedule_render(&mut self, loop_handle: &LoopHandle<'_, State>, output: &Output) {
@ -494,6 +510,7 @@ impl State {
local_offset,
clock,
startup_done: Arc::new((Mutex::new(false), Condvar::new())),
should_stop: false,
gesture_state: None,

View file

@ -4,13 +4,17 @@ use smithay::{
};
pub use super::geometry::*;
use crate::config::{OutputConfig, OutputState};
pub use crate::shell::{SeatExt, Shell, Workspace};
pub use crate::state::{Common, State};
pub use crate::wayland::handlers::xdg_shell::popup::update_reactive_popups;
use std::sync::{
atomic::{AtomicBool, Ordering},
Mutex,
use std::{
cell::{Ref, RefCell, RefMut},
sync::{
atomic::{AtomicBool, Ordering},
Mutex,
},
};
pub trait OutputExt {
@ -19,6 +23,10 @@ pub trait OutputExt {
fn set_adaptive_sync(&self, vrr: bool);
fn mirroring(&self) -> Option<Output>;
fn set_mirroring(&self, output: Option<Output>);
fn is_enabled(&self) -> bool;
fn config(&self) -> Ref<'_, OutputConfig>;
fn config_mut(&self) -> RefMut<'_, OutputConfig>;
}
struct Vrr(AtomicBool);
@ -73,4 +81,25 @@ impl OutputExt for Output {
*user_data.get::<Mirroring>().unwrap().0.lock().unwrap() =
output.map(|output| output.downgrade());
}
fn is_enabled(&self) -> bool {
self.user_data()
.get::<RefCell<OutputConfig>>()
.map(|conf| conf.borrow().enabled != OutputState::Disabled)
.unwrap_or(false)
}
fn config(&self) -> Ref<'_, OutputConfig> {
self.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow()
}
fn config_mut(&self) -> RefMut<'_, OutputConfig> {
self.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut()
}
}

View file

@ -90,51 +90,44 @@ impl State {
current_config.enabled = OutputState::Disabled;
}
}
let mut shell = self.common.shell.write().unwrap();
let res = self.backend.apply_config_for_output(
output,
test_only,
&mut *shell,
&self.common.event_loop_handle,
&mut self.common.workspace_state.update(),
&self.common.xdg_activation_state,
);
if let Err(err) = res {
warn!(
?err,
"Failed to apply config to {}. Resetting",
output.name(),
);
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) = self.backend.apply_config_for_output(
output,
false,
&mut *shell,
&self.common.event_loop_handle,
&mut self.common.workspace_state.update(),
&self.common.xdg_activation_state,
) {
error!(?err, "Failed to reset output config for {}.", output.name(),);
}
}
}
return false;
}
std::mem::drop(shell);
self.common.refresh();
}
let res = self.backend.apply_config_for_outputs(
test_only,
&self.common.event_loop_handle,
self.common.shell.clone(),
&mut self.common.workspace_state.update(),
&self.common.xdg_activation_state,
self.common.startup_done.clone(),
);
if let Err(err) = res {
warn!(?err, "Failed to apply config. Resetting");
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) = self.backend.apply_config_for_outputs(
false,
&self.common.event_loop_handle,
self.common.shell.clone(),
&mut self.common.workspace_state.update(),
&self.common.xdg_activation_state,
self.common.startup_done.clone(),
) {
error!(?err, "Failed to reset output config.");
}
}
return false;
}
self.common.refresh();
for output in conf
.iter()
.filter(|(_, c)| {