From 9c7c41c508d4836fb70437780f14ed49de19e135 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 18 Sep 2024 16:02:41 +0200 Subject: [PATCH] toplevel-info/mgmt: Update to v2/v3 --- src/shell/element/surface.rs | 34 ++- src/shell/mod.rs | 14 +- src/state.rs | 5 - src/wayland/handlers/compositor.rs | 1 - src/wayland/handlers/foreign_toplevel_list.rs | 60 +---- src/wayland/handlers/toplevel_info.rs | 19 +- src/wayland/handlers/toplevel_management.rs | 28 +++ src/wayland/handlers/xdg_shell/mod.rs | 1 - src/wayland/protocols/toplevel_info.rs | 206 +++++++++++++++--- src/wayland/protocols/toplevel_management.rs | 13 +- src/xwayland.rs | 8 +- 11 files changed, 270 insertions(+), 119 deletions(-) diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 3c75345a..d13272e6 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -1,6 +1,9 @@ use std::{ borrow::Cow, - sync::atomic::{AtomicBool, Ordering}, + sync::{ + atomic::{AtomicBool, Ordering}, + Mutex, + }, time::Duration, }; @@ -84,6 +87,12 @@ impl PartialEq for CosmicSurface { #[derive(Default)] struct Minimized(AtomicBool); +#[derive(Default)] +struct Sticky(AtomicBool); + +#[derive(Default)] +pub struct GlobalGeometry(pub Mutex>>); + pub const SSD_HEIGHT: i32 = 36; pub const RESIZE_BORDER: i32 = 10; @@ -130,6 +139,13 @@ impl CosmicSurface { } pub fn set_geometry(&self, geo: Rectangle) { + *self + .0 + .user_data() + .get_or_insert_threadsafe(GlobalGeometry::default) + .0 + .lock() + .unwrap() = Some(geo); match self.0.underlying_surface() { WindowSurface::Wayland(toplevel) => { toplevel.with_pending_state(|state| state.size = Some(geo.size.as_logical())) @@ -387,6 +403,22 @@ impl CosmicSurface { } } + pub fn is_sticky(&self) -> bool { + self.0 + .user_data() + .get_or_insert_threadsafe(Sticky::default) + .0 + .load(Ordering::SeqCst) + } + + pub fn set_sticky(&self, sticky: bool) { + self.0 + .user_data() + .get_or_insert_threadsafe(Sticky::default) + .0 + .store(sticky, Ordering::SeqCst); + } + pub fn set_suspended(&self, suspended: bool) { match self.0.underlying_surface() { WindowSurface::Wayland(window) => window.with_pending_state(|state| { diff --git a/src/shell/mod.rs b/src/shell/mod.rs index eb149f7a..5a4824ea 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -44,7 +44,6 @@ use smithay::{ utils::{IsAlive, Logical, Point, Rectangle, Serial, Size}, wayland::{ compositor::with_states, - foreign_toplevel_list::ForeignToplevelListState, seat::WaylandFocus, session_lock::LockSurface, shell::wlr_layer::{KeyboardInteractivity, Layer, LayerSurfaceCachedState}, @@ -61,11 +60,7 @@ use crate::{ utils::{prelude::*, quirks::WORKSPACE_OVERVIEW_NAMESPACE}, wayland::{ handlers::{ - foreign_toplevel_list::{ - new_foreign_toplevel, refresh_foreign_toplevels, remove_foreign_toplevel, - }, - toplevel_management::minimize_rectangle, - xdg_activation::ActivationContext, + toplevel_management::minimize_rectangle, xdg_activation::ActivationContext, xdg_shell::popup::get_popup_toplevel, }, protocols::{ @@ -1194,7 +1189,6 @@ impl Common { ); self.popups.cleanup(); self.toplevel_info_state.refresh(&self.workspace_state); - refresh_foreign_toplevels(&self.shell.read().unwrap()); self.refresh_idle_inhibit(); } @@ -2083,7 +2077,6 @@ impl Shell { &mut self, window: &CosmicSurface, toplevel_info: &mut ToplevelInfoState, - foreign_toplevel_list: &mut ForeignToplevelListState, workspace_state: &mut WorkspaceState, evlh: &LoopHandle<'static, State>, ) -> Option { @@ -2166,7 +2159,6 @@ impl Shell { toplevel_info.new_toplevel(&window, workspace_state); toplevel_enter_output(&window, &output); toplevel_enter_workspace(&window, &workspace.handle); - new_foreign_toplevel(&window, foreign_toplevel_list); let mut workspace_state = workspace_state.update(); @@ -2298,7 +2290,6 @@ impl Shell { surface: &S, seat: &Seat, toplevel_info: &mut ToplevelInfoState, - foreign_toplevel_list: &mut ForeignToplevelListState, ) where CosmicSurface: PartialEq, { @@ -2348,7 +2339,6 @@ impl Shell { if let Some(surface) = surface { toplevel_info.remove_toplevel(&surface); - remove_foreign_toplevel(&surface, foreign_toplevel_list); self.pending_windows.push((surface, seat.clone(), None)); return; } @@ -3619,6 +3609,7 @@ impl Shell { let handle = workspace.handle; for (window, _) in mapped.windows() { + window.set_sticky(true); toplevel_leave_workspace(&window, &handle); } @@ -3640,6 +3631,7 @@ impl Shell { let workspace = &mut set.workspaces[set.active]; for (window, _) in mapped.windows() { toplevel_enter_workspace(&window, &workspace.handle); + window.set_sticky(false); } match mapped diff --git a/src/state.rs b/src/state.rs index 9eba149e..5144cd9f 100644 --- a/src/state.rs +++ b/src/state.rs @@ -68,7 +68,6 @@ use smithay::{ compositor::{CompositorClientState, CompositorState, SurfaceData}, cursor_shape::CursorShapeManagerState, dmabuf::{DmabufFeedback, DmabufGlobal, DmabufState}, - foreign_toplevel_list::ForeignToplevelListState, fractional_scale::{with_fractional_scale, FractionalScaleManagerState}, idle_inhibit::IdleInhibitManagerState, idle_notify::IdleNotifierState, @@ -195,7 +194,6 @@ pub struct Common { pub compositor_state: CompositorState, pub data_device_state: DataDeviceState, pub dmabuf_state: DmabufState, - pub foreign_toplevel_list: ForeignToplevelListState, pub fractional_scale_state: FractionalScaleManagerState, pub keyboard_shortcuts_inhibit_state: KeyboardShortcutsInhibitState, pub output_state: OutputManagerState, @@ -496,8 +494,6 @@ impl State { let compositor_state = CompositorState::new::(dh); let data_device_state = DataDeviceState::new::(dh); let dmabuf_state = DmabufState::new(); - let foreign_toplevel_list = - ForeignToplevelListState::new_with_filter::(dh, client_is_privileged); let fractional_scale_state = FractionalScaleManagerState::new::(dh); let keyboard_shortcuts_inhibit_state = KeyboardShortcutsInhibitState::new::(dh); let output_state = OutputManagerState::new_with_xdg_output::(dh); @@ -595,7 +591,6 @@ impl State { compositor_state, data_device_state, dmabuf_state, - foreign_toplevel_list, fractional_scale_state, idle_notifier_state, idle_inhibit_manager_state, diff --git a/src/wayland/handlers/compositor.rs b/src/wayland/handlers/compositor.rs index a71fbb9d..1c43d3c3 100644 --- a/src/wayland/handlers/compositor.rs +++ b/src/wayland/handlers/compositor.rs @@ -265,7 +265,6 @@ impl State { let res = shell.map_window( &window, &mut self.common.toplevel_info_state, - &mut self.common.foreign_toplevel_list, &mut self.common.workspace_state, &self.common.event_loop_handle, ); diff --git a/src/wayland/handlers/foreign_toplevel_list.rs b/src/wayland/handlers/foreign_toplevel_list.rs index ad0676ae..3954ef67 100644 --- a/src/wayland/handlers/foreign_toplevel_list.rs +++ b/src/wayland/handlers/foreign_toplevel_list.rs @@ -1,67 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::{ - shell::{CosmicSurface, Shell}, - state::State, -}; +use crate::{state::State, wayland::protocols::toplevel_info::ToplevelInfoHandler}; use smithay::wayland::foreign_toplevel_list::{ - ForeignToplevelHandle, ForeignToplevelListHandler, ForeignToplevelListState, + ForeignToplevelListHandler, ForeignToplevelListState, }; -use std::sync::Mutex; impl ForeignToplevelListHandler for State { fn foreign_toplevel_list_state(&mut self) -> &mut ForeignToplevelListState { - &mut self.common.foreign_toplevel_list - } -} - -pub fn new_foreign_toplevel( - window: &CosmicSurface, - foreign_toplevel_list: &mut ForeignToplevelListState, -) { - let toplevel_handle = - foreign_toplevel_list.new_toplevel::(window.title(), window.app_id()); - *window - .user_data() - .get_or_insert::>, _>(Default::default) - .lock() - .unwrap() = Some(toplevel_handle); -} - -pub fn remove_foreign_toplevel( - window: &CosmicSurface, - foreign_toplevel_list: &mut ForeignToplevelListState, -) { - if let Some(handle) = window - .user_data() - .get::>>() - { - if let Some(handle) = handle.lock().unwrap().take() { - foreign_toplevel_list.remove_toplevel(&handle); - } - } -} - -pub fn refresh_foreign_toplevels(shell: &Shell) { - for (window, _) in shell - .workspaces - .spaces() - .flat_map(|workspace| workspace.mapped()) - .flat_map(|mapped| mapped.windows()) - { - let foreign_toplevel_handle = window - .user_data() - .get::>>() - .and_then(|handle| handle.lock().unwrap().clone()); - if let Some(handle) = foreign_toplevel_handle { - let app_id = window.app_id(); - let title = window.title(); - if handle.app_id() != app_id || handle.title() != title { - handle.send_app_id(&app_id); - handle.send_title(&title); - handle.send_done(); - } - } + &mut self.toplevel_info_state_mut().foreign_toplevel_list } } diff --git a/src/wayland/handlers/toplevel_info.rs b/src/wayland/handlers/toplevel_info.rs index 1002b93d..2b196c39 100644 --- a/src/wayland/handlers/toplevel_info.rs +++ b/src/wayland/handlers/toplevel_info.rs @@ -1,10 +1,11 @@ // SPDX-License-Identifier: GPL-3.0-only -use smithay::utils::user_data::UserDataMap; +use smithay::utils::{user_data::UserDataMap, Rectangle}; use crate::{ - shell::CosmicSurface, + shell::{element::surface::GlobalGeometry, CosmicSurface}, state::State, + utils::prelude::Global, wayland::protocols::toplevel_info::{ delegate_toplevel_info, ToplevelInfoHandler, ToplevelInfoState, Window, }, @@ -46,6 +47,20 @@ impl Window for CosmicSurface { CosmicSurface::is_minimized(self) } + fn is_sticky(&self) -> bool { + CosmicSurface::is_sticky(&self) + } + + fn global_geometry(&self) -> Option> { + self.user_data() + .get_or_insert(GlobalGeometry::default) + .0 + .lock() + .unwrap() + .clone() + .filter(|_| !self.is_minimized()) + } + fn user_data(&self) -> &UserDataMap { CosmicSurface::user_data(self) } diff --git a/src/wayland/handlers/toplevel_management.rs b/src/wayland/handlers/toplevel_management.rs index 95d0c634..7bed781f 100644 --- a/src/wayland/handlers/toplevel_management.rs +++ b/src/wayland/handlers/toplevel_management.rs @@ -228,6 +228,34 @@ impl ToplevelManagementHandler for State { } } } + + fn set_sticky(&mut self, _dh: &DisplayHandle, window: &::Window) { + if window.is_sticky() { + return; + } + + let mut shell = self.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(window).cloned() { + let seat = shell.seats.last_active().clone(); + shell.toggle_sticky(&seat, &mapped); + } + } + + fn unset_sticky( + &mut self, + _dh: &DisplayHandle, + window: &::Window, + ) { + if !window.is_sticky() { + return; + } + + let mut shell = self.common.shell.write().unwrap(); + if let Some(mapped) = shell.element_for_surface(window).cloned() { + let seat = shell.seats.last_active().clone(); + shell.toggle_sticky(&seat, &mapped); + } + } } impl ManagementWindow for CosmicSurface { diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index da9cbb42..f3a4e5fe 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -387,7 +387,6 @@ impl XdgShellHandler for State { surface.wl_surface(), &seat, &mut self.common.toplevel_info_state, - &mut self.common.foreign_toplevel_list, ); let output = shell diff --git a/src/wayland/protocols/toplevel_info.rs b/src/wayland/protocols/toplevel_info.rs index 7e9fd36e..540975d7 100644 --- a/src/wayland/protocols/toplevel_info.rs +++ b/src/wayland/protocols/toplevel_info.rs @@ -10,14 +10,20 @@ use smithay::{ Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak, }, utils::{user_data::UserDataMap, IsAlive, Logical, Rectangle}, + wayland::foreign_toplevel_list::{ + ForeignToplevelHandle, ForeignToplevelListHandler, ForeignToplevelListState, + }, }; +use crate::utils::prelude::{Global, OutputExt, RectGlobalExt}; + use super::workspace::{WorkspaceHandle, WorkspaceHandler, WorkspaceState}; use cosmic_protocols::toplevel_info::v1::server::{ zcosmic_toplevel_handle_v1::{self, State as States, ZcosmicToplevelHandleV1}, zcosmic_toplevel_info_v1::{self, ZcosmicToplevelInfoV1}, }; +use tracing::error; pub trait Window: IsAlive + Clone + PartialEq + Send { fn title(&self) -> String; @@ -26,6 +32,8 @@ pub trait Window: IsAlive + Clone + PartialEq + Send { fn is_maximized(&self) -> bool; fn is_fullscreen(&self) -> bool; fn is_minimized(&self) -> bool; + fn is_sticky(&self) -> bool; + fn global_geometry(&self) -> Option>; fn user_data(&self) -> &UserDataMap; } @@ -34,11 +42,14 @@ pub struct ToplevelInfoState { dh: DisplayHandle, pub(super) toplevels: Vec, instances: Vec, + dirty: bool, + last_dirty: bool, + pub(in crate::wayland) foreign_toplevel_list: ForeignToplevelListState, global: GlobalId, _dispatch_data: std::marker::PhantomData, } -pub trait ToplevelInfoHandler: WorkspaceHandler + Sized { +pub trait ToplevelInfoHandler: ForeignToplevelListHandler + WorkspaceHandler + Sized { type Window: Window; fn toplevel_info_state(&self) -> &ToplevelInfoState; fn toplevel_info_state_mut(&mut self) -> &mut ToplevelInfoState; @@ -50,6 +61,7 @@ pub struct ToplevelInfoGlobalData { #[derive(Default)] pub(super) struct ToplevelStateInner { + foreign_handle: Option, instances: Vec, outputs: Vec, workspaces: Vec, @@ -57,8 +69,18 @@ pub(super) struct ToplevelStateInner { } pub(super) type ToplevelState = Mutex; +impl ToplevelStateInner { + fn from_foreign(foreign_handle: ForeignToplevelHandle) -> Mutex { + Mutex::new(ToplevelStateInner { + foreign_handle: Some(foreign_handle), + ..Default::default() + }) + } +} + pub struct ToplevelHandleStateInner { outputs: Vec, + geometry: Option>, wl_outputs: HashSet, workspaces: Vec, title: String, @@ -72,6 +94,7 @@ impl ToplevelHandleStateInner { fn from_window(window: &W) -> ToplevelHandleState { ToplevelHandleState::new(ToplevelHandleStateInner { outputs: Vec::new(), + geometry: None, wl_outputs: HashSet::new(), workspaces: Vec::new(), title: String::new(), @@ -119,7 +142,7 @@ where + Dispatch> + ToplevelInfoHandler + 'static, - W: Window, + W: Window + 'static, { fn request( state: &mut D, @@ -128,14 +151,47 @@ where request: zcosmic_toplevel_info_v1::Request, _data: &(), _dh: &DisplayHandle, - _data_init: &mut DataInit<'_, D>, + data_init: &mut DataInit<'_, D>, ) { match request { + zcosmic_toplevel_info_v1::Request::GetCosmicToplevel { + cosmic_toplevel, + foreign_toplevel, + } => { + let toplevel_state = state.toplevel_info_state(); + if let Some(window) = toplevel_state.toplevels.iter().find(|w| { + w.user_data().get::().and_then(|inner| { + inner + .lock() + .unwrap() + .foreign_handle + .as_ref() + .map(|handle| handle.identifier()) + }) == ForeignToplevelHandle::from_resource(&foreign_toplevel) + .map(|handle| handle.identifier()) + }) { + let instance = data_init.init( + cosmic_toplevel, + ToplevelHandleStateInner::from_window(window), + ); + window + .user_data() + .get::() + .unwrap() + .lock() + .unwrap() + .instances + .push(instance); + } else { + error!(?foreign_toplevel, "Toplevel for foreign-toplevel-list not registered for cosmic-toplevel-info."); + } + } zcosmic_toplevel_info_v1::Request::Stop => { state .toplevel_info_state_mut() .instances .retain(|i| i != obj); + obj.finished(); } _ => {} } @@ -216,37 +272,47 @@ where D: GlobalDispatch + Dispatch + Dispatch> + + ForeignToplevelListHandler + ToplevelInfoHandler + 'static, W: Window + 'static, { pub fn new(dh: &DisplayHandle, client_filter: F) -> ToplevelInfoState where - F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static, + F: for<'a> Fn(&'a Client) -> bool + Send + Sync + Clone + 'static, { let global = dh.create_global::( - 1, + 2, ToplevelInfoGlobalData { - filter: Box::new(client_filter), + filter: Box::new(client_filter.clone()), }, ); + let foreign_toplevel_list = + ForeignToplevelListState::new_with_filter::(dh, client_filter); ToplevelInfoState { dh: dh.clone(), toplevels: Vec::new(), instances: Vec::new(), + dirty: false, + last_dirty: false, + foreign_toplevel_list, global, _dispatch_data: std::marker::PhantomData, } } pub fn new_toplevel(&mut self, toplevel: &W, workspace_state: &WorkspaceState) { + let toplevel_handle = self + .foreign_toplevel_list + .new_toplevel::(toplevel.title(), toplevel.app_id()); toplevel .user_data() - .insert_if_missing(ToplevelState::default); + .insert_if_missing(move || ToplevelStateInner::from_foreign(toplevel_handle)); for instance in &self.instances { send_toplevel_to_client::(&self.dh, workspace_state, instance, toplevel); } self.toplevels.push(toplevel.clone()); + self.dirty = true; } pub fn remove_toplevel(&mut self, toplevel: &W) { @@ -254,20 +320,27 @@ where let mut state_inner = state.lock().unwrap(); for handle in &state_inner.instances { // don't send events to stopped instances - if self - .instances - .iter() - .any(|i| i.id().same_client_as(&handle.id())) + if handle.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE + && self + .instances + .iter() + .any(|i| i.id().same_client_as(&handle.id())) { handle.closed(); } } + if let Some(handle) = state_inner.foreign_handle.take() { + self.foreign_toplevel_list.remove_toplevel(&handle); + } *state_inner = Default::default(); + self.dirty = true; } self.toplevels.retain(|w| w != toplevel); } pub fn refresh(&mut self, workspace_state: &WorkspaceState) { + let mut dirty = std::mem::replace(&mut self.dirty, false); + self.toplevels.retain(|window| { let mut state = window .user_data() @@ -281,23 +354,41 @@ where if window.alive() { std::mem::drop(state); for instance in &self.instances { - send_toplevel_to_client::(&self.dh, workspace_state, instance, window); + let changed = send_toplevel_to_client::( + &self.dh, + workspace_state, + instance, + window, + ); + dirty = dirty || changed; } true } else { for handle in &state.instances { // don't send events to stopped instances - if self - .instances - .iter() - .any(|i| i.id().same_client_as(&handle.id())) + if handle.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE + && self + .instances + .iter() + .any(|i| i.id().same_client_as(&handle.id())) { handle.closed(); } } + dirty = true; false } }); + + if !dirty && self.last_dirty { + for instance in &self.instances { + if instance.version() >= zcosmic_toplevel_info_v1::EVT_DONE_SINCE { + instance.done(); + } + } + } + + self.last_dirty = dirty; } pub fn global_id(&self) -> GlobalId { @@ -310,7 +401,8 @@ fn send_toplevel_to_client( workspace_state: &WorkspaceState, info: &ZcosmicToplevelInfoV1, window: &W, -) where +) -> bool +where D: GlobalDispatch + Dispatch + Dispatch> @@ -331,22 +423,26 @@ fn send_toplevel_to_client( { Some(i) => i, None => { - if let Ok(client) = dh.get_client(info.id()) { - if let Ok(toplevel_handle) = client - .create_resource::( - dh, - info.version(), - ToplevelHandleStateInner::from_window(window), - ) - { - info.toplevel(&toplevel_handle); - state.instances.push(toplevel_handle); - state.instances.last().unwrap() + if info.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE { + if let Ok(client) = dh.get_client(info.id()) { + if let Ok(toplevel_handle) = client + .create_resource::( + dh, + info.version(), + ToplevelHandleStateInner::from_window(window), + ) + { + info.toplevel(&toplevel_handle); + state.instances.push(toplevel_handle); + state.instances.last().unwrap() + } else { + return false; + } } else { - return; + return false; } } else { - return; + return false; } } }; @@ -356,15 +452,27 @@ fn send_toplevel_to_client( .unwrap() .lock() .unwrap(); + let foreign_toplevel_handle = state.foreign_handle.as_ref(); let mut changed = false; + if handle_state.title != window.title() { handle_state.title = window.title(); - instance.title(handle_state.title.clone()); + if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE { + instance.title(handle_state.title.clone()); + } + if let Some(handle) = foreign_toplevel_handle { + handle.send_title(&handle_state.title); + } changed = true; } if handle_state.app_id != window.app_id() { handle_state.app_id = window.app_id(); - instance.app_id(handle_state.app_id.clone()); + if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE { + instance.app_id(handle_state.app_id.clone()); + } + if let Some(handle) = foreign_toplevel_handle { + handle.send_app_id(&handle_state.app_id); + } changed = true; } @@ -386,6 +494,11 @@ fn send_toplevel_to_client( if window.is_minimized() { states.push(States::Minimized); } + if instance.version() >= zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE + && window.is_sticky() + { + states.push(States::Sticky); + } handle_state.states = states.clone(); let states: Vec = { @@ -400,15 +513,35 @@ fn send_toplevel_to_client( changed = true; } + let geometry = window.global_geometry(); + let mut geometry_changed = false; + if handle_state.geometry != geometry { + handle_state.geometry = geometry; + changed = true; + geometry_changed = true; + } + if let Ok(client) = dh.get_client(instance.id()) { handle_state.outputs = state.outputs.clone(); let handle_state = &mut *handle_state; for output in &handle_state.outputs { + let geometry = handle_state + .geometry + .filter(|_| instance.version() >= zcosmic_toplevel_handle_v1::EVT_GEOMETRY_SINCE) + .filter(|geo| output.geometry().intersection(*geo).is_some()) + .map(|geo| geo.to_local(&output)); for wl_output in output.client_outputs(&client) { if handle_state.wl_outputs.insert(wl_output.clone()) { instance.output_enter(&wl_output); + if let Some(geo) = geometry { + instance.geometry(&wl_output, geo.loc.x, geo.loc.y, geo.size.w, geo.size.h); + } changed = true; + } else if geometry_changed { + if let Some(geo) = geometry { + instance.geometry(&wl_output, geo.loc.x, geo.loc.y, geo.size.w, geo.size.h); + } } } } @@ -449,8 +582,15 @@ fn send_toplevel_to_client( handle_state.workspaces = state.workspaces.clone(); if changed { - instance.done(); + if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE { + instance.done(); + } + if let Some(handle) = foreign_toplevel_handle { + handle.send_done(); + } } + + changed } pub fn window_from_handle(handle: ZcosmicToplevelHandleV1) -> Option { diff --git a/src/wayland/protocols/toplevel_management.rs b/src/wayland/protocols/toplevel_management.rs index b823f55f..efd8d325 100644 --- a/src/wayland/protocols/toplevel_management.rs +++ b/src/wayland/protocols/toplevel_management.rs @@ -61,6 +61,9 @@ where fn unmaximize(&mut self, dh: &DisplayHandle, window: &::Window) {} fn minimize(&mut self, dh: &DisplayHandle, window: &::Window) {} fn unminimize(&mut self, dh: &DisplayHandle, window: &::Window) {} + fn set_sticky(&mut self, dh: &DisplayHandle, window: &::Window) {} + fn unset_sticky(&mut self, dh: &DisplayHandle, window: &::Window) { + } fn move_to_workspace( &mut self, dh: &DisplayHandle, @@ -113,7 +116,7 @@ impl ToplevelManagementState { F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static, { let global = dh.create_global::( - 2, + 3, ToplevelManagerGlobalData { filter: Box::new(client_filter), }, @@ -216,6 +219,14 @@ where let window = window_from_handle(toplevel).unwrap(); state.unminimize(dh, &window); } + zcosmic_toplevel_manager_v1::Request::SetSticky { toplevel } => { + let window = window_from_handle(toplevel).unwrap(); + state.set_sticky(dh, &window); + } + zcosmic_toplevel_manager_v1::Request::UnsetSticky { toplevel } => { + let window = window_from_handle(toplevel).unwrap(); + state.unset_sticky(dh, &window); + } zcosmic_toplevel_manager_v1::Request::SetRectangle { toplevel, surface, diff --git a/src/xwayland.rs b/src/xwayland.rs index c423fcdd..bec87771 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -384,7 +384,6 @@ impl XwmHandler for State { let res = shell.map_window( &window, &mut self.common.toplevel_info_state, - &mut self.common.foreign_toplevel_list, &mut self.common.workspace_state, &self.common.event_loop_handle, ); @@ -414,12 +413,7 @@ impl XwmHandler for State { shell.override_redirect_windows.retain(|or| or != &window); } else { let seat = shell.seats.last_active().clone(); - shell.unmap_surface( - &window, - &seat, - &mut self.common.toplevel_info_state, - &mut self.common.foreign_toplevel_list, - ); + shell.unmap_surface(&window, &seat, &mut self.common.toplevel_info_state); } let outputs = if let Some(wl_surface) = window.wl_surface() {