diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 598d7242..72b1637d 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -1093,8 +1093,8 @@ fn render_node_for_output( ) -> DrmNode { let workspace = shell.active_space(output); let nodes = workspace - .get_fullscreen(output) - .map(|w| vec![w.surface()]) + .get_fullscreen() + .map(|w| vec![w.clone()]) .unwrap_or_else(|| workspace.windows().collect::>()) .into_iter() .flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s, dh))) diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 97437b1c..725c6b9e 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -492,15 +492,11 @@ where let (resize_mode, resize_indicator) = state.shell.resize_mode(); let resize_indicator = resize_indicator.map(|indicator| (resize_mode, indicator)); let swap_tree = if let OverviewMode::Started(Trigger::KeyboardSwap(_, desc), _) = &overview.0 { - if let Some(desc_output) = desc.output.upgrade() { - if output != &desc_output || current.0 != desc.handle { - state - .shell - .space_for_handle(&desc.handle) - .and_then(|w| w.tiling_layer.tree_for_output(&desc_output)) - } else { - None - } + if current.0 != desc.handle { + state + .shell + .space_for_handle(&desc.handle) + .map(|w| w.tiling_layer.tree()) } else { None } @@ -530,9 +526,9 @@ where let has_fullscreen = workspace .fullscreen - .get(output) + .as_ref() .filter(|f| !f.is_animating()) - .map(|f| f.exclusive); + .is_some(); let (overlay_elements, overlay_popups) = split_layer_elements(renderer, output, Layer::Overlay, exclude_workspace_overview); @@ -540,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.unwrap_or(false) { + let mut window_elements = if !has_fullscreen { let (top_elements, top_popups) = split_layer_elements(renderer, output, Layer::Top, exclude_workspace_overview); elements.extend(top_popups.into_iter().map(Into::into)); @@ -557,7 +553,7 @@ where .shell .space_for_handle(&previous) .ok_or(OutputNoMode)?; - let has_fullscreen = workspace.fullscreen.contains_key(output); + let has_fullscreen = workspace.fullscreen.is_some(); let is_active_space = workspace.outputs().any(|o| o == &active_output); let percentage = { @@ -581,9 +577,8 @@ where }); let (w_elements, p_elements) = workspace - .render_output::( + .render::( renderer, - output, &state.shell.override_redirect_windows, state.xwayland_state.as_mut(), (!move_active && is_active_space).then_some(&last_active_seat), @@ -639,9 +634,8 @@ where let is_active_space = workspace.outputs().any(|o| o == &active_output); let (w_elements, p_elements) = workspace - .render_output::( + .render::( renderer, - output, &state.shell.override_redirect_windows, state.xwayland_state.as_mut(), (!move_active && is_active_space).then_some(&last_active_seat), @@ -665,7 +659,7 @@ where )) })); - if has_fullscreen.is_none() { + if !has_fullscreen { let (w_elements, p_elements) = background_layer_elements(renderer, output, exclude_workspace_overview); diff --git a/src/input/mod.rs b/src/input/mod.rs index cbc1f262..e7bae32b 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -39,7 +39,7 @@ use smithay::{ input::event::pointer::PointerAxisEvent as LibinputPointerAxisEvent, wayland_server::DisplayHandle, }, - utils::{Logical, Point, Rectangle, Serial, SERIAL_COUNTER}, + utils::{Point, Serial, SERIAL_COUNTER}, wayland::{ keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat, pointer_constraints::{with_pointer_constraint, PointerConstraint}, @@ -1539,9 +1539,6 @@ impl State { Action::Move(direction) => { let current_output = seat.active_output(); let workspace = self.common.shell.active_space_mut(¤t_output); - if workspace.get_fullscreen(¤t_output).is_some() { - return; // TODO, is this what we want? How do we indicate the switch? - } match workspace.move_current_element(direction, seat) { MoveResult::MoveFurther(_move_further) => { @@ -1602,7 +1599,7 @@ impl State { Action::SwapWindow => { let current_output = seat.active_output(); let workspace = self.common.shell.active_space_mut(¤t_output); - if workspace.get_fullscreen(¤t_output).is_some() { + if workspace.get_fullscreen().is_some() { return; // TODO, is this what we want? Maybe disengage fullscreen instead? } @@ -1624,11 +1621,7 @@ impl State { let focus_stack = workspace.focus_stack.get(seat); let focused_window = focus_stack.last(); if let Some(window) = focused_window.map(|f| f.active_window()) { - workspace.maximize_toggle( - &window, - ¤t_output, - self.common.event_loop_handle.clone(), - ); + workspace.maximize_toggle(&window); } } Action::Resizing(direction) => self.common.shell.set_resize_mode( @@ -1791,7 +1784,7 @@ impl State { fn sessions_for_output(state: &Common, output: &Output) -> impl Iterator { let workspace = state.shell.active_space(&output); - let maybe_fullscreen = workspace.get_fullscreen(&output); + let maybe_fullscreen = workspace.get_fullscreen(); workspace .screencopy_sessions .iter() @@ -1800,7 +1793,7 @@ fn sessions_for_output(state: &Common, output: &Output) -> impl Iterator() { + if let Some(sessions) = w.user_data().get::() { Some( sessions .0 diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 361a301a..78df78ba 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -74,7 +74,7 @@ use tracing::debug; use super::{ focus::FocusDirection, layout::{floating::ResizeState, tiling::NodeDesc}, - Direction, + Direction, ManagedLayer, }; space_elements! { @@ -84,12 +84,19 @@ space_elements! { Stack=CosmicStack, } +#[derive(Debug, Clone)] +pub struct MaximizedState { + pub original_geometry: Rectangle, + pub original_layer: ManagedLayer, +} + #[derive(Clone)] pub struct CosmicMapped { element: CosmicMappedInternal, // associated data last_cursor_position: Arc>>>, + pub maximized_state: Arc>>, //tiling pub tiling_node_id: Arc>>, @@ -106,6 +113,7 @@ impl fmt::Debug for CosmicMapped { f.debug_struct("CosmicMapped") .field("element", &self.element) .field("last_cursor_position", &self.last_cursor_position) + .field("maximized_state", &self.maximized_state) .field("tiling_node_id", &self.tiling_node_id) .field("resize_state", &self.resize_state) .field("last_geometry", &self.last_geometry) @@ -1064,6 +1072,7 @@ impl From for CosmicMapped { CosmicMapped { element: CosmicMappedInternal::Window(w), last_cursor_position: Arc::new(Mutex::new(HashMap::new())), + maximized_state: Arc::new(Mutex::new(None)), tiling_node_id: Arc::new(Mutex::new(None)), resize_state: Arc::new(Mutex::new(None)), last_geometry: Arc::new(Mutex::new(None)), @@ -1078,6 +1087,7 @@ impl From for CosmicMapped { CosmicMapped { element: CosmicMappedInternal::Stack(s), last_cursor_position: Arc::new(Mutex::new(HashMap::new())), + maximized_state: Arc::new(Mutex::new(None)), tiling_node_id: Arc::new(Mutex::new(None)), resize_state: Arc::new(Mutex::new(None)), last_geometry: Arc::new(Mutex::new(None)), diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 92588f29..1e45ed0b 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -241,27 +241,20 @@ impl Program for CosmicWindowInternal { } } Message::Maximize => { - if let Some((seat, _serial)) = self.last_seat.lock().unwrap().clone() { - if let Some(surface) = self.window.wl_surface() { - loop_handle.insert_idle(move |state| { - if let Some(mapped) = - state.common.shell.element_for_wl_surface(&surface).cloned() - { - if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { - let output = seat.active_output(); - let (window, _) = mapped - .windows() - .find(|(w, _)| w.wl_surface().as_ref() == Some(&surface)) - .unwrap(); - workspace.maximize_toggle( - &window, - &output, - state.common.event_loop_handle.clone(), - ) - } + if let Some(surface) = self.window.wl_surface() { + loop_handle.insert_idle(move |state| { + if let Some(mapped) = + state.common.shell.element_for_wl_surface(&surface).cloned() + { + if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { + let (window, _) = mapped + .windows() + .find(|(w, _)| w.wl_surface().as_ref() == Some(&surface)) + .unwrap(); + workspace.maximize_toggle(&window) } - }); - } + } + }); } } Message::Close => self.window.close(), diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index bc3f5e6a..1e4b588d 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -100,11 +100,9 @@ impl Shell { // update FocusStack and notify layouts about new focus (if any window) let element = match target { Some(KeyboardFocusTarget::Element(mapped)) => Some(mapped.clone()), - Some(KeyboardFocusTarget::Fullscreen(window)) => state - .common - .shell - .element_for_surface(&window.surface()) - .cloned(), + Some(KeyboardFocusTarget::Fullscreen(window)) => { + state.common.shell.element_for_surface(window).cloned() + } _ => None, }; @@ -237,7 +235,7 @@ impl Common { let workspace = state.common.shell.active_space(&output); let focus_stack = workspace.focus_stack.get(&seat); if focus_stack.last().map(|m| m == &mapped).unwrap_or(false) - && workspace.get_fullscreen(&output).is_none() + && workspace.get_fullscreen().is_none() { continue; // Focus is valid } else { @@ -268,9 +266,9 @@ impl Common { if focus_stack .last() - .map(|m| m.has_active_window(&window.surface())) + .map(|m| m.has_active_window(&window)) .unwrap_or(false) - && workspace.get_fullscreen(&output).is_some() + && workspace.get_fullscreen().is_some() { continue; // Focus is valid } else { @@ -313,7 +311,7 @@ impl Common { .common .shell .active_space(&output) - .get_fullscreen(&output) + .get_fullscreen() .cloned() .map(KeyboardFocusTarget::Fullscreen) .or_else(|| { diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index a8663a91..15a69a7a 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -1,10 +1,7 @@ use std::sync::Weak; use crate::{ - shell::{ - element::{CosmicMapped, CosmicWindow}, - layout::tiling::ResizeForkTarget, - }, + shell::{element::CosmicMapped, layout::tiling::ResizeForkTarget, CosmicSurface}, utils::prelude::*, wayland::handlers::xdg_shell::popup::get_popup_toplevel, }; @@ -22,7 +19,6 @@ use smithay::{ }, Seat, }, - output::WeakOutput, reexports::wayland_server::{backend::ObjectId, protocol::wl_surface::WlSurface, Resource}, utils::{IsAlive, Serial}, wayland::seat::WaylandFocus, @@ -32,7 +28,7 @@ use smithay::{ #[derive(Debug, Clone, PartialEq)] pub enum PointerFocusTarget { Element(CosmicMapped), - Fullscreen(CosmicWindow), + Fullscreen(CosmicSurface), LayerSurface(LayerSurface), Popup(PopupKind), OverrideRedirect(X11Surface), @@ -42,7 +38,7 @@ pub enum PointerFocusTarget { #[derive(Debug, Clone, PartialEq)] pub enum KeyboardFocusTarget { Element(CosmicMapped), - Fullscreen(CosmicWindow), + Fullscreen(CosmicSurface), Group(WindowGroup), LayerSurface(LayerSurface), Popup(PopupKind), @@ -561,8 +557,8 @@ impl From for PointerFocusTarget { } } -impl From for PointerFocusTarget { - fn from(s: CosmicWindow) -> Self { +impl From for PointerFocusTarget { + fn from(s: CosmicSurface) -> Self { PointerFocusTarget::Fullscreen(s) } } @@ -597,8 +593,8 @@ impl From for KeyboardFocusTarget { } } -impl From for KeyboardFocusTarget { - fn from(s: CosmicWindow) -> Self { +impl From for KeyboardFocusTarget { + fn from(s: CosmicSurface) -> Self { KeyboardFocusTarget::Fullscreen(s) } } diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 7f8452f3..f4de3793 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -68,6 +68,21 @@ impl FloatingLayout { self.map_internal(mapped, position, None) } + pub fn map_maximized(&mut self, mapped: CosmicMapped) { + let output = self.space.outputs().next().unwrap().clone(); + let layers = layer_map_for_output(&output); + let geometry = layers.non_exclusive_zone().as_local(); + + mapped.set_bounds(geometry.size.as_logical()); + mapped.set_tiled(true); + mapped.set_maximized(true); + mapped.set_geometry(geometry.to_global(&output)); + mapped.configure(); + + self.space + .map_element(mapped, geometry.loc.as_logical(), true); + } + pub(in crate::shell) fn map_internal( &mut self, mapped: CosmicMapped, @@ -140,10 +155,7 @@ impl FloatingLayout { } pub fn unmap(&mut self, window: &CosmicMapped) -> bool { - #[allow(irrefutable_let_patterns)] - let is_maximized = window.is_maximized(true); - - if !is_maximized { + if !window.is_maximized(true) || !window.is_fullscreen(true) { if let Some(location) = self.space.element_location(window) { *window.last_geometry.lock().unwrap() = Some(Rectangle::from_loc_and_size(location, window.geometry().size).as_local()); @@ -159,54 +171,6 @@ impl FloatingLayout { self.space.element_geometry(elem).map(RectExt::as_local) } - pub fn maximize_request(&mut self, window: &CosmicSurface) { - if let Some(mapped) = self - .space - .elements() - .find(|m| m.windows().any(|(w, _)| &w == window)) - { - if let Some(location) = self.space.element_location(mapped) { - *mapped.last_geometry.lock().unwrap() = Some(Rectangle::from_loc_and_size( - location, - mapped.geometry().size, - )); - } - } - } - - pub fn unmaximize_request(&mut self, window: &CosmicSurface) -> Option> { - let maybe_mapped = self - .space - .elements() - .find(|m| m.windows().any(|(w, _)| &w == window)) - .cloned(); - - if let Some(mapped) = maybe_mapped { - let last_geometry = mapped.last_geometry.lock().unwrap().clone(); - let last_size = last_geometry.map(|g| g.size).expect("No previous size?"); - let last_location = last_geometry.map(|g| g.loc).expect("No previous location?"); - let output = self - .space - .output_under(last_location.to_f64()) - .next() - .unwrap_or(self.space.outputs().next().unwrap()); - let offset = output.geometry().loc - - self - .space - .output_geometry(output) - .map(|g| g.loc) - .unwrap_or_default(); - mapped.set_geometry(Rectangle::from_loc_and_size( - last_location + offset, - last_size, - )); - self.space.map_element(mapped, last_location, true); - Some(last_size) - } else { - None - } - } - pub fn resize_request( &mut self, mapped: &CosmicMapped, @@ -247,6 +211,9 @@ impl FloatingLayout { else { return false; }; + if mapped.is_maximized(true) { + return false; + } let Some(original_geo) = self.space.element_geometry(mapped) else { return false; // we don't have that window diff --git a/src/shell/mod.rs b/src/shell/mod.rs index ca6c75cb..6d47efec 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -43,11 +43,11 @@ use crate::{ wayland::{ handlers::output, protocols::{ - toplevel_info::ToplevelInfoState, - toplevel_management::{ManagementCapabilities, ToplevelManagementState}, - workspace::{ - WorkspaceCapabilities, WorkspaceGroupHandle, WorkspaceHandle, WorkspaceState, - WorkspaceUpdateGuard, + toplevel_info::ToplevelInfoState, + toplevel_management::{ManagementCapabilities, ToplevelManagementState}, + workspace::{ + WorkspaceCapabilities, WorkspaceGroupHandle, WorkspaceHandle, WorkspaceState, + WorkspaceUpdateGuard, }, }, }, @@ -318,26 +318,26 @@ impl WorkspaceSet { } fn add_empty_workspace(&mut self, state: &mut WorkspaceUpdateGuard) { - let mut workspace = create_workspace( + let mut workspace = create_workspace( state, &self.output, - &self.group, - false, - self.tiling_enabled, - self.gaps, - ); - workspace_set_idx( + &self.group, + false, + self.tiling_enabled, + self.gaps, + ); + workspace_set_idx( state, - self.workspaces.len() as u8 + 1, - self.idx, - &workspace.handle, - ); - state.set_workspace_capabilities( - &workspace.handle, - [WorkspaceCapabilities::Activate].into_iter(), - ); - self.workspaces.push(workspace); - } + self.workspaces.len() as u8 + 1, + self.idx, + &workspace.handle, + ); + state.set_workspace_capabilities( + &workspace.handle, + [WorkspaceCapabilities::Activate].into_iter(), + ); + self.workspaces.push(workspace); + } fn ensure_last_empty<'a>(&mut self, state: &mut WorkspaceUpdateGuard) { // add empty at the end, if necessary @@ -460,10 +460,10 @@ impl WorkspaceSet { pub struct Workspaces { sets: IndexMap, backup_set: Option, - amount: WorkspaceAmount, + amount: WorkspaceAmount, mode: WorkspaceMode, - tiling_enabled: bool, - gaps: (u8, u8), + tiling_enabled: bool, + gaps: (u8, u8), } impl Workspaces { @@ -534,7 +534,7 @@ impl Workspaces { if &seat.active_output() == output { seat.set_active_output(&new_output); } - } + } let new_set = self.sets.get_mut(&new_output).unwrap(); let workspace_group = new_set.group; @@ -692,33 +692,33 @@ impl Workspaces { pub fn get_mut(&mut self, num: usize, output: &Output) -> Option<&mut Workspace> { self.sets - .get_mut(output) + .get_mut(output) .and_then(|set| set.workspaces.get_mut(num)) } pub fn active(&self, output: &Output) -> (Option<(&Workspace, Instant)>, &Workspace) { let set = self.sets.get(output).unwrap(); - ( - set.previously_active - .map(|(idx, start)| (&set.workspaces[idx], start)), - &set.workspaces[set.active], - ) + ( + set.previously_active + .map(|(idx, start)| (&set.workspaces[idx], start)), + &set.workspaces[set.active], + ) } pub fn active_mut(&mut self, output: &Output) -> &mut Workspace { let set = self.sets.get_mut(output).unwrap(); - &mut set.workspaces[set.active] + &mut set.workspaces[set.active] } pub fn active_num(&self, output: &Output) -> (Option, usize) { let set = self.sets.get(output).unwrap(); - (set.previously_active.map(|(idx, _)| idx), set.active) + (set.previously_active.map(|(idx, _)| idx), set.active) } pub fn len(&self, output: &Output) -> usize { let set = self.sets.get(output).unwrap(); - set.workspaces.len() - } + set.workspaces.len() + } pub fn iter(&self) -> impl Iterator { self.sets.iter() @@ -731,7 +731,7 @@ impl Workspaces { pub fn spaces_for_output(&self, output: &Output) -> impl Iterator { self.sets .get(output) - .into_iter() + .into_iter() .flat_map(|set| set.workspaces.iter()) } @@ -745,7 +745,7 @@ impl Workspaces { pub fn update_tiling_status(&mut self, seat: &Seat, tiling: bool) { for set in self.sets.values_mut() { - set.update_tiling_status(seat, tiling) + set.update_tiling_status(seat, tiling) } } } @@ -802,7 +802,7 @@ impl Shell { pub fn add_output(&mut self, output: &Output) { self.workspaces.add_output( - output, + output, &mut self.workspace_state.update(), &mut self.toplevel_info_state, ); @@ -815,8 +815,8 @@ impl Shell { &mut self.workspace_state.update(), &mut self.toplevel_info_state, ); - self.refresh(); // cleans up excess of workspaces and empty workspaces - } + self.refresh(); // cleans up excess of workspaces and empty workspaces + } /* pub fn set_mode(&mut self, mode: ConfigMode) { @@ -1061,13 +1061,13 @@ impl Shell { } set.activate(idx, &mut self.workspace_state.update())?; - let output_geo = output.geometry(); - Ok(Some( + let output_geo = output.geometry(); + Ok(Some( output_geo.loc + Point::from((output_geo.size.w / 2, output_geo.size.h / 2)), - )) - } else { - Ok(None) + )) + } else { + Ok(None) } } WorkspaceMode::Global => { @@ -1320,7 +1320,7 @@ impl Shell { self.workspaces.refresh( &mut self.workspace_state.update(), - &mut self.toplevel_info_state, + &mut self.toplevel_info_state, ); for output in self.outputs() { @@ -1348,7 +1348,7 @@ impl Shell { let (window, seat) = state.common.shell.pending_windows.remove(pos); let workspace = state.common.shell.workspaces.active_mut(output); - workspace.remove_fullscreen(output); + workspace.remove_fullscreen(); state.common.shell.toplevel_info_state.new_toplevel(&window); state .common @@ -1372,6 +1372,15 @@ impl Shell { if layout::should_be_floating(&window) || !workspace.tiling_enabled { workspace.floating_layer.map(mapped.clone(), None); } else { + for mapped in workspace + .mapped() + .filter(|m| m.maximized_state.lock().unwrap().is_some()) + .cloned() + .collect::>() + .into_iter() + { + workspace.unmaximize_request(&mapped.active_window()); + } let focus_stack = workspace.focus_stack.get(&seat); workspace .tiling_layer @@ -1514,44 +1523,17 @@ impl Shell { .unwrap(); // checked above let focus_stack = to_workspace.focus_stack.get(&seat); if window_state.layer == ManagedLayer::Floating { - to_workspace.floating_layer.map(mapped.clone(), &seat, None); + to_workspace.floating_layer.map(mapped.clone(), None); } else { to_workspace .tiling_layer - .map(mapped.clone(), &seat, focus_stack.iter(), direction); + .map(mapped.clone(), focus_stack.iter(), direction); } - let focus_target = match window_state.was_fullscreen { - Some(true) => { - to_workspace.fullscreen_request( - &mapped.active_window(), - &to_output, - state.common.event_loop_handle.clone(), - ); - KeyboardFocusTarget::from( - to_workspace - .fullscreen - .get(to_output) - .unwrap() - .window - .clone(), - ) - } - Some(false) => { - to_workspace.maximize_request( - &mapped.active_window(), - &to_output, - state.common.event_loop_handle.clone(), - ); - KeyboardFocusTarget::from( - to_workspace - .fullscreen - .get(to_output) - .unwrap() - .window - .clone(), - ) - } - None => KeyboardFocusTarget::from(mapped.clone()), + let focus_target = if window_state.was_fullscreen.is_some() { + to_workspace.fullscreen_request(&mapped.active_window()); + KeyboardFocusTarget::from(to_workspace.get_fullscreen().unwrap().clone()) + } else { + KeyboardFocusTarget::from(mapped.clone()) }; for (toplevel, _) in mapped.windows() { diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 4daee1f8..444708ab 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -21,7 +21,6 @@ use crate::{ xwayland::XWaylandState, }; -use calloop::LoopHandle; use id_tree::Tree; use indexmap::IndexSet; use keyframe::{ease, functions::EaseInOutCubic}; @@ -47,7 +46,7 @@ use smithay::{ xwayland::X11Surface, }; use std::{ - collections::HashMap, + collections::{HashMap, VecDeque}, sync::{ atomic::{AtomicBool, Ordering}, Arc, @@ -61,7 +60,7 @@ use super::{ element::{ resize_indicator::ResizeIndicator, stack::CosmicStackRenderElement, swap_indicator::SwapIndicator, window::CosmicWindowRenderElement, CosmicMapped, - CosmicWindow, + MaximizedState, }, focus::{ target::{KeyboardFocusTarget, PointerFocusTarget, WindowGroup}, @@ -80,7 +79,8 @@ pub struct Workspace { pub tiling_layer: TilingLayout, pub floating_layer: FloatingLayout, pub tiling_enabled: bool, - pub fullscreen: HashMap, + pub fullscreen: Option, + pub handle: WorkspaceHandle, pub focus_stack: FocusStacks, pub pending_buffers: Vec<(ScreencopySession, BufferParams)>, @@ -92,14 +92,19 @@ pub struct Workspace { #[derive(Debug, Clone)] pub struct FullscreenSurface { - pub window: CosmicWindow, - pub exclusive: bool, - original_size: Size, + pub surface: CosmicSurface, + original_geometry: Rectangle, start_at: Option, ended_at: Option, animation_signal: Option>, } +impl PartialEq for FullscreenSurface { + fn eq(&self, other: &Self) -> bool { + self.surface == other.surface + } +} + struct FullscreenBlocker { signal: Arc, } @@ -122,17 +127,17 @@ impl FullscreenSurface { impl IsAlive for FullscreenSurface { fn alive(&self) -> bool { - self.window.alive() + self.surface.alive() } } #[derive(Debug, Default)] pub struct FocusStacks(HashMap, IndexSet>); -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq)] pub struct ManagedState { pub layer: ManagedLayer, - pub was_fullscreen: Option, + pub was_fullscreen: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ManagedLayer { @@ -214,7 +219,7 @@ impl Workspace { tiling_layer, floating_layer, tiling_enabled, - fullscreen: HashMap::new(), + fullscreen: None, handle, focus_stack: FocusStacks::default(), pending_buffers: Vec::new(), @@ -229,7 +234,11 @@ impl Workspace { #[cfg(feature = "debug")] puffin::profile_function!(); - self.fullscreen.retain(|_, w| w.alive()); + // TODO: `Option::take_if` once stabilitized + if self.fullscreen.as_ref().is_some_and(|w| !w.alive()) { + let _ = self.fullscreen.take(); + }; + self.floating_layer.refresh(); self.tiling_layer.refresh(); } @@ -245,15 +254,15 @@ impl Workspace { self.tiling_layer.animations_going() || self .fullscreen - .values() - .any(|f| f.start_at.is_some() || f.ended_at.is_some()) + .as_ref() + .is_some_and(|f| f.start_at.is_some() || f.ended_at.is_some()) || self.dirty.swap(false, Ordering::SeqCst) } pub fn update_animations(&mut self) -> HashMap { let mut clients = HashMap::new(); - for f in self.fullscreen.values_mut() { + if let Some(f) = self.fullscreen.as_mut() { if let Some(start) = f.start_at.as_ref() { let duration_since = Instant::now().duration_since(*start); if duration_since > FULLSCREEN_ANIMATION_DURATION { @@ -264,36 +273,32 @@ impl Workspace { if let Some(signal) = f.animation_signal.take() { signal.store(true, Ordering::SeqCst); if let Some(client) = - f.window.wl_surface().as_ref().and_then(Resource::client) + f.surface.wl_surface().as_ref().and_then(Resource::client) { clients.insert(client.id(), client); } } } } - } - let len = self.fullscreen.len(); - self.fullscreen.retain(|_, f| match f.ended_at { - None => true, - Some(instant) => { - let duration_since = Instant::now().duration_since(instant); + if let Some(end) = f.ended_at { + let duration_since = Instant::now().duration_since(end); if duration_since * 2 > FULLSCREEN_ANIMATION_DURATION { if let Some(signal) = f.animation_signal.take() { signal.store(true, Ordering::SeqCst); if let Some(client) = - f.window.wl_surface().as_ref().and_then(Resource::client) + f.surface.wl_surface().as_ref().and_then(Resource::client) { clients.insert(client.id(), client); } } } - duration_since < FULLSCREEN_ANIMATION_DURATION + if duration_since >= FULLSCREEN_ANIMATION_DURATION { + let _ = self.fullscreen.take(); + self.dirty.store(true, Ordering::SeqCst); + } } - }); - if len != self.fullscreen.len() { - self.dirty.store(true, Ordering::SeqCst); } clients.extend(self.tiling_layer.update_animation_state()); @@ -332,18 +337,25 @@ impl Workspace { } pub fn unmap(&mut self, mapped: &CosmicMapped) -> Option { + let was_fullscreen = self + .fullscreen + .as_ref() + .filter(|f| f.ended_at.is_none()) + .map(|f| mapped.windows().any(|(w, _)| w == f.surface)) + .unwrap_or(false) + .then(|| self.fullscreen.take().unwrap()); + + if mapped.is_maximized(true) { + // If surface is maximized then unmaximize it, so it is assigned to only one layer + let _ = self.unmaximize_request(&mapped.active_window()); + } + let was_floating = self.floating_layer.unmap(&mapped); let was_tiling = self.tiling_layer.unmap(&mapped); if was_floating || was_tiling { assert!(was_floating != was_tiling); } - let was_maximized = mapped.is_maximized(true); - let was_fullscreen = mapped.is_fullscreen(true); - if was_maximized || was_fullscreen { - self.unmaximize_request(&mapped.active_window()); - } - self.focus_stack .0 .values_mut() @@ -351,12 +363,12 @@ impl Workspace { if was_floating { Some(ManagedState { layer: ManagedLayer::Floating, - was_fullscreen: (was_fullscreen || was_maximized).then_some(was_fullscreen), + was_fullscreen, }) } else if was_tiling { Some(ManagedState { layer: ManagedLayer::Tiling, - was_fullscreen: (was_fullscreen || was_maximized).then_some(was_fullscreen), + was_fullscreen, }) } else { None @@ -377,7 +389,7 @@ impl Workspace { .find(|e| { e.windows() .any(|(w, _)| w.wl_surface().as_ref() == Some(surface)) - }) + }) } pub fn element_under( @@ -405,80 +417,69 @@ impl Workspace { self.floating_layer.refresh(); } - pub fn maximize_request( - &mut self, - window: &CosmicSurface, - output: &Output, - evlh: LoopHandle<'static, crate::state::State>, - ) { - if self.fullscreen.contains_key(output) { + pub fn maximize_request(&mut self, window: &CosmicSurface) { + if self.fullscreen.is_some() { return; } - self.floating_layer.maximize_request(window); - - window.set_fullscreen(false); - window.set_maximized(true); - self.set_fullscreen(window, output, false, evlh) + if let Some(elem) = self.element_for_surface(window).cloned() { + let mut state = elem.maximized_state.lock().unwrap(); + if state.is_none() { + *state = Some(MaximizedState { + original_geometry: self.element_geometry(&elem).unwrap(), + original_layer: if self.is_floating(&elem) { + ManagedLayer::Floating + } else { + ManagedLayer::Tiling + }, + }); + std::mem::drop(state); + self.floating_layer.map_maximized(elem); + } + } } pub fn unmaximize_request(&mut self, window: &CosmicSurface) -> Option> { - if self - .fullscreen - .values() - .any(|f| &f.window.surface() == window) - { - self.unfullscreen_request(window) - } else { - None + if let Some(elem) = self.element_for_surface(window).cloned() { + let mut state = elem.maximized_state.lock().unwrap(); + if let Some(state) = state.take() { + match state.original_layer { + ManagedLayer::Tiling => { + // should still be mapped in tiling + self.floating_layer.unmap(&elem); + elem.output_enter(&self.output, elem.bbox()); + elem.set_maximized(false); + elem.set_geometry(state.original_geometry.to_global(&self.output)); + elem.configure(); + self.tiling_layer.recalculate(); + return self + .tiling_layer + .element_geometry(&elem) + .map(|geo| geo.size.as_logical()); + } + ManagedLayer::Floating => { + elem.set_maximized(false); + self.floating_layer.map_internal( + elem.clone(), + Some(state.original_geometry.loc), + Some(state.original_geometry.size.as_logical()), + ); + return Some(state.original_geometry.size.as_logical()); + } + } + } } + None } - pub fn fullscreen_request( - &mut self, - window: &CosmicSurface, - output: &Output, - evlh: LoopHandle<'static, crate::state::State>, - ) { - if self - .fullscreen - .get(output) - .map(|f| &f.window.surface() != window) - .unwrap_or(false) - { + pub fn fullscreen_request(&mut self, window: &CosmicSurface) { + if self.fullscreen.is_some() { 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, true, evlh) - } - - fn set_fullscreen<'a>( - &mut self, - window: &'a CosmicSurface, - output: &Output, - exclusive: bool, - evlh: LoopHandle<'static, crate::state::State>, - ) { - if let Some(mapped) = self - .mapped() - .find(|m| m.windows().any(|(w, _)| &w == window)) - { - mapped.set_active(window); - } - - let window = CosmicWindow::new(window.clone(), evlh); - let geo = if exclusive { - output.geometry() - } else { - layer_map_for_output(output).non_exclusive_zone() - }; - let original_size = window.geometry().size; + let geo = self.output.geometry(); + let original_geometry = window.geometry().as_global(); let signal = if let Some(surface) = window.wl_surface() { let signal = Arc::new(AtomicBool::new(false)); add_blocker( @@ -492,35 +493,26 @@ impl Workspace { None }; window.set_geometry(geo); - window.output_enter(output, Rectangle::from_loc_and_size((0, 0), geo.size)); - window.surface().send_configure(); + window.send_configure(); - self.fullscreen.insert( - output.clone(), - FullscreenSurface { - window: window.clone(), - exclusive, - original_size, - start_at: Some(Instant::now()), - ended_at: None, - animation_signal: signal, - }, - ); + self.fullscreen = Some(FullscreenSurface { + surface: window.clone(), + original_geometry, + start_at: Some(Instant::now()), + ended_at: None, + animation_signal: signal, + }); } pub fn unfullscreen_request(&mut self, window: &CosmicSurface) -> Option> { - if let Some((output, f)) = self - .fullscreen - .iter_mut() - .find(|(_, f)| &f.window.surface() == window) - { - f.window.output_leave(output); - window.set_maximized(false); + if let Some(f) = self.fullscreen.as_mut().filter(|f| &f.surface == window) { window.set_fullscreen(false); - let result = self.floating_layer.unmaximize_request(window); + window.set_geometry(f.original_geometry); + self.floating_layer.refresh(); - self.tiling_layer.recalculate(output); + self.tiling_layer.recalculate(); self.tiling_layer.refresh(); + let signal = if let Some(surface) = window.wl_surface() { let signal = Arc::new(AtomicBool::new(false)); add_blocker( @@ -559,84 +551,26 @@ impl Workspace { } } - pub fn remove_fullscreen(&mut self, output: &Output) { - if let Some(FullscreenSurface { - window, - ended_at, - start_at, - animation_signal, - .. - }) = self.fullscreen.get_mut(output) - { - window.output_leave(output); - let surface = window.surface(); - 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(); - let signal = if let Some(surface) = surface.wl_surface() { - let signal = Arc::new(AtomicBool::new(false)); - add_blocker( - &surface, - FullscreenBlocker { - signal: signal.clone(), - }, - ); - Some(signal) - } else { - None - }; - surface.send_configure(); - - *ended_at = Some( - Instant::now() - - (FULLSCREEN_ANIMATION_DURATION - - start_at - .take() - .map(|earlier| { - Instant::now() - .duration_since(earlier) - .min(FULLSCREEN_ANIMATION_DURATION) - }) - .unwrap_or(FULLSCREEN_ANIMATION_DURATION)), - ); - if let Some(new_signal) = signal { - if let Some(old_signal) = animation_signal.replace(new_signal) { - old_signal.store(true, Ordering::SeqCst); - } - } + pub fn remove_fullscreen(&mut self) { + if let Some(surface) = self.fullscreen.as_ref().map(|f| f.surface.clone()) { + self.unfullscreen_request(&surface); } } - pub fn maximize_toggle( - &mut self, - window: &CosmicSurface, - output: &Output, - evlh: LoopHandle<'static, crate::state::State>, - ) { - if self.fullscreen.contains_key(output) { + pub fn maximize_toggle(&mut self, window: &CosmicSurface) { + if window.is_maximized(true) { self.unmaximize_request(window); } else { - self.maximize_request(window, output, evlh); + self.maximize_request(window); } } - pub fn get_fullscreen(&self, output: &Output) -> Option<&CosmicWindow> { + pub fn get_fullscreen(&self) -> Option<&CosmicSurface> { self.fullscreen - .get(output) - .filter(|f| f.alive() && f.exclusive) + .as_ref() + .filter(|f| f.alive()) .filter(|f| f.ended_at.is_none() && f.start_at.is_none()) - .map(|f| &f.window) - } - - pub fn get_maximized(&self, output: &Output) -> Option<&CosmicWindow> { - self.fullscreen - .get(output) - .filter(|f| f.alive() && !f.exclusive) - .filter(|f| f.ended_at.is_none() && f.start_at.is_none()) - .map(|f| &f.window) + .map(|f| &f.surface) } pub fn resize_request( @@ -669,8 +603,8 @@ impl Workspace { if let Some(toplevel) = focused.toplevel() { if self .fullscreen - .values() - .any(|f| f.window.surface().wl_surface().as_ref() == Some(&toplevel)) + .as_ref() + .is_some_and(|f| f.surface.wl_surface().as_ref() == Some(&toplevel)) { return false; } @@ -692,12 +626,24 @@ impl Workspace { indicator_thickness: u8, ) -> Option { let pointer = seat.get_pointer().unwrap(); - let pos = pointer.current_location(); + let pos = pointer.current_location().as_global(); + + if self + .fullscreen + .as_ref() + .is_some_and(|f| &f.surface == window) + { + self.remove_fullscreen(); + } let mapped = self.element_for_surface(&window)?.clone(); - let mut initial_window_location = self.element_geometry(&mapped).unwrap().loc; + let mut initial_window_location = self + .element_geometry(&mapped) + .unwrap() + .loc + .to_global(&self.output); - if mapped.is_fullscreen(true) || mapped.is_maximized(true) { + if mapped.is_maximized(true) { // If surface is maximized then unmaximize it let new_size = self.unmaximize_request(window); let ratio = pos.x / output.geometry().size.w as f64; @@ -785,30 +731,16 @@ impl Workspace { pub fn is_fullscreen(&self, mapped: &CosmicMapped) -> bool { self.fullscreen - .values() - .any(|f| f.exclusive && f.window.surface() == mapped.active_window()) - } - - pub fn is_maximized(&self, mapped: &CosmicMapped) -> bool { - self.fullscreen - .values() - .any(|f| !f.exclusive && f.window.surface() == mapped.active_window()) + .as_ref() + .is_some_and(|f| f.surface == mapped.active_window()) } pub fn is_floating(&self, mapped: &CosmicMapped) -> bool { - !self - .fullscreen - .values() - .any(|f| f.window.surface() == mapped.active_window()) - && self.floating_layer.mapped().any(|m| m == mapped) + !self.is_fullscreen(mapped) && self.floating_layer.mapped().any(|m| m == mapped) } pub fn is_tiled(&self, mapped: &CosmicMapped) -> bool { - !self - .fullscreen - .values() - .any(|f| f.window.surface() == mapped.active_window()) - && self.tiling_layer.mapped().any(|(_, m, _)| m == mapped) + !self.is_fullscreen(mapped) && self.tiling_layer.mapped().any(|(_, m, _)| m == mapped) } pub fn node_desc(&self, focus: KeyboardFocusTarget) -> Option { @@ -854,7 +786,7 @@ impl Workspace { seat: &Seat, swap_desc: Option, ) -> FocusResult { - if self.fullscreen.contains_key(&seat.active_output()) { + if self.fullscreen.is_some() { return FocusResult::None; } @@ -872,8 +804,8 @@ impl Workspace { direction: Direction, seat: &Seat, ) -> MoveResult { - if let Some(f) = self.fullscreen.get(&seat.active_output()) { - MoveResult::MoveFurther(KeyboardFocusTarget::Fullscreen(f.window.clone())) + if let Some(f) = self.fullscreen.as_ref() { + MoveResult::MoveFurther(KeyboardFocusTarget::Fullscreen(f.surface.clone())) } else { self.floating_layer .move_current_element(direction, seat) @@ -940,32 +872,27 @@ impl Workspace { }), ); - if let Some(fullscreen) = self.fullscreen.get(output) { + if let Some(fullscreen) = self.fullscreen.as_ref() { // fullscreen window - let bbox = fullscreen.window.bbox(); + let bbox = fullscreen.surface.bbox().as_local(); let element_geo = Rectangle::from_loc_and_size( - self.element_for_surface(&fullscreen.window.surface()) + self.element_for_surface(&fullscreen.surface) .and_then(|elem| { self.floating_layer - .space .element_geometry(elem) .or_else(|| self.tiling_layer.element_geometry(elem)) .map(|mut geo| { - geo.loc -= elem.geometry().loc; + geo.loc -= elem.geometry().loc.as_local(); geo }) }) .unwrap_or(bbox) .loc, - fullscreen.original_size, + fullscreen.original_geometry.size.as_local(), ); - let mut full_geo = if fullscreen.exclusive { - Rectangle::from_loc_and_size((0, 0), output.geometry().size) - } else { - layer_map.non_exclusive_zone() - }; - + let mut full_geo = + Rectangle::from_loc_and_size((0, 0), self.output.geometry().size.as_local()); if fullscreen.start_at.is_none() { if bbox != full_geo { if bbox.size.w < full_geo.size.w { @@ -1021,7 +948,7 @@ impl Workspace { }; let (w_elements, p_elements) = fullscreen - .window + .surface .split_render_elements::>( renderer, render_loc, @@ -1039,12 +966,12 @@ impl Workspace { if self .fullscreen - .get(output) + .as_ref() .map(|f| f.start_at.is_some() || f.ended_at.is_some()) .unwrap_or(true) { let focused = draw_focus_indicator - .filter(|_| !self.fullscreen.contains_key(output)) + .filter(|_| !self.fullscreen.is_some()) .and_then(|seat| self.focus_stack.get(seat).last().cloned()); // floating surfaces