From 82f34afdef475e28efbac46e42eff79304dcf36d Mon Sep 17 00:00:00 2001 From: Takao Fujiwara Date: Thu, 24 Nov 2022 17:59:03 +0900 Subject: [PATCH] Implement new process_key_event for ibus-x11 --- ibus-HEAD.patch | 432 ++++++++++++++++++++++++++++++++++++++++++++++++ ibus.spec | 5 +- 2 files changed, 436 insertions(+), 1 deletion(-) diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch index 9f57a0d..c95240e 100644 --- a/ibus-HEAD.patch +++ b/ibus-HEAD.patch @@ -318,3 +318,435 @@ index a4529c88..9400e9ba 100644 -- 2.37.3 +From 506ac9993d5166196b7c4e9bfa9fb0f9d3792ffa Mon Sep 17 00:00:00 2001 +From: fujiwarat +Date: Thu, 10 Nov 2022 18:38:05 +0900 +Subject: [PATCH] client/x11: Implement new process_key_event for ibus-x11 + +The new process_key_event is ported from GTK4 to X11 because +hangul maintainers wish to delete forward_key_event as much as possible +and currently we could apply forward_key_event to the sync mode only +and the new process_key_event is a new async key event process in X11 +and hangul might disable forward_key_event by default. + +Now the definition of IBUS_CAP_SYNC_PROCESS_KEY_V2 capability is changed +to set only if the sync mode. + +Also switch a heavy GMainLoop to the light GSource. + +Fixes: https://github.com/ibus/ibus/commit/c957c5f +--- + client/gtk2/ibusimcontext.c | 61 ++++++++++---- + client/x11/main.c | 157 +++++++++++++++++++++++++++++++----- + src/ibustypes.h | 1 + + 3 files changed, 184 insertions(+), 35 deletions(-) + +diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c +index 6e338157..1f3723e6 100644 +--- a/client/gtk2/ibusimcontext.c ++++ b/client/gtk2/ibusimcontext.c +@@ -382,8 +382,9 @@ typedef struct { + } ProcessKeyEventData; + + typedef struct { +- GMainLoop *loop; +- gboolean retval; ++ int count; ++ guint count_cb_id; ++ gboolean retval; + } ProcessKeyEventReplyData; + + static void +@@ -453,7 +454,23 @@ _process_key_event_reply_done (GObject *object, + } + g_return_if_fail (data); + data->retval = retval; +- g_main_loop_quit (data->loop); ++ data->count = 0; ++ g_source_remove (data->count_cb_id); ++} ++ ++static gboolean ++_process_key_event_count_cb (gpointer user_data) ++{ ++ ProcessKeyEventReplyData *data = (ProcessKeyEventReplyData *)user_data; ++ g_return_val_if_fail (data, G_SOURCE_REMOVE); ++ if (!data->count) ++ return G_SOURCE_REMOVE; ++ /* Wait for about 10 secs. */ ++ if (data->count++ == 10000) { ++ data->count = 0; ++ return G_SOURCE_REMOVE; ++ } ++ return G_SOURCE_CONTINUE; + } + + static gboolean +@@ -496,10 +513,10 @@ _process_key_event (IBusInputContext *context, + break; + } + case 2: { +- GMainLoop *loop = g_main_loop_new (NULL, TRUE); ++ GSource *source = g_timeout_source_new (1); + ProcessKeyEventReplyData *data = NULL; + +- if (loop) ++ if (source) + data = g_slice_new0 (ProcessKeyEventReplyData); + if (!data) { + g_warning ("Cannot wait for the reply of the process key event."); +@@ -507,11 +524,14 @@ _process_key_event (IBusInputContext *context, + keyval, + keycode - 8, + state); +- if (loop) +- g_main_loop_quit (loop); ++ if (source) ++ g_source_destroy (source); + break; + } +- data->loop = loop; ++ data->count = 1; ++ g_source_attach (source, NULL); ++ g_source_unref (source); ++ data->count_cb_id = g_source_get_id (source); + ibus_input_context_process_key_event_async (context, + keyval, + keycode - 8, +@@ -520,7 +540,14 @@ _process_key_event (IBusInputContext *context, + NULL, + _process_key_event_reply_done, + data); +- g_main_loop_run (loop); ++ g_source_set_callback (source, _process_key_event_count_cb, data, NULL); ++ while (data->count) ++ g_main_context_iteration (NULL, TRUE); ++ if (source->ref_count > 0) { ++ /* g_source_get_id() could causes a SEGV */ ++ g_info ("Broken GSource.ref_count and maybe a timing issue in %p.", ++ source); ++ } + retval = data->retval; + g_slice_free (ProcessKeyEventReplyData, data); + break; +@@ -994,8 +1021,8 @@ ibus_im_context_init (GObject *obj) + #else + ibusimcontext->caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS; + #endif +- if (_use_sync_mode != 1) +- ibusimcontext->caps |= IBUS_CAP_SYNC_PROCESS_KEY; ++ if (_use_sync_mode == 1) ++ ibusimcontext->caps |= IBUS_CAP_SYNC_PROCESS_KEY_V2; + + ibusimcontext->events_queue = g_queue_new (); + +@@ -1338,7 +1365,7 @@ ibus_im_context_reset (GtkIMContext *context) + * IBus uses button-press-event instead until GTK is fixed. + * https://gitlab.gnome.org/GNOME/gtk/issues/1534 + */ +- if (_use_sync_mode == 1) ++ if (_use_sync_mode != 0) + ibus_im_context_clear_preedit_text (ibusimcontext); + ibus_input_context_reset (ibusimcontext->ibuscontext); + } +@@ -1453,7 +1480,7 @@ ibus_im_context_set_client_window (GtkIMContext *context, + + if (ibusimcontext->client_window) { + #if !GTK_CHECK_VERSION (3, 98, 4) +- if (ibusimcontext->use_button_press_event && _use_sync_mode != 1) ++ if (ibusimcontext->use_button_press_event && _use_sync_mode == 0) + _connect_button_press_event (ibusimcontext, FALSE); + #endif + g_object_unref (ibusimcontext->client_window); +@@ -1463,7 +1490,7 @@ ibus_im_context_set_client_window (GtkIMContext *context, + if (client != NULL) { + ibusimcontext->client_window = g_object_ref (client); + #if !GTK_CHECK_VERSION (3, 98, 4) +- if (!ibusimcontext->use_button_press_event && _use_sync_mode != 1) ++ if (!ibusimcontext->use_button_press_event && _use_sync_mode == 0) + _connect_button_press_event (ibusimcontext, TRUE); + #endif + } +@@ -2085,7 +2112,7 @@ _ibus_context_update_preedit_text_cb (IBusInputContext *ibuscontext, + #if !GTK_CHECK_VERSION (3, 98, 4) + if (!ibusimcontext->use_button_press_event && + mode == IBUS_ENGINE_PREEDIT_COMMIT && +- _use_sync_mode != 1) { ++ _use_sync_mode == 0) { + if (ibusimcontext->client_window) { + _connect_button_press_event (ibusimcontext, TRUE); + } +@@ -2459,8 +2486,8 @@ _create_fake_input_context_done (IBusBus *bus, + NULL); + + guint32 caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS | IBUS_CAP_SURROUNDING_TEXT; +- if (_use_sync_mode != 1) +- caps |= IBUS_CAP_SYNC_PROCESS_KEY; ++ if (_use_sync_mode == 1) ++ caps |= IBUS_CAP_SYNC_PROCESS_KEY_V2; + ibus_input_context_set_capabilities (_fake_context, caps); + + /* focus in/out the fake context */ +diff --git a/client/x11/main.c b/client/x11/main.c +index 6057cc03..905fd251 100644 +--- a/client/x11/main.c ++++ b/client/x11/main.c +@@ -124,7 +124,7 @@ static gint g_debug_level = 0; + + static IBusBus *_bus = NULL; + +-static gboolean _use_sync_mode = TRUE; ++static char _use_sync_mode = 2; + + static void + _xim_preedit_start (XIMS xims, const X11IC *x11ic) +@@ -331,6 +331,7 @@ xim_create_ic (XIMS xims, IMChangeICStruct *call_data) + { + static int base_icid = 1; + X11IC *x11ic; ++ guint32 capabilities = IBUS_CAP_FOCUS; + + call_data->icid = base_icid ++; + +@@ -375,12 +376,11 @@ xim_create_ic (XIMS xims, IMChangeICStruct *call_data) + G_CALLBACK (_context_disabled_cb), x11ic); + + +- if (x11ic->input_style & XIMPreeditCallbacks) { +- ibus_input_context_set_capabilities (x11ic->context, IBUS_CAP_FOCUS | IBUS_CAP_PREEDIT_TEXT); +- } +- else { +- ibus_input_context_set_capabilities (x11ic->context, IBUS_CAP_FOCUS); +- } ++ if (x11ic->input_style & XIMPreeditCallbacks) ++ capabilities |= IBUS_CAP_PREEDIT_TEXT; ++ if (_use_sync_mode == 1) ++ capabilities |= IBUS_CAP_SYNC_PROCESS_KEY_V2; ++ ibus_input_context_set_capabilities (x11ic->context, capabilities); + + g_hash_table_insert (_x11_ic_table, + GINT_TO_POINTER (x11ic->icid), (gpointer)x11ic); +@@ -461,6 +461,13 @@ xim_unset_ic_focus (XIMS xims, IMChangeFocusStruct *call_data) + + } + ++typedef struct { ++ IMForwardEventStruct *pfe; ++ int count; ++ guint count_cb_id; ++ gboolean retval; ++} ProcessKeyEventReplyData; ++ + static void + _process_key_event_done (GObject *object, + GAsyncResult *res, +@@ -493,6 +500,43 @@ _process_key_event_done (GObject *object, + g_slice_free (IMForwardEventStruct, pfe); + } + ++static void ++_process_key_event_reply_done (GObject *object, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ IBusInputContext *context = (IBusInputContext *)object; ++ ProcessKeyEventReplyData *data = (ProcessKeyEventReplyData *)user_data; ++ GError *error = NULL; ++ gboolean retval = ibus_input_context_process_key_event_async_finish ( ++ context, ++ res, ++ &error); ++ if (error != NULL) { ++ g_warning ("Process Key Event failed: %s.", error->message); ++ g_error_free (error); ++ } ++ g_return_if_fail (data); ++ data->retval = retval; ++ data->count = 0; ++ g_source_remove (data->count_cb_id); ++} ++ ++static gboolean ++_process_key_event_count_cb (gpointer user_data) ++{ ++ ProcessKeyEventReplyData *data = (ProcessKeyEventReplyData *)user_data; ++ g_return_val_if_fail (data, G_SOURCE_REMOVE); ++ if (!data->count) ++ return G_SOURCE_REMOVE; ++ /* Wait for about 10 secs. */ ++ if (data->count++ == 10000) { ++ data->count = 0; ++ return G_SOURCE_REMOVE; ++ } ++ return G_SOURCE_CONTINUE; ++} ++ + static int + xim_forward_event (XIMS xims, IMForwardEventStruct *call_data) + { +@@ -520,14 +564,15 @@ xim_forward_event (XIMS xims, IMForwardEventStruct *call_data) + event.state |= IBUS_RELEASE_MASK; + } + +- if (_use_sync_mode) { ++ switch (_use_sync_mode) { ++ case 1: { + retval = ibus_input_context_process_key_event ( + x11ic->context, + event.keyval, + event.hardware_keycode - 8, + event.state); + if (retval) { +- if (! x11ic->has_preedit_area) { ++ if (!x11ic->has_preedit_area) { + _xim_set_cursor_location (x11ic); + } + return 1; +@@ -546,8 +591,80 @@ xim_forward_event (XIMS xims, IMForwardEventStruct *call_data) + IMForwardEvent (_xims, (XPointer) &fe); + + retval = 1; ++ break; + } +- else { ++ case 2: { ++ GSource *source = g_timeout_source_new (1); ++ ProcessKeyEventReplyData *data = NULL; ++ IMForwardEventStruct fe; ++ ++ if (source) ++ data = g_slice_new0 (ProcessKeyEventReplyData); ++ if (!data) { ++ g_warning ("Cannot wait for the reply of the process key event."); ++ retval = ibus_input_context_process_key_event ( ++ x11ic->context, ++ event.keyval, ++ event.hardware_keycode - 8, ++ event.state); ++ if (source) ++ g_source_destroy (source); ++ } else { ++ CARD16 connect_id = x11ic->connect_id; ++ data->count = 1; ++ g_source_attach (source, NULL); ++ g_source_unref (source); ++ data->count_cb_id = g_source_get_id (source); ++ ibus_input_context_process_key_event_async ( ++ x11ic->context, ++ event.keyval, ++ event.hardware_keycode - 8, ++ event.state, ++ -1, ++ NULL, ++ _process_key_event_reply_done, ++ data); ++ g_source_set_callback (source, _process_key_event_count_cb, ++ data, NULL); ++ while (data->count) ++ g_main_context_iteration (NULL, TRUE); ++ if (source->ref_count > 0) { ++ /* g_source_get_id() could causes a SEGV */ ++ g_info ("Broken GSource.ref_count and maybe a timing " ++ "issue in %p.", source); ++ } ++ retval = data->retval; ++ g_slice_free (ProcessKeyEventReplyData, data); ++ ++ if (g_hash_table_lookup (_connections, ++ GINT_TO_POINTER ((gint)connect_id)) ++ == NULL) { ++ return 1; ++ } ++ } ++ ++ if (retval) { ++ if (! x11ic->has_preedit_area) { ++ _xim_set_cursor_location (x11ic); ++ } ++ return 1; ++ } ++ ++ memset (&fe, 0, sizeof (fe)); ++ ++ fe.major_code = XIM_FORWARD_EVENT; ++ fe.icid = x11ic->icid; ++ fe.connect_id = x11ic->connect_id; ++ fe.sync_bit = 0; ++ fe.serial_number = 0L; ++ fe.event = call_data->event; ++ ++ IMForwardEvent (_xims, (XPointer) &fe); ++ ++ retval = 1; ++ break; ++ } ++ default: { + IMForwardEventStruct *pfe; + + pfe = g_slice_new0 (IMForwardEventStruct); +@@ -569,6 +686,7 @@ xim_forward_event (XIMS xims, IMForwardEventStruct *call_data) + pfe); + retval = 1; + } ++ } + return retval; + } + +@@ -1026,23 +1144,26 @@ _context_disabled_cb (IBusInputContext *context, + _xim_preedit_end (_xims, x11ic); + } + +-static gboolean +-_get_boolean_env(const gchar *name, +- gboolean defval) ++static char ++_get_char_env (const gchar *name, ++ char defval) + { + const gchar *value = g_getenv (name); + + if (value == NULL) +- return defval; ++ return defval; + + if (g_strcmp0 (value, "") == 0 || + g_strcmp0 (value, "0") == 0 || + g_strcmp0 (value, "false") == 0 || + g_strcmp0 (value, "False") == 0 || +- g_strcmp0 (value, "FALSE") == 0) +- return FALSE; ++ g_strcmp0 (value, "FALSE") == 0) { ++ return 0; ++ } else if (!g_strcmp0 (value, "2")) { ++ return 2; ++ } + +- return TRUE; ++ return 1; + } + + static void +@@ -1059,7 +1180,7 @@ _init_ibus (void) + G_CALLBACK (_bus_disconnected_cb), NULL); + + /* https://github.com/ibus/ibus/issues/1713 */ +- _use_sync_mode = _get_boolean_env ("IBUS_ENABLE_SYNC_MODE", TRUE); ++ _use_sync_mode = _get_char_env ("IBUS_ENABLE_SYNC_MODE", 2); + } + + static void +diff --git a/src/ibustypes.h b/src/ibustypes.h +index a8eee319..ba2a0010 100644 +--- a/src/ibustypes.h ++++ b/src/ibustypes.h +@@ -124,6 +124,7 @@ typedef enum { + IBUS_CAP_SURROUNDING_TEXT = 1 << 5, + IBUS_CAP_OSK = 1 << 6, + IBUS_CAP_SYNC_PROCESS_KEY = 1 << 7, ++ IBUS_CAP_SYNC_PROCESS_KEY_V2 = IBUS_CAP_SYNC_PROCESS_KEY, + } IBusCapabilite; + + /** +-- +2.37.3 + diff --git a/ibus.spec b/ibus.spec index 01e1d49..92dbc75 100644 --- a/ibus.spec +++ b/ibus.spec @@ -39,7 +39,7 @@ Name: ibus Version: 1.5.27 -Release: 5%{?dist} +Release: 6%{?dist} Summary: Intelligent Input Bus for Linux OS License: LGPL-2.0-or-later URL: https://github.com/ibus/%name/wiki @@ -523,6 +523,9 @@ dconf update || : %{_datadir}/installed-tests/ibus %changelog +* Thu Nov 24 2022 Takao Fujiwara - 1.5.27-6 +- Implement new process_key_event for ibus-x11 + * Wed Nov 16 2022 Takao Fujiwara - 1.5.27-5 - Migrate license tag to SPDX