From 9b1221edc5232516ce3e89b1ef6d1f9be2ebee6a Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Mon, 23 Jan 2023 18:25:01 +0100 Subject: [PATCH] xwm: More fixes --- src/backend/render/mod.rs | 26 +++++- src/shell/element/mod.rs | 6 +- src/shell/element/stack.rs | 9 +- src/shell/element/surface.rs | 21 ++++- src/shell/element/window.rs | 18 +++- src/shell/layout/floating/grabs/moving.rs | 6 +- src/shell/layout/floating/grabs/resize.rs | 15 ++- src/shell/layout/floating/mod.rs | 12 +-- src/shell/layout/mod.rs | 2 + src/shell/layout/tiling/mod.rs | 13 ++- src/shell/mod.rs | 50 ++++++---- src/shell/workspace.rs | 100 +++++++++++++++++--- src/wayland/handlers/compositor.rs | 6 +- src/xwayland.rs | 107 +++++++++++++++++++--- 14 files changed, 311 insertions(+), 80 deletions(-) diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 69e81134..efb27b41 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -157,7 +157,7 @@ where Source: Clone, { let handle = state.shell.workspaces.active(output).handle; - render_workspace( + let result = render_workspace( gpu, renderer, target, @@ -169,7 +169,27 @@ where cursor_mode, screencopy, fps, - ) + ); + + /* + if let Ok((_, states)) = result.as_ref() { + for xwm in state + .xwayland_state + .values_mut() + .flat_map(|state| state.xwm.as_mut()) + { + if let Err(err) = xwm.update_stacking_order_upwards(states.states.keys()) { + slog_scope::warn!( + "Failed to update Xwm ({:?}) stacking order: {}", + xwm.id(), + err + ); + } + } + } + */ + + result } pub fn render_workspace<'frame, R, Target, OffTarget, Source>( @@ -258,7 +278,7 @@ where elements.extend( workspace - .render_output::(renderer, output) + .render_output::(renderer, output, &state.shell.override_redirect_windows) .map_err(|_| OutputNoMode)? .into_iter() .map(Into::into), diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 648c044b..d604de0f 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -332,10 +332,10 @@ impl CosmicMapped { window.is_activated() } - pub fn set_size(&self, size: Size) { + pub fn set_geometry(&self, geo: Rectangle) { match &self.element { - CosmicMappedInternal::Stack(s) => s.set_size(size), - CosmicMappedInternal::Window(w) => w.set_size(size), + CosmicMappedInternal::Stack(s) => s.set_geometry(geo), + CosmicMappedInternal::Window(w) => w.set_geometry(geo), _ => {} } } diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 6034bae7..f2489a3e 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -150,15 +150,16 @@ impl CosmicStack { Point::from((0, TAB_HEIGHT)) } - pub fn set_size(&self, size: Size) { + pub fn set_geometry(&self, geo: Rectangle) { self.0.with_program(|p| { - let surface_size = (size.w, size.h - TAB_HEIGHT).into(); + let loc = (geo.loc.x, geo.loc.y + TAB_HEIGHT); + let size = (geo.size.w, geo.size.h - TAB_HEIGHT); for window in p.windows.lock().unwrap().iter() { - window.set_size(surface_size); + window.set_geometry(Rectangle::from_loc_and_size(loc, size)); } }); - self.0.resize(Size::from((size.w, TAB_HEIGHT))); + self.0.resize(Size::from((geo.size.w, TAB_HEIGHT))); } fn keyboard_leave_if_previous( diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 760baebc..2a572680 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -102,14 +102,13 @@ impl CosmicSurface { } } - pub fn set_size(&self, size: Size) { + pub fn set_geometry(&self, geo: Rectangle) { match self { CosmicSurface::Wayland(window) => window .toplevel() - .with_pending_state(|state| state.size = Some(size)), + .with_pending_state(|state| state.size = Some(geo.size)), CosmicSurface::X11(surface) => { - let rect = Rectangle::from_loc_and_size(surface.geometry().loc, size); - let _ = surface.configure(rect); + let _ = surface.configure(geo); } _ => {} } @@ -296,6 +295,13 @@ impl CosmicSurface { CosmicSurface::X11(surface) => surface.min_size(), _ => unreachable!(), } + .map(|size| { + if self.is_decorated() { + size + (0, SSD_HEIGHT).into() + } else { + size + } + }) } pub fn max_size(&self) -> Option> { @@ -315,6 +321,13 @@ impl CosmicSurface { CosmicSurface::X11(surface) => surface.max_size(), _ => unreachable!(), } + .map(|size| { + if self.is_decorated() { + size + (0, SSD_HEIGHT).into() + } else { + size + } + }) } pub fn send_configure(&self) { diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index c382046e..a4ceefec 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -120,12 +120,20 @@ impl CosmicWindow { )) } - pub fn set_size(&self, size: Size) { + pub fn set_geometry(&self, geo: Rectangle) { self.0.with_program(|p| { - let surface_size = (size.w, size.h - if p.has_ssd() { SSD_HEIGHT } else { 0 }).into(); - p.window.set_size(surface_size) + let loc = ( + geo.loc.x, + geo.loc.y + if p.has_ssd() { SSD_HEIGHT } else { 0 }, + ); + let size = ( + geo.size.w, + geo.size.h - if p.has_ssd() { SSD_HEIGHT } else { 0 }, + ); + p.window + .set_geometry(Rectangle::from_loc_and_size(loc, size)); }); - self.0.resize(Size::from((size.w, SSD_HEIGHT))); + self.0.resize(Size::from((geo.size.w, SSD_HEIGHT))); } pub fn surface(&self) -> CosmicSurface { @@ -264,6 +272,7 @@ impl SpaceElement for CosmicWindow { self.0.with_program(|p| { let mut bbox = SpaceElement::bbox(&p.window); if p.has_ssd() { + bbox.loc.y -= SSD_HEIGHT; bbox.size.h += SSD_HEIGHT; } bbox @@ -301,6 +310,7 @@ impl SpaceElement for CosmicWindow { self.0.with_program(|p| { let mut geo = SpaceElement::geometry(&p.window); if p.has_ssd() { + geo.loc.y -= SSD_HEIGHT; geo.size.h += SSD_HEIGHT; } geo diff --git a/src/shell/layout/floating/grabs/moving.rs b/src/shell/layout/floating/grabs/moving.rs index a56a278d..cd022183 100644 --- a/src/shell/layout/floating/grabs/moving.rs +++ b/src/shell/layout/floating/grabs/moving.rs @@ -23,7 +23,7 @@ use smithay::{ Seat, }, output::Output, - utils::{IsAlive, Logical, Point, Serial}, + utils::{IsAlive, Logical, Point, Rectangle, Serial}, }; use std::cell::RefCell; @@ -183,6 +183,10 @@ impl MoveSurfaceGrab { .output_geometry(&output) .unwrap() .loc; + grab_state.window.set_geometry(Rectangle::from_loc_and_size( + window_location + offset, + grab_state.window.geometry().size, + )); state .common .shell diff --git a/src/shell/layout/floating/grabs/resize.rs b/src/shell/layout/floating/grabs/resize.rs index 897c0bba..7e11d9e6 100644 --- a/src/shell/layout/floating/grabs/resize.rs +++ b/src/shell/layout/floating/grabs/resize.rs @@ -12,7 +12,7 @@ use smithay::{ AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle, }, - utils::{IsAlive, Logical, Point, Size}, + utils::{IsAlive, Logical, Point, Rectangle, Size}, }; /// Information about the resize operation. @@ -97,7 +97,10 @@ impl PointerGrab for ResizeSurfaceGrab { self.last_window_size = (new_window_width, new_window_height).into(); self.window.set_resizing(true); - self.window.set_size(self.last_window_size); + self.window.set_geometry(Rectangle::from_loc_and_size( + self.window.geometry().loc, + self.last_window_size, + )); self.window.configure(); } @@ -118,7 +121,10 @@ impl PointerGrab for ResizeSurfaceGrab { } self.window.set_resizing(false); - self.window.set_size(self.last_window_size); + self.window.set_geometry(Rectangle::from_loc_and_size( + self.window.geometry().loc, + self.last_window_size, + )); self.window.configure(); let mut resize_state = self.window.resize_state.lock().unwrap(); @@ -220,6 +226,9 @@ impl ResizeSurfaceGrab { ); } } + let mut geometry = window.geometry(); + geometry.loc = new_location; + let _ = window.set_geometry(geometry); space .floating_layer .space diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index e197de9a..c8633fe7 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -75,9 +75,7 @@ impl FloatingLayout { let geometry = layers.non_exclusive_zone(); let last_geometry = mapped.last_geometry.lock().unwrap().clone(); - let mut geo_updated = false; if let Some(size) = last_geometry.map(|g| g.size) { - geo_updated = win_geo.size != size; win_geo.size = size; } { @@ -98,7 +96,6 @@ impl FloatingLayout { } // but no matter the supported sizes, don't be larger than our non-exclusive-zone win_geo.size.w = std::cmp::min(width, geometry.size.w); - geo_updated = true; } if win_geo.size.h > geometry.size.h / 3 * 2 { // try a more reasonable size @@ -113,7 +110,6 @@ impl FloatingLayout { } // but no matter the supported sizes, don't be larger than our non-exclusive-zone win_geo.size.h = std::cmp::min(height, geometry.size.h); - geo_updated = true; } } @@ -128,11 +124,8 @@ impl FloatingLayout { }); mapped.set_tiled(false); - if geo_updated { - mapped.set_size(win_geo.size); - } + mapped.set_geometry(Rectangle::from_loc_and_size(position, win_geo.size)); mapped.configure(); - self.space.map_element(mapped, position, false); } @@ -183,8 +176,8 @@ impl FloatingLayout { 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?"); - mapped.set_size(last_size); let last_location = last_geometry.map(|g| g.loc).expect("No previous location?"); + mapped.set_geometry(Rectangle::from_loc_and_size(last_location, last_size)); self.space.map_element(mapped, last_location, true); Some(last_size) } else { @@ -293,6 +286,7 @@ impl FloatingLayout { .get(&output) .copied() .unwrap_or_else(|| (0, 0).into()); + element.set_geometry(elem_geo); self.space.map_element(element.clone(), elem_geo.loc, false); } self.refresh(); //fixup any out of bounds elements diff --git a/src/shell/layout/mod.rs b/src/shell/layout/mod.rs index bbfe215b..9652802e 100644 --- a/src/shell/layout/mod.rs +++ b/src/shell/layout/mod.rs @@ -44,6 +44,7 @@ lazy_static::lazy_static! { r"update-manager", r"Solaar", r"Steam", + r"", r"TelegramDesktop", r"Zotero", r"gjs", @@ -77,6 +78,7 @@ lazy_static::lazy_static! { r"Software Updater", r".*", r"^.*?(Guard|Login).*", + r"Steam", r"Media viewer", r"Quick Format Citation", r".*", diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index d35a7908..1eeedfe0 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -1129,14 +1129,17 @@ impl TilingLayout { data.update_geometry(geo); } Data::Mapped { mapped, .. } => { + geo.loc += (inner, inner).into(); if !(mapped.is_fullscreen() || mapped.is_maximized()) { mapped.set_tiled(true); - mapped.set_size( - (geo.size.w - inner * 2, geo.size.h - inner * 2).into(), - ); - mapped.configure(); + let size = (geo.size.w - inner * 2, geo.size.h - inner * 2); + let internal_geometry = + Rectangle::from_loc_and_size(geo.loc, size); + if mapped.geometry() != internal_geometry { + mapped.set_geometry(internal_geometry); + mapped.configure(); + } } - geo.loc += (inner, inner).into(); data.update_geometry(geo); } } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 47e7b7e0..9cbeb134 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -3,16 +3,14 @@ use std::{cell::RefCell, collections::HashMap}; use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState; use smithay::{ + backend::renderer::element::Id, desktop::{layer_map_for_output, LayerSurface, PopupManager, WindowSurfaceType}, input::{ pointer::{Focus, GrabStartData as PointerGrabStartData}, Seat, }, output::Output, - reexports::{ - wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle}, - x11rb::protocol::xproto::Window as X11Window, - }, + reexports::wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle}, utils::{Logical, Point, Rectangle, Serial, SERIAL_COUNTER}, wayland::{ compositor::with_states, @@ -71,9 +69,17 @@ pub struct Shell { pub workspace_state: WorkspaceState, } +#[derive(Debug)] pub struct OverrideRedirectWindow { pub surface: X11Surface, - pub above: Option, + pub above: Ordering, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Ordering { + Above, + AboveWindow(Id), + Below, } #[derive(Debug)] @@ -837,12 +843,25 @@ impl Shell { Some(output) => { Box::new(std::iter::once(output.clone())) as Box> } - None => Box::new(self.outputs().map(|o| self.active_space(o)).flat_map(|w| { - w.mapped() - .find(|e| e.has_surface(surface, WindowSurfaceType::ALL)) - .into_iter() - .flat_map(|e| w.outputs_for_element(e)) - })), + None => Box::new( + self.outputs() + .filter(|o| { + self.override_redirect_windows.iter().any(|or| { + if or.surface.wl_surface().as_ref() == Some(surface) { + or.surface.geometry().intersection(o.geometry()).is_some() + } else { + false + } + }) + }) + .cloned() + .chain(self.outputs().map(|o| self.active_space(o)).flat_map(|w| { + w.mapped() + .find(|e| e.has_surface(surface, WindowSurfaceType::ALL)) + .into_iter() + .flat_map(|e| w.outputs_for_element(e)) + })), + ), } } @@ -975,6 +994,9 @@ impl Shell { map.cleanup(); } + self.override_redirect_windows + .retain(|or| or.surface.alive()); + self.toplevel_info_state .refresh(Some(&self.workspace_state)); } @@ -1019,10 +1041,6 @@ impl Shell { } if let CosmicSurface::X11(surface) = window { - let geometry = workspace.element_geometry(&mapped); - if let Err(err) = surface.configure(geometry) { - slog_scope::warn!("Failed to configure X11 surface ({:?}): {}", surface, err); - }; if let Some(xwm) = state .common .xwayland_state @@ -1062,7 +1080,7 @@ impl Shell { .override_redirect_windows .push(OverrideRedirectWindow { surface: window, - above: None, + above: Ordering::Above, }); } diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 6a36a9fe..8ff5304d 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -34,7 +34,7 @@ use super::{ element::CosmicMapped, focus::{FocusStack, FocusStackMut}, grabs::{ResizeEdge, ResizeGrab}, - CosmicMappedRenderElement, CosmicSurface, + CosmicMappedRenderElement, CosmicSurface, Ordering, }; #[derive(Debug)] @@ -232,15 +232,14 @@ impl Workspace { mapped.set_active(window); } - window.set_size( - output - .current_mode() - .map(|m| m.size) - .unwrap_or((0, 0).into()) - .to_f64() - .to_logical(output.current_scale().fractional_scale()) - .to_i32_round(), - ); + let size = output + .current_mode() + .map(|m| m.size) + .unwrap_or((0, 0).into()) + .to_f64() + .to_logical(output.current_scale().fractional_scale()) + .to_i32_round(); + window.set_geometry(Rectangle::from_loc_and_size((0, 0), size)); window.send_configure(); self.fullscreen.insert(output.clone(), window.clone()); } @@ -397,6 +396,7 @@ impl Workspace { &self, renderer: &mut R, output: &Output, + override_redirect_windows: &[super::OverrideRedirectWindow], ) -> Result>, OutputNotMapped> where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, @@ -469,8 +469,33 @@ impl Workspace { lower }; + let mut window_elements = Vec::new(); + + // OR windows above all + window_elements.extend( + override_redirect_windows + .iter() + .filter(|or| { + or.above == Ordering::Above + && or + .surface + .geometry() + .intersection(output.geometry()) + .is_some() + }) + .flat_map(|or| { + AsRenderElements::::render_elements::>( + &or.surface, + renderer, + (or.surface.geometry().loc - output.geometry().loc) + .to_physical_precise_round(output_scale), + Scale::from(output_scale), + ) + }), + ); + // floating surfaces - render_elements.extend( + window_elements.extend( self.floating_layer .render_output::(renderer, output)? .into_iter() @@ -478,13 +503,64 @@ impl Workspace { ); //tiling surfaces - render_elements.extend( + window_elements.extend( self.tiling_layer .render_output::(renderer, output)? .into_iter() .map(WorkspaceRenderElement::from), ); + // Sort other OR windows in between + for or in override_redirect_windows.iter().filter(|or| { + matches!(or.above, Ordering::AboveWindow(_)) + && or + .surface + .geometry() + .intersection(output.geometry()) + .is_some() + }) { + let pos = window_elements + .iter() + .position(|w| Ordering::AboveWindow(w.id().clone()) == or.above) + .unwrap_or(0); + for element in AsRenderElements::::render_elements::>( + &or.surface, + renderer, + (or.surface.geometry().loc - output.geometry().loc) + .to_physical_precise_round(output_scale), + Scale::from(output_scale), + ) + .into_iter() + .rev() + { + window_elements.insert(pos, element); + } + } + + render_elements.extend(window_elements.into_iter()); + + // OR windows below all + render_elements.extend( + override_redirect_windows + .iter() + .filter(|or| { + or.above == Ordering::Below + && or + .surface + .geometry() + .intersection(output.geometry()) + .is_some() + }) + .flat_map(|or| { + AsRenderElements::::render_elements::>( + &or.surface, + renderer, + (or.surface.geometry().loc - output.geometry().loc) + .to_physical_precise_round(output_scale), + Scale::from(output_scale), + ) + }), + ); // bottom and background layer surfaces { render_elements.extend( diff --git a/src/wayland/handlers/compositor.rs b/src/wayland/handlers/compositor.rs index a188a55d..28670ddf 100644 --- a/src/wayland/handlers/compositor.rs +++ b/src/wayland/handlers/compositor.rs @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - shell::CosmicSurface, state::BackendData, utils::prelude::*, + shell::CosmicSurface, + state::{BackendData, Data}, + utils::prelude::*, wayland::protocols::screencopy::SessionType, }; use smithay::{ @@ -109,7 +111,7 @@ impl CompositorHandler for State { } fn commit(&mut self, surface: &WlSurface) { - X11Wm::commit_hook(surface); + X11Wm::commit_hook::(surface); // first load the buffer for various smithay helper functions on_commit_buffer_handler(surface); diff --git a/src/xwayland.rs b/src/xwayland.rs index 32b186ea..679a051c 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -1,12 +1,12 @@ use crate::{ backend::render::cursor::Cursor, - shell::{CosmicSurface, Shell}, + shell::{CosmicSurface, Ordering, Shell}, state::{Data, State}, utils::prelude::SeatExt, wayland::{handlers::screencopy::PendingScreencopyBuffers, protocols::screencopy::SessionType}, }; use smithay::{ - backend::drm::DrmNode, + backend::{drm::DrmNode, renderer::element::Id}, reexports::x11rb::protocol::xproto::Window as X11Window, utils::{Logical, Point, Rectangle, Size}, xwayland::{ @@ -131,6 +131,16 @@ impl XwmHandler for Data { err ); } + if self + .state + .common + .shell + .element_for_surface(&CosmicSurface::X11(window.clone())) + .is_some() + { + return; + } + let window = CosmicSurface::X11(window); self.state .common @@ -149,11 +159,27 @@ impl XwmHandler for Data { } fn mapped_override_redirect_window(&mut self, _xwm: XwmId, window: X11Surface) { + if self + .state + .common + .shell + .override_redirect_windows + .iter() + .any(|or| or.surface == window) + { + return; + } Shell::map_override_redirect(&mut self.state, window) } fn unmapped_window(&mut self, _xwm: XwmId, window: X11Surface) { - if let Some((element, space)) = self + if window.is_override_redirect() { + self.state + .common + .shell + .override_redirect_windows + .retain(|or| or.surface != window); + } else if let Some((element, space)) = self .state .common .shell @@ -225,30 +251,45 @@ impl XwmHandler for Data { &mut self, _xwm: XwmId, window: X11Surface, - _x: Option, - _y: Option, + x: Option, + y: Option, w: Option, h: Option, _reorder: Option, ) { // We only allow floating X11 windows to resize themselves. Nothing else - let current_size = window.geometry().size; + let mut current_geo = window.geometry(); if let Some(mapped) = self .state .common .shell - .element_for_surface(&CosmicSurface::X11(window)) + .element_for_surface(&CosmicSurface::X11(window.clone())) { let space = self.state.common.shell.space_for(mapped).unwrap(); if space.is_floating(mapped) { - mapped.set_size( + mapped.set_geometry(Rectangle::from_loc_and_size( + current_geo.loc, ( - w.map(|w| w as i32).unwrap_or(current_size.w), - h.map(|h| h as i32).unwrap_or(current_size.h), - ) - .into(), - ) + w.map(|w| w as i32).unwrap_or(current_geo.size.w), + h.map(|h| h as i32).unwrap_or(current_geo.size.h), + ), + )) } + } else { + if let Some(x) = x { + current_geo.loc.x = x; + } + if let Some(y) = y { + current_geo.loc.y = y; + } + if let Some(w) = w { + current_geo.size.w = w as i32; + } + if let Some(h) = h { + current_geo.size.h = h as i32; + } + // the window is not yet mapped. Lets give it what it wants + let _ = window.configure(current_geo); } } @@ -260,6 +301,44 @@ impl XwmHandler for Data { above: Option, ) { if window.is_override_redirect() { + let ordering = match above { + None => Ordering::Below, + Some(id) => self + .state + .common + .shell + .override_redirect_windows + .iter() + .find_map(|or| { + if or.surface.window_id() == id { + or.surface + .wl_surface() + .map(|s| Id::from_wayland_resource(&s)) + } else { + None + } + }) + .or_else(|| { + self.state + .common + .shell + .workspaces + .spaces() + .flat_map(|s| s.windows()) + .find_map(|w| { + if let CosmicSurface::X11(w) = w { + if w.window_id() == id || w.mapped_window_id() == Some(id) { + return w + .wl_surface() + .map(|s| Id::from_wayland_resource(&s)); + } + } + None + }) + }) + .map(Ordering::AboveWindow) + .unwrap_or(Ordering::Above), + }; if let Some(or) = self .state .common @@ -268,7 +347,7 @@ impl XwmHandler for Data { .iter_mut() .find(|or| or.surface == window) { - or.above = above; + or.above = ordering; } } }