Skip to content

Commit 1dd4a88

Browse files
committed
Wayland: Unstuck keys with same keycode
This fixes once and for all the core issue of different Godot `keycode`s released from the same raw XKB keycode. The `InputEventKey` `keycode` value _should_ map to the "unmodified" key, but unfortunately there's an ambiguity with their encoding for "special" keys ("delete", "insert", etc.), in witch they ignore their unicode representation. This means that a key that is special when plain but a character when modified would never be properly picked up, so we do indeed change its keycode. As a consequence of this exception, some Godot keys never receive release events and get "stuck". This patch adds an extra check through an `HashMap` to "unstuck" keys that changed while having the same keycode. I also could not resist simplifying a bit the regular key event generation method but this makes things more consistent and predictable IMO.
1 parent 261e7d3 commit 1dd4a88

File tree

4 files changed

+81
-65
lines changed

4 files changed

+81
-65
lines changed

platform/linuxbsd/wayland/key_mapping_xkb.cpp

-27
Original file line numberDiff line numberDiff line change
@@ -369,33 +369,6 @@ void KeyMappingXKB::initialize() {
369369
location_map[0x86] = KeyLocation::RIGHT;
370370
}
371371

372-
bool KeyMappingXKB::is_sym_numpad(xkb_keysym_t p_keysym) {
373-
switch (p_keysym) {
374-
case XKB_KEY_KP_Equal:
375-
case XKB_KEY_KP_Add:
376-
case XKB_KEY_KP_Subtract:
377-
case XKB_KEY_KP_Multiply:
378-
case XKB_KEY_KP_Divide:
379-
case XKB_KEY_KP_Separator:
380-
case XKB_KEY_KP_Decimal:
381-
case XKB_KEY_KP_Delete:
382-
case XKB_KEY_KP_0:
383-
case XKB_KEY_KP_1:
384-
case XKB_KEY_KP_2:
385-
case XKB_KEY_KP_3:
386-
case XKB_KEY_KP_4:
387-
case XKB_KEY_KP_5:
388-
case XKB_KEY_KP_6:
389-
case XKB_KEY_KP_7:
390-
case XKB_KEY_KP_8:
391-
case XKB_KEY_KP_9: {
392-
return true;
393-
} break;
394-
}
395-
396-
return false;
397-
}
398-
399372
Key KeyMappingXKB::get_keycode(xkb_keycode_t p_keysym) {
400373
if (p_keysym >= 0x20 && p_keysym < 0x7E) { // ASCII, maps 1-1
401374
if (p_keysym > 0x60 && p_keysym < 0x7B) { // Lowercase ASCII.

platform/linuxbsd/wayland/key_mapping_xkb.h

-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ class KeyMappingXKB {
5656
public:
5757
static void initialize();
5858

59-
static bool is_sym_numpad(xkb_keysym_t p_keysym);
6059
static Key get_keycode(xkb_keysym_t p_keysym);
6160
static xkb_keycode_t get_xkb_keycode(Key p_keycode);
6261
static Key get_scancode(unsigned int p_code);

platform/linuxbsd/wayland/wayland_thread.cpp

+77-36
Original file line numberDiff line numberDiff line change
@@ -191,31 +191,34 @@ Vector<uint8_t> WaylandThread::_wp_primary_selection_offer_read(struct wl_displa
191191
return Vector<uint8_t>();
192192
}
193193

194-
// Sets up an `InputEventKey` and returns whether it has any meaningful value.
195-
bool WaylandThread::_seat_state_configure_key_event(SeatState &p_ss, Ref<InputEventKey> p_event, xkb_keycode_t p_keycode, bool p_pressed) {
196-
xkb_keysym_t shifted_sym = xkb_state_key_get_one_sym(p_ss.xkb_state, p_keycode);
194+
Ref<InputEventKey> WaylandThread::_seat_state_get_key_event(SeatState *p_ss, xkb_keycode_t p_keycode, bool p_pressed) {
195+
Ref<InputEventKey> event;
197196

198-
xkb_keysym_t plain_sym = XKB_KEY_NoSymbol;
197+
ERR_FAIL_NULL_V(p_ss, event);
198+
199+
Key shifted_key = KeyMappingXKB::get_keycode(xkb_state_key_get_one_sym(p_ss->xkb_state, p_keycode));
200+
201+
Key plain_key = Key::NONE;
199202
// NOTE: xkbcommon's API really encourages to apply the modifier state but we
200203
// only want a "plain" symbol so that we can convert it into a godot keycode.
201204
const xkb_keysym_t *syms = nullptr;
202-
int num_sys = xkb_keymap_key_get_syms_by_level(p_ss.xkb_keymap, p_keycode, p_ss.current_layout_index, 0, &syms);
205+
int num_sys = xkb_keymap_key_get_syms_by_level(p_ss->xkb_keymap, p_keycode, p_ss->current_layout_index, 0, &syms);
203206
if (num_sys > 0 && syms) {
204-
plain_sym = syms[0];
207+
plain_key = KeyMappingXKB::get_keycode(syms[0]);
205208
}
206209

207210
Key physical_keycode = KeyMappingXKB::get_scancode(p_keycode);
208211
KeyLocation key_location = KeyMappingXKB::get_location(p_keycode);
209-
uint32_t unicode = xkb_state_key_get_utf32(p_ss.xkb_state, p_keycode);
212+
uint32_t unicode = xkb_state_key_get_utf32(p_ss->xkb_state, p_keycode);
210213

211214
Key keycode = Key::NONE;
212215

213-
if (KeyMappingXKB::is_sym_numpad(shifted_sym) || KeyMappingXKB::is_sym_numpad(plain_sym)) {
214-
keycode = KeyMappingXKB::get_keycode(shifted_sym);
216+
if ((shifted_key & Key::SPECIAL) != Key::NONE || (plain_key & Key::SPECIAL) != Key::NONE) {
217+
keycode = shifted_key;
215218
}
216219

217220
if (keycode == Key::NONE) {
218-
keycode = KeyMappingXKB::get_keycode(plain_sym);
221+
keycode = plain_key;
219222
}
220223

221224
if (keycode == Key::NONE) {
@@ -227,41 +230,70 @@ bool WaylandThread::_seat_state_configure_key_event(SeatState &p_ss, Ref<InputEv
227230
}
228231

229232
if (physical_keycode == Key::NONE && keycode == Key::NONE && unicode == 0) {
230-
return false;
233+
return event;
231234
}
232235

233-
p_event->set_window_id(DisplayServer::MAIN_WINDOW_ID);
236+
event.instantiate();
237+
238+
event->set_window_id(DisplayServer::MAIN_WINDOW_ID);
234239

235240
// Set all pressed modifiers.
236-
p_event->set_shift_pressed(p_ss.shift_pressed);
237-
p_event->set_ctrl_pressed(p_ss.ctrl_pressed);
238-
p_event->set_alt_pressed(p_ss.alt_pressed);
239-
p_event->set_meta_pressed(p_ss.meta_pressed);
241+
event->set_shift_pressed(p_ss->shift_pressed);
242+
event->set_ctrl_pressed(p_ss->ctrl_pressed);
243+
event->set_alt_pressed(p_ss->alt_pressed);
244+
event->set_meta_pressed(p_ss->meta_pressed);
240245

241-
p_event->set_pressed(p_pressed);
242-
p_event->set_keycode(keycode);
243-
p_event->set_physical_keycode(physical_keycode);
244-
p_event->set_location(key_location);
246+
event->set_pressed(p_pressed);
247+
event->set_keycode(keycode);
248+
event->set_physical_keycode(physical_keycode);
249+
event->set_location(key_location);
245250

246251
if (unicode != 0) {
247-
p_event->set_key_label(fix_key_label(unicode, keycode));
252+
event->set_key_label(fix_key_label(unicode, keycode));
248253
} else {
249-
p_event->set_key_label(keycode);
254+
event->set_key_label(keycode);
250255
}
251256

252257
if (p_pressed) {
253-
p_event->set_unicode(fix_unicode(unicode));
258+
event->set_unicode(fix_unicode(unicode));
254259
}
255260

256261
// Taken from DisplayServerX11.
257-
if (p_event->get_keycode() == Key::BACKTAB) {
262+
if (event->get_keycode() == Key::BACKTAB) {
258263
// Make it consistent across platforms.
259-
p_event->set_keycode(Key::TAB);
260-
p_event->set_physical_keycode(Key::TAB);
261-
p_event->set_shift_pressed(true);
264+
event->set_keycode(Key::TAB);
265+
event->set_physical_keycode(Key::TAB);
266+
event->set_shift_pressed(true);
262267
}
263268

264-
return true;
269+
return event;
270+
}
271+
272+
// NOTE: Due to the nature of the way keys are encoded, there's an ambiguity
273+
// regarding "special" keys. In other words: there's no reliable way of
274+
// switching between a special key and a character key if not marking a
275+
// different Godot keycode, even if we're actually using the same XKB raw
276+
// keycode. This means that, during this switch, the old key will get "stuck",
277+
// as it will never receive a release event. This method returns the necessary
278+
// event to fix this if needed.
279+
Ref<InputEventKey> WaylandThread::_seat_state_get_unstuck_key_event(SeatState *p_ss, xkb_keycode_t p_keycode, bool p_pressed, Key p_key) {
280+
Ref<InputEventKey> event;
281+
282+
if (p_pressed) {
283+
Key *old_key = p_ss->pressed_keycodes.getptr(p_keycode);
284+
if (old_key != nullptr && *old_key != p_key) {
285+
print_verbose(vformat("%s and %s have same keycode. Generating release event for %s", keycode_get_string(*old_key), keycode_get_string(p_key), keycode_get_string(*old_key)));
286+
event = _seat_state_get_key_event(p_ss, p_keycode, false);
287+
if (event.is_valid()) {
288+
event->set_keycode(*old_key);
289+
}
290+
}
291+
p_ss->pressed_keycodes[p_keycode] = p_key;
292+
} else {
293+
p_ss->pressed_keycodes.erase(p_keycode);
294+
}
295+
296+
return event;
265297
}
266298

267299
void WaylandThread::_set_current_seat(struct wl_seat *p_seat) {
@@ -1920,13 +1952,19 @@ void WaylandThread::_wl_keyboard_on_key(void *data, struct wl_keyboard *wl_keybo
19201952
ss->repeating_keycode = XKB_KEYCODE_INVALID;
19211953
}
19221954

1923-
Ref<InputEventKey> k;
1924-
k.instantiate();
1925-
1926-
if (!_seat_state_configure_key_event(*ss, k, xkb_keycode, pressed)) {
1955+
Ref<InputEventKey> k = _seat_state_get_key_event(ss, xkb_keycode, pressed);
1956+
if (!k.is_valid()) {
19271957
return;
19281958
}
19291959

1960+
Ref<InputEventKey> uk = _seat_state_get_unstuck_key_event(ss, xkb_keycode, pressed, k->get_keycode());
1961+
if (uk.is_valid()) {
1962+
Ref<InputEventMessage> u_msg;
1963+
u_msg.instantiate();
1964+
u_msg->event = uk;
1965+
wayland_thread->push_message(u_msg);
1966+
}
1967+
19301968
Ref<InputEventMessage> msg;
19311969
msg.instantiate();
19321970
msg->event = k;
@@ -3236,15 +3274,18 @@ void WaylandThread::seat_state_echo_keys(SeatState *p_ss) {
32363274
int keys_amount = (ticks_delta / p_ss->repeat_key_delay_msec);
32373275

32383276
for (int i = 0; i < keys_amount; i++) {
3239-
Ref<InputEventKey> k;
3240-
k.instantiate();
3241-
3242-
if (!_seat_state_configure_key_event(*p_ss, k, p_ss->repeating_keycode, true)) {
3277+
Ref<InputEventKey> k = _seat_state_get_key_event(p_ss, p_ss->repeating_keycode, true);
3278+
if (!k.is_valid()) {
32433279
continue;
32443280
}
32453281

32463282
k->set_echo(true);
32473283

3284+
Ref<InputEventKey> uk = _seat_state_get_unstuck_key_event(p_ss, p_ss->repeating_keycode, true, k->get_keycode());
3285+
if (uk.is_valid()) {
3286+
Input::get_singleton()->parse_input_event(uk);
3287+
}
3288+
32483289
Input::get_singleton()->parse_input_event(k);
32493290
}
32503291

platform/linuxbsd/wayland/wayland_thread.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,8 @@ class WaylandThread {
428428
const char *keymap_buffer = nullptr;
429429
uint32_t keymap_buffer_size = 0;
430430

431+
HashMap<xkb_keycode_t, Key> pressed_keycodes;
432+
431433
xkb_layout_index_t current_layout_index = 0;
432434

433435
int32_t repeat_key_delay_msec = 0;
@@ -903,7 +905,8 @@ class WaylandThread {
903905
static Vector<uint8_t> _wp_primary_selection_offer_read(struct wl_display *wl_display, const char *p_mime, struct zwp_primary_selection_offer_v1 *wp_primary_selection_offer);
904906

905907
static void _seat_state_set_current(WaylandThread::SeatState &p_ss);
906-
static bool _seat_state_configure_key_event(WaylandThread::SeatState &p_seat, Ref<InputEventKey> p_event, xkb_keycode_t p_keycode, bool p_pressed);
908+
static Ref<InputEventKey> _seat_state_get_key_event(SeatState *p_ss, xkb_keycode_t p_keycode, bool p_pressed);
909+
static Ref<InputEventKey> _seat_state_get_unstuck_key_event(SeatState *p_ss, xkb_keycode_t p_keycode, bool p_pressed, Key p_key);
907910

908911
static void _wayland_state_update_cursor();
909912

0 commit comments

Comments
 (0)