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, state::ClientState, 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 { // Privileged clients always get valid tokens if data .client_id .and_then(|client_id| { self.common .display_handle .backend_handle() .get_client_data(client_id) .ok() }) .and_then(|data| { data.downcast_ref::() .map(|data| data.privileged) }) .unwrap_or(false) { if let Some(seat) = data.serial.and_then(|(_, seat)| Seat::from_resource(&seat)) { 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 for privileged client"); } else { data.user_data .insert_if_missing(|| ActivationContext::UrgentOnly); debug!( ?token, "created urgent-only token for privileged client without seat" ); } return true; }; // 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); } let target = element.into(); if workspace == ¤t_workspace.handle { Shell::set_focus(self, Some(&target), &seat, None); } else { Shell::append_focus_stack(self, Some(&target), &seat); self.common.shell.set_urgent(workspace); } } } } else { self.common .shell .pending_activations .insert(ActivationKey::Wayland(surface), context.clone()); } } } } delegate_xdg_activation!(State);