Repository: godotengine/godot (language: cpp)

Issue:
Numpad is not properly detected on Wayland
### Tested versions

Not reproducible in 4.3.stable
Reproducible in v4.4.beta.custom_build [1586c5674]

I will try to bisect the issue later, but I think this is at least 2 weeks old now.

### System information

Godot v4.4.beta (1586c5674) - Manjaro Linux #1 SMP PREEMPT_DYNAMIC Thu, 05 Dec 2024 16:26:44 +0000 on Wayland - Wayland display driver, Single-window, 2 monitors - Vulkan (Forward+) - dedicated AMD Radeon RX 7900 XT (RADV NAVI31) - AMD Ryzen 9 7900X 12-Core Processor (24 threads)

### Issue description

In the Wayland version of the editor, there is a regression whereby typing using the numpad does not work properly anymore. It appears that everything will work the first time Godot is launched in the Wayland session, then after some time (probably simply alt-tabbing out and back in), numpad behaves as if it was off (typing numbers becomes impossible and the cursor is moved instead). This behavior is exclusive to Godot, the numpad works properly everywhere else in the session.
In Godot 4.3, there is no such issue, it appeared sometime around 4.4 beta 1 or maybe a bit earlier, I will try to bisect the exact commit.

### Steps to reproduce

On a Wayland session, and with the Wayland version of the editor (Run/Platforms/Prefer Wayland on - no issue on X11 version), alt-tab out and back in the editor. Numpad should not be detected properly anymore, even after completely closing the editor (and even the terminal that launched it) and opening it again.

### Minimal reproduction project (MRP)

N/A


Discussion / hints:

Fix patch:
diff --git a/platform/linuxbsd/wayland/key_mapping_xkb.cpp b/platform/linuxbsd/wayland/key_mapping_xkb.cpp
index bd1a1e383515..7d3a4272f5ad 100644
--- a/platform/linuxbsd/wayland/key_mapping_xkb.cpp
+++ b/platform/linuxbsd/wayland/key_mapping_xkb.cpp
@@ -369,6 +369,30 @@ void KeyMappingXKB::initialize() {
 	location_map[0x86] = KeyLocation::RIGHT;
 }
 
