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() .unwrap()
.seats .seats
.add_seat(initial_seat); .add_seat(initial_seat);
{
{
let (lock, cvar) = &*state.common.startup_done;
let mut startup = lock.lock().unwrap();
*startup = true;
cvar.notify_all();
}
}
} }
res res
} }

View file

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

View file

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

View file

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

View file

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