ibus: Add support for ForwardKeyEvent signal

The ForwardKeyEvent ibus signal is used to maintain ordering between CommitText events and non-IME key events.
The ordering is important for Korean IMEs, where it automatically commits text whenever a character is fully composed. This is different from Chinese/Japanese IMEs, where user manually presses a key to commit each word.

Without this patch, kitty can't handle non-IME key events when used with ibus-hangul, which utilizes ForwardKeyEvent signal.

Without ForwardKeyEvent:
```
key | sequence of events from application's view
r   | UpdatePreeditText 'ㄱ'
k   | UpdatePreeditText '가'
1   | (receives the reply to ProcessKeyEvent call: "native_key: 0x31 release: 0 handled: 0")
    | -> UpdatePreeditText ''
    | -> CommitText '가'
```
Application receives "1가", which is incorrect.

With ForwardKeyEvent:
```
key | sequence of events from application's view
r   | UpdatePreeditText 'ㄱ'
k   | UpdatePreeditText '가'
1   | (receives the reply to ProcessKeyEvent call: "native_key: 0x31 release: 0 handled: 1", and kitty discards the event)
    | -> UpdatePreeditText ''
    | -> CommitText '가'
    | -> ForwardKeyEvent keysym=0x31("1")
```
Application receives "가1", which is correct.

Relevant ibus-hangul github issue to implement ForwardKeyEvent: https://github.com/libhangul/ibus-hangul/issues/42
Relevant changes in Qt to handle ForwardKeyEvent: https://codereview.qt-project.org/c/qt/qtbase/+/212179, https://codereview.qt-project.org/c/qt/qtbase/+/255587
This commit is contained in:
Park, Jeongmin 2021-09-29 21:20:09 +09:00
parent b1375e5ed1
commit 4232904055
3 changed files with 106 additions and 52 deletions

144
glfw/ibus_glfw.c vendored
View File

