diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2025-03-17 14:56:29 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2025-03-17 22:31:19 -0700 |
| commit | 39f52b75856936db01325aa1c7f15fe8f379485d (patch) | |
| tree | 08fcbf8c938aa2b10a9be3014bf292f17225063d /src/layout | |
| parent | b447b1f4de65ee706bdbbdb9b650ab030459c0fb (diff) | |
| download | niri-39f52b75856936db01325aa1c7f15fe8f379485d.tar.gz niri-39f52b75856936db01325aa1c7f15fe8f379485d.tar.bz2 niri-39f52b75856936db01325aa1c7f15fe8f379485d.zip | |
Implement toggle-windowed-fullscreen
Windowed, or fake, or detached, fullscreen, is when a window thinks that it's
fullscreen, but the compositor treats it as a normal window.
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/mod.rs | 41 | ||||
| -rw-r--r-- | src/layout/tests.rs | 75 |
2 files changed, 116 insertions, 0 deletions
diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 15d48d6a..64e6877c 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -244,6 +244,13 @@ pub trait LayoutElement { Some(requested) } + fn is_pending_windowed_fullscreen(&self) -> bool { + false + } + fn request_windowed_fullscreen(&mut self, value: bool) { + let _ = value; + } + fn is_child_of(&self, parent: &Self) -> bool; fn rules(&self) -> &ResolvedWindowRules; @@ -3458,6 +3465,20 @@ impl<W: LayoutElement> Layout<W> { } pub fn set_fullscreen(&mut self, id: &W::Id, is_fullscreen: bool) { + // Check if this is a request to unset the windowed fullscreen state. + if !is_fullscreen { + let mut handled = false; + self.with_windows_mut(|window, _| { + if window.id() == id && window.is_pending_windowed_fullscreen() { + window.request_windowed_fullscreen(false); + handled = true; + } + }); + if handled { + return; + } + } + if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move { if move_.tile.window().id() == id { return; @@ -3487,6 +3508,26 @@ impl<W: LayoutElement> Layout<W> { } } + pub fn toggle_windowed_fullscreen(&mut self, id: &W::Id) { + let (_, window) = self.windows().find(|(_, win)| win.id() == id).unwrap(); + if window.is_pending_fullscreen() { + // Remove the real fullscreen. + for ws in self.workspaces_mut() { + if ws.has_window(id) { + ws.set_fullscreen(id, false); + break; + } + } + } + + // This will switch is_pending_fullscreen() to false right away. + self.with_windows_mut(|window, _| { + if window.id() == id { + window.request_windowed_fullscreen(!window.is_pending_windowed_fullscreen()); + } + }); + } + pub fn workspace_switch_gesture_begin(&mut self, output: &Output, is_touchpad: bool) { let monitors = match &mut self.monitor_set { MonitorSet::Normal { monitors, .. } => monitors, diff --git a/src/layout/tests.rs b/src/layout/tests.rs index 266b615e..8e1c3adf 100644 --- a/src/layout/tests.rs +++ b/src/layout/tests.rs @@ -29,6 +29,8 @@ struct TestWindowInner { pending_fullscreen: Cell<bool>, pending_activated: Cell<bool>, is_fullscreen: Cell<bool>, + is_windowed_fullscreen: Cell<bool>, + is_pending_windowed_fullscreen: Cell<bool>, } #[derive(Debug, Clone)] @@ -72,6 +74,8 @@ impl TestWindow { pending_fullscreen: Cell::new(false), pending_activated: Cell::new(false), is_fullscreen: Cell::new(false), + is_windowed_fullscreen: Cell::new(false), + is_pending_windowed_fullscreen: Cell::new(false), })) } @@ -101,6 +105,13 @@ impl TestWindow { changed = true; } + if self.0.is_windowed_fullscreen.get() != self.0.is_pending_windowed_fullscreen.get() { + self.0 + .is_windowed_fullscreen + .set(self.0.is_pending_windowed_fullscreen.get()); + changed = true; + } + changed } } @@ -144,6 +155,10 @@ impl LayoutElement for TestWindow { ) { self.0.requested_size.set(Some(size)); self.0.pending_fullscreen.set(is_fullscreen); + + if is_fullscreen { + self.0.is_pending_windowed_fullscreen.set(false); + } } fn min_size(&self) -> Size<i32, Logical> { @@ -191,10 +206,18 @@ impl LayoutElement for TestWindow { fn set_floating(&mut self, _floating: bool) {} fn is_fullscreen(&self) -> bool { + if self.0.is_windowed_fullscreen.get() { + return false; + } + self.0.is_fullscreen.get() } fn is_pending_fullscreen(&self) -> bool { + if self.0.is_pending_windowed_fullscreen.get() { + return false; + } + self.0.pending_fullscreen.get() } @@ -202,6 +225,14 @@ impl LayoutElement for TestWindow { self.0.requested_size.get() } + fn is_pending_windowed_fullscreen(&self) -> bool { + self.0.is_pending_windowed_fullscreen.get() + } + + fn request_windowed_fullscreen(&mut self, value: bool) { + self.0.is_pending_windowed_fullscreen.set(value); + } + fn is_child_of(&self, parent: &Self) -> bool { self.0.parent_id.get() == Some(parent.0.id) } @@ -374,6 +405,7 @@ enum Op { window: usize, is_fullscreen: bool, }, + ToggleWindowedFullscreen(#[proptest(strategy = "1..=5usize")] usize), FocusColumnLeft, FocusColumnRight, FocusColumnFirst, @@ -901,14 +933,26 @@ impl Op { layout.remove_window(&id, Transaction::new()); } Op::FullscreenWindow(id) => { + if !layout.has_window(&id) { + return; + } layout.toggle_fullscreen(&id); } Op::SetFullscreenWindow { window, is_fullscreen, } => { + if !layout.has_window(&window) { + return; + } layout.set_fullscreen(&window, is_fullscreen); } + Op::ToggleWindowedFullscreen(id) => { + if !layout.has_window(&id) { + return; + } + layout.toggle_windowed_fullscreen(&id); + } Op::FocusColumnLeft => layout.focus_left(), Op::FocusColumnRight => layout.focus_right(), Op::FocusColumnFirst => layout.focus_column_first(), @@ -3187,6 +3231,37 @@ fn unfullscreen_with_large_border() { check_ops_with_options(options, &ops); } +#[test] +fn fullscreen_to_windowed_fullscreen() { + let ops = [ + Op::AddOutput(0), + Op::AddWindow { + params: TestWindowParams::new(0), + }, + Op::FullscreenWindow(0), + Op::Communicate(0), // Make sure it goes into fullscreen. + Op::ToggleWindowedFullscreen(0), + ]; + + check_ops(&ops); +} + +#[test] +fn windowed_fullscreen_to_fullscreen() { + let ops = [ + Op::AddOutput(0), + Op::AddWindow { + params: TestWindowParams::new(0), + }, + Op::FullscreenWindow(0), + Op::Communicate(0), // Commit fullscreen state. + Op::ToggleWindowedFullscreen(0), // Switch is_fullscreen() to false. + Op::FullscreenWindow(0), // Switch is_fullscreen() back to true. + ]; + + check_ops(&ops); +} + fn parent_id_causes_loop(layout: &Layout<TestWindow>, id: usize, mut parent_id: usize) -> bool { if parent_id == id { return true; |
