diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7b84ecf2..833a82e4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -52,8 +52,8 @@ endif() 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 0ec65900..9c0e201d 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -682,6 +682,10 @@ int _glfwInitWayland(void) _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.hints.init.wl.libdecorMode == GLFW_WAYLAND_PREFER_LIBDECOR) _glfw.wl.libdecor.handle = _glfwPlatformLoadModule("libdecor-0.so.0"); diff --git a/src/wl_platform.h b/src/wl_platform.h index d00e28fe..d5f82aa1 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -44,7 +44,6 @@ typedef struct VkWaylandSurfaceCreateInfoKHR 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); @@ -176,6 +175,8 @@ typedef int (* PFN_xkb_state_key_get_syms)(struct xkb_state*, xkb_keycode_t, con 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 @@ -189,6 +190,8 @@ typedef int (* PFN_xkb_state_mod_index_is_active)(struct xkb_state*,xkb_mod_inde #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*); @@ -494,6 +497,8 @@ typedef struct _GLFWlibraryWayland 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 7c509896..4870af92 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1086,8 +1086,8 @@ static void inputText(_GLFWwindow* window, uint32_t scancode) 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)); @@ -2584,23 +2584,23 @@ const char* _glfwGetScancodeNameWayland(int scancode) 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]; }