@ -49,6 +49,70 @@ enum Capabilities {
IBUS_CAP_SURROUNDING_TEXT = 1 << 5
};
typedef enum
{
IBUS_SHIFT_MASK = 1 << 0,
IBUS_LOCK_MASK = 1 << 1,
IBUS_CONTROL_MASK = 1 << 2,
IBUS_MOD1_MASK = 1 << 3,
IBUS_MOD2_MASK = 1 << 4,
IBUS_MOD3_MASK = 1 << 5,
IBUS_MOD4_MASK = 1 << 6,
IBUS_MOD5_MASK = 1 << 7,
IBUS_BUTTON1_MASK = 1 << 8,
IBUS_BUTTON2_MASK = 1 << 9,
IBUS_BUTTON3_MASK = 1 << 10,
IBUS_BUTTON4_MASK = 1 << 11,
IBUS_BUTTON5_MASK = 1 << 12,
/* The next few modifiers are used by XKB, so we skip to the end.
* Bits 15 - 23 are currently unused. Bit 29 is used internally.
*/
/* ibus mask */
IBUS_HANDLED_MASK = 1 << 24,
IBUS_FORWARD_MASK = 1 << 25,
IBUS_IGNORED_MASK = IBUS_FORWARD_MASK,
IBUS_SUPER_MASK = 1 << 26,
IBUS_HYPER_MASK = 1 << 27,
IBUS_META_MASK = 1 << 28,
IBUS_RELEASE_MASK = 1 << 30,
IBUS_MODIFIER_MASK = 0x5f001fff
} IBusModifierType;
static uint32_t
ibus_key_state(unsigned int glfw_modifiers, int action) {
uint32_t ans = action == GLFW_RELEASE ? IBUS_RELEASE_MASK : 0;
#define M(g, i) if(glfw_modifiers & GLFW_MOD_##g) ans |= i
M(SHIFT, IBUS_SHIFT_MASK);
M(CAPS_LOCK, IBUS_LOCK_MASK);
M(CONTROL, IBUS_CONTROL_MASK);
M(ALT, IBUS_MOD1_MASK);
M(NUM_LOCK, IBUS_MOD2_MASK);
M(SUPER, IBUS_MOD4_MASK);
/* To do: figure out how to get super/hyper/meta */
#undef M
return ans;
}
static unsigned int
glfw_modifiers(uint32_t ibus_key_state) {
unsigned int ans = 0;
#define M(g, i) if(ibus_key_state & i) ans |= GLFW_MOD_##g
M(SHIFT, IBUS_SHIFT_MASK);
M(CAPS_LOCK, IBUS_LOCK_MASK);
M(CONTROL, IBUS_CONTROL_MASK);
M(ALT, IBUS_MOD1_MASK);
M(NUM_LOCK, IBUS_MOD2_MASK);
M(SUPER, IBUS_MOD4_MASK);
/* To do: figure out how to get super/hyper/meta */
#undef M
return ans;
}
static bool
test_env_var(const char *name, const char *val) {
@ -106,6 +170,29 @@ get_ibus_text_from_message(DBusMessage *msg) {
return text;
}
static void
handle_ibus_forward_key_event(DBusMessage *msg) {
uint32_t keysym, keycode, state;
DBusMessageIter iter;
dbus_message_iter_init(msg, &iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) return;
dbus_message_iter_get_basic(&iter, &keysym);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) return;
dbus_message_iter_get_basic(&iter, &keycode);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) return;
dbus_message_iter_get_basic(&iter, &state);
int mods = glfw_modifiers(state);
debug("IBUS: ForwardKeyEvent: keysym=%x, keycode=%x, state=%x, glfw_mods=%x\n", keysym, keycode, state, mods);
glfw_xkb_forwarded_key_from_ime(keysym, mods);
}
static void
send_text(const char *text, GLFWIMEState ime_state) {
_GLFWwindow *w = _glfwFocusedWindow();
@ -126,7 +213,7 @@ message_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data)
_GLFWIBUSData *ibus = (_GLFWIBUSData*)user_data;
(void)ibus;
const char *text;
switch(glfw_dbus_match_signal(msg, IBUS_INPUT_INTERFACE, "CommitText", "UpdatePreeditText", "HidePreeditText", "ShowPreeditText", NULL)) {
switch(glfw_dbus_match_signal(msg, IBUS_INPUT_INTERFACE, "CommitText", "UpdatePreeditText", "HidePreeditText", "ShowPreeditText", "ForwardKeyEvent", NULL)) {
case 0:
text = get_ibus_text_from_message(msg);
debug("IBUS: CommitText: '%s'\n", text ? text : "(nil)");
@ -134,8 +221,8 @@ message_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data)
break;
case 1:
text = get_ibus_text_from_message(msg);
send_text(text, GLFW_IME_PREEDIT_CHANGED);
debug("IBUS: UpdatePreeditText: '%s'\n", text ? text : "(nil)");
send_text(text, GLFW_IME_PREEDIT_CHANGED);
break;
case 2:
debug("IBUS: HidePreeditText\n");
@ -143,6 +230,9 @@ message_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data)
case 3:
debug("IBUS: ShowPreeditText\n");
break;
case 4:
handle_ibus_forward_key_event(msg);
break;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
@ -376,56 +466,6 @@ glfw_ibus_set_cursor_geometry(_GLFWIBUSData *ibus, int x, int y, int w, int h) {
}
}
typedef enum
{
IBUS_SHIFT_MASK = 1 << 0,
IBUS_LOCK_MASK = 1 << 1,
IBUS_CONTROL_MASK = 1 << 2,
IBUS_MOD1_MASK = 1 << 3,
IBUS_MOD2_MASK = 1 << 4,
IBUS_MOD3_MASK = 1 << 5,
IBUS_MOD4_MASK = 1 << 6,
IBUS_MOD5_MASK = 1 << 7,
IBUS_BUTTON1_MASK = 1 << 8,
IBUS_BUTTON2_MASK = 1 << 9,
IBUS_BUTTON3_MASK = 1 << 10,
IBUS_BUTTON4_MASK = 1 << 11,
IBUS_BUTTON5_MASK = 1 << 12,
/* The next few modifiers are used by XKB, so we skip to the end.
* Bits 15 - 23 are currently unused. Bit 29 is used internally.
*/
/* ibus mask */
IBUS_HANDLED_MASK = 1 << 24,
IBUS_FORWARD_MASK = 1 << 25,
IBUS_IGNORED_MASK = IBUS_FORWARD_MASK,
IBUS_SUPER_MASK = 1 << 26,
IBUS_HYPER_MASK = 1 << 27,
IBUS_META_MASK = 1 << 28,
IBUS_RELEASE_MASK = 1 << 30,
IBUS_MODIFIER_MASK = 0x5f001fff
} IBusModifierType;
static uint32_t
ibus_key_state(unsigned int glfw_modifiers, int action) {
uint32_t ans = action == GLFW_RELEASE ? IBUS_RELEASE_MASK : 0;
#define M(g, i) if(glfw_modifiers & GLFW_MOD_##g) ans |= i
M(SHIFT, IBUS_SHIFT_MASK);
M(CAPS_LOCK, IBUS_LOCK_MASK);
M(CONTROL, IBUS_CONTROL_MASK);
M(ALT, IBUS_MOD1_MASK);
M(NUM_LOCK, IBUS_MOD2_MASK);
M(SUPER, IBUS_MOD4_MASK);
/* To do: figure out how to get super/hyper/meta */
#undef M
return ans;
}
void
key_event_processed(DBusMessage *msg, const char* errmsg, void *data) {
uint32_t handled = 0;

13
glfw/xkb_glfw.c vendored
View File

@ -842,6 +842,19 @@ glfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed) {
last_handled_press_keycode = ev->glfw_ev.native_key;
}
void
glfw_xkb_forwarded_key_from_ime(xkb_keysym_t keysym, unsigned int glfw_mods) {
_GLFWwindow *w = _glfwFocusedWindow();
if (w && w->callbacks.keyboard) {
GLFWkeyevent fake_ev = {.action = GLFW_PRESS};
fake_ev.native_key = keysym;
fake_ev.key = glfw_key_for_sym(keysym);
fake_ev.mods = glfw_mods;
fake_ev.ime_state = GLFW_IME_NONE;
w->callbacks.keyboard((GLFWwindow*) w, &fake_ev);
}
}
static bool
is_switch_layout_key(xkb_keysym_t xkb_sym) {
return xkb_sym == XKB_KEY_ISO_First_Group || xkb_sym == XKB_KEY_ISO_Last_Group || xkb_sym == XKB_KEY_ISO_Next_Group || xkb_sym == XKB_KEY_ISO_Prev_Group || xkb_sym == XKB_KEY_Mode_switch;

1
glfw/xkb_glfw.h vendored
View File

@ -98,3 +98,4 @@ void glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keyco
int glfw_xkb_keysym_from_name(const char *name, bool case_sensitive);
void glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, const GLFWIMEUpdateEvent *ev);
void glfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed);
void glfw_xkb_forwarded_key_from_ime(xkb_keysym_t keysym, unsigned int glfw_mods);