Skip to content

Commit

Permalink
fix(linux): Fix reordering of output
Browse files Browse the repository at this point in the history
This change implements a commit queue which allows to control the
order of the output. This ensures that any backspace we generate
will be processed before the character we're adding.

Requires changes in ibus (surrounding text fix (see #7072) and
prefilter change)

Fixes #1489, #4028, #4029, #4030, #4505, #5510, #6639
  • Loading branch information
ermshiperete committed Aug 17, 2022
1 parent bc0b716 commit 70ea643
Showing 1 changed file with 82 additions and 36 deletions.
118 changes: 82 additions & 36 deletions linux/ibus-keyman/src/engine.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,34 +51,52 @@
#define KEYMAN_LALT 56 // 0x38
#define KEYMAN_RCTRL 97 // 0x61
#define KEYMAN_RALT 100 // 0x64
#define KEYMAN_F24_KEYCODE_OUTPUT_SENTINEL 202
#define KEYMAN_NOCHAR_KEYSYM (0xffff | 0x1000000) // Unicode NOCHAR

typedef struct _IBusKeymanEngine IBusKeymanEngine;
typedef struct _IBusKeymanEngineClass IBusKeymanEngineClass;

#define MAX_QUEUE_SIZE 100

struct commit_queue_item {
gchar *char_buffer;
gboolean emitting_keystroke;
guint keyval;
guint keycode;
guint state;
};

struct _IBusKeymanEngine {
IBusEngine parent;

/* members */
km_kbp_keyboard *keyboard;
km_kbp_state *state;
gchar *ldmlfile;
gchar *kb_name;
gchar *char_buffer;
gunichar firstsurrogate;
gboolean lctrl_pressed;
gboolean rctrl_pressed;
gboolean lalt_pressed;
gboolean ralt_pressed;
gboolean emitting_keystroke;
IBusLookupTable *table;
IBusProperty *status_prop;
IBusPropList *prop_list;
IBusEngine parent;

/* members */
km_kbp_keyboard *keyboard;
km_kbp_state *state;
gchar *ldmlfile;
gchar *kb_name;
gchar *char_buffer;
gunichar firstsurrogate;
gboolean lctrl_pressed;
gboolean rctrl_pressed;
gboolean lalt_pressed;
gboolean ralt_pressed;
gboolean emitting_keystroke;
guint event_keyval;
guint event_keycode;
guint event_state;
IBusLookupTable *table;
IBusProperty *status_prop;
IBusPropList *prop_list;
#ifdef GDK_WINDOWING_X11
Display *xdisplay;
Display *xdisplay;
#endif
#ifdef GDK_WINDOWING_WAYLAND
GdkWaylandDisplay *wldisplay;
GdkWaylandDisplay *wldisplay;
#endif

struct commit_queue_item commit_queue[MAX_QUEUE_SIZE];
int queue_end;
};

struct _IBusKeymanEngineClass {
Expand Down Expand Up @@ -653,12 +671,8 @@ process_persist_action(
return TRUE;
}

static gboolean process_emit_keystroke_action(IBusKeymanEngine *keyman) {
if (keyman->char_buffer != NULL) {
ibus_keyman_engine_commit_string(keyman, keyman->char_buffer);
g_free(keyman->char_buffer);
keyman->char_buffer = NULL;
}
static gboolean
process_emit_keystroke_action(IBusKeymanEngine *keyman) {
keyman->emitting_keystroke = TRUE;
return TRUE;
}
Expand Down Expand Up @@ -692,18 +706,42 @@ process_capslock_action(
return TRUE;
}

static gboolean process_end_action(IBusKeymanEngine *keyman) {
keyman->firstsurrogate = 0;
if (keyman->char_buffer != NULL) {
ibus_keyman_engine_commit_string(keyman, keyman->char_buffer);
g_free(keyman->char_buffer);
keyman->char_buffer = NULL;
}
if (keyman->emitting_keystroke) {
keyman->emitting_keystroke = FALSE;
return FALSE;
static void
commit_text(IBusKeymanEngine *keyman) {
if (keyman->queue_end > 0) {
keyman->firstsurrogate = 0;
if (keyman->commit_queue[0].char_buffer != NULL) {
ibus_keyman_engine_commit_string(keyman, keyman->commit_queue[0].char_buffer);
g_free(keyman->commit_queue[0].char_buffer);
}
if (keyman->commit_queue[0].emitting_keystroke) {
ibus_engine_forward_key_event(keyman, keyman->commit_queue[0].keyval, keyman->commit_queue[0].keycode, keyman->commit_queue[0].state);
}
keyman->queue_end--;
memmove(keyman->commit_queue, &keyman->commit_queue[1], sizeof(struct commit_queue_item) * keyman->queue_end);
}
}

static gboolean
process_end_action(IBusKeymanEngine *keyman) {
keyman->commit_queue[keyman->queue_end].char_buffer = keyman->char_buffer;
keyman->commit_queue[keyman->queue_end].emitting_keystroke = keyman->emitting_keystroke;
keyman->commit_queue[keyman->queue_end].keyval = keyman->event_keyval;
keyman->commit_queue[keyman->queue_end].keycode = keyman->event_keycode;
keyman->commit_queue[keyman->queue_end].state = keyman->event_state;
keyman->queue_end++;
if (keyman->queue_end >= MAX_QUEUE_SIZE) {
g_error("Overflow of keyman commit_queue!");
keyman->queue_end = MAX_QUEUE_SIZE - 1;
}
keyman->char_buffer = NULL;
keyman->emitting_keystroke = FALSE;

// Forward a fake key event to get the correct order of events so that any backspace key we
// generated will be processed before the character we're adding. We need to send a
// valid keyval/keycode combination so that it doesn't get swallowed by GTK but which
// isn't very likely used in real keyboards. F24 seems to work for that.
ibus_engine_forward_key_event(keyman, KEYMAN_NOCHAR_KEYSYM, KEYMAN_F24_KEYCODE_OUTPUT_SENTINEL, IBUS_PREFILTER_MASK);
return TRUE;
}

Expand Down Expand Up @@ -769,6 +807,9 @@ ibus_keyman_engine_process_key_event(
guint state
) {
IBusKeymanEngine *keyman = (IBusKeymanEngine *)engine;
keyman->event_keyval = keyval;
keyman->event_keycode = keycode;
keyman->event_state = state;

gboolean isKeyDown = !(state & IBUS_RELEASE_MASK);

Expand All @@ -777,6 +818,11 @@ ibus_keyman_engine_process_key_event(
"DAR: ibus_keyman_engine_process_key_event - keyval=0x%02x keycode=0x%02x, state=0x%02x, isKeyDown=%d", keyval, keycode,
state, isKeyDown);

if (keycode == KEYMAN_F24_KEYCODE_OUTPUT_SENTINEL && (state & IBUS_PREFILTER_MASK)) {
commit_text(keyman);
return TRUE;
}

// REVIEW: why don't we handle these keys?
switch (keycode) {
case KEYMAN_LCTRL:
Expand Down Expand Up @@ -850,7 +896,7 @@ ibus_keyman_engine_process_key_event(
const km_kbp_action_item *action_items = km_kbp_state_action_items(keyman->state, &num_action_items);

if (!process_actions(engine, action_items, num_action_items))
return FALSE;
return TRUE;

context = km_kbp_state_context(keyman->state);
g_message("after processing all actions");
Expand Down

0 comments on commit 70ea643

Please sign in to comment.