From abf430f9564b0566f452252cb8d6dcf5ee336853 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 13 Sep 2023 20:14:54 +0200 Subject: [PATCH] shell: Rework fullscreen/maximize --- src/backend/render/mod.rs | 73 ++++++----- src/input/mod.rs | 81 +++++++----- src/shell/mod.rs | 2 +- src/shell/workspace.rs | 190 +++++++++++++++++----------- src/wayland/handlers/compositor.rs | 2 +- src/wayland/handlers/layer_shell.rs | 2 +- 6 files changed, 208 insertions(+), 142 deletions(-) diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 75e31d39..8b396905 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -528,7 +528,7 @@ where .space_for_handle(¤t.0) .ok_or(OutputNoMode)?; - let has_fullscreen = workspace.fullscreen.contains_key(output); + let has_fullscreen = workspace.fullscreen.get(output).map(|f| f.exclusive); let (overlay_elements, overlay_popups) = split_layer_elements(renderer, output, Layer::Overlay, exclude_workspace_overview); @@ -536,7 +536,7 @@ where elements.extend(overlay_popups.into_iter().map(Into::into)); elements.extend(overlay_elements.into_iter().map(Into::into)); - let mut window_elements = if !has_fullscreen { + let mut window_elements = if !has_fullscreen.unwrap_or(false) { let (top_elements, top_popups) = split_layer_elements(renderer, output, Layer::Top, exclude_workspace_overview); elements.extend(top_popups.into_iter().map(Into::into)); @@ -553,6 +553,7 @@ where .shell .space_for_handle(&previous) .ok_or(OutputNoMode)?; + let has_fullscreen = workspace.fullscreen.contains_key(output); let is_active_space = workspace.outputs().any(|o| o == &active_output); let percentage = { @@ -602,22 +603,24 @@ where )) })); - let (w_elements, p_elements) = - background_layer_elements(renderer, output, exclude_workspace_overview); - elements.extend(p_elements.into_iter().map(|p_element| { - CosmicElement::Workspace(RelocateRenderElement::from_element( - p_element, - offset.to_physical_precise_round(output_scale), - Relocate::Relative, - )) - })); - window_elements.extend(w_elements.into_iter().map(|w_element| { - CosmicElement::Workspace(RelocateRenderElement::from_element( - w_element, - offset.to_physical_precise_round(output_scale), - Relocate::Relative, - )) - })); + if !has_fullscreen { + let (w_elements, p_elements) = + background_layer_elements(renderer, output, exclude_workspace_overview); + elements.extend(p_elements.into_iter().map(|p_element| { + CosmicElement::Workspace(RelocateRenderElement::from_element( + p_element, + offset.to_physical_precise_round(output_scale), + Relocate::Relative, + )) + })); + window_elements.extend(w_elements.into_iter().map(|w_element| { + CosmicElement::Workspace(RelocateRenderElement::from_element( + w_element, + offset.to_physical_precise_round(output_scale), + Relocate::Relative, + )) + })); + } Point::::from(match (layout, *previous_idx < current.1) { (WorkspaceLayout::Vertical, true) => (0, output_size.h + offset.y), @@ -658,24 +661,26 @@ where )) })); - let (w_elements, p_elements) = - background_layer_elements(renderer, output, exclude_workspace_overview); + if has_fullscreen.is_none() { + let (w_elements, p_elements) = + background_layer_elements(renderer, output, exclude_workspace_overview); - elements.extend(p_elements.into_iter().map(|p_element| { - CosmicElement::Workspace(RelocateRenderElement::from_element( - p_element, - offset.to_physical_precise_round(output_scale), - Relocate::Relative, - )) - })); + elements.extend(p_elements.into_iter().map(|p_element| { + CosmicElement::Workspace(RelocateRenderElement::from_element( + p_element, + offset.to_physical_precise_round(output_scale), + Relocate::Relative, + )) + })); - window_elements.extend(w_elements.into_iter().map(|w_element| { - CosmicElement::Workspace(RelocateRenderElement::from_element( - w_element, - offset.to_physical_precise_round(output_scale), - Relocate::Relative, - )) - })); + window_elements.extend(w_elements.into_iter().map(|w_element| { + CosmicElement::Workspace(RelocateRenderElement::from_element( + w_element, + offset.to_physical_precise_round(output_scale), + Relocate::Relative, + )) + })); + } elements.extend(window_elements); diff --git a/src/input/mod.rs b/src/input/mod.rs index 83148a3a..ad62847c 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -733,30 +733,35 @@ impl State { } }; if !done { - if let Some((target, _)) = - workspace.element_under(relative_pos, overview.0) - { - under = Some(target); + if let Some(surface) = workspace.get_maximized(&output) { + under = Some(surface.clone().into()); } else { - let layers = layer_map_for_output(&output); - if let Some(layer) = - layers.layer_under(WlrLayer::Bottom, pos).or_else( - || layers.layer_under(WlrLayer::Background, pos), - ) + if let Some((target, _)) = + workspace.element_under(relative_pos, overview.0) { - let layer_loc = - layers.layer_geometry(layer).unwrap().loc; - if layer.can_receive_keyboard_focus() - && layer - .surface_under( - relative_pos - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) - .is_some() + under = Some(target); + } else { + let layers = layer_map_for_output(&output); + if let Some(layer) = layers + .layer_under(WlrLayer::Bottom, pos) + .or_else(|| { + layers.layer_under(WlrLayer::Background, pos) + }) { - under = Some(layer.clone().into()); - } - }; + let layer_loc = + layers.layer_geometry(layer).unwrap().loc; + if layer.can_receive_keyboard_focus() + && layer + .surface_under( + relative_pos - layer_loc.to_f64(), + WindowSurfaceType::ALL, + ) + .is_some() + { + under = Some(layer.clone().into()); + } + }; + } } } } @@ -1579,21 +1584,29 @@ impl State { { return Some((or.clone().into(), or.geometry().loc)); } - if let Some((target, loc)) = workspace.element_under(relative_pos, overview) { - return Some((target, loc + (global_pos - relative_pos).to_i32_round())); - } - { - let layers = layer_map_for_output(output); - if let Some(layer) = layers - .layer_under(WlrLayer::Bottom, relative_pos) - .or_else(|| layers.layer_under(WlrLayer::Background, relative_pos)) + if let Some(surface) = workspace.get_maximized(output) { + let offset = layer_map_for_output(output).non_exclusive_zone().loc; + return Some((surface.clone().into(), output_geo.loc + offset)); + } else { + if let Some((target, loc)) = workspace.element_under(relative_pos, overview) { + return Some((target, loc + (global_pos - relative_pos).to_i32_round())); + } { - let layer_loc = layers.layer_geometry(layer).unwrap().loc; - if layer - .surface_under(relative_pos - layer_loc.to_f64(), WindowSurfaceType::ALL) - .is_some() + let layers = layer_map_for_output(output); + if let Some(layer) = layers + .layer_under(WlrLayer::Bottom, relative_pos) + .or_else(|| layers.layer_under(WlrLayer::Background, relative_pos)) { - return Some((layer.clone().into(), output_geo.loc + layer_loc)); + let layer_loc = layers.layer_geometry(layer).unwrap().loc; + if layer + .surface_under( + relative_pos - layer_loc.to_f64(), + WindowSurfaceType::ALL, + ) + .is_some() + { + return Some((layer.clone().into(), output_geo.loc + layer_loc)); + } } } } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index f6b365ae..47a34db6 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1334,7 +1334,7 @@ impl Shell { let (window, seat) = state.common.shell.pending_windows.remove(pos); let workspace = state.common.shell.workspaces.active_mut(output); - workspace.set_fullscreen(None, output); + workspace.remove_fullscreen(output); state.common.shell.toplevel_info_state.new_toplevel(&window); state .common diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 5875f2f3..19b73e5a 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -67,7 +67,7 @@ pub struct Workspace { pub tiling_layer: TilingLayout, pub floating_layer: FloatingLayout, pub tiling_enabled: bool, - pub fullscreen: HashMap, + pub fullscreen: HashMap, pub handle: WorkspaceHandle, pub focus_stack: FocusStacks, pub pending_buffers: Vec<(ScreencopySession, BufferParams)>, @@ -75,6 +75,18 @@ pub struct Workspace { pub(super) backdrop_id: Id, } +#[derive(Debug, Clone)] +pub struct FullscreenSurface { + pub surface: CosmicSurface, + pub exclusive: bool, +} + +impl IsAlive for FullscreenSurface { + fn alive(&self) -> bool { + self.surface.alive() + } +} + #[derive(Debug, Default)] pub struct FocusStacks(HashMap, IndexSet>); @@ -145,7 +157,7 @@ impl Workspace { toplevel_info: &mut ToplevelInfoState, ) { if let Some(dead_output_window) = self.fullscreen.remove(output) { - self.unfullscreen_request(&dead_output_window); + self.unfullscreen_request(&dead_output_window.surface); } self.tiling_layer.unmap_output(output, toplevel_info); self.floating_layer.unmap_output(output, toplevel_info); @@ -243,6 +255,16 @@ impl Workspace { }) } + pub fn recalculate(&mut self, output: &Output) { + if let Some(f) = self.fullscreen.get(output) { + if !f.exclusive { + f.surface + .set_geometry(layer_map_for_output(output).non_exclusive_zone()); + } + } + self.tiling_layer.recalculate(output); + } + pub fn maximize_request(&mut self, window: &CosmicSurface, output: &Output) { if self.fullscreen.contains_key(output) { return; @@ -252,10 +274,10 @@ impl Workspace { window.set_fullscreen(false); window.set_maximized(true); - self.set_fullscreen(window, output) + self.set_fullscreen(window, output, false) } pub fn unmaximize_request(&mut self, window: &CosmicSurface) -> Option> { - if self.fullscreen.values().any(|w| w == window) { + if self.fullscreen.values().any(|w| &w.surface == window) { self.unfullscreen_request(window); self.floating_layer.unmaximize_request(window) } else { @@ -264,50 +286,69 @@ impl Workspace { } pub fn fullscreen_request(&mut self, window: &CosmicSurface, output: &Output) { - if self.fullscreen.contains_key(output) { + if self + .fullscreen + .get(output) + .map(|w| &w.surface != window) + .unwrap_or(false) + { return; } + if !window.is_maximized(true) { + self.floating_layer.maximize_request(window); + } + window.set_maximized(false); window.set_fullscreen(true); - self.set_fullscreen(window, output) + self.set_fullscreen(window, output, true) } - pub(super) fn set_fullscreen<'a>( - &mut self, - window: impl Into>, - output: &Output, - ) { - match window.into() { - Some(window) => { - if let Some(mapped) = self - .mapped() - .find(|m| m.windows().any(|(w, _)| &w == window)) - { - mapped.set_active(window); - } - - window.set_geometry(output.geometry()); - window.send_configure(); - self.fullscreen.insert(output.clone(), window.clone()); - } - None => { - if let Some(surface) = self.fullscreen.get(output).cloned() { - self.unfullscreen_request(&surface); - } - } + fn set_fullscreen<'a>(&mut self, window: &'a CosmicSurface, output: &Output, exclusive: bool) { + if let Some(mapped) = self + .mapped() + .find(|m| m.windows().any(|(w, _)| &w == window)) + { + mapped.set_active(window); } + + window.set_geometry(if exclusive { + output.geometry() + } else { + layer_map_for_output(output).non_exclusive_zone() + }); + window.send_configure(); + self.fullscreen.insert( + output.clone(), + FullscreenSurface { + surface: window.clone(), + exclusive, + }, + ); } pub fn unfullscreen_request(&mut self, window: &CosmicSurface) { - if let Some((output, _)) = self.fullscreen.iter().find(|(_, w)| *w == window) { + if let Some((output, _)) = self.fullscreen.iter().find(|(_, w)| &w.surface == window) { window.set_maximized(false); window.set_fullscreen(false); + self.floating_layer.unmaximize_request(window); self.floating_layer.refresh(); self.tiling_layer.recalculate(output); self.tiling_layer.refresh(); window.send_configure(); - self.fullscreen.retain(|_, w| w != window); + self.fullscreen.retain(|_, w| &w.surface != window); + } + } + + pub fn remove_fullscreen(&mut self, output: &Output) { + if let Some(FullscreenSurface { surface, .. }) = self.fullscreen.remove(output) { + surface.set_maximized(false); + surface.set_fullscreen(false); + self.floating_layer.unmaximize_request(&surface); + self.floating_layer.refresh(); + self.tiling_layer.recalculate(output); + self.tiling_layer.refresh(); + surface.send_configure(); } } @@ -320,7 +361,17 @@ impl Workspace { } pub fn get_fullscreen(&self, output: &Output) -> Option<&CosmicSurface> { - self.fullscreen.get(output).filter(|w| w.alive()) + self.fullscreen + .get(output) + .filter(|w| w.alive() && w.exclusive) + .map(|w| &w.surface) + } + + pub fn get_maximized(&self, output: &Output) -> Option<&CosmicSurface> { + self.fullscreen + .get(output) + .filter(|w| w.alive() && !w.exclusive) + .map(|w| &w.surface) } pub fn resize_request( @@ -354,7 +405,7 @@ impl Workspace { if self .fullscreen .values() - .any(|surface| surface.wl_surface().as_ref() == Some(&toplevel)) + .any(|f| f.surface.wl_surface().as_ref() == Some(&toplevel)) { return false; } @@ -472,14 +523,20 @@ impl Workspace { pub fn is_fullscreen(&self, mapped: &CosmicMapped) -> bool { self.fullscreen .values() - .any(|s| s == &mapped.active_window()) + .any(|f| f.exclusive && f.surface == mapped.active_window()) + } + + pub fn is_maximized(&self, mapped: &CosmicMapped) -> bool { + self.fullscreen + .values() + .any(|f| !f.exclusive && f.surface == mapped.active_window()) } pub fn is_floating(&self, mapped: &CosmicMapped) -> bool { !self .fullscreen .values() - .any(|s| s == &mapped.active_window()) + .any(|f| f.surface == mapped.active_window()) && self.floating_layer.mapped().any(|m| m == mapped) } @@ -487,7 +544,7 @@ impl Workspace { !self .fullscreen .values() - .any(|s| s == &mapped.active_window()) + .any(|f| f.surface == mapped.active_window()) && self.tiling_layer.mapped().any(|(_, m, _)| m == mapped) } @@ -563,30 +620,38 @@ impl Workspace { let layer_map = layer_map_for_output(output); let zone = layer_map.non_exclusive_zone(); - if let Some(fullscreen) = self.fullscreen.get(output) { - popup_elements.extend( - override_redirect_windows - .iter() - .filter(|or| (*or).geometry().intersection(output.geometry()).is_some()) - .flat_map(|or| { - AsRenderElements::::render_elements::>( - or, - renderer, - (or.geometry().loc - output.geometry().loc) - .to_physical_precise_round(output_scale), - Scale::from(output_scale), - 1.0, - ) - }), - ); + // OR windows above all + popup_elements.extend( + override_redirect_windows + .iter() + .filter(|or| (*or).geometry().intersection(output.geometry()).is_some()) + .flat_map(|or| { + AsRenderElements::::render_elements::>( + or, + renderer, + (or.geometry().loc - output.geometry().loc) + .to_physical_precise_round(output_scale), + Scale::from(output_scale), + 1.0, + ) + }), + ); + if let Some(fullscreen) = self.fullscreen.get(output) { // fullscreen window window_elements.extend(AsRenderElements::::render_elements::< WorkspaceRenderElement, >( - fullscreen, + &fullscreen.surface, renderer, - (0, 0).into(), + if fullscreen.exclusive { + (0, 0).into() + } else { + layer_map + .non_exclusive_zone() + .loc + .to_physical_precise_round(output_scale) + }, output_scale.into(), 1.0, )); @@ -603,23 +668,6 @@ impl Workspace { } } } else { - // OR windows above all - popup_elements.extend( - override_redirect_windows - .iter() - .filter(|or| (*or).geometry().intersection(output.geometry()).is_some()) - .flat_map(|or| { - AsRenderElements::::render_elements::>( - or, - renderer, - (or.geometry().loc - output.geometry().loc) - .to_physical_precise_round(output_scale), - Scale::from(output_scale), - 1.0, - ) - }), - ); - let focused = draw_focus_indicator.and_then(|seat| self.focus_stack.get(seat).last().cloned()); diff --git a/src/wayland/handlers/compositor.rs b/src/wayland/handlers/compositor.rs index 5eed36a6..806c4529 100644 --- a/src/wayland/handlers/compositor.rs +++ b/src/wayland/handlers/compositor.rs @@ -251,7 +251,7 @@ impl CompositorHandler for State { let changed = layer_map_for_output(&output).arrange(); if changed { for workspace in self.common.shell.workspaces.spaces_mut() { - workspace.tiling_layer.recalculate(&output); + workspace.recalculate(&output); } } } diff --git a/src/wayland/handlers/layer_shell.rs b/src/wayland/handlers/layer_shell.rs index febf8c65..f2726921 100644 --- a/src/wayland/handlers/layer_shell.rs +++ b/src/wayland/handlers/layer_shell.rs @@ -76,7 +76,7 @@ impl WlrLayerShellHandler for State { } for workspace in self.common.shell.workspaces.spaces_mut() { - workspace.tiling_layer.recalculate(&output); + workspace.recalculate(&output); } // collect screencopy sessions needing an update