diff --git a/glfw/ibus_glfw.c b/glfw/ibus_glfw.c index 65a987432..772e353dd 100644 --- a/glfw/ibus_glfw.c +++ b/glfw/ibus_glfw.c @@ -391,26 +391,28 @@ ibus_key_state(unsigned int glfw_modifiers, int action) { void key_event_processed(DBusMessage *msg, const char* errmsg, void *data) { + uint32_t handled = 0; + KeyEvent *ev = (KeyEvent*)data; + GLFWbool is_release = ev->action == GLFW_RELEASE; if (errmsg) { _glfwInputError(GLFW_PLATFORM_ERROR, "IBUS: Failed to process key with error: %s", errmsg); - return; + } else { + glfw_dbus_get_args(msg, "Failed to get IBUS handled key from reply", DBUS_TYPE_BOOLEAN, &handled, DBUS_TYPE_INVALID); + debug("IBUS processed scancode: 0x%x release: %d handled: %u\n", ev->keycode, is_release, handled); } - uint32_t handled; - if (!glfw_dbus_get_args(msg, "Failed to get IBUS handled key from reply", DBUS_TYPE_BOOLEAN, &handled, DBUS_TYPE_INVALID)) return; - KeyEvent *ev = (KeyEvent*)data; - debug("IBUS processed scancode: 0x%x release: %d handled: %u\n", ev->keycode, ev->action == GLFW_RELEASE, handled); + glfw_xkb_key_from_ime(ev, handled ? GLFW_TRUE : GLFW_FALSE); free(ev); } GLFWbool -ibus_process_key(const KeyEvent *ev_) { - if (!check_connection(ev_->ibus)) return GLFW_FALSE; +ibus_process_key(const KeyEvent *ev_, _GLFWIBUSData *ibus) { + if (!check_connection(ibus)) return GLFW_FALSE; KeyEvent *ev = malloc(sizeof(KeyEvent)); if (!ev) return GLFW_FALSE; memcpy(ev, ev_, sizeof(KeyEvent)); uint32_t state = ibus_key_state(ev->glfw_modifiers, ev->action); if (!glfw_dbus_call_method_with_reply( - ev->ibus->conn, IBUS_SERVICE, ev->ibus->input_ctx_path, IBUS_INPUT_INTERFACE, "ProcessKeyEvent", + ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, "ProcessKeyEvent", key_event_processed, ev, DBUS_TYPE_UINT32, &ev->keysym, DBUS_TYPE_UINT32, &ev->keycode, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID)) { diff --git a/glfw/ibus_glfw.h b/glfw/ibus_glfw.h index ac43e1784..210597cf3 100644 --- a/glfw/ibus_glfw.h +++ b/glfw/ibus_glfw.h @@ -42,12 +42,12 @@ typedef struct { xkb_keysym_t keysym; unsigned int glfw_modifiers; int action; - _GLFWIBUSData *ibus; + GLFWid window_id; } KeyEvent; void glfw_connect_to_ibus(_GLFWIBUSData *ibus); void glfw_ibus_terminate(_GLFWIBUSData *ibus); void glfw_ibus_set_focused(_GLFWIBUSData *ibus, GLFWbool focused); void glfw_ibus_dispatch(_GLFWIBUSData *ibus); -GLFWbool ibus_process_key(const KeyEvent *ev_); +GLFWbool ibus_process_key(const KeyEvent *ev_, _GLFWIBUSData *ibus); void glfw_ibus_set_cursor_geometry(_GLFWIBUSData *ibus, int x, int y, int w, int h); diff --git a/glfw/internal.h b/glfw/internal.h index 824cee5ed..dcc1b00a3 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -705,7 +705,6 @@ void _glfwPlatformUnlockMutex(_GLFWmutex* mutex); ////////////////////////////////////////////////////////////////////////// void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused); -_GLFWwindow* _glfwFocusedWindow(); void _glfwInputWindowPos(_GLFWwindow* window, int xpos, int ypos); void _glfwInputWindowSize(_GLFWwindow* window, int width, int height); void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height); @@ -771,5 +770,7 @@ const char* _glfwGetKeyName(int key); GLFWbool _glfwInitVulkan(int mode); void _glfwTerminateVulkan(void); const char* _glfwGetVulkanResultString(VkResult result); +_GLFWwindow* _glfwFocusedWindow(); +_GLFWwindow* _glfwWindowForId(GLFWid id); char* _glfw_strdup(const char* source); diff --git a/glfw/window.c b/glfw/window.c index d1ee20bd2..f25452f53 100644 --- a/glfw/window.c +++ b/glfw/window.c @@ -79,6 +79,15 @@ _GLFWwindow* _glfwFocusedWindow() { return NULL; } +_GLFWwindow* _glfwWindowForId(GLFWid id) { + _GLFWwindow *w = _glfw.windowListHead; + while (w) { + if (w->id == _glfw.focusedWindowId) return w; + w = w->next; + } + return NULL; +} + // Notifies shared code that a window has moved // The position is specified in client-area relative screen coordinates // diff --git a/glfw/wl_init.c b/glfw/wl_init.c index b2192f39d..2f204a10f 100644 --- a/glfw/wl_init.c +++ b/glfw/wl_init.c @@ -308,7 +308,7 @@ static void pointerHandleButton(void* data, state == WL_POINTER_BUTTON_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE, - _glfw.wl.xkb.modifiers); + _glfw.wl.xkb.states.modifiers); } static void pointerHandleAxis(void* data, diff --git a/glfw/xkb_glfw.c b/glfw/xkb_glfw.c index d4e6e1566..528f7572a 100644 --- a/glfw/xkb_glfw.c +++ b/glfw/xkb_glfw.c @@ -32,6 +32,7 @@ #define debug(...) if (_glfw.hints.init.debugKeyboard) printf(__VA_ARGS__); + #define map_key(key) { \ switch(key) { \ S(space, SPACE); \ @@ -126,6 +127,7 @@ glfw_xkb_sym_for_key(int key) { } #ifdef _GLFW_X11 +#define XKB_POINTER (&_glfw.x11.xkb) GLFWbool glfw_xkb_set_x11_events_mask(void) { @@ -165,37 +167,31 @@ glfw_xkb_update_x11_keyboard_id(_GLFWXKBData *xkb) { #else +#define XKB_POINTER (&_glfw.wl.xkb) #define xkb_glfw_load_keymap(keymap, map_str) keymap = xkb_keymap_new_from_string(xkb->context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0); #define xkb_glfw_load_state(keymap, state, ...) state = xkb_state_new(keymap); #endif +static void +release_keyboard_data(_GLFWXKBData *xkb) { +#define US(group, state, unref) if (xkb->group.state) { unref(xkb->group.state); xkb->group.state = NULL; } +#define UK(keymap) if(xkb->keymap) { xkb_keymap_unref(xkb->keymap); xkb->keymap = NULL; } + US(states, composeState, xkb_compose_state_unref); + US(ime_states, composeState, xkb_compose_state_unref); + UK(keymap); + UK(default_keymap); + US(states, state, xkb_state_unref); + US(states, clean_state, xkb_state_unref); + US(states, default_state, xkb_state_unref); +#undef US +#undef UK + +} + void glfw_xkb_release(_GLFWXKBData *xkb) { - if (xkb->composeState) { - xkb_compose_state_unref(xkb->composeState); - xkb->composeState = NULL; - } - if (xkb->keymap) { - xkb_keymap_unref(xkb->keymap); - xkb->keymap = NULL; - } - if (xkb->default_keymap) { - xkb_keymap_unref(xkb->default_keymap); - xkb->default_keymap = NULL; - } - if (xkb->state) { - xkb_state_unref(xkb->state); - xkb->state = NULL; - } - if (xkb->clean_state) { - xkb_state_unref(xkb->clean_state); - xkb->clean_state = NULL; - } - if (xkb->default_state) { - xkb_state_unref(xkb->default_state); - xkb->default_state = NULL; - } + release_keyboard_data(xkb); if (xkb->context) { xkb_context_unref(xkb->context); xkb->context = NULL; @@ -216,113 +212,120 @@ glfw_xkb_create_context(_GLFWXKBData *xkb) { return GLFW_TRUE; } +static const char* +load_keymaps(_GLFWXKBData *xkb, const char *map_str) { + (void)(map_str); // not needed on X11 + xkb_glfw_load_keymap(xkb->keymap, map_str); + if (!xkb->keymap) return "Failed to compile XKB keymap"; + // The system default keymap, can be overridden by the XKB_DEFAULT_RULES + // env var, see + // https://xkbcommon.org/doc/current/structxkb__rule__names.html + static struct xkb_rule_names default_rule_names = {0}; + xkb->default_keymap = xkb_keymap_new_from_names(xkb->context, &default_rule_names, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!xkb->default_keymap) return "Failed to create default XKB keymap"; + return NULL; +} + +static const char* +load_states(_GLFWXKBData *xkb) { + xkb_glfw_load_state(xkb->keymap, xkb->states.state); + xkb_glfw_load_state(xkb->keymap, xkb->ime_states.state); + xkb->states.clean_state = xkb_state_new(xkb->keymap); + xkb->ime_states.clean_state = xkb_state_new(xkb->keymap); + xkb->states.default_state = xkb_state_new(xkb->default_keymap); + xkb->ime_states.default_state = xkb_state_new(xkb->default_keymap); + if (!xkb->states.state || !xkb->ime_states.state || !xkb->states.clean_state || !xkb->ime_states.clean_state || !xkb->states.default_state || !xkb->ime_states.default_state) return "Failed to create XKB state"; + return NULL; +} + +static void +load_compose_tables(_GLFWXKBData *xkb) { + /* Look up the preferred locale, falling back to "C" as default. */ + struct xkb_compose_table* compose_table = NULL; + const char *locale = getenv("LC_ALL"); + if (!locale) locale = getenv("LC_CTYPE"); + if (!locale) locale = getenv("LANG"); + if (!locale) locale = "C"; + compose_table = xkb_compose_table_new_from_locale(xkb->context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); + if (!compose_table) { + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create XKB compose table for locale %s", locale); + return; + } + xkb->states.composeState = xkb_compose_state_new(compose_table, XKB_COMPOSE_STATE_NO_FLAGS); + xkb->ime_states.composeState = xkb_compose_state_new(compose_table, XKB_COMPOSE_STATE_NO_FLAGS); + if (!xkb->states.composeState || !xkb->ime_states.composeState) { + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create XKB compose state"); + } +} GLFWbool glfw_xkb_compile_keymap(_GLFWXKBData *xkb, const char *map_str) { - const char* locale = NULL; - struct xkb_state* state = NULL, *clean_state = NULL; - struct xkb_keymap* keymap = NULL; - struct xkb_compose_table* compose_table = NULL; - struct xkb_compose_state* compose_state = NULL; - (void)(map_str); // not needed on X11 - GLFWbool ok = GLFW_FALSE; - - xkb_glfw_load_keymap(keymap, map_str); - if (!keymap) _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to compile XKB keymap"); - else { - xkb_glfw_load_state(keymap, state); - clean_state = xkb_state_new(keymap); - if (!state || !clean_state) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create XKB state"); - xkb_keymap_unref(keymap); keymap = NULL; - } else { - ok = GLFW_TRUE; - /* Look up the preferred locale, falling back to "C" as default. */ - locale = getenv("LC_ALL"); - if (!locale) locale = getenv("LC_CTYPE"); - if (!locale) locale = getenv("LANG"); - if (!locale) locale = "C"; - compose_table = xkb_compose_table_new_from_locale(xkb->context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); - if (!compose_table) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create XKB compose table"); - } else { - compose_state = xkb_compose_state_new(compose_table, XKB_COMPOSE_STATE_NO_FLAGS); - xkb_compose_table_unref(compose_table); compose_table = NULL; - if (!compose_state) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create XKB compose state"); - } - } - } + const char *err; + release_keyboard_data(xkb); + err = load_keymaps(xkb, map_str); + if (err) { + _glfwInputError(GLFW_PLATFORM_ERROR, "%s", err); + release_keyboard_data(xkb); + return GLFW_FALSE; } - if (keymap && state && clean_state) { - if (xkb->composeState) xkb_compose_state_unref(xkb->composeState); - xkb->composeState = compose_state; - if (xkb->keymap) xkb_keymap_unref(xkb->keymap); - xkb->keymap = keymap; - if (xkb->state) xkb_state_unref(xkb->state); - xkb->state = state; - if (xkb->clean_state) xkb_state_unref(xkb->clean_state); - xkb->clean_state = clean_state; + err = load_states(xkb); + if (err) { + _glfwInputError(GLFW_PLATFORM_ERROR, "%s", err); + release_keyboard_data(xkb); + return GLFW_FALSE; } - if (xkb->keymap) { + load_compose_tables(xkb); #define S(a, n) xkb->a##Idx = xkb_keymap_mod_get_index(xkb->keymap, n); xkb->a##Mask = 1 << xkb->a##Idx; - S(control, XKB_MOD_NAME_CTRL); - S(alt, XKB_MOD_NAME_ALT); - S(shift, XKB_MOD_NAME_SHIFT); - S(super, XKB_MOD_NAME_LOGO); - S(capsLock, XKB_MOD_NAME_CAPS); - S(numLock, XKB_MOD_NAME_NUM); + S(control, XKB_MOD_NAME_CTRL); + S(alt, XKB_MOD_NAME_ALT); + S(shift, XKB_MOD_NAME_SHIFT); + S(super, XKB_MOD_NAME_LOGO); + S(capsLock, XKB_MOD_NAME_CAPS); + S(numLock, XKB_MOD_NAME_NUM); #undef S - size_t capacity = sizeof(xkb->unknownModifiers)/sizeof(xkb->unknownModifiers[0]), j = 0; - for (xkb_mod_index_t i = 0; i < capacity; i++) xkb->unknownModifiers[i] = XKB_MOD_INVALID; - for (xkb_mod_index_t i = 0; i < xkb_keymap_num_mods(xkb->keymap) && j < capacity - 1; i++) { - if (i != xkb->controlIdx && i != xkb->altIdx && i != xkb->shiftIdx && i != xkb->superIdx && i != xkb->capsLockIdx && i != xkb->numLockIdx) xkb->unknownModifiers[j++] = i; - } - xkb->modifiers = 0; - xkb->activeUnknownModifiers = 0; + size_t capacity = sizeof(xkb->unknownModifiers)/sizeof(xkb->unknownModifiers[0]), j = 0; + for (xkb_mod_index_t i = 0; i < capacity; i++) xkb->unknownModifiers[i] = XKB_MOD_INVALID; + for (xkb_mod_index_t i = 0; i < xkb_keymap_num_mods(xkb->keymap) && j < capacity - 1; i++) { + if (i != xkb->controlIdx && i != xkb->altIdx && i != xkb->shiftIdx && i != xkb->superIdx && i != xkb->capsLockIdx && i != xkb->numLockIdx) xkb->unknownModifiers[j++] = i; } - if (!xkb->default_keymap && xkb->context) { - // The system default keymap, can be overridden by the XKB_DEFAULT_RULES - // env var, see - // https://xkbcommon.org/doc/current/structxkb__rule__names.html - static struct xkb_rule_names default_rule_names = {0}; - xkb->default_keymap = xkb_keymap_new_from_names(xkb->context, &default_rule_names, XKB_KEYMAP_COMPILE_NO_FLAGS); - if (xkb->default_keymap) { - xkb->default_state = xkb_state_new(keymap); - if (!xkb->default_state) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create default XKB state, fallback key processing is unavailable"); - } - } else { - _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create default XKB keymap, fallback key processing is unavailable"); - } - } - return ok; + xkb->states.modifiers = 0; + xkb->states.activeUnknownModifiers = 0; + xkb->ime_states.modifiers = 0; + xkb->ime_states.activeUnknownModifiers = 0; + return GLFW_TRUE; } static inline xkb_mod_mask_t -active_unknown_modifiers(_GLFWXKBData *xkb) { +active_unknown_modifiers(_GLFWXKBData *xkb, struct xkb_state *state) { size_t i = 0; xkb_mod_mask_t ans = 0; while (xkb->unknownModifiers[i] != XKB_MOD_INVALID) { - if (xkb_state_mod_index_is_active(xkb->state, xkb->unknownModifiers[i], XKB_STATE_MODS_EFFECTIVE)) ans |= (1 << xkb->unknownModifiers[i]); + if (xkb_state_mod_index_is_active(state, xkb->unknownModifiers[i], XKB_STATE_MODS_EFFECTIVE)) ans |= (1 << xkb->unknownModifiers[i]); i++; } return ans; } +static void +update_modifiers(_GLFWXKBData *xkb, XKBStateGroup *group) { +#define S(attr, name) if (xkb_state_mod_index_is_active(group->state, xkb->attr##Idx, XKB_STATE_MODS_EFFECTIVE)) group->modifiers |= GLFW_MOD_##name + S(control, CONTROL); S(alt, ALT); S(shift, SHIFT); S(super, SUPER); S(capsLock, CAPS_LOCK); S(numLock, NUM_LOCK); +#undef S + xkb->states.activeUnknownModifiers = active_unknown_modifiers(xkb, xkb->states.state); + +} void glfw_xkb_update_modifiers(_GLFWXKBData *xkb, xkb_mod_mask_t depressed, xkb_mod_mask_t latched, xkb_mod_mask_t locked, xkb_layout_index_t base_group, xkb_layout_index_t latched_group, xkb_layout_index_t locked_group) { if (!xkb->keymap) return; - xkb->modifiers = 0; - xkb_state_update_mask(xkb->state, depressed, latched, locked, base_group, latched_group, locked_group); + xkb->states.modifiers = 0; + xkb_state_update_mask(xkb->states.state, depressed, latched, locked, base_group, latched_group, locked_group); // We have to update the groups in clean_state, as they change for // different keyboard layouts, see https://github.com/kovidgoyal/kitty/issues/488 - xkb_state_update_mask(xkb->clean_state, 0, 0, 0, base_group, latched_group, locked_group); -#define S(attr, name) if (xkb_state_mod_index_is_active(xkb->state, xkb->attr##Idx, XKB_STATE_MODS_EFFECTIVE)) xkb->modifiers |= GLFW_MOD_##name - S(control, CONTROL); S(alt, ALT); S(shift, SHIFT); S(super, SUPER); S(capsLock, CAPS_LOCK); S(numLock, NUM_LOCK); -#undef S - xkb->activeUnknownModifiers = active_unknown_modifiers(xkb); + xkb_state_update_mask(xkb->states.clean_state, 0, 0, 0, base_group, latched_group, locked_group); + xkb_state_update_mask(xkb->ime_states.state, 0, 0, 0, base_group, latched_group, locked_group); + xkb_state_update_mask(xkb->ime_states.clean_state, 0, 0, 0, base_group, latched_group, locked_group); + update_modifiers(xkb, &xkb->states); } GLFWbool @@ -337,13 +340,13 @@ glfw_xkb_should_repeat(_GLFWXKBData *xkb, xkb_keycode_t scancode) { static char text[256]; static inline xkb_keysym_t -compose_symbol(_GLFWXKBData *xkb, xkb_keysym_t sym) { - if (sym == XKB_KEY_NoSymbol || !xkb->composeState) return sym; - if (xkb_compose_state_feed(xkb->composeState, sym) != XKB_COMPOSE_FEED_ACCEPTED) return sym; - switch (xkb_compose_state_get_status(xkb->composeState)) { +compose_symbol(struct xkb_compose_state *composeState, xkb_keysym_t sym) { + if (sym == XKB_KEY_NoSymbol || !composeState) return sym; + if (xkb_compose_state_feed(composeState, sym) != XKB_COMPOSE_FEED_ACCEPTED) return sym; + switch (xkb_compose_state_get_status(composeState)) { case XKB_COMPOSE_COMPOSED: - xkb_compose_state_get_utf8(xkb->composeState, text, sizeof(text)); - return xkb_compose_state_get_one_sym(xkb->composeState); + xkb_compose_state_get_utf8(composeState, text, sizeof(text)); + return xkb_compose_state_get_one_sym(composeState); case XKB_COMPOSE_COMPOSING: case XKB_COMPOSE_CANCELLED: return XKB_KEY_NoSymbol; @@ -420,8 +423,8 @@ glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, int which, int a, i } } -void -glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t scancode, int action) { +static inline void +handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t scancode, int action, GLFWbool from_ime) { const xkb_keysym_t *syms, *clean_syms, *default_syms; static KeyEvent ev; xkb_keysym_t glfw_sym; @@ -429,27 +432,29 @@ glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t #ifdef _GLFW_WAYLAND code_for_sym += 8; #endif - debug("scancode: 0x%x release: %d ", scancode, action == GLFW_RELEASE); - int num_syms = xkb_state_key_get_syms(xkb->state, code_for_sym, &syms); - int num_clean_syms = xkb_state_key_get_syms(xkb->clean_state, code_for_sym, &clean_syms); + debug("%sscancode: 0x%x release: %d ", from_ime ? "From IBUS-> ": "", scancode, action == GLFW_RELEASE); + XKBStateGroup *sg = from_ime ? &xkb->ime_states : &xkb->states; + int num_syms = xkb_state_key_get_syms(sg->state, code_for_sym, &syms); + int num_clean_syms = xkb_state_key_get_syms(sg->clean_state, code_for_sym, &clean_syms); text[0] = 0; // According to the documentation of xkb_compose_state_feed it does not // support multi-sym events, so we ignore them if (num_syms != 1 || num_clean_syms != 1) { - debug("scancode: 0x%x num_syms: %d num_clean_syms: %d ignoring event\n", scancode, num_syms, num_clean_syms); + debug("num_syms: %d num_clean_syms: %d ignoring event\n", num_syms, num_clean_syms); return; } glfw_sym = clean_syms[0]; debug("clean_sym: %s ", glfw_xkb_keysym_name(clean_syms[0])); - ev.action = action; ev.glfw_modifiers = xkb->modifiers; ev.keycode = scancode; ev.keysym = glfw_sym; - ev.ibus = &xkb->ibus; - if (ibus_process_key(&ev)) { - debug(" queued in IBUS\n"); - return; + if (!from_ime) { + ev.action = action; ev.glfw_modifiers = sg->modifiers; ev.keycode = scancode; ev.keysym = glfw_sym; ev.window_id = window->id; + if (ibus_process_key(&ev, &xkb->ibus)) { + debug(" queued in IBUS\n"); + return; + } } if (action == GLFW_PRESS || action == GLFW_REPEAT) { const char *text_type = "composed_text"; - glfw_sym = compose_symbol(xkb, syms[0]); + glfw_sym = compose_symbol(sg->composeState, syms[0]); if (glfw_sym == XKB_KEY_NoSymbol) { debug("compose not complete, ignoring.\n"); return; @@ -460,12 +465,12 @@ glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t // are active (for example if ISO_Shift_Level_* mods are active // they are not reported by GLFW so the key should be the shifted // key). See https://github.com/kovidgoyal/kitty/issues/171#issuecomment-377557053 - xkb_mod_mask_t consumed_unknown_mods = xkb_state_key_get_consumed_mods(xkb->state, code_for_sym) & xkb->activeUnknownModifiers; - if (xkb->activeUnknownModifiers) debug("%s", format_xkb_mods(xkb, "active_unknown_mods", xkb->activeUnknownModifiers)); + xkb_mod_mask_t consumed_unknown_mods = xkb_state_key_get_consumed_mods(sg->state, code_for_sym) & sg->activeUnknownModifiers; + if (sg->activeUnknownModifiers) debug("%s", format_xkb_mods(xkb, "active_unknown_mods", sg->activeUnknownModifiers)); if (consumed_unknown_mods) { debug("%s", format_xkb_mods(xkb, "consumed_unknown_mods", consumed_unknown_mods)); } else glfw_sym = clean_syms[0]; // xkb returns text even if alt and/or super are pressed - if ( ((GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SUPER) & xkb->modifiers) == 0) xkb_state_key_get_utf8(xkb->state, code_for_sym, text, sizeof(text)); + if ( ((GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SUPER) & sg->modifiers) == 0) xkb_state_key_get_utf8(sg->state, code_for_sym, text, sizeof(text)); text_type = "text"; } if ((1 <= text[0] && text[0] <= 31) || text[0] == 127) text[0] = 0; // dont send text for ascii control codes @@ -474,7 +479,7 @@ glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t int glfw_keycode = glfw_key_for_sym(glfw_sym); GLFWbool is_fallback = GLFW_FALSE; if (glfw_keycode == GLFW_KEY_UNKNOWN && !text[0]) { - int num_default_syms = xkb_state_key_get_syms(xkb->default_state, code_for_sym, &default_syms); + int num_default_syms = xkb_state_key_get_syms(sg->default_state, code_for_sym, &default_syms); if (num_default_syms > 0) { glfw_sym = default_syms[0]; glfw_keycode = glfw_key_for_sym(glfw_sym); @@ -483,9 +488,35 @@ glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t } debug( "%s%s: %s xkb_key_name: %s\n", - format_mods(xkb->modifiers), + format_mods(sg->modifiers), is_fallback ? "glfw_fallback_key" : "glfw_key", _glfwGetKeyName(glfw_keycode), glfw_xkb_keysym_name(glfw_sym) ); - _glfwInputKeyboard(window, glfw_keycode, glfw_sym, action, xkb->modifiers, text, 0); + _glfwInputKeyboard(window, glfw_keycode, glfw_sym, action, sg->modifiers, text, 0); + +} + +void +glfw_xkb_key_from_ime(KeyEvent *ev, GLFWbool handled_by_ime) { + _GLFWXKBData *xkb = XKB_POINTER; + _GLFWwindow *window = _glfwWindowForId(ev->window_id); + static xkb_keycode_t last_handled_press_keycode = 0; + // We filter out release events that correspond to the last press event + // handled by the IME system. This wont fix the case of multiple key + // presses before a release, but is better than nothing. For that case + // you'd need to implement a ring buffer to store pending key presses. + xkb_keycode_t prev_handled_press = last_handled_press_keycode; + last_handled_press_keycode = 0; + GLFWbool is_release = ev->action == GLFW_RELEASE; + if (window && !handled_by_ime && !(is_release && ev->keycode == prev_handled_press)) { + handle_key_event(window, xkb, ev->keycode, ev->action, GLFW_TRUE); + } + if (!is_release && handled_by_ime) last_handled_press_keycode = ev->keycode; + xkb_state_update_key(xkb->ime_states.state, ev->keycode, is_release ? XKB_KEY_UP : XKB_KEY_DOWN); + update_modifiers(xkb, &xkb->ime_states); +} + +void +glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t scancode, int action) { + handle_key_event(window, xkb, scancode, action, GLFW_FALSE); } diff --git a/glfw/xkb_glfw.h b/glfw/xkb_glfw.h index 01f978fac..1e2c3ce76 100644 --- a/glfw/xkb_glfw.h +++ b/glfw/xkb_glfw.h @@ -35,13 +35,20 @@ #include "ibus_glfw.h" typedef struct { - struct xkb_context* context; - struct xkb_keymap* keymap; - struct xkb_keymap* default_keymap; struct xkb_state* state; struct xkb_state* clean_state; struct xkb_state* default_state; struct xkb_compose_state* composeState; + xkb_mod_mask_t activeUnknownModifiers; + unsigned int modifiers; +} XKBStateGroup; + + +typedef struct { + struct xkb_context* context; + struct xkb_keymap* keymap; + struct xkb_keymap* default_keymap; + XKBStateGroup states, ime_states; xkb_mod_index_t controlIdx; xkb_mod_index_t altIdx; @@ -55,8 +62,6 @@ typedef struct { xkb_mod_mask_t superMask; xkb_mod_mask_t capsLockMask; xkb_mod_mask_t numLockMask; - xkb_mod_mask_t activeUnknownModifiers; - unsigned int modifiers; xkb_mod_index_t unknownModifiers[256]; _GLFWIBUSData ibus; @@ -88,3 +93,4 @@ xkb_keysym_t glfw_xkb_sym_for_key(int key); void glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t scancode, int action); int glfw_xkb_keysym_from_name(const char *name, GLFWbool case_sensitive); void glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, int which, int a, int b, int c, int d); +void glfw_xkb_key_from_ime(KeyEvent *ev, GLFWbool handled_by_ime);