refactor: dynamic output configuration

This commit is contained in:
Victoria Brekenfeld 2022-04-14 22:16:37 +02:00
parent 99cc97b0d9
commit a8aeba8f09
6 changed files with 197 additions and 239 deletions

View file

@ -5,7 +5,7 @@ use crate::state::Fps;
use crate::{ use crate::{
backend::render, backend::render,
config::{Config, OutputConfig}, config::OutputConfig,
shell::Shell, shell::Shell,
state::{BackendData, Common, State}, state::{BackendData, Common, State},
}; };
@ -297,58 +297,64 @@ impl State {
let outputs = device.enumerate_surfaces()?.added; // There are no removed outputs on newly added devices let outputs = device.enumerate_surfaces()?.added; // There are no removed outputs on newly added devices
let mut wl_outputs = Vec::new(); let mut wl_outputs = Vec::new();
let mut w = self.common.shell.global_space().size.w;
for (crtc, conn) in outputs { for (crtc, conn) in outputs {
match device.setup_surface( match device.setup_surface(
&drm_node,
crtc, crtc,
conn, conn,
self.backend.kms().signaler.clone(),
&mut self.common.event_loop_handle, &mut self.common.event_loop_handle,
(0, w),
) { ) {
Ok(output) => { Ok(output) => {
self.common.shell.map_output( w += output.user_data().get::<RefCell<OutputConfig>>().unwrap().borrow().mode_size().w;
&output,
&mut self.backend,
&mut self.common.config,
);
wl_outputs.push(output); wl_outputs.push(output);
} }
Err(err) => slog_scope::warn!("Failed to initialize output: {}", err), Err(err) => slog_scope::warn!("Failed to initialize output: {}", err),
}; };
} }
self.backend.kms().devices.insert(drm_node, device);
self.common.output_conf.add_heads(wl_outputs.iter()); self.common.output_conf.add_heads(wl_outputs.iter());
self.common self.common
.output_conf .output_conf
.update(&mut *self.common.display.borrow_mut()); .update(&mut *self.common.display.borrow_mut());
for output in wl_outputs {
if let Err(err) = self.backend.kms().apply_config_for_output(&output, &mut self.common.shell, false) {
slog_scope::warn!("Failed to initialize output: {}", err);
}
}
self.common.config.read_outputs(self.common.output_conf.outputs(), &mut self.backend, &mut self.common.shell);
self.common.shell.refresh_outputs();
self.common.config.write_outputs(self.common.output_conf.outputs());
self.backend.kms().devices.insert(drm_node, device);
Ok(()) Ok(())
} }
fn device_changed(&mut self, dev: dev_t) -> Result<()> { fn device_changed(&mut self, dev: dev_t) -> Result<()> {
let drm_node = DrmNode::from_dev_id(dev)?; let drm_node = DrmNode::from_dev_id(dev)?;
let signaler = self.backend.kms().signaler.clone();
let mut outputs_removed = Vec::new(); let mut outputs_removed = Vec::new();
let mut outputs_added = Vec::new(); let mut outputs_added = Vec::new();
if let Some(device) = self.backend.kms().devices.get_mut(&drm_node) { if let Some(device) = self.backend.kms().devices.get_mut(&drm_node) {
let changes = device.enumerate_surfaces()?; let changes = device.enumerate_surfaces()?;
let mut w = self.common.shell.global_space().size.w;
for crtc in changes.removed { for crtc in changes.removed {
if let Some(surface) = device.surfaces.get_mut(&crtc) { if let Some(surface) = device.surfaces.get_mut(&crtc) {
if let Some(token) = surface.render_timer_token.take() { if let Some(token) = surface.render_timer_token.take() {
self.common.event_loop_handle.remove(token); self.common.event_loop_handle.remove(token);
} }
w -= surface.output.current_mode().map(|m| m.size.w).unwrap_or(0);
outputs_removed.push(surface.output.clone()); outputs_removed.push(surface.output.clone());
} }
} }
for (crtc, conn) in changes.added { for (crtc, conn) in changes.added {
match device.setup_surface( match device.setup_surface(
&drm_node,
crtc, crtc,
conn, conn,
signaler.clone(),
&mut self.common.event_loop_handle, &mut self.common.event_loop_handle,
(0, w),
) { ) {
Ok(output) => { Ok(output) => {
w += output.user_data().get::<RefCell<OutputConfig>>().unwrap().borrow().mode_size().w;
outputs_added.push(output); outputs_added.push(output);
} }
Err(err) => slog_scope::warn!("Failed to initialize output: {}", err), Err(err) => slog_scope::warn!("Failed to initialize output: {}", err),
@ -357,20 +363,19 @@ impl State {
} }
self.common.output_conf.remove_heads(outputs_removed.iter()); self.common.output_conf.remove_heads(outputs_removed.iter());
for output in outputs_removed.into_iter() {
self.common
.shell
.unmap_output(&output, &mut self.backend, &mut self.common.config);
}
self.common.output_conf.add_heads(outputs_added.iter()); self.common.output_conf.add_heads(outputs_added.iter());
for output in outputs_added.into_iter() { for output in outputs_added {
self.common if let Err(err) = self.backend.kms().apply_config_for_output(&output, &mut self.common.shell, false) {
.shell slog_scope::warn!("Failed to initialize output: {}", err);
.map_output(&output, &mut self.backend, &mut self.common.config) }
} }
self.common self.common
.output_conf .output_conf
.update(&mut self.common.display.borrow_mut()); .update(&mut self.common.display.borrow_mut());
self.common.config.read_outputs(self.common.output_conf.outputs(), &mut self.backend, &mut self.common.shell);
self.common.shell.refresh_outputs();
self.common.config.write_outputs(self.common.output_conf.outputs());
Ok(()) Ok(())
} }
@ -392,14 +397,12 @@ impl State {
} }
} }
self.common.output_conf.remove_heads(outputs_removed.iter()); self.common.output_conf.remove_heads(outputs_removed.iter());
for output in outputs_removed.into_iter() {
self.common
.shell
.unmap_output(&output, &mut self.backend, &mut self.common.config);
}
self.common self.common
.output_conf .output_conf
.update(&mut *self.common.display.borrow_mut()); .update(&mut *self.common.display.borrow_mut());
self.common.config.read_outputs(self.common.output_conf.outputs(), &mut self.backend, &mut self.common.shell);
self.common.shell.refresh_outputs();
self.common.config.write_outputs(self.common.output_conf.outputs());
Ok(()) Ok(())
} }
@ -440,11 +443,10 @@ impl Device {
fn setup_surface( fn setup_surface(
&mut self, &mut self,
drm_node: &DrmNode,
crtc: crtc::Handle, crtc: crtc::Handle,
conn: connector::Handle, conn: connector::Handle,
signaler: Signaler<Signal>,
loop_handle: &mut LoopHandle<'static, State>, loop_handle: &mut LoopHandle<'static, State>,
position: (i32, i32),
) -> Result<Output> { ) -> Result<Output> {
let drm = &mut *self.drm.as_source_mut(); let drm = &mut *self.drm.as_source_mut();
let crtc_info = drm.get_crtc(crtc)?; let crtc_info = drm.get_crtc(crtc)?;
@ -461,14 +463,6 @@ impl Device {
.unwrap_or(conn_info.modes()[0]) .unwrap_or(conn_info.modes()[0])
}); });
let refresh_rate = drm_helpers::calculate_refresh_rate(mode); let refresh_rate = drm_helpers::calculate_refresh_rate(mode);
let mut surface = drm.create_surface(crtc, mode, &[conn])?;
surface.link(signaler);
let target =
GbmBufferedSurface::new(surface, self.allocator.clone(), self.formats.clone(), None)
.with_context(|| format!("Failed to initialize Gbm surface for {}", interface))?;
//let is_nvidia = driver(drm.device_id()).ok().flatten().map(|x| x == "nvidia").unwrap_or(false);
let output_mode = OutputMode { let output_mode = OutputMode {
size: (mode.size().0 as i32, mode.size().1 as i32).into(), size: (mode.size().0 as i32, mode.size().1 as i32).into(),
refresh: refresh_rate as i32, refresh: refresh_rate as i32,
@ -499,12 +493,13 @@ impl Device {
// TODO: Readout property for monitor rotation // TODO: Readout property for monitor rotation
Some(wl_output::Transform::Normal), Some(wl_output::Transform::Normal),
None, None,
None, Some(position.into()),
); );
output.user_data().insert_if_missing(|| { output.user_data().insert_if_missing(|| {
RefCell::new(OutputConfig { RefCell::new(OutputConfig {
mode: ((output_mode.size.w, output_mode.size.h), Some(refresh_rate)), mode: ((output_mode.size.w, output_mode.size.h), Some(refresh_rate)),
vrr, vrr,
position,
..Default::default() ..Default::default()
}) })
}); });
@ -529,16 +524,15 @@ impl Device {
} }
}) })
.unwrap(); .unwrap();
timer_handle.add_timeout(Duration::ZERO, (*drm_node, crtc));
let data = Surface { let data = Surface {
output: output.clone(), output: output.clone(),
surface: Some(target), surface: None,
connector: conn, connector: conn,
vrr, vrr,
refresh_rate, refresh_rate,
last_submit: None, last_submit: None,
pending: true, pending: false,
render_timer: timer_handle, render_timer: timer_handle,
render_timer_token: Some(timer_token), render_timer_token: Some(timer_token),
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
@ -633,7 +627,6 @@ impl KmsState {
pub fn apply_config_for_output( pub fn apply_config_for_output(
&mut self, &mut self,
output: &Output, output: &Output,
config: &mut Config,
shell: &mut Shell, shell: &mut Shell,
test_only: bool, test_only: bool,
) -> Result<(), anyhow::Error> { ) -> Result<(), anyhow::Error> {
@ -657,7 +650,7 @@ impl KmsState {
if !test_only { if !test_only {
if surface.surface.take().is_some() { if surface.surface.take().is_some() {
// just drop it // just drop it
shell.disable_output(output, config); shell.remove_output(output);
} }
} }
false false
@ -707,7 +700,7 @@ impl KmsState {
) )
})?; })?;
surface.surface = Some(target); surface.surface = Some(target);
shell.enable_output(output, config); shell.add_output(output);
true true
} }
} else { } else {

View file

@ -169,10 +169,8 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
} }
Err(winit::WinitError::WindowClosed) => { Err(winit::WinitError::WindowClosed) => {
let output = state.backend.winit().output.clone(); let output = state.backend.winit().output.clone();
state.common.shell.unmap_output( state.common.shell.remove_output(
&output, &output,
&mut state.backend,
&mut state.common.config,
); );
if let Some(token) = token.take() { if let Some(token) = token.take() {
event_loop_handle.remove(token); event_loop_handle.remove(token);
@ -197,7 +195,10 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
state state
.common .common
.shell .shell
.map_output(&output, &mut state.backend, &mut state.common.config); .add_output(&output);
state.common.config.read_outputs(std::iter::once(&output), &mut state.backend, &mut state.common.shell);
state.common.shell.refresh_outputs();
state.common.config.write_outputs(std::iter::once(&output));
Ok(()) Ok(())
} }
@ -271,6 +272,7 @@ impl State {
self.common self.common
.output_conf .output_conf
.update(&mut *self.common.display.borrow_mut()); .update(&mut *self.common.display.borrow_mut());
self.common.shell.refresh_outputs();
render_ping.ping(); render_ping.ping();
} }
WinitEvent::Refresh => render_ping.ping(), WinitEvent::Refresh => render_ping.ping(),

View file

@ -262,7 +262,12 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
state state
.common .common
.shell .shell
.map_output(&output, &mut state.backend, &mut state.common.config); .add_output(&output);
state.common.config.read_outputs(std::iter::once(&output), &mut state.backend, &mut state.common.shell);
state.common.shell.refresh_outputs();
state.common.config.write_outputs(std::iter::once(&output));
event_loop event_loop
.handle() .handle()
@ -286,10 +291,8 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
.surfaces .surfaces
.retain(|s| s.window.id() != window_id); .retain(|s| s.window.id() != window_id);
for output in outputs_removed.into_iter() { for output in outputs_removed.into_iter() {
state.common.shell.unmap_output( state.common.shell.remove_output(
&output, &output,
&mut state.backend,
&mut state.common.config,
); );
} }
} }
@ -327,6 +330,7 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
.common .common
.output_conf .output_conf
.update(&mut *state.common.display.borrow_mut()); .update(&mut *state.common.display.borrow_mut());
state.common.shell.refresh_outputs();
surface.dirty = true; surface.dirty = true;
if !surface.pending { if !surface.pending {
surface.render.ping(); surface.render.ping();

View file

@ -1,17 +1,22 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::shell::layout::FocusDirection; use crate::{
shell::{
Shell,
layout::FocusDirection,
},
state::BackendData,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smithay::wayland::seat::Keysym;
pub use smithay::{ pub use smithay::{
backend::input::KeyState, backend::input::KeyState,
utils::{Logical, Physical, Point, Size, Transform}, utils::{Logical, Physical, Point, Size, Transform},
wayland::{ wayland::{
output::Output, output::{Mode, Output},
seat::{keysyms as KeySyms, ModifiersState as KeyModifiers}, seat::{keysyms as KeySyms, Keysym, ModifiersState as KeyModifiers},
}, },
}; };
use std::{collections::HashMap, fs::OpenOptions, path::PathBuf}; use std::{cell::RefCell, collections::HashMap, fs::OpenOptions, path::PathBuf};
use xkbcommon::xkb; use xkbcommon::xkb;
pub struct Config { pub struct Config {
@ -89,6 +94,13 @@ impl OutputConfig {
pub fn mode_refresh(&self) -> u32 { pub fn mode_refresh(&self) -> u32 {
self.mode.1.unwrap_or(60_000) self.mode.1.unwrap_or(60_000)
} }
pub fn output_mode(&self) -> Mode {
Mode {
size: self.mode_size(),
refresh: self.mode_refresh() as i32,
}
}
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -175,6 +187,103 @@ impl Config {
config: HashMap::new(), config: HashMap::new(),
} }
} }
pub fn read_outputs<'a>(
&mut self,
outputs: impl Iterator<Item=impl std::borrow::Borrow<Output>>,
backend: &mut BackendData,
shell: &mut Shell,
) {
let outputs = outputs.map(|x| x.borrow().clone()).collect::<Vec<_>>();
let mut infos = outputs
.iter()
.cloned()
.map(Into::<crate::config::OutputInfo>::into)
.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| {
output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow()
.clone()
})
.collect::<Vec<_>>();
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();
*output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut() = output_config;
if let Err(err) = backend.apply_config_for_output(&output, false, shell) {
slog_scope::warn!(
"Failed to set new config for output {}: {}",
output.name(),
err
);
reset = true;
break;
}
}
if reset {
for (output, output_config) in outputs
.clone()
.into_iter()
.zip(known_good_configs.into_iter())
{
*output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut() = output_config;
if let Err(err) = backend.apply_config_for_output(&output, false, shell)
{
slog_scope::error!(
"Failed to reset config for output {}: {}",
output.name(),
err
);
}
}
}
}
}
pub fn write_outputs<'a>(&mut self, outputs: impl Iterator<Item=impl std::borrow::Borrow<Output>>) {
let mut infos = outputs
.map(|o| {
let o = o.borrow();
(
Into::<crate::config::OutputInfo>::into(o.clone()),
o.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow()
.clone(),
)
})
.collect::<Vec<(OutputInfo, OutputConfig)>>();
infos.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
let (infos, configs) = infos.into_iter().unzip();
self
.dynamic_conf
.outputs_mut()
.config
.insert(infos, configs);
}
} }
pub struct PersistenceGuard<'a, T: Serialize>(Option<PathBuf>, &'a mut T); pub struct PersistenceGuard<'a, T: Serialize>(Option<PathBuf>, &'a mut T);

