xdg-activation: Initial implementation
This commit is contained in:
parent
9ced8c346d
commit
f7cc1985e3
11 changed files with 419 additions and 66 deletions
|
|
@ -30,5 +30,6 @@ pub mod viewporter;
|
|||
pub mod virtual_keyboard;
|
||||
pub mod wl_drm;
|
||||
pub mod workspace;
|
||||
pub mod xdg_activation;
|
||||
pub mod xdg_shell;
|
||||
pub mod xwayland_keyboard_grab;
|
||||
|
|
|
|||
150
src/wayland/handlers/xdg_activation.rs
Normal file
150
src/wayland/handlers/xdg_activation.rs
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
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 {
|
||||
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::<ActivationContext>() {
|
||||
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);
|
||||
|
|
@ -312,7 +312,7 @@ impl XdgShellHandler for State {
|
|||
.visible_outputs_for_surface(surface.wl_surface())
|
||||
.collect::<Vec<_>>();
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue