Fix shell focus, launcher touch dismissal, and sandbox clipboard access

This commit is contained in:
Samuel Onoja committed 2026-10-14 18:11:13 +01:00
1 parent c8dfb3074d
commit a62f1367bd
8 files changed
+126 -79

No files matched your search

+5 -29
View File
@@ -6,6 +6,7 @@ use smithay::reexports::wayland_server::{Resource, backend::ClientId};
use smithay::utils::Serial;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
#[cfg(test)]
use smithay::utils::SERIAL_COUNTER;
use smithay::wayland::xdg_activation::{
XdgActivationHandler, XdgActivationState, XdgActivationToken, XdgActivationTokenData,
@@ -110,34 +111,10 @@ impl XdgActivationHandler for Ferese {
return;
}
let Some(window) = self
.space
.elements()
.find(|window| {
window
.toplevel()
.is_some_and(|toplevel| toplevel.wl_surface() == &surface)
})
.cloned()
else {
let Some(id) = self.windows.id_for_surface(&surface) else {
return;
};
self.focused_window = self.windows.ids().get(&window).copied();
if let Some(focused) = self.focused_window
&& let Err(error) = self.workspaces.focus_window(focused)
{
tracing::error!(%error, ?focused, "failed to update workspace focus");
return;
}
self.raise_window(&window, true);
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
keyboard.set_focus(self, Some(surface), SERIAL_COUNTER.next_serial());
self.space.elements().for_each(|window| {
if let Some(toplevel) = window.toplevel() {
toplevel.send_pending_configure();
}
});
self.activate_managed_window(id);
}
}
@@ -252,8 +229,7 @@ mod tests {
assert_ne!(approved.client_id, Some(recipient.id()));
assert!(approved.user_data.get::<ApprovedInteraction>().unwrap().0.elapsed() < TOKEN_MAX_AGE);
// Exercise the production handler with a real recipient toplevel.
// Creating the window needs no GPU or client buffer.
// A valid handoff must not focus a recipient before its first buffer.
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_wm_base::XdgWmBase;
use smithay::reexports::wayland_server::protocol::wl_compositor::WlCompositor;
use smithay::wayland::shell::xdg::XdgWmBaseUserData;
@@ -279,7 +255,7 @@ mod tests {
})
.expect("recipient's toplevel");
state.request_activation(token, approved, target.clone());
assert_eq!(state.seat.get_keyboard().unwrap().current_focus(), Some(target));
assert_eq!(state.seat.get_keyboard().unwrap().current_focus(), None);
}
#[test]
+15 -6
View File
@@ -195,11 +195,14 @@ pub fn handle_commit(state: &mut Ferese, surface: &WlSurface) {
}
impl Ferese {
pub(crate) fn layer_keyboard_eligible(&self, layer: &DesktopLayerSurface) -> bool {
layer.can_receive_keyboard_focus() && self.layer_mapped_and_visible(layer)
}
/// Layer-map membership tracks the role and output, not buffer mapping.
/// Use the renderer state updated by on_commit_buffer_handler, as for windows.
pub(crate) fn layer_keyboard_eligible(&self, layer: &DesktopLayerSurface) -> bool {
fn layer_mapped_and_visible(&self, layer: &DesktopLayerSurface) -> bool {
layer.layer_surface().alive()
&& layer.can_receive_keyboard_focus()
&& smithay::backend::renderer::utils::with_renderer_surface_state(layer.wl_surface(), |state| {
state.buffer().is_some()
})
@@ -262,10 +265,16 @@ impl Ferese {
return;
}
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
if let Some(root) = self.keyboard_layer_root().filter(|_| {
!self
.keyboard_focused_layer()
.is_some_and(|layer| self.layer_keyboard_eligible(&layer))
if let Some(root) = self.keyboard_layer_root().filter(|root| {
!self.keyboard_focused_layer().is_some_and(|layer| {
// A popup owns its keyboard focus independently of the bar's
// interactivity policy, but still needs a visible, mapped parent.
if keyboard.current_focus().as_ref() != Some(root) {
self.layer_mapped_and_visible(&layer)
} else {
self.layer_keyboard_eligible(&layer)
}
})
}) {
// A popup grab cannot keep an ineligible layer alive as input owner.
for (popup, _) in smithay::desktop::PopupManager::popups_for_surface(&root) {
+1
View File
@@ -6,6 +6,7 @@ mod layer_shell;
pub(crate) mod screencopy;
pub(crate) mod screenshot;
pub(crate) mod screenshot_worker;
pub(crate) mod security_context;
pub(crate) mod window_capture;
mod xdg_shell;
@@ -0,0 +1,31 @@
use std::sync::Arc;
use smithay::reexports::wayland_server::Client;
use smithay::wayland::security_context::{SecurityContext, SecurityContextHandler, SecurityContextListenerSource};
use crate::Ferese;
use crate::state::ClientState;
pub(crate) fn unrestricted_client(client: &Client) -> bool {
client
.get_data::<ClientState>()
.is_some_and(|state| state.security_context.is_none())
}
impl SecurityContextHandler for Ferese {
fn context_created(&mut self, source: SecurityContextListenerSource, context: SecurityContext) {
if let Err(error) = self.loop_handle.insert_source(source, move |stream, _, state| {
let data = ClientState {
security_context: Some(context.clone()),
..Default::default()
};
if let Err(error) = state.display_handle.insert_client(stream, Arc::new(data)) {
tracing::warn!(%error, "failed to register sandboxed Wayland client");
}
}) {
tracing::warn!(%error, "failed to register security context listener");
}
}
}
smithay::delegate_security_context!(Ferese);