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:
parent
b1375e5ed1
commit
4232904055
144
glfw/ibus_glfw.c
vendored
144
glfw/ibus_glfw.c
vendored
@ -49,6 +49,70 @@ enum Capabilities {
|
|||||||
IBUS_CAP_SURROUNDING_TEXT = 1 << 5
|
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
|
static bool
|
||||||
test_env_var(const char *name, const char *val) {
|
test_env_var(const char *name, const char *val) {
|
||||||
@ -106,6 +170,29 @@ get_ibus_text_from_message(DBusMessage *msg) {
|
|||||||
return text;
|
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
|
static void
|
||||||
send_text(const char *text, GLFWIMEState ime_state) {
|
send_text(const char *text, GLFWIMEState ime_state) {
|
||||||
_GLFWwindow *w = _glfwFocusedWindow();
|
_GLFWwindow *w = _glfwFocusedWindow();
|
||||||
@ -126,7 +213,7 @@ message_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data)
|
|||||||
_GLFWIBUSData *ibus = (_GLFWIBUSData*)user_data;
|
_GLFWIBUSData *ibus = (_GLFWIBUSData*)user_data;
|
||||||
(void)ibus;
|
(void)ibus;
|
||||||
const char *text;
|
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:
|
case 0:
|
||||||
text = get_ibus_text_from_message(msg);
|
text = get_ibus_text_from_message(msg);
|
||||||
debug("IBUS: CommitText: '%s'\n", text ? text : "(nil)");
|
debug("IBUS: CommitText: '%s'\n", text ? text : "(nil)");
|
||||||
@ -134,8 +221,8 @@ message_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data)
|
|||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
text = get_ibus_text_from_message(msg);
|
text = get_ibus_text_from_message(msg);
|
||||||
send_text(text, GLFW_IME_PREEDIT_CHANGED);
|
|
||||||
debug("IBUS: UpdatePreeditText: '%s'\n", text ? text : "(nil)");
|
debug("IBUS: UpdatePreeditText: '%s'\n", text ? text : "(nil)");
|
||||||
|
send_text(text, GLFW_IME_PREEDIT_CHANGED);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
debug("IBUS: HidePreeditText\n");
|
debug("IBUS: HidePreeditText\n");
|
||||||
@ -143,6 +230,9 @@ message_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data)
|
|||||||
case 3:
|
case 3:
|
||||||
debug("IBUS: ShowPreeditText\n");
|
debug("IBUS: ShowPreeditText\n");
|
||||||
break;
|
break;
|
||||||
|
case 4:
|
||||||
|
handle_ibus_forward_key_event(msg);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
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
|
void
|
||||||
key_event_processed(DBusMessage *msg, const char* errmsg, void *data) {
|
key_event_processed(DBusMessage *msg, const char* errmsg, void *data) {
|
||||||
uint32_t handled = 0;
|
uint32_t handled = 0;
|
||||||
|
|||||||
13
glfw/xkb_glfw.c
vendored
13
glfw/xkb_glfw.c
vendored
@ -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;
|
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
|
static bool
|
||||||
is_switch_layout_key(xkb_keysym_t xkb_sym) {
|
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;
|
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
1
glfw/xkb_glfw.h
vendored
@ -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);
|
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_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_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);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user