View file

@ -1,9 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::{ use crate::{
config::{Config, OutputConfig, OutputInfo}, config::{Config, OutputConfig},
input::active_output, input::active_output,
state::{BackendData, Common}, state::Common,
}; };
pub use smithay::{ pub use smithay::{
desktop::{PopupGrab, PopupManager, PopupUngrabStrategy, Space, Window}, desktop::{PopupGrab, PopupManager, PopupUngrabStrategy, Space, Window},
@ -92,129 +92,38 @@ impl Shell {
} }
} }
fn refresh_config(&mut self, backend: &mut BackendData, config: &mut Config) -> bool { pub fn refresh_outputs(&mut self) {
let mut infos = self if let Mode::Global { active } = self.mode {
.outputs() let workspace = &mut self.spaces[active];
.cloned() for output in self.outputs.iter() {
.map(Into::<crate::config::OutputInfo>::into) let config = output
.collect::<Vec<_>>();
infos.sort();
if let Some(configs) = config.dynamic_conf.outputs().config.get(&infos).cloned() {
let mut reset = false;
let known_good_configs = self
.outputs
.iter()
.map(|output| {
output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow()
.clone()
})
.collect::<Vec<_>>();
for (name, output_config) in infos.iter().map(|o| &o.connector).zip(configs.into_iter())
{
let output = self
.outputs
.iter()
.find(|o| &o.name() == name)
.unwrap()
.clone();
*output
.user_data() .user_data()
.get::<RefCell<OutputConfig>>() .get::<RefCell<OutputConfig>>()
.unwrap() .unwrap()
.borrow_mut() = output_config; .borrow();
if let Err(err) = backend.apply_config_for_output(&output, false, config, self) { workspace
slog_scope::warn!( .space
"Failed to set new config for output {}: {}", .map_output(output, config.scale, config.position);
output.name(),
err
);
reset = true;
break;
}
} }
if reset {
for (output, output_config) in self
.outputs
.clone()
.into_iter()
.zip(known_good_configs.into_iter())
{
*output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut() = output_config;
if let Err(err) = backend.apply_config_for_output(&output, false, config, self)
{
slog_scope::error!(
"Failed to reset config for output {}: {}",
output.name(),
err
);
}
}
}
if let Mode::Global { active } = self.mode {
let workspace = &mut self.spaces[active];
for output in self.outputs.iter() {
let config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow();
workspace
.space
.map_output(output, config.scale, config.position);
}
} else {
for output in self.outputs.iter() {
let config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow();
let workspace = Self::assign_next_free_output(&mut self.spaces, output);
workspace.space.map_output(output, config.scale, (0, 0));
}
}
true
} else { } else {
false for output in self.outputs.iter() {
let config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow();
let active = output
.user_data()
.get::<ActiveWorkspace>()
.unwrap()
.get()
.unwrap();
let workspace = &mut self.spaces[active];
workspace.space.map_output(output, config.scale, (0, 0));
}
} }
} }
pub fn save_config(&self, config: &mut Config) {
let mut infos = self
.outputs()
.cloned()
.map(|o| {
(
Into::<crate::config::OutputInfo>::into(o.clone()),
o.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow()
.clone(),
)
})
.collect::<Vec<(OutputInfo, OutputConfig)>>();
infos.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
let (infos, configs) = infos.into_iter().unzip();
config
.dynamic_conf
.outputs_mut()
.config
.insert(infos, configs);
}
fn assign_next_free_output<'a>( fn assign_next_free_output<'a>(
spaces: &'a mut [Workspace], spaces: &'a mut [Workspace],
output: &Output, output: &Output,
@ -236,7 +145,8 @@ impl Shell {
workspace workspace
} }
fn add_output(&mut self, output: &Output) { pub fn add_output(&mut self, output: &Output) {
self.outputs.push(output.clone());
let config = output let config = output
.user_data() .user_data()
.get::<RefCell<OutputConfig>>() .get::<RefCell<OutputConfig>>()
@ -255,9 +165,10 @@ impl Shell {
.map_output(output, config.scale, config.position); .map_output(output, config.scale, config.position);
} }
} }
output.change_current_state(None, None, Some(config.scale.ceil() as i32), None);
} }
fn remove_output(&mut self, output: &Output) { pub fn remove_output(&mut self, output: &Output) {
match self.mode { match self.mode {
Mode::OutputBound => { Mode::OutputBound => {
if let Some(idx) = output if let Some(idx) = output
@ -277,65 +188,6 @@ impl Shell {
} }
} }
pub fn map_output(&mut self, output: &Output, backend: &mut BackendData, config: &mut Config) {
self.outputs.push(output.clone());
if !self.refresh_config(backend, config) {
let new_pos_x = self
.outputs()
.take(self.outputs.len() - 1)
.map(|o| {
let logical_size = self
.active_space(o)
.space
.output_geometry(o)
.map(|x| x.size)
.unwrap_or((0, 0).into());
o.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow()
.position
.0
+ logical_size.w
})
.max()
.unwrap_or(0);
let new_pos = (new_pos_x, 0);
output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut()
.position = new_pos;
output.change_current_state(None, None, None, Some(new_pos.into()));
self.add_output(output);
self.save_config(config);
}
}
pub fn unmap_output(
&mut self,
output: &Output,
backend: &mut BackendData,
config: &mut Config,
) {
self.remove_output(output);
self.refresh_config(backend, config);
}
pub fn enable_output(&mut self, output: &Output, config: &mut Config) {
self.outputs.push(output.clone());
self.add_output(output);
self.save_config(config);
}
pub fn disable_output(&mut self, output: &Output, config: &mut Config) {
self.remove_output(output);
self.save_config(config);
}
pub fn output_size(&self, output: &Output) -> Size<i32, Logical> { pub fn output_size(&self, output: &Output) -> Size<i32, Logical> {
let workspace = self.active_space(output); let workspace = self.active_space(output);
workspace workspace

View file

@ -116,12 +116,11 @@ impl BackendData {
&mut self, &mut self,
output: &Output, output: &Output,
test_only: bool, test_only: bool,
config: &mut Config,
shell: &mut Shell, shell: &mut Shell,
) -> Result<(), anyhow::Error> { ) -> Result<(), anyhow::Error> {
let result = match self { let result = match self {
BackendData::Kms(ref mut state) => { BackendData::Kms(ref mut state) => {
state.apply_config_for_output(output, config, shell, test_only) state.apply_config_for_output(output, shell, test_only)
} }
BackendData::Winit(ref mut state) => state.apply_config_for_output(output, test_only), 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::X11(ref mut state) => state.apply_config_for_output(output, test_only),
@ -150,7 +149,6 @@ impl BackendData {
let location = let location =
Some(final_config.position.into()).filter(|x| *x != output.current_location()); Some(final_config.position.into()).filter(|x| *x != output.current_location());
output.change_current_state(mode, transform, scale, location); output.change_current_state(mode, transform, scale, location);
shell.save_config(config);
} }
result result
@ -263,7 +261,6 @@ impl State {
if let Err(err) = state.backend.apply_config_for_output( if let Err(err) = state.backend.apply_config_for_output(
output, output,
test_only, test_only,
&mut state.common.config,
&mut state.common.shell, &mut state.common.shell,
) { ) {
slog_scope::warn!( slog_scope::warn!(
@ -284,7 +281,6 @@ impl State {
if let Err(err) = state.backend.apply_config_for_output( if let Err(err) = state.backend.apply_config_for_output(
output, output,
false, false,
&mut state.common.config,
&mut state.common.shell, &mut state.common.shell,
) { ) {
slog_scope::error!( slog_scope::error!(
@ -297,6 +293,7 @@ impl State {
} }
return false; return false;
} }
} }
for output in conf.iter().filter(|(_, c)| c.is_some()).map(|(o, _)| o) { for output in conf.iter().filter(|(_, c)| c.is_some()).map(|(o, _)| o) {
@ -305,6 +302,7 @@ impl State {
for output in conf.iter().filter(|(_, c)| c.is_none()).map(|(o, _)| o) { for output in conf.iter().filter(|(_, c)| c.is_none()).map(|(o, _)| o) {
wlr_configuration::disable_head(output); wlr_configuration::disable_head(output);
} }
state.common.config.write_outputs(state.common.output_conf.outputs());
state.common.event_loop_handle.insert_idle(move |state| { state.common.event_loop_handle.insert_idle(move |state| {
state state
.common .common