diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 710b91ba..caba2d44 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -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 } diff --git a/src/backend/winit.rs b/src/backend/winit.rs index d1025876..646b8d89 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -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, 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::>() .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); diff --git a/src/backend/x11.rs b/src/backend/x11.rs index 97db92be..7bf27077 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -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, 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::>() .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); diff --git a/src/config/mod.rs b/src/config/mod.rs index 9f3ea3c5..c4352e77 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -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, backend: &mut BackendData, - shell: &mut Shell, - loop_handle: &LoopHandle<'_, State>, + shell: &Arc>, + loop_handle: &LoopHandle<'static, State>, workspace_state: &mut WorkspaceUpdateGuard<'_, State>, xdg_activation_state: &XdgActivationState, + startup_done: Arc<(Mutex, Condvar)>, ) { let outputs = output_state.outputs().collect::>(); let mut infos = outputs @@ -325,7 +332,6 @@ impl Config { .collect::>(); 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::>(); + 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::>() .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::>() .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::>() diff --git a/src/state.rs b/src/state.rs index 2d4ac8e4..258de36c 100644 --- a/src/state.rs +++ b/src/state.rs @@ -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>, pub clock: Clock, + pub startup_done: Arc<(Mutex, Condvar)>, pub should_stop: bool, pub local_offset: time::UtcOffset, pub gesture_state: Option, @@ -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>, workspace_state: &mut WorkspaceUpdateGuard<'_, State>, xdg_activation_state: &XdgActivationState, + startup_done: Arc<(Mutex, 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, diff --git a/src/utils/prelude.rs b/src/utils/prelude.rs index dd3fe8e7..39d71a7e 100644 --- a/src/utils/prelude.rs +++ b/src/utils/prelude.rs @@ -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; fn set_mirroring(&self, output: Option); + + 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::().unwrap().0.lock().unwrap() = output.map(|output| output.downgrade()); } + + fn is_enabled(&self) -> bool { + self.user_data() + .get::>() + .map(|conf| conf.borrow().enabled != OutputState::Disabled) + .unwrap_or(false) + } + + fn config(&self) -> Ref<'_, OutputConfig> { + self.user_data() + .get::>() + .unwrap() + .borrow() + } + + fn config_mut(&self) -> RefMut<'_, OutputConfig> { + self.user_data() + .get::>() + .unwrap() + .borrow_mut() + } } diff --git a/src/wayland/handlers/output_configuration.rs b/src/wayland/handlers/output_configuration.rs index f0f5b280..4b5993b3 100644 --- a/src/wayland/handlers/output_configuration.rs +++ b/src/wayland/handlers/output_configuration.rs @@ -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::>() - .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::>() + .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)| {