2023-11-07 18:46:25 +01:00
|
|
|
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::<ClientState>()
|
|
|
|
|
.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 {
|
2024-02-21 13:24:56 -08:00
|
|
|
data.user_data
|
|
|
|
|
.insert_if_missing(|| ActivationContext::UrgentOnly);
|
2023-11-07 18:46:25 +01:00
|
|
|
debug!(?token, "created urgent-only token for missing seat/serial");
|
2024-02-21 13:24:56 -08:00
|
|
|
return true;
|
2023-11-07 18:46:25 +01:00
|
|
|
};
|
|
|
|
|
let Some(seat) = Seat::from_resource(&seat) else {
|
2024-02-21 13:24:56 -08:00
|
|
|
data.user_data
|
|
|
|
|
.insert_if_missing(|| ActivationContext::UrgentOnly);
|
2023-11-07 18:46:25 +01:00
|
|
|
debug!(?token, "created urgent-only token for unknown seat");
|
2024-02-21 13:24:56 -08:00
|
|
|
return true;
|
2023-11-07 18:46:25 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 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::<ActivationContext>() {
|
2024-03-21 12:44:40 +01:00
|
|
|
if let Some(element) = self.common.shell.element_for_surface(&surface).cloned() {
|
2023-11-07 18:46:25 +01:00
|
|
|
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) => {
|
2024-04-05 13:53:35 +02:00
|
|
|
let seat = self.common.shell.seats.last_active().clone();
|
2023-11-07 18:46:25 +01:00
|
|
|
let current_output = seat.active_output();
|
2024-02-27 14:20:13 +01:00
|
|
|
|
|
|
|
|
if element.is_minimized() {
|
|
|
|
|
self.common.shell.unminimize_request(&element, &seat);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-07 18:46:25 +01:00
|
|
|
let current_workspace = self.common.shell.active_space_mut(¤t_output);
|
|
|
|
|
|
2023-11-16 12:00:27 -05:00
|
|
|
let in_current_workspace = current_workspace
|
2023-11-07 18:46:25 +01:00
|
|
|
.floating_layer
|
|
|
|
|
.mapped()
|
2023-11-16 12:00:27 -05:00
|
|
|
.any(|m| m == &element);
|
|
|
|
|
|
|
|
|
|
if in_current_workspace {
|
2023-11-07 18:46:25 +01:00
|
|
|
current_workspace
|
|
|
|
|
.floating_layer
|
|
|
|
|
.space
|
|
|
|
|
.raise_element(&element, true);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-14 17:30:11 +01:00
|
|
|
if element.is_stack() {
|
|
|
|
|
if let Some((window, _)) = element.windows().find(|(window, _)| {
|
|
|
|
|
let mut found = false;
|
|
|
|
|
window.with_surfaces(|wl_surface, _| {
|
|
|
|
|
if wl_surface == &surface {
|
|
|
|
|
found = true;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
found
|
|
|
|
|
}) {
|
|
|
|
|
element.set_active(&window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-27 14:20:13 +01:00
|
|
|
if workspace == ¤t_workspace.handle || in_current_workspace {
|
2024-02-23 17:25:40 +01:00
|
|
|
let target = element.into();
|
2023-11-07 18:46:25 +01:00
|
|
|
Shell::set_focus(self, Some(&target), &seat, None);
|
2024-02-23 17:25:40 +01:00
|
|
|
} else if let Some(w) = self
|
|
|
|
|
.common
|
|
|
|
|
.shell
|
|
|
|
|
.space_for(&element)
|
|
|
|
|
.map(|w| w.handle.clone())
|
2023-11-16 12:00:27 -05:00
|
|
|
{
|
2024-04-05 13:53:35 +02:00
|
|
|
self.common.shell.append_focus_stack(&element, &seat);
|
2023-11-16 12:00:27 -05:00
|
|
|
self.common.shell.set_urgent(&w);
|
2023-11-07 18:46:25 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
self.common
|
|
|
|
|
.shell
|
|
|
|
|
.pending_activations
|
|
|
|
|
.insert(ActivationKey::Wayland(surface), context.clone());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delegate_xdg_activation!(State);
|