+bool KeyMappingXKB::is_sym_numpad(xkb_keysym_t p_keysym) {
+	switch (p_keysym) {
+		case XKB_KEY_KP_Multiply:
+		case XKB_KEY_KP_Divide:
+		case XKB_KEY_KP_Subtract:
+		case XKB_KEY_KP_Separator:
+		case XKB_KEY_KP_Add:
+		case XKB_KEY_KP_0:
+		case XKB_KEY_KP_1:
+		case XKB_KEY_KP_2:
+		case XKB_KEY_KP_3:
+		case XKB_KEY_KP_4:
+		case XKB_KEY_KP_5:
+		case XKB_KEY_KP_6:
+		case XKB_KEY_KP_7:
+		case XKB_KEY_KP_8:
+		case XKB_KEY_KP_9: {
+			return true;
+		} break;
+	}
+
+	return false;
+}
+
 Key KeyMappingXKB::get_keycode(xkb_keycode_t p_keysym) {
 	if (p_keysym >= 0x20 && p_keysym < 0x7E) { // ASCII, maps 1-1
 		if (p_keysym > 0x60 && p_keysym < 0x7B) { // Lowercase ASCII.
diff --git a/platform/linuxbsd/wayland/key_mapping_xkb.h b/platform/linuxbsd/wayland/key_mapping_xkb.h
index 9b8c90a445e0..508bb0287866 100644
--- a/platform/linuxbsd/wayland/key_mapping_xkb.h
+++ b/platform/linuxbsd/wayland/key_mapping_xkb.h
@@ -56,6 +56,7 @@ class KeyMappingXKB {
 public:
 	static void initialize();
 
+	static bool is_sym_numpad(xkb_keysym_t p_keysym);
 	static Key get_keycode(xkb_keysym_t p_keysym);
 	static xkb_keycode_t get_xkb_keycode(Key p_keycode);
 	static Key get_scancode(unsigned int p_code);
diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp
index f048a3f294d6..6155d9e3675d 100644
--- a/platform/linuxbsd/wayland/wayland_thread.cpp
+++ b/platform/linuxbsd/wayland/wayland_thread.cpp
@@ -193,18 +193,26 @@ Vector<uint8_t> WaylandThread::_wp_primary_selection_offer_read(struct wl_displa
 
 // Sets up an `InputEventKey` and returns whether it has any meaningful value.
 bool WaylandThread::_seat_state_configure_key_event(SeatState &p_ss, Ref<InputEventKey> p_event, xkb_keycode_t p_keycode, bool p_pressed) {
-	// NOTE: xkbcommon's API really encourages to apply the modifier state but we
-	// only want a "plain" symbol so that we can convert it into a godot keycode.
-	const xkb_keysym_t *syms = nullptr;
-	int num_sys = xkb_keymap_key_get_syms_by_level(p_ss.xkb_keymap, p_keycode, p_ss.current_layout_index, 0, &syms);
+	xkb_keysym_t shifted_sym = xkb_state_key_get_one_sym(p_ss.xkb_state, p_keycode);
 
 	Key physical_keycode = KeyMappingXKB::get_scancode(p_keycode);
 	KeyLocation key_location = KeyMappingXKB::get_location(p_keycode);
 	uint32_t unicode = xkb_state_key_get_utf32(p_ss.xkb_state, p_keycode);
 
 	Key keycode = Key::NONE;
-	if (num_sys > 0 && syms) {
-		keycode = KeyMappingXKB::get_keycode(syms[0]);
+
+	if (KeyMappingXKB::is_sym_numpad(shifted_sym)) {
+		keycode = KeyMappingXKB::get_keycode(shifted_sym);
+	}
+
+	if (keycode == Key::NONE) {
+		// NOTE: xkbcommon's API really encourages to apply the modifier state but we
+		// only want a "plain" symbol so that we can convert it into a godot keycode.
+		const xkb_keysym_t *syms = nullptr;
+		int num_sys = xkb_keymap_key_get_syms_by_level(p_ss.xkb_keymap, p_keycode, p_ss.current_layout_index, 0, &syms);
+		if (num_sys > 0 && syms) {
+			keycode = KeyMappingXKB::get_keycode(syms[0]);
+		}
 	}
 
 	if (keycode == Key::NONE) {
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index cbd3662986c7..18500f701963 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -3562,8 +3562,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
 	// XLookupString returns keysyms usable as nice keycodes.
 	char str[256] = {};
 	XKeyEvent xkeyevent_no_mod = *xkeyevent;
-	xkeyevent_no_mod.state &= ~ShiftMask;
-	xkeyevent_no_mod.state &= ~ControlMask;
+	xkeyevent_no_mod.state &= 0xFF00;
 	XLookupString(xkeyevent, str, 255, &keysym_unicode, nullptr);
 	XLookupString(&xkeyevent_no_mod, nullptr, 0, &keysym_keycode, nullptr);
 
@@ -3601,7 +3600,17 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
 
 		if (status == XLookupChars) {
 			bool keypress = xkeyevent->type == KeyPress;
-			Key keycode = KeyMappingX11::get_keycode(keysym_keycode);
+
+			Key keycode = Key::NONE;
+			if (KeyMappingX11::is_sym_numpad(keysym_unicode)) {
+				// Special case for numpad keys.
+				keycode = KeyMappingX11::get_keycode(keysym_unicode);
+			}
+
+			if (keycode == Key::NONE) {
+				keycode = KeyMappingX11::get_keycode(keysym_keycode);
+			}
+
 			Key physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode);
 
 			if (keycode >= Key::A + 32 && keycode <= Key::Z + 32) {
@@ -3669,10 +3678,19 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
 		if (res == XKB_COMPOSE_FEED_ACCEPTED) {
 			if (xkb_compose_state_get_status(wd.xkb_state) == XKB_COMPOSE_COMPOSED) {
 				bool keypress = xkeyevent->type == KeyPress;
-				Key keycode = KeyMappingX11::get_keycode(keysym_keycode);
 				Key physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode);
 				KeyLocation key_location = KeyMappingX11::get_location(xkeyevent->keycode);
 
+				Key keycode = Key::NONE;
+				if (KeyMappingX11::is_sym_numpad(keysym_unicode)) {
+					// Special case for numpad keys.
+					keycode = KeyMappingX11::get_keycode(keysym_unicode);
+				}
+
+				if (keycode == Key::NONE) {
+					keycode = KeyMappingX11::get_keycode(keysym_keycode);
+				}
+
 				if (keycode >= Key::A + 32 && keycode <= Key::Z + 32) {
 					keycode -= 'a' - 'A';
 				}
@@ -3733,7 +3751,16 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
 	// KeyMappingX11 just translated the X11 keysym to a PIGUI
 	// keysym, so it works in all platforms the same.
 
-	Key keycode = KeyMappingX11::get_keycode(keysym_keycode);
+	Key keycode = Key::NONE;
+	if (KeyMappingX11::is_sym_numpad(keysym_unicode)) {
+		// Special case for numpad keys.
+		keycode = KeyMappingX11::get_keycode(keysym_unicode);
+	}
+
+	if (keycode == Key::NONE) {
+		keycode = KeyMappingX11::get_keycode(keysym_keycode);
+	}
+
 	Key physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode);
 
 	KeyLocation key_location = KeyMappingX11::get_location(xkeyevent->keycode);
diff --git a/platform/linuxbsd/x11/key_mapping_x11.cpp b/platform/linuxbsd/x11/key_mapping_x11.cpp
index b589a2a57344..e6494985a4a2 100644
--- a/platform/linuxbsd/x11/key_mapping_x11.cpp
+++ b/platform/linuxbsd/x11/key_mapping_x11.cpp
@@ -1129,6 +1129,30 @@ void KeyMappingX11::initialize() {
 	location_map[0x86] = KeyLocation::RIGHT;
 }
 
+bool KeyMappingX11::is_sym_numpad(KeySym p_keysym) {
+	switch (p_keysym) {
+		case XK_KP_Multiply:
+		case XK_KP_Divide:
+		case XK_KP_Subtract:
+		case XK_KP_Separator:
+		case XK_KP_Add:
+		case XK_KP_0:
+		case XK_KP_1:
+		case XK_KP_2:
+		case XK_KP_3:
+		case XK_KP_4:
+		case XK_KP_5:
+		case XK_KP_6:
+		case XK_KP_7:
+		case XK_KP_8:
+		case XK_KP_9: {
+			return true;
+		} break;
+	}
+
+	return false;
+}
+
 Key KeyMappingX11::get_keycode(KeySym p_keysym) {
 	if (p_keysym >= 0x20 && p_keysym < 0x7E) { // ASCII, maps 1-1
 		if (p_keysym > 0x60 && p_keysym < 0x7B) { // Lowercase ASCII.
diff --git a/platform/linuxbsd/x11/key_mapping_x11.h b/platform/linuxbsd/x11/key_mapping_x11.h
index a51ee5f48eab..27cd8af7cf58 100644
--- a/platform/linuxbsd/x11/key_mapping_x11.h
+++ b/platform/linuxbsd/x11/key_mapping_x11.h
@@ -61,6 +61,7 @@ class KeyMappingX11 {
 public:
 	static void initialize();
 
+	static bool is_sym_numpad(KeySym p_keysym);
 	static Key get_keycode(KeySym p_keysym);
 	static unsigned int get_xlibcode(Key p_keysym);
 	static Key get_scancode(unsigned int p_code);


Test patch: