mirror of
https://github.com/YaLTeR/niri.git
synced 2025-10-05 16:12:47 +02:00
Add per-output layout config
This commit is contained in:
@@ -791,6 +791,7 @@ mod tests {
|
|||||||
bottom_right: true,
|
bottom_right: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
layout: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
use niri_ipc::{ConfiguredMode, Transform};
|
use niri_ipc::{ConfiguredMode, Transform};
|
||||||
|
|
||||||
use crate::gestures::HotCorners;
|
use crate::gestures::HotCorners;
|
||||||
use crate::{Color, FloatOrInt};
|
use crate::{Color, FloatOrInt, LayoutPart};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq)]
|
#[derive(Debug, Default, Clone, PartialEq)]
|
||||||
pub struct Outputs(pub Vec<Output>);
|
pub struct Outputs(pub Vec<Output>);
|
||||||
@@ -24,12 +24,15 @@ pub struct Output {
|
|||||||
pub variable_refresh_rate: Option<Vrr>,
|
pub variable_refresh_rate: Option<Vrr>,
|
||||||
#[knuffel(child)]
|
#[knuffel(child)]
|
||||||
pub focus_at_startup: bool,
|
pub focus_at_startup: bool,
|
||||||
|
// Deprecated; use layout.background_color.
|
||||||
#[knuffel(child)]
|
#[knuffel(child)]
|
||||||
pub background_color: Option<Color>,
|
pub background_color: Option<Color>,
|
||||||
#[knuffel(child)]
|
#[knuffel(child)]
|
||||||
pub backdrop_color: Option<Color>,
|
pub backdrop_color: Option<Color>,
|
||||||
#[knuffel(child)]
|
#[knuffel(child)]
|
||||||
pub hot_corners: Option<HotCorners>,
|
pub hot_corners: Option<HotCorners>,
|
||||||
|
#[knuffel(child)]
|
||||||
|
pub layout: Option<LayoutPart>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Output {
|
impl Output {
|
||||||
@@ -60,6 +63,7 @@ impl Default for Output {
|
|||||||
background_color: None,
|
background_color: None,
|
||||||
backdrop_color: None,
|
backdrop_color: None,
|
||||||
hot_corners: None,
|
hot_corners: None,
|
||||||
|
layout: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -72,7 +72,7 @@ impl Layout {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut layout = niri::layout::Layout::with_options(clock.clone(), options);
|
let mut layout = niri::layout::Layout::with_options(clock.clone(), options);
|
||||||
layout.add_output(output.clone());
|
layout.add_output(output.clone(), None);
|
||||||
|
|
||||||
let start_time = clock.now_unadjusted();
|
let start_time = clock.now_unadjusted();
|
||||||
|
|
||||||
|
@@ -39,7 +39,7 @@ use std::time::Duration;
|
|||||||
use monitor::{InsertHint, InsertPosition, InsertWorkspace, MonitorAddWindowTarget};
|
use monitor::{InsertHint, InsertPosition, InsertWorkspace, MonitorAddWindowTarget};
|
||||||
use niri_config::utils::MergeWith as _;
|
use niri_config::utils::MergeWith as _;
|
||||||
use niri_config::{
|
use niri_config::{
|
||||||
Config, CornerRadius, PresetSize, Workspace as WorkspaceConfig, WorkspaceReference,
|
Config, CornerRadius, LayoutPart, PresetSize, Workspace as WorkspaceConfig, WorkspaceReference,
|
||||||
};
|
};
|
||||||
use niri_ipc::{ColumnDisplay, PositionChange, SizeChange, WindowLayout};
|
use niri_ipc::{ColumnDisplay, PositionChange, SizeChange, WindowLayout};
|
||||||
use scrolling::{Column, ColumnWidth};
|
use scrolling::{Column, ColumnWidth};
|
||||||
@@ -380,6 +380,10 @@ struct InteractiveMoveData<W: LayoutElement> {
|
|||||||
///
|
///
|
||||||
/// This helps the pointer remain inside the window as it resizes.
|
/// This helps the pointer remain inside the window as it resizes.
|
||||||
pub(self) pointer_ratio_within_window: (f64, f64),
|
pub(self) pointer_ratio_within_window: (f64, f64),
|
||||||
|
/// Config overrides for the output where the window is currently located.
|
||||||
|
///
|
||||||
|
/// Cached here to be accessible while an output is removed.
|
||||||
|
pub(self) output_config: Option<niri_config::LayoutPart>,
|
||||||
/// Config overrides for the workspace where the window is currently located.
|
/// Config overrides for the workspace where the window is currently located.
|
||||||
///
|
///
|
||||||
/// To avoid sudden window changes when starting an interactive move, it will remember the
|
/// To avoid sudden window changes when starting an interactive move, it will remember the
|
||||||
@@ -658,7 +662,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_output(&mut self, output: Output) {
|
pub fn add_output(&mut self, output: Output, layout_config: Option<LayoutPart>) {
|
||||||
self.monitor_set = match mem::take(&mut self.monitor_set) {
|
self.monitor_set = match mem::take(&mut self.monitor_set) {
|
||||||
MonitorSet::Normal {
|
MonitorSet::Normal {
|
||||||
mut monitors,
|
mut monitors,
|
||||||
@@ -700,7 +704,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
// workspaces set up across multiple monitors. Without this check, the
|
// workspaces set up across multiple monitors. Without this check, the
|
||||||
// first monitor to connect can end up with the first empty workspace
|
// first monitor to connect can end up with the first empty workspace
|
||||||
// focused instead of the first named workspace.
|
// focused instead of the first named workspace.
|
||||||
&& !(self.options.layout.empty_workspace_above_first
|
&& !(primary.options.layout.empty_workspace_above_first
|
||||||
&& primary.active_workspace_idx == 1)
|
&& primary.active_workspace_idx == 1)
|
||||||
{
|
{
|
||||||
primary.active_workspace_idx =
|
primary.active_workspace_idx =
|
||||||
@@ -731,6 +735,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
ws_id_to_activate,
|
ws_id_to_activate,
|
||||||
self.clock.clone(),
|
self.clock.clone(),
|
||||||
self.options.clone(),
|
self.options.clone(),
|
||||||
|
layout_config,
|
||||||
);
|
);
|
||||||
monitor.overview_open = self.overview_open;
|
monitor.overview_open = self.overview_open;
|
||||||
monitor.set_overview_progress(self.overview_progress.as_ref());
|
monitor.set_overview_progress(self.overview_progress.as_ref());
|
||||||
@@ -751,6 +756,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
ws_id_to_activate,
|
ws_id_to_activate,
|
||||||
self.clock.clone(),
|
self.clock.clone(),
|
||||||
self.options.clone(),
|
self.options.clone(),
|
||||||
|
layout_config,
|
||||||
);
|
);
|
||||||
monitor.overview_open = self.overview_open;
|
monitor.overview_open = self.overview_open;
|
||||||
monitor.set_overview_progress(self.overview_progress.as_ref());
|
monitor.set_overview_progress(self.overview_progress.as_ref());
|
||||||
@@ -782,10 +788,16 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
monitor.workspaces[monitor.active_workspace_idx].id(),
|
monitor.workspaces[monitor.active_workspace_idx].id(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let workspaces = monitor.into_workspaces();
|
let mut workspaces = monitor.into_workspaces();
|
||||||
|
|
||||||
if monitors.is_empty() {
|
if monitors.is_empty() {
|
||||||
// Removed the last monitor.
|
// Removed the last monitor.
|
||||||
|
|
||||||
|
for ws in &mut workspaces {
|
||||||
|
// Reset base options to layout ones.
|
||||||
|
ws.update_config(self.options.clone());
|
||||||
|
}
|
||||||
|
|
||||||
MonitorSet::NoOutputs { workspaces }
|
MonitorSet::NoOutputs { workspaces }
|
||||||
} else {
|
} else {
|
||||||
if primary_idx >= idx {
|
if primary_idx >= idx {
|
||||||
@@ -2310,6 +2322,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
|
|
||||||
let scale = move_.output.current_scale().fractional_scale();
|
let scale = move_.output.current_scale().fractional_scale();
|
||||||
let options = Options::clone(&self.options)
|
let options = Options::clone(&self.options)
|
||||||
|
.with_merged_layout(move_.output_config.as_ref())
|
||||||
.with_merged_layout(move_.workspace_config.as_ref().map(|(_, c)| c))
|
.with_merged_layout(move_.workspace_config.as_ref().map(|(_, c)| c))
|
||||||
.adjusted_for_scale(scale);
|
.adjusted_for_scale(scale);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -2409,8 +2422,8 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
for (idx, monitor) in monitors.iter().enumerate() {
|
for (idx, monitor) in monitors.iter().enumerate() {
|
||||||
assert_eq!(self.clock, monitor.clock);
|
assert_eq!(self.clock, monitor.clock);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
monitor.options, self.options,
|
monitor.base_options, self.options,
|
||||||
"monitor options must be synchronized with layout"
|
"monitor base options must be synchronized with layout"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(self.overview_open, monitor.overview_open);
|
assert_eq!(self.overview_open, monitor.overview_open);
|
||||||
@@ -2863,6 +2876,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
let view_size = output_size(&move_.output);
|
let view_size = output_size(&move_.output);
|
||||||
let scale = move_.output.current_scale().fractional_scale();
|
let scale = move_.output.current_scale().fractional_scale();
|
||||||
let options = Options::clone(&options)
|
let options = Options::clone(&options)
|
||||||
|
.with_merged_layout(move_.output_config.as_ref())
|
||||||
.with_merged_layout(move_.workspace_config.as_ref().map(|(_, c)| c))
|
.with_merged_layout(move_.workspace_config.as_ref().map(|(_, c)| c))
|
||||||
.adjusted_for_scale(scale);
|
.adjusted_for_scale(scale);
|
||||||
move_.tile.update_config(view_size, scale, Rc::new(options));
|
move_.tile.update_config(view_size, scale, Rc::new(options));
|
||||||
@@ -3786,6 +3800,11 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let output_config = self
|
||||||
|
.monitors()
|
||||||
|
.find(|mon| mon.output() == &output)
|
||||||
|
.and_then(|mon| mon.layout_config().cloned());
|
||||||
|
|
||||||
// If the pointer is currently on the window's own output, then we can animate the
|
// If the pointer is currently on the window's own output, then we can animate the
|
||||||
// window movement from its current (rubberbanded and possibly moved away) position
|
// window movement from its current (rubberbanded and possibly moved away) position
|
||||||
// to the pointer. Otherwise, we just teleport it as the layout code is not aware
|
// to the pointer. Otherwise, we just teleport it as the layout code is not aware
|
||||||
@@ -3832,6 +3851,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
let view_size = output_size(&output);
|
let view_size = output_size(&output);
|
||||||
let scale = output.current_scale().fractional_scale();
|
let scale = output.current_scale().fractional_scale();
|
||||||
let options = Options::clone(&self.options)
|
let options = Options::clone(&self.options)
|
||||||
|
.with_merged_layout(output_config.as_ref())
|
||||||
.with_merged_layout(workspace_config.as_ref().map(|(_, c)| c))
|
.with_merged_layout(workspace_config.as_ref().map(|(_, c)| c))
|
||||||
.adjusted_for_scale(scale);
|
.adjusted_for_scale(scale);
|
||||||
tile.update_config(view_size, scale, Rc::new(options));
|
tile.update_config(view_size, scale, Rc::new(options));
|
||||||
@@ -3887,6 +3907,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
is_full_width,
|
is_full_width,
|
||||||
is_floating,
|
is_floating,
|
||||||
pointer_ratio_within_window,
|
pointer_ratio_within_window,
|
||||||
|
output_config,
|
||||||
workspace_config,
|
workspace_config,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -3930,6 +3951,11 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
);
|
);
|
||||||
move_.output = output.clone();
|
move_.output = output.clone();
|
||||||
self.focus_output(&output);
|
self.focus_output(&output);
|
||||||
|
|
||||||
|
move_.output_config = self
|
||||||
|
.monitor_for_output(&output)
|
||||||
|
.and_then(|mon| mon.layout_config().cloned());
|
||||||
|
|
||||||
update_config = true;
|
update_config = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3937,6 +3963,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
let view_size = output_size(&output);
|
let view_size = output_size(&output);
|
||||||
let scale = output.current_scale().fractional_scale();
|
let scale = output.current_scale().fractional_scale();
|
||||||
let options = Options::clone(&self.options)
|
let options = Options::clone(&self.options)
|
||||||
|
.with_merged_layout(move_.output_config.as_ref())
|
||||||
.with_merged_layout(move_.workspace_config.as_ref().map(|(_, c)| c))
|
.with_merged_layout(move_.workspace_config.as_ref().map(|(_, c)| c))
|
||||||
.adjusted_for_scale(scale);
|
.adjusted_for_scale(scale);
|
||||||
move_.tile.update_config(view_size, scale, Rc::new(options));
|
move_.tile.update_config(view_size, scale, Rc::new(options));
|
||||||
@@ -4125,7 +4152,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
.position(|ws| ws.id() == ws_id)
|
.position(|ws| ws.id() == ws_id)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
InsertWorkspace::NewAt(ws_idx) => {
|
InsertWorkspace::NewAt(ws_idx) => {
|
||||||
if self.options.layout.empty_workspace_above_first && ws_idx == 0 {
|
if mon.options.layout.empty_workspace_above_first && ws_idx == 0 {
|
||||||
// Reuse the top empty workspace.
|
// Reuse the top empty workspace.
|
||||||
0
|
0
|
||||||
} else if mon.workspaces.len() - 1 <= ws_idx {
|
} else if mon.workspaces.len() - 1 <= ws_idx {
|
||||||
@@ -4454,7 +4481,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
} = &mut self.monitor_set
|
} = &mut self.monitor_set
|
||||||
{
|
{
|
||||||
let monitor = &mut monitors[*active_monitor_idx];
|
let monitor = &mut monitors[*active_monitor_idx];
|
||||||
if self.options.layout.empty_workspace_above_first
|
if monitor.options.layout.empty_workspace_above_first
|
||||||
&& monitor
|
&& monitor
|
||||||
.workspaces
|
.workspaces
|
||||||
.first()
|
.first()
|
||||||
|
@@ -3,7 +3,7 @@ use std::iter::zip;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use niri_config::CornerRadius;
|
use niri_config::{CornerRadius, LayoutPart};
|
||||||
use smithay::backend::renderer::element::utils::{
|
use smithay::backend::renderer::element::utils::{
|
||||||
CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement,
|
CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement,
|
||||||
};
|
};
|
||||||
@@ -81,8 +81,12 @@ pub struct Monitor<W: LayoutElement> {
|
|||||||
overview_progress: Option<OverviewProgress>,
|
overview_progress: Option<OverviewProgress>,
|
||||||
/// Clock for driving animations.
|
/// Clock for driving animations.
|
||||||
pub(super) clock: Clock,
|
pub(super) clock: Clock,
|
||||||
|
/// Configurable properties of the layout as received from the parent layout.
|
||||||
|
pub(super) base_options: Rc<Options>,
|
||||||
/// Configurable properties of the layout.
|
/// Configurable properties of the layout.
|
||||||
pub(super) options: Rc<Options>,
|
pub(super) options: Rc<Options>,
|
||||||
|
/// Layout config overrides for this monitor.
|
||||||
|
layout_config: Option<niri_config::LayoutPart>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -280,8 +284,12 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
mut workspaces: Vec<Workspace<W>>,
|
mut workspaces: Vec<Workspace<W>>,
|
||||||
ws_id_to_activate: Option<WorkspaceId>,
|
ws_id_to_activate: Option<WorkspaceId>,
|
||||||
clock: Clock,
|
clock: Clock,
|
||||||
options: Rc<Options>,
|
base_options: Rc<Options>,
|
||||||
|
layout_config: Option<LayoutPart>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let options =
|
||||||
|
Rc::new(Options::clone(&base_options).with_merged_layout(layout_config.as_ref()));
|
||||||
|
|
||||||
let scale = output.current_scale();
|
let scale = output.current_scale();
|
||||||
let view_size = output_size(&output);
|
let view_size = output_size(&output);
|
||||||
let working_area = compute_working_area(&output);
|
let working_area = compute_working_area(&output);
|
||||||
@@ -293,6 +301,7 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
assert!(ws.has_windows_or_name());
|
assert!(ws.has_windows_or_name());
|
||||||
|
|
||||||
ws.set_output(Some(output.clone()));
|
ws.set_output(Some(output.clone()));
|
||||||
|
ws.update_config(options.clone());
|
||||||
|
|
||||||
if ws_id_to_activate.is_some_and(|id| ws.id() == id) {
|
if ws_id_to_activate.is_some_and(|id| ws.id() == id) {
|
||||||
active_workspace_idx = idx;
|
active_workspace_idx = idx;
|
||||||
@@ -324,7 +333,9 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
overview_progress: None,
|
overview_progress: None,
|
||||||
workspace_switch: None,
|
workspace_switch: None,
|
||||||
clock,
|
clock,
|
||||||
|
base_options,
|
||||||
options,
|
options,
|
||||||
|
layout_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -666,6 +677,7 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
|
|
||||||
pub fn insert_workspace(&mut self, mut ws: Workspace<W>, mut idx: usize, activate: bool) {
|
pub fn insert_workspace(&mut self, mut ws: Workspace<W>, mut idx: usize, activate: bool) {
|
||||||
ws.set_output(Some(self.output.clone()));
|
ws.set_output(Some(self.output.clone()));
|
||||||
|
ws.update_config(self.options.clone());
|
||||||
|
|
||||||
// Don't insert past the last empty workspace.
|
// Don't insert past the last empty workspace.
|
||||||
if idx == self.workspaces.len() {
|
if idx == self.workspaces.len() {
|
||||||
@@ -699,6 +711,7 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
|
|
||||||
for ws in &mut workspaces {
|
for ws in &mut workspaces {
|
||||||
ws.set_output(Some(self.output.clone()));
|
ws.set_output(Some(self.output.clone()));
|
||||||
|
ws.update_config(self.options.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let empty_was_focused = self.active_workspace_idx == self.workspaces.len() - 1;
|
let empty_was_focused = self.active_workspace_idx == self.workspaces.len() - 1;
|
||||||
@@ -1151,7 +1164,10 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_config(&mut self, options: Rc<Options>) {
|
pub fn update_config(&mut self, base_options: Rc<Options>) {
|
||||||
|
let options =
|
||||||
|
Rc::new(Options::clone(&base_options).with_merged_layout(self.layout_config.as_ref()));
|
||||||
|
|
||||||
if self.options.layout.empty_workspace_above_first
|
if self.options.layout.empty_workspace_above_first
|
||||||
!= options.layout.empty_workspace_above_first
|
!= options.layout.empty_workspace_above_first
|
||||||
&& self.workspaces.len() > 1
|
&& self.workspaces.len() > 1
|
||||||
@@ -1171,9 +1187,21 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
self.insert_hint_element
|
self.insert_hint_element
|
||||||
.update_config(options.layout.insert_hint);
|
.update_config(options.layout.insert_hint);
|
||||||
|
|
||||||
|
self.base_options = base_options;
|
||||||
self.options = options;
|
self.options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_layout_config(&mut self, layout_config: Option<niri_config::LayoutPart>) -> bool {
|
||||||
|
if self.layout_config == layout_config {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.layout_config = layout_config;
|
||||||
|
self.update_config(self.base_options.clone());
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_shaders(&mut self) {
|
pub fn update_shaders(&mut self) {
|
||||||
for ws in &mut self.workspaces {
|
for ws in &mut self.workspaces {
|
||||||
ws.update_shaders();
|
ws.update_shaders();
|
||||||
@@ -2017,10 +2045,18 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
self.working_area
|
self.working_area
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn layout_config(&self) -> Option<&niri_config::LayoutPart> {
|
||||||
|
self.layout_config.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(super) fn verify_invariants(&self) {
|
pub(super) fn verify_invariants(&self) {
|
||||||
use approx::assert_abs_diff_eq;
|
use approx::assert_abs_diff_eq;
|
||||||
|
|
||||||
|
let options =
|
||||||
|
Options::clone(&self.base_options).with_merged_layout(self.layout_config.as_ref());
|
||||||
|
assert_eq!(&*self.options, &options);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
!self.workspaces.is_empty(),
|
!self.workspaces.is_empty(),
|
||||||
"monitor must have at least one workspace"
|
"monitor must have at least one workspace"
|
||||||
@@ -2107,7 +2143,7 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
workspace.base_options, self.options,
|
workspace.base_options, self.options,
|
||||||
"workspace options must be synchronized with layout"
|
"workspace options must be synchronized with monitor"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -406,9 +406,17 @@ enum Op {
|
|||||||
id: usize,
|
id: usize,
|
||||||
#[proptest(strategy = "arbitrary_scale()")]
|
#[proptest(strategy = "arbitrary_scale()")]
|
||||||
scale: f64,
|
scale: f64,
|
||||||
|
#[proptest(strategy = "prop::option::of(arbitrary_layout_part().prop_map(Box::new))")]
|
||||||
|
layout_config: Option<Box<niri_config::LayoutPart>>,
|
||||||
},
|
},
|
||||||
RemoveOutput(#[proptest(strategy = "1..=5usize")] usize),
|
RemoveOutput(#[proptest(strategy = "1..=5usize")] usize),
|
||||||
FocusOutput(#[proptest(strategy = "1..=5usize")] usize),
|
FocusOutput(#[proptest(strategy = "1..=5usize")] usize),
|
||||||
|
UpdateOutputLayoutConfig {
|
||||||
|
#[proptest(strategy = "1..=5usize")]
|
||||||
|
id: usize,
|
||||||
|
#[proptest(strategy = "prop::option::of(arbitrary_layout_part().prop_map(Box::new))")]
|
||||||
|
layout_config: Option<Box<niri_config::LayoutPart>>,
|
||||||
|
},
|
||||||
AddNamedWorkspace {
|
AddNamedWorkspace {
|
||||||
#[proptest(strategy = "1..=5usize")]
|
#[proptest(strategy = "1..=5usize")]
|
||||||
ws_name: usize,
|
ws_name: usize,
|
||||||
@@ -771,9 +779,13 @@ impl Op {
|
|||||||
model: None,
|
model: None,
|
||||||
serial: None,
|
serial: None,
|
||||||
});
|
});
|
||||||
layout.add_output(output.clone());
|
layout.add_output(output.clone(), None);
|
||||||
}
|
}
|
||||||
Op::AddScaledOutput { id, scale } => {
|
Op::AddScaledOutput {
|
||||||
|
id,
|
||||||
|
scale,
|
||||||
|
layout_config,
|
||||||
|
} => {
|
||||||
let name = format!("output{id}");
|
let name = format!("output{id}");
|
||||||
if layout.outputs().any(|o| o.name() == name) {
|
if layout.outputs().any(|o| o.name() == name) {
|
||||||
return;
|
return;
|
||||||
@@ -804,7 +816,7 @@ impl Op {
|
|||||||
model: None,
|
model: None,
|
||||||
serial: None,
|
serial: None,
|
||||||
});
|
});
|
||||||
layout.add_output(output.clone());
|
layout.add_output(output.clone(), layout_config.map(|x| *x));
|
||||||
}
|
}
|
||||||
Op::RemoveOutput(id) => {
|
Op::RemoveOutput(id) => {
|
||||||
let name = format!("output{id}");
|
let name = format!("output{id}");
|
||||||
@@ -822,6 +834,14 @@ impl Op {
|
|||||||
|
|
||||||
layout.focus_output(&output);
|
layout.focus_output(&output);
|
||||||
}
|
}
|
||||||
|
Op::UpdateOutputLayoutConfig { id, layout_config } => {
|
||||||
|
let name = format!("output{id}");
|
||||||
|
let Some(mon) = layout.monitors_mut().find(|m| m.output_name() == &name) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
mon.update_layout_config(layout_config.map(|x| *x));
|
||||||
|
}
|
||||||
Op::AddNamedWorkspace {
|
Op::AddNamedWorkspace {
|
||||||
ws_name,
|
ws_name,
|
||||||
output_name,
|
output_name,
|
||||||
|
30
src/niri.rs
30
src/niri.rs
@@ -1667,6 +1667,26 @@ impl State {
|
|||||||
recolored_outputs.push(output.clone());
|
recolored_outputs.push(output.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for mon in self.niri.layout.monitors_mut() {
|
||||||
|
if mon.output() != output {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut layout_config = config.and_then(|c| c.layout.clone());
|
||||||
|
// Support the deprecated non-layout background-color key.
|
||||||
|
if let Some(layout) = &mut layout_config {
|
||||||
|
if layout.background_color.is_none() {
|
||||||
|
layout.background_color = config.and_then(|c| c.background_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mon.update_layout_config(layout_config) {
|
||||||
|
// Also redraw these; if anything, the background color could've changed.
|
||||||
|
recolored_outputs.push(output.clone());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for output in resized_outputs {
|
for output in resized_outputs {
|
||||||
@@ -2903,6 +2923,14 @@ impl Niri {
|
|||||||
if name.connector == "winit" {
|
if name.connector == "winit" {
|
||||||
transform = Transform::Flipped180;
|
transform = Transform::Flipped180;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut layout_config = c.and_then(|c| c.layout.clone());
|
||||||
|
// Support the deprecated non-layout background-color key.
|
||||||
|
if let Some(layout) = &mut layout_config {
|
||||||
|
if layout.background_color.is_none() {
|
||||||
|
layout.background_color = c.and_then(|c| c.background_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
drop(config);
|
drop(config);
|
||||||
|
|
||||||
// Set scale and transform before adding to the layout since that will read the output size.
|
// Set scale and transform before adding to the layout since that will read the output size.
|
||||||
@@ -2913,7 +2941,7 @@ impl Niri {
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.layout.add_output(output.clone());
|
self.layout.add_output(output.clone(), layout_config);
|
||||||
|
|
||||||
let lock_render_state = if self.is_locked() {
|
let lock_render_state = if self.is_locked() {
|
||||||
// We haven't rendered anything yet so it's as good as locked.
|
// We haven't rendered anything yet so it's as good as locked.
|
||||||
|
Reference in New Issue
Block a user