shell: Properly handle fullscreen outputs
This commit is contained in:
parent
aec8263e1b
commit
64143e75e7
6 changed files with 211 additions and 50 deletions
|
|
@ -363,15 +363,15 @@ impl TilingLayout {
|
|||
pub fn map<'a>(
|
||||
&mut self,
|
||||
window: CosmicMapped,
|
||||
focus_stack: impl Iterator<Item = &'a CosmicMapped> + 'a,
|
||||
focus_stack: Option<impl Iterator<Item = &'a CosmicMapped> + 'a>,
|
||||
direction: Option<Direction>,
|
||||
) {
|
||||
window.output_enter(&self.output, window.bbox());
|
||||
window.set_bounds(self.output.geometry().size.as_logical());
|
||||
self.map_internal(window, Some(focus_stack), direction);
|
||||
self.map_internal(window, focus_stack, direction);
|
||||
}
|
||||
|
||||
fn map_internal<'a>(
|
||||
pub fn map_internal<'a>(
|
||||
&mut self,
|
||||
window: impl Into<CosmicMapped>,
|
||||
focus_stack: Option<impl Iterator<Item = &'a CosmicMapped> + 'a>,
|
||||
|
|
@ -528,7 +528,7 @@ impl TilingLayout {
|
|||
}
|
||||
|
||||
mapped.set_tiled(true);
|
||||
other.map(mapped.clone(), focus_stack, None);
|
||||
other.map(mapped.clone(), Some(focus_stack), None);
|
||||
return Some(KeyboardFocusTarget::Element(mapped));
|
||||
}
|
||||
None => {
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ pub struct Shell {
|
|||
|
||||
pub popups: PopupManager,
|
||||
pub maximize_mode: MaximizeMode,
|
||||
pub pending_windows: Vec<(CosmicSurface, Seat<State>)>,
|
||||
pub pending_windows: Vec<(CosmicSurface, Seat<State>, Option<Output>)>,
|
||||
pub pending_layers: Vec<(LayerSurface, Output, Seat<State>)>,
|
||||
pub override_redirect_windows: Vec<X11Surface>,
|
||||
|
||||
|
|
@ -382,7 +382,8 @@ impl WorkspaceSet {
|
|||
|
||||
for mut workspace in overflow {
|
||||
if last_space.fullscreen.is_some() {
|
||||
workspace.remove_fullscreen();
|
||||
// Don't handle the returned original workspace, for this nieche case.
|
||||
let _ = workspace.remove_fullscreen();
|
||||
}
|
||||
|
||||
for element in workspace.mapped() {
|
||||
|
|
@ -1250,18 +1251,78 @@ impl Shell {
|
|||
.refresh(Some(&self.workspace_state));
|
||||
}
|
||||
|
||||
pub fn map_window(state: &mut State, window: &CosmicSurface, output: &Output) {
|
||||
pub fn remap_unfullscreened_window(
|
||||
&mut self,
|
||||
mapped: CosmicMapped,
|
||||
current_workspace: &WorkspaceHandle,
|
||||
previous_workspace: &WorkspaceHandle,
|
||||
target_layer: ManagedLayer,
|
||||
) {
|
||||
if self.space_for_handle(previous_workspace).is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
let Some(workspace) = self.space_for_handle_mut(¤t_workspace) else { return };
|
||||
let _ = workspace.unmap(&mapped);
|
||||
}
|
||||
|
||||
let new_workspace_output = self
|
||||
.space_for_handle(&previous_workspace)
|
||||
.unwrap()
|
||||
.output()
|
||||
.clone();
|
||||
for (window, _) in mapped.windows() {
|
||||
self.toplevel_info_state
|
||||
.toplevel_enter_output(&window, &new_workspace_output);
|
||||
self.toplevel_info_state
|
||||
.toplevel_enter_workspace(&window, &previous_workspace);
|
||||
}
|
||||
|
||||
let new_workspace = self.space_for_handle_mut(&previous_workspace).unwrap();
|
||||
match target_layer {
|
||||
ManagedLayer::Floating => new_workspace.floating_layer.map(mapped, None),
|
||||
ManagedLayer::Tiling => {
|
||||
new_workspace
|
||||
.tiling_layer
|
||||
.map(mapped, Option::<std::iter::Empty<_>>::None, None)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn map_window(state: &mut State, window: &CosmicSurface) {
|
||||
let pos = state
|
||||
.common
|
||||
.shell
|
||||
.pending_windows
|
||||
.iter()
|
||||
.position(|(w, _)| w == window)
|
||||
.position(|(w, _, _)| w == window)
|
||||
.unwrap();
|
||||
let (window, seat) = state.common.shell.pending_windows.remove(pos);
|
||||
let (window, seat, output) = state.common.shell.pending_windows.remove(pos);
|
||||
|
||||
let workspace = state.common.shell.workspaces.active_mut(output);
|
||||
workspace.remove_fullscreen();
|
||||
let should_be_fullscreen = output.is_some();
|
||||
let output = output.unwrap_or_else(|| seat.active_output());
|
||||
|
||||
let workspace = state.common.shell.workspaces.active_mut(&output);
|
||||
if let Some((mapped, layer, previous_workspace)) = workspace.remove_fullscreen() {
|
||||
let old_handle = workspace.handle.clone();
|
||||
let new_workspace_handle = state
|
||||
.common
|
||||
.shell
|
||||
.space_for_handle(&previous_workspace)
|
||||
.is_some()
|
||||
.then_some(previous_workspace)
|
||||
.unwrap_or(old_handle);
|
||||
|
||||
state.common.shell.remap_unfullscreened_window(
|
||||
mapped,
|
||||
&old_handle,
|
||||
&new_workspace_handle,
|
||||
layer,
|
||||
);
|
||||
};
|
||||
|
||||
let workspace = state.common.shell.workspaces.active_mut(&output);
|
||||
state.common.shell.toplevel_info_state.new_toplevel(&window);
|
||||
state
|
||||
.common
|
||||
|
|
@ -1297,17 +1358,21 @@ impl Shell {
|
|||
let focus_stack = workspace.focus_stack.get(&seat);
|
||||
workspace
|
||||
.tiling_layer
|
||||
.map(mapped.clone(), focus_stack.iter(), None);
|
||||
.map(mapped.clone(), Some(focus_stack.iter()), None);
|
||||
}
|
||||
|
||||
if let CosmicSurface::X11(surface) = window {
|
||||
if should_be_fullscreen {
|
||||
workspace.fullscreen_request(&mapped.active_window(), None);
|
||||
}
|
||||
|
||||
if let CosmicSurface::X11(ref surface) = window {
|
||||
if let Some(xwm) = state
|
||||
.common
|
||||
.xwayland_state
|
||||
.as_mut()
|
||||
.and_then(|state| state.xwm.as_mut())
|
||||
{
|
||||
if let Err(err) = xwm.raise_window(&surface) {
|
||||
if let Err(err) = xwm.raise_window(surface) {
|
||||
warn!(?err, "Failed to update Xwayland stacking order.");
|
||||
}
|
||||
}
|
||||
|
|
@ -1315,7 +1380,7 @@ impl Shell {
|
|||
|
||||
Shell::set_focus(state, Some(&KeyboardFocusTarget::from(mapped)), &seat, None);
|
||||
|
||||
let active_space = state.common.shell.active_space(output);
|
||||
let active_space = state.common.shell.active_space(&output);
|
||||
for mapped in active_space.mapped() {
|
||||
state.common.shell.update_reactive_popups(mapped);
|
||||
}
|
||||
|
|
@ -1440,10 +1505,10 @@ impl Shell {
|
|||
} else {
|
||||
to_workspace
|
||||
.tiling_layer
|
||||
.map(mapped.clone(), focus_stack.iter(), direction);
|
||||
.map(mapped.clone(), Some(focus_stack.iter()), direction);
|
||||
}
|
||||
let focus_target = if window_state.was_fullscreen.is_some() {
|
||||
to_workspace.fullscreen_request(&mapped.active_window());
|
||||
let focus_target = if let Some(f) = window_state.was_fullscreen {
|
||||
to_workspace.fullscreen_request(&mapped.active_window(), f.previously);
|
||||
KeyboardFocusTarget::from(to_workspace.get_fullscreen().unwrap().clone())
|
||||
} else {
|
||||
KeyboardFocusTarget::from(mapped.clone())
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ pub struct Workspace {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct FullscreenSurface {
|
||||
pub surface: CosmicSurface,
|
||||
pub previously: Option<(ManagedLayer, WorkspaceHandle)>,
|
||||
original_geometry: Rectangle<i32, Global>,
|
||||
start_at: Option<Instant>,
|
||||
ended_at: Option<Instant>,
|
||||
|
|
@ -472,7 +473,11 @@ impl Workspace {
|
|||
None
|
||||
}
|
||||
|
||||
pub fn fullscreen_request(&mut self, window: &CosmicSurface) {
|
||||
pub fn fullscreen_request(
|
||||
&mut self,
|
||||
window: &CosmicSurface,
|
||||
previously: Option<(ManagedLayer, WorkspaceHandle)>,
|
||||
) {
|
||||
if self.fullscreen.is_some() {
|
||||
return;
|
||||
}
|
||||
|
|
@ -497,6 +502,7 @@ impl Workspace {
|
|||
|
||||
self.fullscreen = Some(FullscreenSurface {
|
||||
surface: window.clone(),
|
||||
previously,
|
||||
original_geometry,
|
||||
start_at: Some(Instant::now()),
|
||||
ended_at: None,
|
||||
|
|
@ -504,7 +510,11 @@ impl Workspace {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn unfullscreen_request(&mut self, window: &CosmicSurface) -> Option<Size<i32, Logical>> {
|
||||
#[must_use]
|
||||
pub fn unfullscreen_request(
|
||||
&mut self,
|
||||
window: &CosmicSurface,
|
||||
) -> Option<(ManagedLayer, WorkspaceHandle)> {
|
||||
if let Some(f) = self.fullscreen.as_mut().filter(|f| &f.surface == window) {
|
||||
window.set_fullscreen(false);
|
||||
window.set_geometry(f.original_geometry);
|
||||
|
|
@ -545,15 +555,19 @@ impl Workspace {
|
|||
}
|
||||
}
|
||||
|
||||
Some(f.original_geometry.size.as_logical())
|
||||
f.previously
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_fullscreen(&mut self) {
|
||||
#[must_use]
|
||||
pub fn remove_fullscreen(&mut self) -> Option<(CosmicMapped, ManagedLayer, WorkspaceHandle)> {
|
||||
if let Some(surface) = self.fullscreen.as_ref().map(|f| f.surface.clone()) {
|
||||
self.unfullscreen_request(&surface);
|
||||
self.unfullscreen_request(&surface)
|
||||
.map(|(l, h)| (self.element_for_surface(&surface).unwrap().clone(), l, h))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -633,7 +647,7 @@ impl Workspace {
|
|||
.as_ref()
|
||||
.is_some_and(|f| &f.surface == window)
|
||||
{
|
||||
self.remove_fullscreen();
|
||||
let _ = self.remove_fullscreen(); // We are moving this window, we don't need to send it back to it's original workspace
|
||||
}
|
||||
|
||||
let mapped = self.element_for_surface(&window)?.clone();
|
||||
|
|
@ -692,7 +706,8 @@ impl Workspace {
|
|||
.into_iter()
|
||||
{
|
||||
self.floating_layer.unmap(&window);
|
||||
self.tiling_layer.map(window, focus_stack.iter(), None)
|
||||
self.tiling_layer
|
||||
.map(window, Some(focus_stack.iter()), None)
|
||||
}
|
||||
self.tiling_enabled = true;
|
||||
}
|
||||
|
|
@ -707,7 +722,8 @@ impl Workspace {
|
|||
} else if self.floating_layer.mapped().any(|w| w == &window) {
|
||||
let focus_stack = self.focus_stack.get(seat);
|
||||
self.floating_layer.unmap(&window);
|
||||
self.tiling_layer.map(window, focus_stack.iter(), None)
|
||||
self.tiling_layer
|
||||
.map(window, Some(focus_stack.iter()), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,12 +171,12 @@ impl CompositorHandler for State {
|
|||
on_commit_buffer_handler::<Self>(surface);
|
||||
|
||||
// then handle initial configure events and map windows if necessary
|
||||
if let Some((window, seat)) = self
|
||||
if let Some((window, _, _)) = self
|
||||
.common
|
||||
.shell
|
||||
.pending_windows
|
||||
.iter()
|
||||
.find(|(window, _)| window.wl_surface().as_ref() == Some(surface))
|
||||
.find(|(window, _, _)| window.wl_surface().as_ref() == Some(surface))
|
||||
.cloned()
|
||||
{
|
||||
match window {
|
||||
|
|
@ -185,9 +185,8 @@ impl CompositorHandler for State {
|
|||
if self.toplevel_ensure_initial_configure(&toplevel)
|
||||
&& with_renderer_surface_state(&surface, |state| state.buffer().is_some())
|
||||
{
|
||||
let output = seat.active_output();
|
||||
window.on_commit();
|
||||
Shell::map_window(self, &window, &output);
|
||||
Shell::map_window(self, &window);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{shell::CosmicSurface, utils::prelude::*, wayland::protocols::screencopy::SessionType};
|
||||
use crate::{
|
||||
shell::{element::CosmicWindow, CosmicMapped, CosmicSurface, ManagedLayer},
|
||||
utils::prelude::*,
|
||||
wayland::protocols::screencopy::SessionType,
|
||||
};
|
||||
use smithay::{
|
||||
delegate_xdg_shell,
|
||||
desktop::{
|
||||
|
|
@ -38,7 +42,7 @@ impl XdgShellHandler for State {
|
|||
fn new_toplevel(&mut self, surface: ToplevelSurface) {
|
||||
let seat = self.common.last_active_seat().clone();
|
||||
let window = CosmicSurface::Wayland(Window::new(surface));
|
||||
self.common.shell.pending_windows.push((window, seat));
|
||||
self.common.shell.pending_windows.push((window, seat, None));
|
||||
// We will position the window after the first commit, when we know its size hints
|
||||
}
|
||||
|
||||
|
|
@ -184,14 +188,14 @@ impl XdgShellHandler for State {
|
|||
}
|
||||
|
||||
fn fullscreen_request(&mut self, surface: ToplevelSurface, output: Option<WlOutput>) {
|
||||
let active_output = {
|
||||
let seat = self.common.last_active_seat();
|
||||
seat.active_output()
|
||||
};
|
||||
let output = output
|
||||
.as_ref()
|
||||
.and_then(Output::from_resource)
|
||||
.unwrap_or_else(|| {
|
||||
let seat = self.common.last_active_seat();
|
||||
seat.active_output()
|
||||
});
|
||||
// TODO: If this is not the output? Do we move it?
|
||||
.unwrap_or_else(|| active_output.clone());
|
||||
|
||||
if let Some(mapped) = self
|
||||
.common
|
||||
|
|
@ -200,11 +204,69 @@ impl XdgShellHandler for State {
|
|||
.cloned()
|
||||
{
|
||||
if let Some(workspace) = self.common.shell.space_for_mut(&mapped) {
|
||||
let (window, _) = mapped
|
||||
.windows()
|
||||
.find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface()))
|
||||
.unwrap();
|
||||
workspace.fullscreen_request(&window)
|
||||
if workspace.output != output {
|
||||
let (mapped, layer) = if mapped
|
||||
.stack_ref()
|
||||
.map(|stack| stack.len() > 1)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let stack = mapped.stack_ref().unwrap();
|
||||
let surface = stack
|
||||
.surfaces()
|
||||
.find(|s| s.wl_surface().as_ref() == Some(surface.wl_surface()))
|
||||
.unwrap();
|
||||
stack.remove_window(&surface);
|
||||
(
|
||||
CosmicMapped::from(CosmicWindow::new(
|
||||
surface,
|
||||
self.common.event_loop_handle.clone(),
|
||||
)),
|
||||
if workspace.is_tiled(&mapped) {
|
||||
ManagedLayer::Tiling
|
||||
} else {
|
||||
ManagedLayer::Floating
|
||||
},
|
||||
)
|
||||
} else {
|
||||
let layer = workspace.unmap(&mapped).unwrap().layer;
|
||||
(mapped, layer)
|
||||
};
|
||||
let handle = workspace.handle.clone();
|
||||
std::mem::drop(workspace);
|
||||
|
||||
let workspace_handle = self.common.shell.active_space(&output).handle.clone();
|
||||
for (window, _) in mapped.windows() {
|
||||
self.common
|
||||
.shell
|
||||
.toplevel_info_state
|
||||
.toplevel_enter_output(&window, &output);
|
||||
self.common
|
||||
.shell
|
||||
.toplevel_info_state
|
||||
.toplevel_enter_workspace(&window, &workspace_handle);
|
||||
}
|
||||
|
||||
let workspace = self.common.shell.active_space_mut(&output);
|
||||
workspace.floating_layer.map(mapped.clone(), None);
|
||||
workspace.fullscreen_request(&mapped.active_window(), Some((layer, handle)));
|
||||
} else {
|
||||
let (window, _) = mapped
|
||||
.windows()
|
||||
.find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface()))
|
||||
.unwrap();
|
||||
workspace.fullscreen_request(&window, None)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(o) = self
|
||||
.common
|
||||
.shell
|
||||
.pending_windows
|
||||
.iter_mut()
|
||||
.find(|(s, _, _)| s.wl_surface().as_ref() == Some(surface.wl_surface()))
|
||||
.map(|(_, _, o)| o)
|
||||
{
|
||||
*o = Some(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -221,7 +283,23 @@ impl XdgShellHandler for State {
|
|||
.windows()
|
||||
.find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface()))
|
||||
.unwrap();
|
||||
workspace.unfullscreen_request(&window);
|
||||
if let Some((layer, previous_workspace)) = workspace.unfullscreen_request(&window) {
|
||||
let old_handle = workspace.handle.clone();
|
||||
let new_workspace_handle = self
|
||||
.common
|
||||
.shell
|
||||
.space_for_handle(&previous_workspace)
|
||||
.is_some()
|
||||
.then_some(previous_workspace)
|
||||
.unwrap_or(old_handle); // if the workspace doesn't exist anymore, we can still remap on the right layer
|
||||
|
||||
self.common.shell.remap_unfullscreened_window(
|
||||
mapped,
|
||||
&old_handle,
|
||||
&new_workspace_handle,
|
||||
layer,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,16 +151,19 @@ impl XwmHandler for State {
|
|||
}
|
||||
|
||||
let seat = self.common.last_active_seat().clone();
|
||||
self.common.shell.pending_windows.push((surface, seat));
|
||||
self.common
|
||||
.shell
|
||||
.pending_windows
|
||||
.push((surface, seat, None));
|
||||
}
|
||||
|
||||
fn map_window_notify(&mut self, _xwm: XwmId, surface: X11Surface) {
|
||||
if let Some((window, seat)) = self
|
||||
if let Some((window, _, _)) = self
|
||||
.common
|
||||
.shell
|
||||
.pending_windows
|
||||
.iter()
|
||||
.find(|(window, _)| {
|
||||
.find(|(window, _, _)| {
|
||||
if let CosmicSurface::X11(window) = window {
|
||||
window == &surface
|
||||
} else {
|
||||
|
|
@ -169,8 +172,7 @@ impl XwmHandler for State {
|
|||
})
|
||||
.cloned()
|
||||
{
|
||||
let output = seat.active_output();
|
||||
Shell::map_window(self, &window, &output);
|
||||
Shell::map_window(self, &window);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -386,7 +388,7 @@ impl XwmHandler for State {
|
|||
let surface = CosmicSurface::X11(window);
|
||||
if let Some(mapped) = self.common.shell.element_for_surface(&surface).cloned() {
|
||||
if let Some(workspace) = self.common.shell.space_for_mut(&mapped) {
|
||||
workspace.fullscreen_request(&surface)
|
||||
workspace.fullscreen_request(&surface, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -396,7 +398,8 @@ impl XwmHandler for State {
|
|||
if let Some(mapped) = self.common.shell.element_for_surface(&surface).cloned() {
|
||||
if let Some(workspace) = self.common.shell.space_for_mut(&mapped) {
|
||||
let (window, _) = mapped.windows().find(|(w, _)| w == &surface).unwrap();
|
||||
workspace.unfullscreen_request(&window);
|
||||
let previous = workspace.unfullscreen_request(&window);
|
||||
assert!(previous.is_none());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue