diff --git a/Cargo.lock b/Cargo.lock index 6e12c7415..bbd300f79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3947,7 +3947,7 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "smithay" version = "0.3.0" -source = "git+https://github.com/pop-os/smithay?branch=x11_fixes#15dd1675840295062cee4494fa6d49440ec0f04c" +source = "git+https://github.com/pop-os/smithay?branch=x11_and_activation#47c97480171829c64769d470c00951634f159833" dependencies = [ "appendlist", "ash", diff --git a/Cargo.toml b/Cargo.toml index 548b8db27..a99c6fcb3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,4 +88,4 @@ debug = true lto = "fat" [patch."https://github.com/Smithay/smithay.git"] -smithay = { git = "https://github.com/pop-os/smithay", branch = "x11_fixes" } \ No newline at end of file +smithay = { git = "https://github.com/pop-os/smithay", branch = "x11_and_activation" } \ No newline at end of file diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 75d847d7a..0676ae2a3 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -35,7 +35,7 @@ pub use self::grabs::*; #[derive(Debug, Default)] pub struct FloatingLayout { - pub(in crate::shell) space: Space, + pub(crate) space: Space, } impl FloatingLayout { diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 4bda1f7a5..473558158 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -30,6 +30,7 @@ use smithay::{ }, xdg::XdgShellState, }, + xdg_activation::XdgActivationState, }, xwayland::X11Surface, }; @@ -38,12 +39,15 @@ use crate::{ config::{Config, KeyModifiers, KeyPattern, WorkspaceMode}, state::client_has_security_context, utils::prelude::*, - wayland::protocols::{ - toplevel_info::ToplevelInfoState, - toplevel_management::{ManagementCapabilities, ToplevelManagementState}, - workspace::{ - WorkspaceCapabilities, WorkspaceGroupHandle, WorkspaceHandle, WorkspaceState, - WorkspaceUpdateGuard, + wayland::{ + handlers::xdg_activation::ActivationContext, + protocols::{ + toplevel_info::ToplevelInfoState, + toplevel_management::{ManagementCapabilities, ToplevelManagementState}, + workspace::{ + WorkspaceCapabilities, WorkspaceGroupHandle, WorkspaceHandle, WorkspaceState, + WorkspaceUpdateGuard, + }, }, }, }; @@ -145,6 +149,22 @@ pub enum MaximizeMode { OnTop, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ActivationKey { + Wayland(WlSurface), + X11(u32), +} + +impl From<&CosmicSurface> for ActivationKey { + fn from(value: &CosmicSurface) -> Self { + match value { + CosmicSurface::Wayland(w) => ActivationKey::Wayland(w.toplevel().wl_surface().clone()), + CosmicSurface::X11(s) => ActivationKey::X11(s.window_id()), + _ => unreachable!(), + } + } +} + #[derive(Debug)] pub struct Shell { pub workspaces: Workspaces, @@ -153,6 +173,7 @@ pub struct Shell { pub maximize_mode: MaximizeMode, pub pending_windows: Vec<(CosmicSurface, Seat, Option)>, pub pending_layers: Vec<(LayerSurface, Output, Seat)>, + pub pending_activations: HashMap, pub override_redirect_windows: Vec, // wayland_state @@ -160,6 +181,7 @@ pub struct Shell { pub toplevel_info_state: ToplevelInfoState, pub toplevel_management_state: ToplevelManagementState, pub xdg_shell_state: XdgShellState, + pub xdg_activation_state: XdgActivationState, pub workspace_state: WorkspaceState, theme: cosmic::Theme, @@ -285,6 +307,8 @@ impl WorkspaceSet { if self.active != idx { let old_active = self.active; state.remove_workspace_state(&self.workspaces[old_active].handle, WState::Active); + state.remove_workspace_state(&self.workspaces[old_active].handle, WState::Urgent); + state.remove_workspace_state(&self.workspaces[idx].handle, WState::Urgent); state.add_workspace_state(&self.workspaces[idx].handle, WState::Active); self.previously_active = Some((old_active, Instant::now())); self.active = idx; @@ -305,13 +329,13 @@ impl WorkspaceSet { self.output = new_output.clone(); } - fn refresh<'a>(&mut self) { + fn refresh<'a>(&mut self, xdg_activation_state: &XdgActivationState) { if let Some((_, start)) = self.previously_active { if Instant::now().duration_since(start).as_millis() >= ANIMATION_DURATION.as_millis() { self.previously_active = None; } } else { - self.workspaces[self.active].refresh(); + self.workspaces[self.active].refresh(xdg_activation_state); } } @@ -348,7 +372,8 @@ impl WorkspaceSet { let len = self.workspaces.len(); let mut keep = vec![true; len]; for (i, workspace) in self.workspaces.iter().enumerate() { - let has_windows = workspace.windows().next().is_some(); + let has_windows = + !workspace.pending_tokens.is_empty() || workspace.windows().next().is_some(); if !has_windows && i != self.active && i != len - 1 { state.remove_workspace(workspace.handle); @@ -376,6 +401,7 @@ impl WorkspaceSet { amount: usize, state: &mut WorkspaceUpdateGuard, toplevel_info: &mut ToplevelInfoState, + xdg_activation_state: &XdgActivationState, ) { if amount < self.workspaces.len() { // merge last ones @@ -407,7 +433,7 @@ impl WorkspaceSet { state.remove_workspace(workspace.handle); } - last_space.refresh(); + last_space.refresh(xdg_activation_state); } else if amount > self.workspaces.len() { // add empty ones while amount > self.workspaces.len() { @@ -514,6 +540,7 @@ impl Workspaces { seats: impl Iterator>, workspace_state: &mut WorkspaceUpdateGuard<'_, State>, toplevel_info_state: &mut ToplevelInfoState, + xdg_activation_state: &XdgActivationState, ) { if !self.sets.contains_key(output) { return; @@ -563,7 +590,7 @@ impl Workspaces { // update mapping workspace.set_output(&new_output, toplevel_info_state); - workspace.refresh(); + workspace.refresh(xdg_activation_state); // TODO: merge if mode = static new_set.workspaces.push(workspace); @@ -582,7 +609,7 @@ impl Workspaces { self.backup_set = Some(set); } - self.refresh(workspace_state, toplevel_info_state) + self.refresh(workspace_state, toplevel_info_state, xdg_activation_state) } } @@ -591,6 +618,7 @@ impl Workspaces { config: &Config, workspace_state: &mut WorkspaceUpdateGuard<'_, State>, toplevel_info_state: &mut ToplevelInfoState, + xdg_activation_state: &XdgActivationState, ) { if self.sets.len() <= 1 { return; @@ -651,13 +679,14 @@ impl Workspaces { _ => {} }; - self.refresh(workspace_state, toplevel_info_state) + self.refresh(workspace_state, toplevel_info_state, xdg_activation_state) } pub fn refresh( &mut self, workspace_state: &mut WorkspaceUpdateGuard<'_, State>, toplevel_info_state: &mut ToplevelInfoState, + xdg_activation_state: &XdgActivationState, ) { match self.mode { WorkspaceMode::Global => { @@ -697,10 +726,10 @@ impl Workspaces { let mut active = self.sets[0].active; let mut keep = vec![true; len]; for i in 0..len { - let has_windows = self - .sets - .values() - .any(|s| s.workspaces[i].windows().next().is_some()); + let has_windows = self.sets.values().any(|s| { + !s.workspaces[i].pending_tokens.is_empty() + || s.workspaces[i].windows().next().is_some() + }); if !has_windows && i != active && i != len - 1 { for workspace in self.sets.values().map(|s| &s.workspaces[i]) { @@ -734,7 +763,12 @@ impl Workspaces { } WorkspaceAmount::Static(amount) => { for set in self.sets.values_mut() { - set.ensure_static(amount as usize, workspace_state, toplevel_info_state) + set.ensure_static( + amount as usize, + workspace_state, + toplevel_info_state, + xdg_activation_state, + ) } } } @@ -747,14 +781,19 @@ impl Workspaces { } WorkspaceAmount::Static(amount) => { for set in self.sets.values_mut() { - set.ensure_static(amount as usize, workspace_state, toplevel_info_state) + set.ensure_static( + amount as usize, + workspace_state, + toplevel_info_state, + xdg_activation_state, + ) } } }, } for set in self.sets.values_mut() { - set.refresh() + set.refresh(xdg_activation_state) } } @@ -827,7 +866,7 @@ impl Workspaces { } } - pub fn set_theme(&mut self, theme: cosmic::Theme) { + pub fn set_theme(&mut self, theme: cosmic::Theme, xdg_activation_state: &XdgActivationState) { for (_, s) in &mut self.sets { s.theme = theme.clone(); for mut w in &mut s.workspaces { @@ -838,7 +877,7 @@ impl Workspaces { m.force_redraw(); }); - w.refresh(); + w.refresh(xdg_activation_state); w.dirty.store(true, Ordering::Relaxed); w.recalculate(); } @@ -853,6 +892,7 @@ impl Shell { // TODO: Privileged protocols let layer_shell_state = WlrLayerShellState::new::(dh); let xdg_shell_state = XdgShellState::new::(dh); + let xdg_activation_state = XdgActivationState::new::(dh); let toplevel_info_state = ToplevelInfoState::new( dh, //|client| client.get_data::().map_or(false, |s| s.privileged), @@ -881,12 +921,14 @@ impl Shell { pending_windows: Vec::new(), pending_layers: Vec::new(), + pending_activations: HashMap::new(), override_redirect_windows: Vec::new(), layer_shell_state, toplevel_info_state, toplevel_management_state, xdg_shell_state, + xdg_activation_state, workspace_state, theme, @@ -912,6 +954,7 @@ impl Shell { seats, &mut self.workspace_state.update(), &mut self.toplevel_info_state, + &self.xdg_activation_state, ); self.refresh(); // cleans up excess of workspaces and empty workspaces } @@ -919,8 +962,12 @@ impl Shell { pub fn update_config(&mut self, config: &Config) { let mut workspace_state = self.workspace_state.update(); let toplevel_info_state = &mut self.toplevel_info_state; - self.workspaces - .update_config(config, &mut workspace_state, toplevel_info_state); + self.workspaces.update_config( + config, + &mut workspace_state, + toplevel_info_state, + &self.xdg_activation_state, + ); } pub fn activate( @@ -965,6 +1012,12 @@ impl Shell { self.workspaces.active_mut(output) } + pub fn refresh_active_space(&mut self, output: &Output) { + self.workspaces + .active_mut(output) + .refresh(&self.xdg_activation_state) + } + pub fn visible_outputs_for_surface<'a>( &'a self, surface: &'a WlSurface, @@ -1013,10 +1066,7 @@ impl Shell { } } - pub fn workspaces_for_surface( - &self, - surface: &WlSurface, - ) -> impl Iterator { + pub fn workspace_for_surface(&self, surface: &WlSurface) -> Option<(WorkspaceHandle, Output)> { match self.outputs().find(|o| { let map = layer_map_for_output(o); map.layer_for_surface(surface, WindowSurfaceType::ALL) @@ -1036,7 +1086,6 @@ impl Shell { }) .map(|w| (w.handle.clone(), w.output().clone())), } - .into_iter() } pub fn element_for_surface(&self, surface: &CosmicSurface) -> Option<&CosmicMapped> { @@ -1201,9 +1250,13 @@ impl Shell { self.popups.cleanup(); + self.xdg_activation_state.retain_tokens(|_, data| { + Instant::now().duration_since(data.timestamp) < Duration::from_secs(5) + }); self.workspaces.refresh( &mut self.workspace_state.update(), &mut self.toplevel_info_state, + &self.xdg_activation_state, ); for output in self.outputs() { @@ -1269,10 +1322,42 @@ impl Shell { .unwrap(); let (window, seat, output) = state.common.shell.pending_windows.remove(pos); + let pending_activation = state + .common + .shell + .pending_activations + .remove(&(&window).into()); + let workspace_handle = match pending_activation { + Some(ActivationContext::Workspace(handle)) => Some(handle), + _ => None, + }; + let should_be_fullscreen = output.is_some(); - let output = output.unwrap_or_else(|| seat.active_output()); + let mut output = output.unwrap_or_else(|| seat.active_output()); + + // this is beyond stupid, just to make the borrow checker happy + let workspace = if let Some(handle) = workspace_handle.filter(|handle| { + state + .common + .shell + .workspaces + .spaces() + .any(|space| &space.handle == handle) + }) { + state + .common + .shell + .workspaces + .spaces_mut() + .find(|space| space.handle == handle) + .unwrap() + } else { + state.common.shell.workspaces.active_mut(&output) + }; + if output != workspace.output { + output = workspace.output.clone(); + } - 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 @@ -1291,7 +1376,24 @@ impl Shell { ); }; - let workspace = state.common.shell.workspaces.active_mut(&output); + let workspace = if let Some(handle) = workspace_handle.filter(|handle| { + state + .common + .shell + .workspaces + .spaces() + .any(|space| &space.handle == handle) + }) { + state + .common + .shell + .workspaces + .spaces_mut() + .find(|space| space.handle == handle) + .unwrap() + } else { + state.common.shell.workspaces.active_mut(&output) + }; state.common.shell.toplevel_info_state.new_toplevel(&window); state .common @@ -1313,6 +1415,9 @@ impl Shell { { mapped.set_debug(state.common.egui.active); } + + let workspace_empty = workspace.mapped().next().is_none(); + if layout::should_be_floating(&window) || !workspace.tiling_enabled { workspace.floating_layer.map(mapped.clone(), None); } else { @@ -1334,8 +1439,9 @@ impl Shell { if should_be_fullscreen { workspace.fullscreen_request(&mapped.active_window(), None); } - - Shell::set_focus(state, Some(&KeyboardFocusTarget::from(mapped)), &seat, None); + if workspace_empty || workspace_handle.is_some() || should_be_fullscreen { + Shell::set_focus(state, Some(&KeyboardFocusTarget::from(mapped)), &seat, None); + } let active_space = state.common.shell.active_space(&output); for mapped in active_space.mapped() { @@ -1677,7 +1783,13 @@ impl Shell { pub(crate) fn set_theme(&mut self, theme: cosmic::Theme) { self.theme = theme.clone(); self.refresh(); - self.workspaces.set_theme(theme.clone()); + self.workspaces + .set_theme(theme.clone(), &self.xdg_activation_state); + } + + pub fn set_urgent(&mut self, workspace: &WorkspaceHandle) { + let mut workspace_guard = self.workspace_state.update(); + workspace_guard.add_workspace_state(workspace, WState::Urgent); } } diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 3f1de3790..916d274f1 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -43,11 +43,12 @@ use smithay::{ wayland::{ compositor::{add_blocker, Blocker, BlockerState}, seat::WaylandFocus, + xdg_activation::{XdgActivationState, XdgActivationToken}, }, xwayland::X11Surface, }; use std::{ - collections::{HashMap, VecDeque}, + collections::{HashMap, HashSet, VecDeque}, sync::{ atomic::{AtomicBool, Ordering}, Arc, @@ -87,6 +88,7 @@ pub struct Workspace { pub pending_buffers: Vec<(ScreencopySession, BufferParams)>, pub screencopy_sessions: Vec, pub output_stack: VecDeque, + pub pending_tokens: HashSet, pub(super) backdrop_id: Id, pub dirty: AtomicBool, } @@ -227,12 +229,13 @@ impl Workspace { pending_buffers: Vec::new(), screencopy_sessions: Vec::new(), output_stack: VecDeque::new(), + pending_tokens: HashSet::new(), backdrop_id: Id::new(), dirty: AtomicBool::new(false), } } - pub fn refresh(&mut self) { + pub fn refresh(&mut self, xdg_activation_state: &XdgActivationState) { #[cfg(feature = "debug")] puffin::profile_function!(); @@ -243,6 +246,9 @@ impl Workspace { self.floating_layer.refresh(); self.tiling_layer.refresh(); + + self.pending_tokens + .retain(|token| xdg_activation_state.data_for_token(token).is_some()); } pub fn refresh_focus_stack(&mut self) { diff --git a/src/utils/iced.rs b/src/utils/iced.rs index a3a0cf134..ae59aeb1f 100644 --- a/src/utils/iced.rs +++ b/src/utils/iced.rs @@ -206,7 +206,7 @@ impl fmt::Debug for IcedElementInternal

{ .field("size", &self.size) .field("pending_update", &self.pending_update) .field("cursor_pos", &self.cursor_pos) - .field("theme", &self.theme) + .field("theme", &"...") .field("renderer", &"...") .field("state", &"...") .field("debug", &self.debug) diff --git a/src/wayland/handlers/mod.rs b/src/wayland/handlers/mod.rs index 14aa5308b..1ce631f86 100644 --- a/src/wayland/handlers/mod.rs +++ b/src/wayland/handlers/mod.rs @@ -27,5 +27,6 @@ pub mod toplevel_management; pub mod viewporter; pub mod wl_drm; pub mod workspace; +pub mod xdg_activation; pub mod xdg_shell; pub mod xwayland_keyboard_grab; diff --git a/src/wayland/handlers/screencopy.rs b/src/wayland/handlers/screencopy.rs index 16a37b196..e2dd44438 100644 --- a/src/wayland/handlers/screencopy.rs +++ b/src/wayland/handlers/screencopy.rs @@ -1246,7 +1246,7 @@ impl State { .outputs() .map(|o| (o.clone(), self.common.shell.active_space(o).handle.clone())) .collect::>(); - for (handle, output) in self.common.shell.workspaces_for_surface(surface) { + if let Some((handle, output)) = self.common.shell.workspace_for_surface(surface) { let workspace = self.common.shell.space_for_handle_mut(&handle).unwrap(); if !workspace.pending_buffers.is_empty() { // TODO: replace with drain_filter.... diff --git a/src/wayland/handlers/xdg_activation.rs b/src/wayland/handlers/xdg_activation.rs new file mode 100644 index 000000000..66fc8983f --- /dev/null +++ b/src/wayland/handlers/xdg_activation.rs @@ -0,0 +1,115 @@ +use smithay::{ + delegate_xdg_activation, + input::Seat, + reexports::wayland_server::protocol::wl_surface::WlSurface, + wayland::xdg_activation::{ + XdgActivationHandler, XdgActivationState, XdgActivationToken, XdgActivationTokenData, + }, +}; +use tracing::debug; + +use crate::{shell::ActivationKey, utils::prelude::*}; +use crate::{state::State, wayland::protocols::workspace::WorkspaceHandle}; + +#[derive(Debug, Clone, Copy)] +pub enum ActivationContext { + UrgentOnly, + Workspace(WorkspaceHandle), +} + +impl XdgActivationHandler for State { + fn activation_state(&mut self) -> &mut XdgActivationState { + &mut self.common.shell.xdg_activation_state + } + + fn token_created(&mut self, token: XdgActivationToken, data: XdgActivationTokenData) -> bool { + // Tokens without validation aren't allowed to steal focus + let Some((serial, seat)) = data.serial else { + data.user_data.insert_if_missing(|| ActivationContext::UrgentOnly); + debug!(?token, "created urgent-only token for missing seat/serial"); + return true + }; + let Some(seat) = Seat::from_resource(&seat) else { + data.user_data.insert_if_missing(|| ActivationContext::UrgentOnly); + debug!(?token, "created urgent-only token for unknown seat"); + return true + }; + + // At this point we don't bother with urgent-only tokens. + // If the client provides a bad serial, it should be fixed. + + let keyboard = seat.get_keyboard().unwrap(); + let valid = keyboard + .last_enter() + .map(|last_enter| serial.is_no_older_than(&last_enter)) + .unwrap_or(false); + + if valid { + let output = seat.active_output(); + let workspace = self.common.shell.active_space_mut(&output); + workspace.pending_tokens.insert(token.clone()); + let handle = workspace.handle; + data.user_data + .insert_if_missing(move || ActivationContext::Workspace(handle)); + + debug!(?token, "created workspace token"); + } else { + debug!(?token, "created urgent-only token for invalid serial"); + } + + valid + } + + fn request_activation( + &mut self, + _token: XdgActivationToken, + token_data: XdgActivationTokenData, + surface: WlSurface, + ) { + if let Some(context) = token_data.user_data.get::() { + if let Some(element) = self.common.shell.element_for_wl_surface(&surface).cloned() { + match context { + ActivationContext::UrgentOnly => { + if let Some((workspace, _output)) = + self.common.shell.workspace_for_surface(&surface) + { + self.common.shell.set_urgent(&workspace); + } + } + ActivationContext::Workspace(workspace) => { + let seat = self.common.last_active_seat().clone(); + let current_output = seat.active_output(); + let current_workspace = self.common.shell.active_space_mut(¤t_output); + + if current_workspace + .floating_layer + .mapped() + .any(|m| m == &element) + { + current_workspace + .floating_layer + .space + .raise_element(&element, true); + } + + if workspace == ¤t_workspace.handle { + let target = element.into(); + Shell::set_focus(self, Some(&target), &seat, None); + } else { + let mut focus_stack = current_workspace.focus_stack.get_mut(&seat); + focus_stack.append(&element); + self.common.shell.set_urgent(workspace); + } + } + } + } else { + self.common + .shell + .pending_activations + .insert(ActivationKey::Wayland(surface), context.clone()); + } + } + } +} + +delegate_xdg_activation!(State); diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index bc8b23906..a761b7d77 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -264,7 +264,7 @@ impl XdgShellHandler for State { .shell .pending_windows .iter_mut() - .find(|(s, _, _)| s.wl_surface().as_ref() == Some(surface.wl_surface())) + .find(|(s, _, __)| s.wl_surface().as_ref() == Some(surface.wl_surface())) .map(|(_, _, o)| o) { *o = Some(output); @@ -312,7 +312,7 @@ impl XdgShellHandler for State { .visible_outputs_for_surface(surface.wl_surface()) .collect::>(); for output in outputs.iter() { - self.common.shell.active_space_mut(output).refresh(); + self.common.shell.refresh_active_space(output); } // animations might be unblocked now diff --git a/src/xwayland.rs b/src/xwayland.rs index a344178a1..600632e5c 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -5,23 +5,29 @@ use crate::{ shell::{focus::target::KeyboardFocusTarget, CosmicSurface, Shell}, state::State, utils::prelude::*, - wayland::{handlers::screencopy::PendingScreencopyBuffers, protocols::screencopy::SessionType}, + wayland::{ + handlers::{screencopy::PendingScreencopyBuffers, xdg_activation::ActivationContext}, + protocols::screencopy::SessionType, + }, }; use smithay::{ backend::drm::DrmNode, desktop::space::SpaceElement, reexports::x11rb::protocol::xproto::Window as X11Window, utils::{Logical, Point, Rectangle, Size}, - wayland::selection::{ - data_device::{ - clear_data_device_selection, current_data_device_selection_userdata, - request_data_device_client_selection, set_data_device_selection, - }, - primary_selection::{ - clear_primary_selection, current_primary_selection_userdata, - request_primary_client_selection, set_primary_selection, + wayland::{ + selection::{ + data_device::{ + clear_data_device_selection, current_data_device_selection_userdata, + request_data_device_client_selection, set_data_device_selection, + }, + primary_selection::{ + clear_primary_selection, current_primary_selection_userdata, + request_primary_client_selection, set_primary_selection, + }, + SelectionTarget, }, - SelectionTarget, + xdg_activation::XdgActivationToken, }, xwayland::{ xwm::{Reorder, XwmId}, @@ -145,12 +151,29 @@ impl XwmHandler for State { warn!(?window, ?err, "Failed to send Xwayland Mapped-Event",); } + let startup_id = window.startup_id(); let surface = CosmicSurface::X11(window.clone()); if self.common.shell.element_for_surface(&surface).is_some() { return; } let seat = self.common.last_active_seat().clone(); + if let Some(context) = startup_id + .map(XdgActivationToken::from) + .and_then(|token| { + self.common + .shell + .xdg_activation_state + .data_for_token(&token) + }) + .and_then(|data| data.user_data.get::()) + { + self.common.shell.pending_activations.insert( + crate::shell::ActivationKey::X11(window.window_id()), + context.clone(), + ); + } + self.common .shell .pending_windows @@ -172,6 +195,30 @@ impl XwmHandler for State { }) .cloned() { + if !self + .common + .shell + .pending_activations + .contains_key(&crate::shell::ActivationKey::X11(surface.window_id())) + { + if let Some(startup_id) = match &window { + CosmicSurface::X11(x11) => x11.startup_id(), + _ => None, + } { + if let Some(context) = self + .common + .shell + .xdg_activation_state + .data_for_token(&XdgActivationToken::from(startup_id)) + .and_then(|data| data.user_data.get::()) + { + self.common.shell.pending_activations.insert( + crate::shell::ActivationKey::X11(surface.window_id()), + context.clone(), + ); + } + } + } Shell::map_window(self, &window); } } @@ -224,7 +271,7 @@ impl XwmHandler for State { self.common.shell.outputs().cloned().collect::>() }; for output in outputs.iter() { - self.common.shell.active_space_mut(output).refresh(); + self.common.shell.refresh_active_space(output); } // screencopy