Commit 913b1f9fe652
tarball6 files changed+98-1
@@ -3,6 +3,7 @@
33
44
use crate::state::State;55
use smithay::backend::input::KeyState;6
+use smithay::desktop::PopupKind;67
use smithay::input::Seat;78
use smithay::input::keyboard::{KeyboardTarget, KeysymHandle, ModifiersState};89
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;@@ -90,3 +91,20 @@ impl KeyboardTarget<State> for Focus {
9091
}9192
}9293
}94
+95
+impl From<PopupKind> for Focus {96
+ fn from(p: PopupKind) -> Self {97
+ Focus::Surface(p.wl_surface().clone())98
+ }99
+}100
+101
+impl From<Focus> for WlSurface {102
+ fn from(f: Focus) -> Self {103
+ match f {104
+ Focus::Surface(s) => s,105
+ Focus::X11(x) => x106
+ .wl_surface()107
+ .expect("popup grabs are rooted at wayland surfaces"),108
+ }109
+ }110
+}@@ -54,6 +54,7 @@ impl State {
5454
let Some(pointer) = self.input.seat.get_pointer() else {5555
return;5656
};57
+ let grabbed = self.keyboard_grabbed();5758
pointer.button(5859
self,5960
&ButtonEvent {@@ -63,6 +64,9 @@ impl State {
6364
time,6465
},6566
);67
+ if grabbed {68
+ self.sync_focus();69
+ }6670
}6771
6872
pub fn pointer_axis(&mut self, frame: AxisFrame) {@@ -19,6 +19,7 @@ mod layers;
1919
mod lock;2020
mod nested;2121
mod pacing;22
+mod popups;2223
mod power;2324
mod scene;2425
mod seat;@@ -0,0 +1,69 @@
1
+//! xdg popups, from their grabs to where they are placed.2
+3
+use crate::focus::Focus;4
+use crate::state::State;5
+use smithay::desktop::{6
+ PopupKeyboardGrab, PopupKind, PopupPointerGrab, PopupUngrabStrategy, find_popup_root_surface,7
+};8
+use smithay::input::Seat;9
+use smithay::input::pointer::Focus as GrabFocus;10
+use smithay::reexports::wayland_server::protocol::wl_seat::WlSeat;11
+use smithay::utils::Serial;12
+use smithay::wayland::shell::xdg::PopupSurface;13
+14
+impl State {15
+ /// Popup grabs are the only keyboard grabs, and they hand the focus back16
+ /// to the grab's root, not to whatever the layout has focused, so the17
+ /// callers resync once one may have ended.18
+ pub fn keyboard_grabbed(&self) -> bool {19
+ self.input20
+ .seat21
+ .get_keyboard()22
+ .is_some_and(|k| k.is_grabbed())23
+ }24
+25
+ /// Gives an explicit popup grab the keyboard and pointer, so clicking26
+ /// elsewhere dismisses it. A locked session refuses grabs, since a27
+ /// client could otherwise take the keyboard from the lock surface.28
+ pub fn grab_popup(&mut self, surface: PopupSurface, seat: &WlSeat, serial: Serial) {29
+ let Some(seat) = Seat::from_resource(seat) else {30
+ return;31
+ };32
+ if self.services.lock.active() {33
+ surface.send_popup_done();34
+ return;35
+ }36
+ let kind = PopupKind::Xdg(surface);37
+ let Ok(root) = find_popup_root_surface(&kind) else {38
+ return;39
+ };40
+ let Ok(mut grab) = self41
+ .wm42
+ .popups43
+ .grab_popup(Focus::from(root), kind, &seat, serial)44
+ else {45
+ return;46
+ };47
+ if let Some(keyboard) = seat.get_keyboard() {48
+ if keyboard.is_grabbed()49
+ && !(keyboard.has_grab(serial)50
+ || keyboard.has_grab(grab.previous_serial().unwrap_or(serial)))51
+ {52
+ grab.ungrab(PopupUngrabStrategy::All);53
+ return;54
+ }55
+ keyboard.set_focus(self, grab.current_grab(), serial);56
+ keyboard.set_grab(self, PopupKeyboardGrab::new(&grab), serial);57
+ }58
+ if let Some(pointer) = seat.get_pointer() {59
+ if pointer.is_grabbed()60
+ && !(pointer.has_grab(serial)61
+ || pointer.has_grab(grab.previous_serial().unwrap_or_else(|| grab.serial())))62
+ {63
+ grab.ungrab(PopupUngrabStrategy::All);64
+ return;65
+ }66
+ pointer.set_grab(self, PopupPointerGrab::new(&grab), serial, GrabFocus::Keep);67
+ }68
+ }69
+}@@ -213,6 +213,9 @@ impl State {
213213
pub fn after_dispatch(&mut self) {214214
self.wm.space.refresh();215215
self.wm.popups.cleanup();216
+ if self.keyboard_grabbed() {217
+ self.sync_focus();218
+ }216219
match self.backend {217220
crate::backend::Backend::Drm(_) => crate::drm::render(self),218221
crate::backend::Backend::Nested(_) => crate::nested::render(self),@@ -72,7 +72,9 @@ impl XdgShellHandler for State {
7272
surface.send_repositioned(token);7373
}7474
75
- fn grab(&mut self, _surface: PopupSurface, _seat: WlSeat, _serial: Serial) {}75
+ fn grab(&mut self, surface: PopupSurface, seat: WlSeat, serial: Serial) {76
+ self.grab_popup(surface, &seat, serial);77
+ }7678
}7779
7880
impl XdgDecorationHandler for State {