Wayland: Replace _glfwKeySym2Unicode with xkbcommon

libxkbcommon already provides functions to convert keysyms to codepoints
and UTF-8. The library has offered these functions since 0.5.0[1], so
using them won't cause any compatibility problems.

[1] https://xkbcommon.org/doc/0.5.0/group__keysyms.html

Closes #2444
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index fc4e17c..1a085b2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -53,8 +53,8 @@
 
 if (GLFW_BUILD_WAYLAND)
     target_compile_definitions(glfw PRIVATE _GLFW_WAYLAND)
-    target_sources(glfw PRIVATE wl_platform.h xkb_unicode.h wl_init.c
-                                wl_monitor.c wl_window.c xkb_unicode.c)
+    target_sources(glfw PRIVATE wl_platform.h wl_init.c
+                                wl_monitor.c wl_window.c)
 endif()
 
 if (GLFW_BUILD_X11 OR GLFW_BUILD_WAYLAND)
diff --git a/src/wl_init.c b/src/wl_init.c
index 76054bc..ef9e450 100644
--- a/src/wl_init.c
+++ b/src/wl_init.c
@@ -711,6 +711,10 @@
         _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_compose_state_get_status");
     _glfw.wl.xkb.compose_state_get_one_sym = (PFN_xkb_compose_state_get_one_sym)
         _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym");
+    _glfw.wl.xkb.keysym_to_utf32 = (PFN_xkb_keysym_to_utf32)
+        _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keysym_to_utf32");
+    _glfw.wl.xkb.keysym_to_utf8 = (PFN_xkb_keysym_to_utf8)
+        _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keysym_to_utf8");
 
     if (!_glfw.wl.xkb.context_new ||
         !_glfw.wl.xkb.context_unref ||
diff --git a/src/wl_platform.h b/src/wl_platform.h
index f3e8cba..afa6f50 100644
--- a/src/wl_platform.h
+++ b/src/wl_platform.h
@@ -42,7 +42,6 @@
 typedef VkResult (APIENTRY *PFN_vkCreateWaylandSurfaceKHR)(VkInstance,const VkWaylandSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*);
 typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice,uint32_t,struct wl_display*);
 
-#include "xkb_unicode.h"
 #include "posix_poll.h"
 
 typedef int (* PFN_wl_display_flush)(struct wl_display* display);
@@ -178,6 +177,8 @@
 typedef enum xkb_state_component (* PFN_xkb_state_update_mask)(struct xkb_state*, xkb_mod_mask_t, xkb_mod_mask_t, xkb_mod_mask_t, xkb_layout_index_t, xkb_layout_index_t, xkb_layout_index_t);
 typedef xkb_layout_index_t (* PFN_xkb_state_key_get_layout)(struct xkb_state*,xkb_keycode_t);
 typedef int (* PFN_xkb_state_mod_index_is_active)(struct xkb_state*,xkb_mod_index_t,enum xkb_state_component);
+typedef uint32_t (* PFN_xkb_keysym_to_utf32)(xkb_keysym_t);
+typedef int (* PFN_xkb_keysym_to_utf8)(xkb_keysym_t, char*, size_t);
 #define xkb_context_new _glfw.wl.xkb.context_new
 #define xkb_context_unref _glfw.wl.xkb.context_unref
 #define xkb_keymap_new_from_string _glfw.wl.xkb.keymap_new_from_string
@@ -191,6 +192,8 @@
 #define xkb_state_update_mask _glfw.wl.xkb.state_update_mask
 #define xkb_state_key_get_layout _glfw.wl.xkb.state_key_get_layout
 #define xkb_state_mod_index_is_active _glfw.wl.xkb.state_mod_index_is_active
+#define xkb_keysym_to_utf32 _glfw.wl.xkb.keysym_to_utf32
+#define xkb_keysym_to_utf8 _glfw.wl.xkb.keysym_to_utf8
 
 typedef struct xkb_compose_table* (* PFN_xkb_compose_table_new_from_locale)(struct xkb_context*, const char*, enum xkb_compose_compile_flags);
 typedef void (* PFN_xkb_compose_table_unref)(struct xkb_compose_table*);
@@ -495,6 +498,8 @@
         PFN_xkb_state_update_mask state_update_mask;
         PFN_xkb_state_key_get_layout state_key_get_layout;
         PFN_xkb_state_mod_index_is_active state_mod_index_is_active;
+        PFN_xkb_keysym_to_utf32 keysym_to_utf32;
+        PFN_xkb_keysym_to_utf8 keysym_to_utf8;
 
         PFN_xkb_compose_table_new_from_locale compose_table_new_from_locale;
         PFN_xkb_compose_table_unref compose_table_unref;
diff --git a/src/wl_window.c b/src/wl_window.c
index 2e842aa..72c1a40 100644
--- a/src/wl_window.c
+++ b/src/wl_window.c
@@ -1192,8 +1192,8 @@
     if (xkb_state_key_get_syms(_glfw.wl.xkb.state, keycode, &keysyms) == 1)
     {
         const xkb_keysym_t keysym = composeSymbol(keysyms[0]);
-        const uint32_t codepoint = _glfwKeySym2Unicode(keysym);
-        if (codepoint != GLFW_INVALID_CODEPOINT)
+        const uint32_t codepoint = xkb_keysym_to_utf32(keysym);
+        if (codepoint != 0)
         {
             const int mods = _glfw.wl.xkb.modifiers;
             const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
@@ -2721,23 +2721,23 @@
         return NULL;
     }
 
-    const uint32_t codepoint = _glfwKeySym2Unicode(keysyms[0]);
-    if (codepoint == GLFW_INVALID_CODEPOINT)
+    // WORKAROUND: xkb_keysym_to_utf8() requires the third parameter (size of the output buffer)
+    // to be at least 7 (6 bytes + a null terminator), because it was written when UTF-8
+    // sequences could be up to 6 bytes long. The _glfw.wl.keynames buffers are only 5 bytes
+    // long, because UTF-8 sequences are now limited to 4 bytes and no codepoints were ever assigned
+    // that needed more than that. To work around this, we first copy to a temporary buffer.
+    //
+    // See: https://github.com/xkbcommon/libxkbcommon/issues/418
+    char temp_buffer[7];
+    const int bytes_written = xkb_keysym_to_utf8(keysyms[0], temp_buffer, sizeof(temp_buffer));
+    if (bytes_written <= 0 || bytes_written > 5)
     {
         _glfwInputError(GLFW_PLATFORM_ERROR,
-                        "Wayland: Failed to retrieve codepoint for key name");
+                        "Wayland: Failed to encode keysym as UTF-8");
         return NULL;
     }
+    memcpy(_glfw.wl.keynames[key], temp_buffer, bytes_written);
 
-    const size_t count = _glfwEncodeUTF8(_glfw.wl.keynames[key],  codepoint);
-    if (count == 0)
-    {
-        _glfwInputError(GLFW_PLATFORM_ERROR,
-                        "Wayland: Failed to encode codepoint for key name");
-        return NULL;
-    }
-
-    _glfw.wl.keynames[key][count] = '\0';
     return _glfw.wl.keynames[key];
 }