seat: prevent Chromium from crashing when active keyboard is gone

Chromium (and slurp) expect wl_keyboard.keymap event to be sent before
wl_keyboard.modifiers event. Normally, wl_keyboard.keymap event is sent
on the client first obtains wl_keyboard with wl_seat.get_keyboard
request.
However, after the active (especially virtual) keyboard is destroyed,
wlroots doesn't respond to wl_seat.get_keyboard request with
wl_keyboard.keymap event since there's no active keyboard on the seat.

Therefore, if we run commands like "sleep 2; wtype hello; chromium", the
active keyboard is destroyed when wtype finishes and
wl_keyboard.modifiers event is sent to Chromium when Chromium first maps
the surface and the keyboard focus is moved to it, then Chromium crashes.

With slurp, fcitx5 and multi-monitor setup, a similar thing happens.
When slurp first creates the layer-shell surfaces, the focus moves to it
(without wl_keyboard.enter though), and fcitx5 is transiently deactivated
and it destroys the virtual keyboard. Then when slurp maps those
surfaces, the focus moves between them again and wl_keyboard.modifiers
event is sent, thus slurp crashes.

So with this commit, when the active keyboard on the seat is destroyed,
the keyboard from the keyboard group is set to the seat instead so
wlroots can respond to wl_seat.get_keyboard request with
wl_keyboard.keymap.
This commit is contained in:
tokyo4j 2024-05-06 23:53:18 +09:00 committed by Johan Malm
parent 097ac44eda
commit cafdcd8e19

View file

@ -31,6 +31,19 @@ input_device_destroy(struct wl_listener *listener, void *data)
wl_list_remove(&keyboard->key.link);
wl_list_remove(&keyboard->modifier.link);
keyboard_cancel_keybind_repeat(keyboard);
/*
* If the active keyboard on the seat is destroyed, fall back
* to the keyboard from keyboard group so we can respond to
* wl_seat.get_keyboard requests with wl_keyboard.keymap event.
* This prevents Chromium from crashing when started just after
* the active keyboard is destroyed.
*/
struct wlr_keyboard *active_keyboard =
wlr_seat_get_keyboard(input->seat->seat);
if (!active_keyboard || active_keyboard == keyboard->wlr_keyboard) {
wlr_seat_set_keyboard(input->seat->seat,
&input->seat->keyboard_group->keyboard);
}
}
free(input);
}