output-configuration: Test all outputs at once
This commit is contained in:
parent
aae16c49dc
commit
f481112cf9
7 changed files with 188 additions and 144 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>>()
|
||||
|
|
|
|||
55
src/state.rs
55
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<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,
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)| {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue