From 5cfe838715097d61b50da55f80bcff2c698ca885 Mon Sep 17 00:00:00 2001 From: Changwoo Ryu Date: Fri, 18 Feb 2022 09:07:02 +0900 Subject: [PATCH] client/gtk2/ibusimcontext: Fix forward key keycode for GTK4 When a keycode is provided (!= 0) for a forwarded key event, convert it to a GTK keycode before passing it to gtk_im_context_filter_key(). Also free GdkKeymapKey after gdk_display_map_keyval() is called. BUG=https://github.com/ibus/ibus/issues/2380 BUG=https://github.com/ibus/ibus/issues/2382 --- client/gtk2/ibusimcontext.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c index c2df3a87..a5e5e792 100644 --- a/client/gtk2/ibusimcontext.c +++ b/client/gtk2/ibusimcontext.c @@ -1945,7 +1945,9 @@ _ibus_context_forward_key_event_cb (IBusInputContext *ibuscontext, #if GTK_CHECK_VERSION (3, 98, 4) int group = 0; g_return_if_fail (GTK_IS_IM_CONTEXT (ibusimcontext)); - if (keycode == 0 && ibusimcontext->client_window) { + if (keycode != 0) { + keycode += 8; // to GTK keycode + } else if (ibusimcontext->client_window) { GdkDisplay *display = gtk_widget_get_display (ibusimcontext->client_window); GdkKeymapKey *keys = NULL; @@ -1953,6 +1955,7 @@ _ibus_context_forward_key_event_cb (IBusInputContext *ibuscontext, if (gdk_display_map_keyval (display, keyval, &keys, &n_keys)) { keycode = keys->keycode; group = keys->group; + g_free (keys); } else { g_warning ("Failed to parse keycode from keyval %x", keyval); } -- 2.37.3 From 8711dc83225a7fade3ba67ab796ecb03b38406ff Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Fri, 20 May 2022 20:54:58 +0900 Subject: [PATCH] client/gtk2/ibusimcontext: keycode - 8 for gtk3 keycode generation Since IBus keycode subtracts 8 from Linux keycode, keycodes from gdk_keymap_get_entries_for_keyval() also have to be subtracted 8. The keycodes will add 8 when they bring back the GDK event loop. --- client/gtk2/ibusimcontext.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c index a5e5e792..07835a24 100644 --- a/client/gtk2/ibusimcontext.c +++ b/client/gtk2/ibusimcontext.c @@ -2,8 +2,8 @@ /* vim:set et sts=4: */ /* ibus - The Input Bus * Copyright (C) 2008-2013 Peng Huang - * Copyright (C) 2015-2021 Takao Fujiwara - * Copyright (C) 2008-2021 Red Hat, Inc. + * Copyright (C) 2015-2022 Takao Fujiwara + * Copyright (C) 2008-2022 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1980,6 +1980,9 @@ _ibus_context_forward_key_event_cb (IBusInputContext *ibuscontext, keycode = keys->keycode; else g_warning ("Failed to parse keycode from keyval %x", keyval); + /* _create_gdk_event() will add 8 to keycode. */ + if (keycode != 0) + keycode -= 8; } GdkEventKey *event = _create_gdk_event (ibusimcontext, keyval, keycode, state); gdk_event_put ((GdkEvent *)event); -- 2.37.3 From 3e5fab4991f4e2e22b56cf57d4dfb779a1d1977c Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Fri, 20 May 2022 20:54:59 +0900 Subject: [PATCH] client/gtk2: Revert CCedilla change for pt-BR gtk_im_context_simple_add_table() is deprecated in GTK4. I decide to delete gtk_im_context_simple_add_table() here because the change 03c9e591430c62354bbf26ef7bd4a2e6acfb7c8f is no longer needed because IBusEngineSimple has implemented to load pt_br compose key by locale. Fixes: 03c9e591430c62354bbf26ef7bd4a2e6acfb7c8f BUG=chromium-os:11421 BUG=http://codereview.appspot.com/3989060 --- client/gtk2/ibusimcontext.c | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c index 07835a24..c7f23293 100644 --- a/client/gtk2/ibusimcontext.c +++ b/client/gtk2/ibusimcontext.c @@ -874,33 +874,6 @@ ibus_im_context_class_fini (IBusIMContextClass *class) g_bus_unwatch_name (_daemon_name_watch_id); } -/* Copied from gtk+2.0-2.20.1/modules/input/imcedilla.c to fix crosbug.com/11421. - * Overwrite the original Gtk+'s compose table in gtk+-2.x.y/gtk/gtkimcontextsimple.c. */ - -/* The difference between this and the default input method is the handling - * of C+acute - this method produces C WITH CEDILLA rather than C WITH ACUTE. - * For languages that use CCedilla and not acute, this is the preferred mapping, - * and is particularly important for pt_BR, where the us-intl keyboard is - * used extensively. - */ -static guint16 cedilla_compose_seqs[] = { -#ifdef DEPRECATED_GDK_KEYSYMS - GDK_dead_acute, GDK_C, 0, 0, 0, 0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */ - GDK_dead_acute, GDK_c, 0, 0, 0, 0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */ - GDK_Multi_key, GDK_apostrophe, GDK_C, 0, 0, 0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */ - GDK_Multi_key, GDK_apostrophe, GDK_c, 0, 0, 0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */ - GDK_Multi_key, GDK_C, GDK_apostrophe, 0, 0, 0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */ - GDK_Multi_key, GDK_c, GDK_apostrophe, 0, 0, 0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */ -#else - GDK_KEY_dead_acute, GDK_KEY_C, 0, 0, 0, 0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */ - GDK_KEY_dead_acute, GDK_KEY_c, 0, 0, 0, 0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */ - GDK_KEY_Multi_key, GDK_KEY_apostrophe, GDK_KEY_C, 0, 0, 0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */ - GDK_KEY_Multi_key, GDK_KEY_apostrophe, GDK_KEY_c, 0, 0, 0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */ - GDK_KEY_Multi_key, GDK_KEY_C, GDK_KEY_apostrophe, 0, 0, 0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */ - GDK_KEY_Multi_key, GDK_KEY_c, GDK_KEY_apostrophe, 0, 0, 0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */ -#endif -}; - static void ibus_im_context_init (GObject *obj) { @@ -936,10 +909,6 @@ ibus_im_context_init (GObject *obj) // Create slave im context ibusimcontext->slave = gtk_im_context_simple_new (); - gtk_im_context_simple_add_table (GTK_IM_CONTEXT_SIMPLE (ibusimcontext->slave), - cedilla_compose_seqs, - 4, - G_N_ELEMENTS (cedilla_compose_seqs) / (4 + 2)); g_signal_connect (ibusimcontext->slave, "commit", -- 2.37.3 From b94f0c1cea5d0e423fef3bcc13b23f212f04c930 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Thu, 7 Jul 2022 08:13:57 +0900 Subject: [PATCH] src: Add IBUS_CAP_OSK to IBusCapabilite Some IMEs' behavior is different between the on-screen keyboard and the direct physical keyboard and this flag is useful for the IMEs. Also fix src/ibusaccelgroup.c for gtkdoc-mkhtml. If the API comment of IBusCapabilite is updated, XML & HTML files are rebuilt and gtk-doc-1.33.2 no longer accepts HTML tags in the comments. diff --git a/src/ibusaccelgroup.c b/src/ibusaccelgroup.c index ef2d3976..aec1c7e4 100644 --- a/src/ibusaccelgroup.c +++ b/src/ibusaccelgroup.c @@ -267,14 +267,14 @@ is_keycode (const gchar *string) * modifier mask, %NULL * * Parses a string representing an accelerator. The format looks like - * “a” or “F1” or “z” (the last one is - * for key release). + * “<Control>a” or “<Shift><Alt>F1” or “<Release%gt;z” + * (the last one is for key release). * * The parser is fairly liberal and allows lower or upper case, and also - * abbreviations such as “” and “”. Key names are parsed using - * gdk_keyval_from_name(). For character keys the name is not the symbol, - * but the lowercase name, e.g. one would use “minus” instead of - * “-”. + * abbreviations such as “<Ctl>” and “<Ctrl>”. Key names are + * parsed using gdk_keyval_from_name(). For character keys the name is not the + * symbol, but the lowercase name, e.g. one would use “<Ctrl>minus” + * instead of “<Ctrl>-”. * * If the parse fails, @accelerator_key and @accelerator_mods will * be set to 0 (zero). @@ -403,7 +403,7 @@ out: * * Converts an accelerator keyval and modifier mask into a string * parseable by gtk_accelerator_parse(). For example, if you pass in - * #IBUS_KEY_q and #IBUS_CONTROL_MASK, this function returns “q”. + * #IBUS_KEY_q and #IBUS_CONTROL_MASK, this function returns “<Control>q”. * * If you need to display accelerators in the user interface, * see gtk_accelerator_get_label(). diff --git a/src/ibustypes.h b/src/ibustypes.h index 990659ac..60bcb92b 100644 --- a/src/ibustypes.h +++ b/src/ibustypes.h @@ -108,6 +108,7 @@ typedef enum * @IBUS_CAP_PROPERTY: UI is capable to have property. * @IBUS_CAP_SURROUNDING_TEXT: Client can provide surround text, * or IME can handle surround text. + * @IBUS_CAP_OSK: UI is owned by on-screen keyboard. * * Capability flags of UI. */ @@ -118,6 +119,7 @@ typedef enum { IBUS_CAP_FOCUS = 1 << 3, IBUS_CAP_PROPERTY = 1 << 4, IBUS_CAP_SURROUNDING_TEXT = 1 << 5, + IBUS_CAP_OSK = 1 << 6, } IBusCapabilite; /** -- 2.37.3 From c957c5f6ba07074a8fb56c978c27873c1cfe0783 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Tue, 19 Jul 2022 22:58:24 +0900 Subject: [PATCH] client/gtk2: Implement new process_key_event for GTK4 diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c index c7f23293..bc14df00 100644 --- a/client/gtk2/ibusimcontext.c +++ b/client/gtk2/ibusimcontext.c @@ -111,13 +111,13 @@ static guint _signal_delete_surrounding_id = 0; static guint _signal_retrieve_surrounding_id = 0; #if GTK_CHECK_VERSION (3, 98, 4) -static gboolean _use_sync_mode = TRUE; +static char _use_sync_mode = 2; #else static const gchar *_no_snooper_apps = NO_SNOOPER_APPS; static gboolean _use_key_snooper = ENABLE_SNOOPER; static guint _key_snooper_id = 0; -static gboolean _use_sync_mode = FALSE; +static char _use_sync_mode = 0; #endif static const gchar *_discard_password_apps = ""; @@ -375,12 +375,15 @@ ibus_im_context_commit_event (IBusIMContext *ibusimcontext, return FALSE; } -struct _ProcessKeyEventData { +typedef struct { GdkEvent *event; IBusIMContext *ibusimcontext; -}; +} ProcessKeyEventData; -typedef struct _ProcessKeyEventData ProcessKeyEventData; +typedef struct { + GMainLoop *loop; + gboolean retval; +} ProcessKeyEventReplyData; static void _process_key_event_done (GObject *object, @@ -395,12 +398,12 @@ _process_key_event_done (GObject *object, IBusIMContext *ibusimcontext = data->ibusimcontext; #endif GError *error = NULL; + gboolean retval; g_slice_free (ProcessKeyEventData, data); - gboolean retval = ibus_input_context_process_key_event_async_finish ( - context, - res, - &error); + retval = ibus_input_context_process_key_event_async_finish (context, + res, + &error); if (error != NULL) { g_warning ("Process Key Event failed: %s.", error->message); @@ -431,6 +434,27 @@ _process_key_event_done (GObject *object, #endif } +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; + g_main_loop_quit (data->loop); +} + static gboolean _process_key_event (IBusInputContext *context, #if GTK_CHECK_VERSION (3, 98, 4) @@ -462,13 +486,45 @@ _process_key_event (IBusInputContext *context, #endif keycode = hardware_keycode; - if (_use_sync_mode) { + switch (_use_sync_mode) { + case 1: { retval = ibus_input_context_process_key_event (context, + keyval, + keycode - 8, + state); + break; + } + case 2: { + GMainLoop *loop = g_main_loop_new (NULL, TRUE); + ProcessKeyEventReplyData *data = NULL; + + if (loop) + 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 (context, + keyval, + keycode - 8, + state); + if (loop) + g_main_loop_quit (loop); + break; + } + data->loop = loop; + ibus_input_context_process_key_event_async (context, keyval, keycode - 8, - state); + state, + -1, + NULL, + _process_key_event_reply_done, + data); + g_main_loop_run (loop); + retval = data->retval; + g_slice_free (ProcessKeyEventReplyData, data); + break; } - else { + default: { ProcessKeyEventData *data = g_slice_new0 (ProcessKeyEventData); #if GTK_CHECK_VERSION (3, 98, 4) data->event = gdk_event_ref (event); @@ -487,6 +543,7 @@ _process_key_event (IBusInputContext *context, retval = TRUE; } + } /* GTK4 does not provide gtk_key_snooper_install() and also * GtkIMContextClass->filter_keypress() cannot send the updated @@ -676,24 +733,47 @@ _key_snooper_cb (GtkWidget *widget, #endif static gboolean -_get_boolean_env(const gchar *name, - gboolean defval) +_get_boolean_env (const gchar *name, + gboolean 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 FALSE; + } return TRUE; } +static char +_get_char_env (const gchar *name, + char defval) +{ + const gchar *value = g_getenv (name); + + if (value == NULL) + 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 0; + } else if (!g_strcmp0 (value, "2")) { + return 2; + } + + return 1; +} + static void daemon_name_appeared (GDBusConnection *connection, const gchar *name, @@ -777,11 +857,11 @@ ibus_im_context_class_init (IBusIMContextClass *class) g_assert (_signal_retrieve_surrounding_id != 0); #if GTK_CHECK_VERSION (3, 98, 4) - _use_sync_mode = _get_boolean_env ("IBUS_ENABLE_SYNC_MODE", TRUE); + _use_sync_mode = _get_char_env ("IBUS_ENABLE_SYNC_MODE", 2); #else _use_key_snooper = !_get_boolean_env ("IBUS_DISABLE_SNOOPER", !(ENABLE_SNOOPER)); - _use_sync_mode = _get_boolean_env ("IBUS_ENABLE_SYNC_MODE", FALSE); + _use_sync_mode = (char)_get_char_env ("IBUS_ENABLE_SYNC_MODE", 0); #endif _use_discard_password = _get_boolean_env ("IBUS_DISCARD_PASSWORD", FALSE); @@ -904,6 +984,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; ibusimcontext->events_queue = g_queue_new (); @@ -1246,7 +1328,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) + if (_use_sync_mode == 1) ibus_im_context_clear_preedit_text (ibusimcontext); ibus_input_context_reset (ibusimcontext->ibuscontext); } @@ -1361,7 +1443,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) + if (ibusimcontext->use_button_press_event && _use_sync_mode != 1) _connect_button_press_event (ibusimcontext, FALSE); #endif g_object_unref (ibusimcontext->client_window); @@ -1371,7 +1453,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) + if (!ibusimcontext->use_button_press_event && _use_sync_mode != 1) _connect_button_press_event (ibusimcontext, TRUE); #endif } @@ -1993,7 +2075,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) { + _use_sync_mode != 1) { if (ibusimcontext->client_window) { _connect_button_press_event (ibusimcontext, TRUE); } @@ -2200,6 +2282,8 @@ _create_input_context_done (IBusBus *bus, static void _create_input_context (IBusIMContext *ibusimcontext) { + gchar *prgname = g_strdup (g_get_prgname()); + gchar *client_name; IDEBUG ("%s", __FUNCTION__); g_assert (ibusimcontext->ibuscontext == NULL); @@ -2208,11 +2292,24 @@ _create_input_context (IBusIMContext *ibusimcontext) ibusimcontext->cancellable = g_cancellable_new (); + if (!prgname) + prgname = g_strdup_printf ("(%d)", getpid ()); + client_name = g_strdup_printf ("%s:%s", +#if GTK_CHECK_VERSION (3, 98, 4) + "gtk4-im", +#elif GTK_CHECK_VERSION (2, 91, 0) + "gtk3-im", +#else + "gtk-im", +#endif + prgname); + g_free (prgname); ibus_bus_create_input_context_async (_bus, - "gtk-im", -1, + client_name, -1, ibusimcontext->cancellable, (GAsyncReadyCallback)_create_input_context_done, g_object_ref (ibusimcontext)); + g_free (client_name); } /* Callback functions for slave context */ @@ -2329,6 +2426,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; ibus_input_context_set_capabilities (_fake_context, caps); /* focus in/out the fake context */ diff --git a/src/ibustypes.h b/src/ibustypes.h index 60bcb92b..a8eee319 100644 --- a/src/ibustypes.h +++ b/src/ibustypes.h @@ -109,6 +109,9 @@ typedef enum * @IBUS_CAP_SURROUNDING_TEXT: Client can provide surround text, * or IME can handle surround text. * @IBUS_CAP_OSK: UI is owned by on-screen keyboard. + * @IBUS_CAP_SYNC_PROCESS_KEY: Asynchronous process key events are not + * supported and the ibus_engine_forward_key_event() should not be + * used for the return value of #IBusEngine::process_key_event(). * * Capability flags of UI. */ @@ -120,6 +123,7 @@ typedef enum { IBUS_CAP_PROPERTY = 1 << 4, IBUS_CAP_SURROUNDING_TEXT = 1 << 5, IBUS_CAP_OSK = 1 << 6, + IBUS_CAP_SYNC_PROCESS_KEY = 1 << 7, } IBusCapabilite; /** -- 2.41.0 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.41.0 From 497f0c74230a65309e22ce5569060ce48310406b Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Thu, 23 Mar 2023 13:07:30 +0900 Subject: [PATCH] client/x11: Fix Key typing order ibus-x11 now also uses the hybrid process key events with IBUS_ENABLE_SYNC_MODE=2 and it waits for the async API with GSource and g_main_context_iteration() in xim_forward_event(). But g_main_context_iteration() calls gdk_event_source_dispatch() and it can call another xim_forward_event() and the callbacks of ibus_input_context_process_key_event_async() can be nested. So if the forwarding API is called out of the callbacks of ibus_input_context_process_key_event_async(), the key events order is swapped due to the delayed return of g_main_context_iteration(). To resolve this issue, the forwarding API should be called in the callbacks of ibus_input_context_process_key_event_async(). Fixes: https://github.com/ibus/ibus/commit/506ac99 BUG=https://github.com/ibus/ibus/issues/2480 --- client/x11/main.c | 160 ++++++++++++++++++++++++---------------------- 1 file changed, 83 insertions(+), 77 deletions(-) diff --git a/client/x11/main.c b/client/x11/main.c index 905fd251..83d95cb7 100644 --- a/client/x11/main.c +++ b/client/x11/main.c @@ -2,7 +2,7 @@ /* vim:set et sts=4: */ /* ibus * Copyright (C) 2007-2015 Peng Huang - * Copyright (C) 2015-2021 Takao Fujiwara + * Copyright (C) 2015-2023 Takao Fujiwara * Copyright (C) 2007-2015 Red Hat, Inc. * * main.c: @@ -48,6 +48,9 @@ #include +/* Wait for about 120 secs to return a key from async process-key-event. */ +#define MAX_WAIT_KEY_TIME 120000 + #define LOG(level, fmt_args...) \ if (g_debug_level >= (level)) { \ g_debug (fmt_args); \ @@ -461,11 +463,39 @@ xim_unset_ic_focus (XIMS xims, IMChangeFocusStruct *call_data) } +static void +_xim_forward_key_event_done (X11IC *x11ic, + XEvent *event, + gboolean processed) +{ + IMForwardEventStruct fe; + if (processed) { + if (!x11ic->has_preedit_area) { + _xim_set_cursor_location (x11ic); + } + return; + } + g_assert (x11ic); + g_assert (event); + + 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 = *event; + IMForwardEvent (_xims, (XPointer) &fe); +} + + typedef struct { - IMForwardEventStruct *pfe; int count; guint count_cb_id; gboolean retval; + X11IC *x11ic; + CARD16 connect_id; + XEvent event; } ProcessKeyEventReplyData; static void @@ -474,7 +504,7 @@ _process_key_event_done (GObject *object, gpointer user_data) { IBusInputContext *context = (IBusInputContext *)object; - IMForwardEventStruct *pfe = (IMForwardEventStruct*) user_data; + ProcessKeyEventReplyData *data = (ProcessKeyEventReplyData *)user_data; GError *error = NULL; gboolean retval = ibus_input_context_process_key_event_async_finish ( @@ -488,16 +518,15 @@ _process_key_event_done (GObject *object, } if (g_hash_table_lookup (_connections, - GINT_TO_POINTER ((gint) pfe->connect_id)) + GINT_TO_POINTER ((gint)data->connect_id)) == NULL) { - g_slice_free (IMForwardEventStruct, pfe); + g_slice_free (ProcessKeyEventReplyData, data); return; } - if (retval == FALSE) { - IMForwardEvent (_xims, (XPointer) pfe); - } - g_slice_free (IMForwardEventStruct, pfe); + if (retval == FALSE) + _xim_forward_key_event_done (data->x11ic, &data->event, retval); + g_slice_free (ProcessKeyEventReplyData, data); } static void @@ -518,6 +547,21 @@ _process_key_event_reply_done (GObject *object, } g_return_if_fail (data); data->retval = retval; + if (g_hash_table_lookup (_connections, + GINT_TO_POINTER ((gint)data->connect_id)) + == NULL) { + return; + } + /* _xim_forward_key_event_done() should be called in + * _process_key_event_reply_done() because g_main_context_iteration() + * can call another xim_forward_event() and xim_forward_event() can be + * nested and the first _process_key_event_reply_done() is returned + * at last with g_main_context_iteration() so + * if _xim_forward_key_event_done() is called out of + * _process_key_event_reply_done(), the key events order + * can be swapped. + */ + _xim_forward_key_event_done (data->x11ic, &data->event, retval); data->count = 0; g_source_remove (data->count_cb_id); } @@ -529,9 +573,8 @@ _process_key_event_count_cb (gpointer 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; + if (data->count++ == MAX_WAIT_KEY_TIME) { + g_warning ("Key event is not returned for %usecs.", MAX_WAIT_KEY_TIME); return G_SOURCE_REMOVE; } return G_SOURCE_CONTINUE; @@ -571,32 +614,13 @@ xim_forward_event (XIMS xims, IMForwardEventStruct *call_data) event.keyval, event.hardware_keycode - 8, event.state); - if (retval) { - if (!x11ic->has_preedit_area) { - _xim_set_cursor_location (x11ic); - } - return 1; - } - - IMForwardEventStruct fe; - 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); - + _xim_forward_key_event_done (x11ic, &call_data->event, retval); retval = 1; break; } case 2: { GSource *source = g_timeout_source_new (1); ProcessKeyEventReplyData *data = NULL; - IMForwardEventStruct fe; if (source) data = g_slice_new0 (ProcessKeyEventReplyData); @@ -610,11 +634,13 @@ xim_forward_event (XIMS xims, IMForwardEventStruct *call_data) 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); + data->connect_id = call_data->connect_id; + data->x11ic = x11ic; + data->event = *((XEvent*)xevent); ibus_input_context_process_key_event_async ( x11ic->context, event.keyval, @@ -626,7 +652,7 @@ xim_forward_event (XIMS xims, IMForwardEventStruct *call_data) data); g_source_set_callback (source, _process_key_event_count_cb, data, NULL); - while (data->count) + while (data->count > 0 && data->count < MAX_WAIT_KEY_TIME) g_main_context_iteration (NULL, TRUE); if (source->ref_count > 0) { /* g_source_get_id() could causes a SEGV */ @@ -634,46 +660,33 @@ xim_forward_event (XIMS xims, IMForwardEventStruct *call_data) "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) { + if (data->count == 0) { + g_slice_free (ProcessKeyEventReplyData, data); return 1; } } - if (retval) { - if (! x11ic->has_preedit_area) { - _xim_set_cursor_location (x11ic); - } - return 1; + g_slice_free (ProcessKeyEventReplyData, data); + if (g_hash_table_lookup (_connections, + GINT_TO_POINTER ((gint)call_data->connect_id)) + == NULL) { + 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); - + _xim_forward_key_event_done (x11ic, &call_data->event, retval); retval = 1; break; } default: { - IMForwardEventStruct *pfe; + ProcessKeyEventReplyData *data; - pfe = g_slice_new0 (IMForwardEventStruct); - pfe->major_code = XIM_FORWARD_EVENT; - pfe->icid = x11ic->icid; - pfe->connect_id = x11ic->connect_id; - pfe->sync_bit = 0; - pfe->serial_number = 0L; - pfe->event = call_data->event; + if (!(data = g_slice_new0 (ProcessKeyEventReplyData))) { + g_warning ("Cannot allocate async data"); + _xim_forward_key_event_done (x11ic, &call_data->event, 0); + return 1; + } + data->connect_id = call_data->connect_id; + data->x11ic = x11ic; + data->event = call_data->event; ibus_input_context_process_key_event_async ( x11ic->context, @@ -683,7 +696,7 @@ xim_forward_event (XIMS xims, IMForwardEventStruct *call_data) -1, NULL, _process_key_event_done, - pfe); + data); retval = 1; } } @@ -962,11 +975,10 @@ _xim_forward_key_event (X11IC *x11ic, guint keycode, guint state) { - g_return_if_fail (x11ic != NULL); - - IMForwardEventStruct fe = {0}; XEvent xkp = {0}; + g_return_if_fail (x11ic != NULL); + xkp.xkey.type = (state & IBUS_RELEASE_MASK) ? KeyRelease : KeyPress; xkp.xkey.serial = 0L; xkp.xkey.send_event = False; @@ -975,20 +987,14 @@ _xim_forward_key_event (X11IC *x11ic, xkp.xkey.window = x11ic->focus_window ? x11ic->focus_window : x11ic->client_window; xkp.xkey.subwindow = None; - xkp.xkey.root = DefaultRootWindow (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ())); + xkp.xkey.root = DefaultRootWindow ( + GDK_DISPLAY_XDISPLAY (gdk_display_get_default ())); xkp.xkey.time = 0; xkp.xkey.state = state; xkp.xkey.keycode = (keycode == 0) ? 0 : keycode + 8; - 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 = xkp; - - IMForwardEvent (_xims, (XPointer) & fe); + _xim_forward_key_event_done (x11ic, &xkp, FALSE); } static void -- 2.41.0 From 8f706d160631f1ffdbfa16543a38b9d5f91c16ad Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Thu, 23 Mar 2023 13:07:38 +0900 Subject: [PATCH] util/IMdkit: Disable while loop before call ForwardEventMessageProc() Seems ProcessQueue() had a wrong XFree() with async process-key-event. Fixes: https://github.com/ibus/ibus/commit/506ac99 BUG=https://github.com/ibus/ibus/issues/2484 --- util/IMdkit/i18nPtHdr.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/util/IMdkit/i18nPtHdr.c b/util/IMdkit/i18nPtHdr.c index 8dc52714..ec20e322 100644 --- a/util/IMdkit/i18nPtHdr.c +++ b/util/IMdkit/i18nPtHdr.c @@ -1747,11 +1747,13 @@ static void ProcessQueue (XIMS ims, CARD16 connect_id) XimProtoHdr *hdr = (XimProtoHdr *) client->pending->p; unsigned char *p1 = (unsigned char *) (hdr + 1); IMProtocol call_data; + XIMPending *old = client->pending; call_data.major_code = hdr->major_opcode; call_data.any.minor_code = hdr->minor_opcode; call_data.any.connect_id = connect_id; + client->pending = old->next; switch (hdr->major_opcode) { case XIM_FORWARD_EVENT: @@ -1760,12 +1762,7 @@ static void ProcessQueue (XIMS ims, CARD16 connect_id) } /*endswitch*/ XFree (hdr); - { - XIMPending *old = client->pending; - - client->pending = old->next; - XFree (old); - } + XFree (old); } /*endwhile*/ return; -- 2.41.0 From 38f09c657fd5713e39f698aae43a09a07574f1a6 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Wed, 12 Jul 2023 07:50:22 +0900 Subject: [PATCH] src: Fix sync ibus_input_context_process_key_event() The synchronous "ProcessKeyEvent" D-Bus method cannot receive "CommitText" and "ForwardKeyEvent" D-Bus signals during calling the method. To resolve the issue, now ibus_input_context_set_post_process_key_event() and ibus_input_context_post_process_key_event() are added newly. ibus_input_context_post_process_key_event() retries "CommitText" and "ForwardKeyEvent" D-Bus signals during calling the "ProcessKeyEvent" D-Bus method and ibus-daemon does not handle those signals. "Since: 1.5.00" is added in header files to available APIs before 1.5.29 is released. Will think later how to convert the version comments together when the new version 1.5.29 is committed. BUG=https://github.com/ibus/ibus/issues/2486 --- bus/inputcontext.c | 252 ++++++++++++++++++++++++++++++++---- client/gtk2/ibusimcontext.c | 225 ++++++++++++++++++++++---------- src/ibusinputcontext.c | 162 +++++++++++++++++++++-- src/ibusinputcontext.h | 36 +++++- 4 files changed, 567 insertions(+), 108 deletions(-) diff --git a/bus/inputcontext.c b/bus/inputcontext.c index 8aded5d8..4d1fb041 100644 --- a/bus/inputcontext.c +++ b/bus/inputcontext.c @@ -31,6 +31,8 @@ #include "marshalers.h" #include "types.h" +#define MAX_SYNC_DATA 30 + struct _SetEngineByDescData { /* context related to the data */ BusInputContext *context; @@ -46,6 +48,11 @@ struct _SetEngineByDescData { }; typedef struct _SetEngineByDescData SetEngineByDescData; +typedef struct _SyncForwardingData { + gchar key; + IBusText *text; +} SyncForwardingData; + struct _BusInputContext { IBusService parent; @@ -99,6 +106,9 @@ struct _BusInputContext { BusPanelProxy *emoji_extension; gboolean is_extension_lookup_table; + GQueue *queue_during_process_key_event; + gboolean use_post_process_key_event; + gboolean processing_key_event; }; struct _BusInputContextClass { @@ -156,6 +166,15 @@ static void bus_input_context_service_method_call const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation); +static GVariant * + bus_input_context_service_get_property + (IBusService *service, + GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error); static gboolean bus_input_context_service_set_property (IBusService *service, GDBusConnection *connection, @@ -214,8 +233,21 @@ static const gchar introspection_xml[] = "" " " /* properties */ + " \n" + " \n" + " \n" + " \n" " " " \n" + " \n" + " \n" + " \n" + " \n" /* methods */ " " " " @@ -348,6 +380,8 @@ bus_input_context_class_init (BusInputContextClass *class) /* override the parent class's implementation. */ IBUS_SERVICE_CLASS (class)->service_method_call = bus_input_context_service_method_call; + IBUS_SERVICE_CLASS (class)->service_get_property = + bus_input_context_service_get_property; IBUS_SERVICE_CLASS (class)->service_set_property = bus_input_context_service_set_property; /* register the xml so that bus_ibus_impl_service_method_call will be called on a method call defined in the xml (e.g. 'FocusIn'.) */ @@ -782,6 +816,11 @@ bus_input_context_property_changed (BusInputContext *context, } +typedef struct _PanelProcessKeyEventData { + GDBusMethodInvocation *invocation; + BusInputContext *context; +} PanelProcessKeyEventData; + /** * _panel_process_key_event_cb: * @@ -789,14 +828,21 @@ bus_input_context_property_changed (BusInputContext *context, * bus_panel_proxy_process_key_event() is finished. */ static void -_panel_process_key_event_cb (GObject *source, - GAsyncResult *res, - GDBusMethodInvocation *invocation) +_panel_process_key_event_cb (GObject *source, + GAsyncResult *res, + PanelProcessKeyEventData *data) { GError *error = NULL; GVariant *value = g_dbus_proxy_call_finish ((GDBusProxy *)source, res, &error); + GDBusMethodInvocation *invocation; + BusInputContext *context; + + g_assert (data); + invocation = data->invocation; + context = data->context; + g_slice_free (PanelProcessKeyEventData, data); if (value != NULL) { g_dbus_method_invocation_return_value (invocation, value); g_variant_unref (value); @@ -805,6 +851,7 @@ _panel_process_key_event_cb (GObject *source, g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); } + context->processing_key_event = FALSE; } typedef struct _ProcessKeyEventData ProcessKeyEventData; @@ -841,21 +888,27 @@ _ic_process_key_event_reply_cb (GObject *source, gboolean retval = FALSE; g_variant_get (value, "(b)", &retval); if (context->emoji_extension && !retval) { + PanelProcessKeyEventData *pdata = + g_slice_new (PanelProcessKeyEventData); + pdata->invocation = invocation; + pdata->context = context; bus_panel_proxy_process_key_event (context->emoji_extension, keyval, keycode, modifiers, (GAsyncReadyCallback) _panel_process_key_event_cb, - invocation); + pdata); } else { g_dbus_method_invocation_return_value (invocation, value); + context->processing_key_event = FALSE; } g_variant_unref (value); } else { g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); + context->processing_key_event = FALSE; } g_object_unref (context); @@ -877,6 +930,8 @@ _ic_process_key_event (BusInputContext *context, guint keycode = 0; guint modifiers = 0; + if (context->use_post_process_key_event) + context->processing_key_event = TRUE; g_variant_get (parameters, "(uuu)", &keyval, &keycode, &modifiers); if (G_UNLIKELY (!context->has_focus)) { /* workaround: set focus if context does not have focus */ @@ -1372,17 +1427,109 @@ bus_input_context_service_method_call (IBusService *service, g_return_if_reached (); } -static void +/** + * _ic_get_post_process_key_event: + * + * Implement the "PostProcessKeyEvent" get property of the + * org.freedesktop.IBus.InputContext interface because currently the Gio + * D-Bus method calls don't support multiple nested tuples likes + * G_VARIANT_TYPE ("((ba(yv)))")) in "ProcessKeyEvent" D-Bus method + * So these post events are separated from the return value "b" of + * the "ProcessKeyEvent" D-Bus method call. + */ +static GVariant * +_ic_get_post_process_key_event (BusInputContext *context, + GDBusConnection *connection, + GError **error) +{ + const char *error_message = NULL; + GVariantBuilder array; + SyncForwardingData *data; + + do { + if (!BUS_IS_INPUT_CONTEXT (context)) { + error_message = "BusInputContext is freed"; + break; + } + if (context->processing_key_event) { + error_message = "Another ProcessKeyEvent is called."; + break; + } + g_variant_builder_init (&array, G_VARIANT_TYPE ("a(yv)")); + while ((data = + g_queue_pop_head (context->queue_during_process_key_event))) { + GVariant *variant = ibus_serializable_serialize_object ( + IBUS_SERIALIZABLE (data->text)); + g_variant_builder_add (&array, "(yv)", data->key, variant); + g_object_unref (data->text); + g_slice_free (SyncForwardingData, data); + } + } while (FALSE); + if (error_message) { + g_set_error (error, + G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "%s", error_message); + return NULL; + } + return g_variant_builder_end (&array); +} + +static GVariant * +bus_input_context_service_get_property (IBusService *service, + GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error) +{ + int i; + static const struct { + const char *property_name; + GVariant * (* property_callback) (BusInputContext *, + GDBusConnection *, + GError **); + } properties [] = { + { "PostProcessKeyEvent", _ic_get_post_process_key_event }, + }; + + if (error) + *error = NULL; + if (g_strcmp0 (interface_name, IBUS_INTERFACE_INPUT_CONTEXT) != 0) { + return IBUS_SERVICE_CLASS (bus_input_context_parent_class)-> + service_get_property ( + service, connection, sender, object_path, + interface_name, property_name, + error); + } + for (i = 0; i < G_N_ELEMENTS (properties); i++) { + if (g_strcmp0 (properties[i].property_name, property_name) == 0) { + return properties[i].property_callback ((BusInputContext *)service, + connection, + error); + } + } + + g_set_error (error, + G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "service_get_property received an unknown property: %s", + property_name ? property_name : "(null)"); + g_return_val_if_reached (NULL); +} + +static gboolean _ic_set_content_type (BusInputContext *context, - GVariant *value) + GVariant *value, + GError **error) { guint purpose = 0; guint hints = 0; + gboolean retval = TRUE; g_variant_get (value, "(uu)", &purpose, &hints); if (purpose != context->purpose || hints != context->hints) { - GError *error; - gboolean retval; context->purpose = purpose; context->hints = hints; @@ -1400,24 +1547,30 @@ _ic_set_content_type (BusInputContext *context, context->hints); } - error = NULL; retval = bus_input_context_property_changed (context, "ContentType", value, - &error); - if (!retval) { - g_warning ("Failed to emit PropertiesChanged signal: %s", - error->message); - g_error_free (error); - } + error); } + return retval; } -static void +static gboolean _ic_set_client_commit_preedit (BusInputContext *context, - GVariant *value) + GVariant *value, + GError **error) { g_variant_get (value, "(b)", &context->client_commit_preedit); + return TRUE; +} + +static gboolean +_ic_set_use_post_process_key_event (BusInputContext *context, + GVariant *value, + GError **error) +{ + g_variant_get (value, "(b)", &context->use_post_process_key_event); + return TRUE; } static gboolean @@ -1384,6 +1548,18 @@ bus_input_context_service_set_property ( GVariant *value, GError **error) { + int i; + static const struct { + const char *property_name; + gboolean (* property_callback) (BusInputContext *, + GVariant *, + GError **); + } properties [] = { + { "ContentType", _ic_set_content_type }, + { "ClientCommitPreedit", _ic_set_client_commit_preedit }, + { "EffectivePostProcessKeyEvent", _ic_set_use_post_process_key_event }, + }; + if (g_strcmp0 (interface_name, IBUS_INTERFACE_INPUT_CONTEXT) != 0) { return IBUS_SERVICE_CLASS (bus_input_context_parent_class)-> service_set_property (service, @@ -1401,13 +1566,12 @@ bus_input_context_service_set_property ( g_return_val_if_fail (BUS_IS_INPUT_CONTEXT (service), FALSE); - if (g_strcmp0 (property_name, "ContentType") == 0) { - _ic_set_content_type (BUS_INPUT_CONTEXT (service), value); - return TRUE; - } - if (g_strcmp0 (property_name, "ClientCommitPreedit") == 0) { - _ic_set_client_commit_preedit (BUS_INPUT_CONTEXT (service), value); - return TRUE; + for (i = 0; i < G_N_ELEMENTS (properties); i++) { + if (g_strcmp0 (properties[i].property_name, property_name) == 0) { + return properties[i].property_callback ((BusInputContext *) service, + value, + error); + } } g_return_val_if_reached (FALSE); @@ -2094,7 +2258,23 @@ _engine_forward_key_event_cb (BusEngineProxy *engine, g_assert (BUS_IS_INPUT_CONTEXT (context)); g_assert (context->engine == engine); + g_assert (context->queue_during_process_key_event); + if (context->processing_key_event && g_queue_get_length ( + context->queue_during_process_key_event) <= MAX_SYNC_DATA) { + SyncForwardingData *data; + IBusText *text = ibus_text_new_from_printf ("%u,%u,%u", + keyval, keycode, state); + if (g_queue_get_length (context->queue_during_process_key_event) + == MAX_SYNC_DATA) { + g_warning ("Exceed max number of post process_key_event data"); + } + data = g_slice_new (SyncForwardingData); + data->key = 'f'; + data->text = text; + g_queue_push_tail (context->queue_during_process_key_event, data); + return; + } bus_input_context_emit_signal (context, "ForwardKeyEvent", g_variant_new ("(uuu)", keyval, keycode, state), @@ -2455,6 +2634,7 @@ bus_input_context_new (BusConnection *connection, /* it is a fake input context, just need process hotkey */ context->fake = (strncmp (client, "fake", 4) == 0); + context->queue_during_process_key_event = g_queue_new (); if (connection) { g_object_ref_sink (connection); @@ -2938,11 +3118,17 @@ bus_input_context_set_content_type (BusInputContext *context, guint hints) { GVariant *value; + GError *error = NULL; g_assert (BUS_IS_INPUT_CONTEXT (context)); value = g_variant_ref_sink (g_variant_new ("(uu)", purpose, hints)); - _ic_set_content_type (context, value); + _ic_set_content_type (context, value, &error); + if (error) { + g_warning ("Failed to emit PropertiesChanged signal: %s", + error->message); + g_error_free (error); + } g_variant_unref (value); } @@ -2952,12 +3138,24 @@ bus_input_context_commit_text_use_extension (BusInputContext *context, gboolean use_extension) { g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (context->queue_during_process_key_event); if (text == text_empty || text == NULL) return; if (use_extension && context->emoji_extension) { bus_panel_proxy_commit_text_received (context->emoji_extension, text); + } else if (context->processing_key_event && g_queue_get_length ( + context->queue_during_process_key_event) <= MAX_SYNC_DATA) { + SyncForwardingData *data; + if (g_queue_get_length (context->queue_during_process_key_event) + == MAX_SYNC_DATA) { + g_warning ("Exceed max number of sync process_key_event data"); + } + data = g_slice_new (SyncForwardingData); + data->key = 'c'; + data->text = g_object_ref (text); + g_queue_push_tail (context->queue_during_process_key_event, data); } else { GVariant *variant = ibus_serializable_serialize ( (IBusSerializable *)text); diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c index ea8270bb..7ccc129d 100644 --- a/client/gtk2/ibusimcontext.c +++ b/client/gtk2/ibusimcontext.c @@ -111,7 +111,7 @@ static guint _signal_delete_surrounding_id = 0; static guint _signal_retrieve_surrounding_id = 0; #if GTK_CHECK_VERSION (3, 98, 4) -static char _use_sync_mode = 2; +static char _use_sync_mode = 1; #else static const gchar *_no_snooper_apps = NO_SNOOPER_APPS; static gboolean _use_key_snooper = ENABLE_SNOOPER; @@ -386,6 +386,7 @@ typedef struct { gboolean retval; } ProcessKeyEventReplyData; + static void _process_key_event_done (GObject *object, GAsyncResult *res, @@ -435,6 +436,7 @@ _process_key_event_done (GObject *object, #endif } + static void _process_key_event_reply_done (GObject *object, GAsyncResult *res, @@ -457,6 +459,7 @@ _process_key_event_reply_done (GObject *object, g_source_remove (data->count_cb_id); } + static gboolean _process_key_event_count_cb (gpointer user_data) { @@ -472,6 +475,101 @@ _process_key_event_count_cb (gpointer user_data) return G_SOURCE_CONTINUE; } + +static gboolean +_process_key_event_sync (IBusInputContext *context, + guint keyval, + guint keycode, + guint state) +{ + gboolean retval; + + g_assert (IBUS_IS_INPUT_CONTEXT (context)); + retval = ibus_input_context_process_key_event (context, + keyval, + keycode - 8, + state); + ibus_input_context_post_process_key_event (context); + return retval; +} + + +static gboolean +_process_key_event_async (IBusInputContext *context, + guint keyval, + guint keycode, + guint state, + GdkEvent *event, + IBusIMContext *ibusimcontext) +{ + ProcessKeyEventData *data = g_slice_new0 (ProcessKeyEventData); + + g_assert (event); + if (!data) { + g_warning ("Cannot allocate async data"); + return _process_key_event_sync (context, keyval, keycode, state); + } +#if GTK_CHECK_VERSION (3, 98, 4) + data->event = gdk_event_ref (event); +#else + data->event = gdk_event_copy (event); +#endif + data->ibusimcontext = ibusimcontext; + ibus_input_context_process_key_event_async (context, + keyval, + keycode - 8, + state, + -1, + NULL, + _process_key_event_done, + data); + + return TRUE; +} + + +static gboolean +_process_key_event_hybrid_async (IBusInputContext *context, + guint keyval, + guint keycode, + guint state) +{ + GSource *source = g_timeout_source_new (1); + ProcessKeyEventReplyData *data = NULL; + gboolean retval = FALSE; + + if (source) + data = g_slice_new0 (ProcessKeyEventReplyData); + if (!data) { + g_warning ("Cannot wait for the reply of the process key event."); + retval = _process_key_event_sync (context, keyval, keycode, state); + if (source) + g_source_destroy (source); + return retval; + } + 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, + 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); + /* #2498 Checking source->ref_count might cause Nautilus hang up + */ + retval = data->retval; + g_slice_free (ProcessKeyEventReplyData, data); + return retval; +} + + static gboolean _process_key_event (IBusInputContext *context, #if GTK_CHECK_VERSION (3, 98, 4) @@ -505,70 +603,20 @@ _process_key_event (IBusInputContext *context, switch (_use_sync_mode) { case 1: { - retval = ibus_input_context_process_key_event (context, - keyval, - keycode - 8, - state); + retval = _process_key_event_sync (context, keyval, keycode, state); break; } case 2: { - GSource *source = g_timeout_source_new (1); - ProcessKeyEventReplyData *data = NULL; - - 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 (context, - keyval, - keycode - 8, - state); - if (source) - g_source_destroy (source); - break; - } - 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, - 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); + retval = _process_key_event_hybrid_async (context, + keyval, keycode, state); break; } default: { - ProcessKeyEventData *data = g_slice_new0 (ProcessKeyEventData); -#if GTK_CHECK_VERSION (3, 98, 4) - data->event = gdk_event_ref (event); -#else - data->event = gdk_event_copy ((GdkEvent *)event); -#endif - data->ibusimcontext = ibusimcontext; - ibus_input_context_process_key_event_async (context, - keyval, - keycode - 8, - state, - -1, - NULL, - _process_key_event_done, - data); - - retval = TRUE; + retval = _process_key_event_async (context, + keyval, keycode, state, + (GdkEvent *)event, + ibusimcontext); + break; } } @@ -877,7 +925,55 @@ ibus_im_context_class_init (IBusIMContextClass *class) g_assert (_signal_retrieve_surrounding_id != 0); #if GTK_CHECK_VERSION (3, 98, 4) - _use_sync_mode = _get_char_env ("IBUS_ENABLE_SYNC_MODE", 2); + /* IBus GtkIMModule, QtIMModlue, ibus-x11, ibus-wayland are called as + * IBus clients. + * Each GTK application, each QT application, Xorg server, Wayland + * comppsitor are called as IBus event owners here. + * + * The IBus client processes the key events between the IBus event owner + * and the IBus daemon and the procedure step is to: + * + * receive the key event from the IBus event owner and forward the + * event to the IBus daemon with the "ProcessKeyEvent" D-Bus method at + * first, + * + * receive the return value from the IBus daemon with the "ProessKeyEvent" + * D-Bus method and forward the value to the IBus event owner secondly and + * the return value includes if the key event is processed normally or not. + * + * The procedure behavior can be changed by the "IBUS_ENABLE_SYNC_MODE" + * environment variable with the synchronous procedure or asynchronous + * one and value is: + * + * 1: Synchronous process key event: + * Wait for the return of the IBus "ProcessKeyEvent" D-Bus method + * synchronously and forward the return value to the IBus event owner + * synchronously. + * 0: Asynchronous process key event: + * Return to the IBus event owner as the key event is processed normally + * at first as soon as the IBus client receives the event from the + * IBus event owner and also forward the event to the IBus daemon with + * the "ProcessKeyEvent" D-Bus method and wait for the return value of + * the D-Bus method *asynchronously*. + * If the return value indicates the key event is disposed by IBus, + * the IBus client does not perform anything. Otherwise the IBus client + * forwards the key event with the gdk_event_put() in GTK3, + * gtk_im_context_filter_key() in GTK4, IMForwardEvent() in XIM API. + * 2: Hybrid asynchronous process key event: + * Wait for the return of the IBus "ProcessKeyEvent" D-Bus method + * *asynchronously* with a GSource loop and forward the return value + * to the IBus event owner synchronously. So IBus clients perform + * virtually synchronously to cover problems of IBus synchronous APIs. + * + * The purpose of the asynchronous process is that each IBus input + * method can process the key events without D-Bus timeout and also + * the IBus synchronous process has a problem that the IBus + * "ProcessKeyEvent" D-Bus method cannot send the commit-text and + * forwar-key-event D-Bus signals until the D-Bus method is finished. + * + * Relative issues: #1713, #2486 + */ + _use_sync_mode = _get_char_env ("IBUS_ENABLE_SYNC_MODE", 1); #else _use_key_snooper = !_get_boolean_env ("IBUS_DISABLE_SNOOPER", !(ENABLE_SNOOPER)); @@ -1004,8 +1100,6 @@ 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_V2; ibusimcontext->events_queue = g_queue_new (); @@ -2235,6 +2329,8 @@ _create_input_context_done (IBusBus *bus, } else { ibus_input_context_set_client_commit_preedit (context, TRUE); + if (_use_sync_mode == 1) + ibus_input_context_set_post_process_key_event (context, TRUE); ibusimcontext->ibuscontext = context; g_signal_connect (ibusimcontext->ibuscontext, @@ -2489,9 +2585,8 @@ _create_fake_input_context_done (IBusBus *bus, G_CALLBACK (_ibus_fake_context_destroy_cb), 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_V2; + guint32 caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS + | IBUS_CAP_SURROUNDING_TEXT; ibus_input_context_set_capabilities (_fake_context, caps); /* focus in/out the fake context */ diff --git a/src/ibusinputcontext.c b/src/ibusinputcontext.c index 28ae04ad..def23b25 100644 --- a/src/ibusinputcontext.c +++ b/src/ibusinputcontext.c @@ -2,7 +2,7 @@ /* vim:set et sts=4: */ /* ibus - The Input Bus * Copyright (C) 2008-2013 Peng Huang - * Copyright (C) 2018-2019 Takao Fujiwara + * Copyright (C) 2018-2023 Takao Fujiwara * Copyright (C) 2008-2019 Red Hat, Inc. * * This library is free software; you can redistribute it and/or @@ -1190,14 +1190,14 @@ ibus_input_context_set_content_type (IBusInputContext *context, g_assert (IBUS_IS_INPUT_CONTEXT (context)); cached_content_type = - g_dbus_proxy_get_cached_property ((GDBusProxy *) context, + g_dbus_proxy_get_cached_property ((GDBusProxy *)context, "ContentType"); content_type = g_variant_new ("(uu)", purpose, hints); g_variant_ref_sink (content_type); - if (cached_content_type == NULL || + if (!cached_content_type || !g_variant_equal (content_type, cached_content_type)) { - g_dbus_proxy_call ((GDBusProxy *) context, + g_dbus_proxy_call ((GDBusProxy *)context, "org.freedesktop.DBus.Properties.Set", g_variant_new ("(ssv)", IBUS_INTERFACE_INPUT_CONTEXT, @@ -1209,9 +1209,13 @@ ibus_input_context_set_content_type (IBusInputContext *context, NULL, /* callback */ NULL /* user_data */ ); + /* Need to update the cache by manual since there is a timing issue. */ + g_dbus_proxy_set_cached_property ((GDBusProxy *)context, + "ContentType", + content_type); } - if (cached_content_type != NULL) + if (cached_content_type) g_variant_unref (cached_content_type); g_variant_unref (content_type); } @@ -1324,19 +1328,20 @@ void ibus_input_context_set_client_commit_preedit (IBusInputContext *context, gboolean client_commit) { - GVariant *cached_content_type; + GVariant *cached_var_client_commit; GVariant *var_client_commit; g_assert (IBUS_IS_INPUT_CONTEXT (context)); - cached_content_type = - g_dbus_proxy_get_cached_property ((GDBusProxy *) context, + cached_var_client_commit = + g_dbus_proxy_get_cached_property ((GDBusProxy *)context, "ClientCommitPreedit"); var_client_commit = g_variant_new ("(b)", client_commit); g_variant_ref_sink (var_client_commit); - if (cached_content_type == NULL) { - g_dbus_proxy_call ((GDBusProxy *) context, + if (!cached_var_client_commit || + !g_variant_equal (var_client_commit, cached_var_client_commit)) { + g_dbus_proxy_call ((GDBusProxy *)context, "org.freedesktop.DBus.Properties.Set", g_variant_new ("(ssv)", IBUS_INTERFACE_INPUT_CONTEXT, @@ -1348,13 +1353,146 @@ ibus_input_context_set_client_commit_preedit (IBusInputContext *context, NULL, /* callback */ NULL /* user_data */ ); + /* Need to update the cache by manual since there is a timing issue. */ + g_dbus_proxy_set_cached_property ((GDBusProxy *)context, + "ClientCommitPreedit", + var_client_commit); } - if (cached_content_type != NULL) - g_variant_unref (cached_content_type); + if (cached_var_client_commit) + g_variant_unref (cached_var_client_commit); g_variant_unref (var_client_commit); } +void +ibus_input_context_set_post_process_key_event (IBusInputContext *context, + gboolean enable) +{ + GVariant *cached_var_post; + GVariant *var_post; + + g_assert (IBUS_IS_INPUT_CONTEXT (context)); + + cached_var_post = + g_dbus_proxy_get_cached_property ((GDBusProxy *)context, + "EffectivePostProcessKeyEvent"); + var_post = g_variant_new ("(b)", enable); + g_variant_ref_sink (var_post); + if (!cached_var_post || + !g_variant_equal (var_post, cached_var_post)) { + g_dbus_proxy_call ((GDBusProxy *)context, + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", + IBUS_INTERFACE_INPUT_CONTEXT, + "EffectivePostProcessKeyEvent", + var_post), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* cancellable */ + NULL, /* callback */ + NULL /* user_data */ + ); + /* Need to update the cache by manual since there is a timing issue. */ + g_dbus_proxy_set_cached_property ((GDBusProxy *)context, + "EffectivePostProcessKeyEvent", + var_post); + } + + if (cached_var_post) + g_variant_unref (cached_var_post); + g_variant_unref (var_post); +} + +void +ibus_input_context_post_process_key_event (IBusInputContext *context) +{ + GVariant *cached_var_post; + gboolean enable = FALSE; + GVariant *result; + GError *error = NULL; + GVariant *variant = NULL; + GVariantIter iter; + gsize size; + char type = 0; + GVariant *vtext = NULL; + + g_assert (IBUS_IS_INPUT_CONTEXT (context)); + + cached_var_post = + g_dbus_proxy_get_cached_property ((GDBusProxy *)context, + "EffectivePostProcessKeyEvent"); + if (cached_var_post) + g_variant_get (cached_var_post, "(b)", &enable); + if (!enable) { + g_warning ("%s: ibus_input_context_set_post_process_key_event() " + "needs to be called before.", + G_STRFUNC); + if (cached_var_post) + g_variant_unref (cached_var_post); + return; + } + g_variant_unref (cached_var_post); + result = g_dbus_proxy_call_sync ( + (GDBusProxy *)context, + "org.freedesktop.DBus.Properties.Get", + g_variant_new ("(ss)", + IBUS_INTERFACE_INPUT_CONTEXT, + "PostProcessKeyEvent"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (error) { + g_warning ("%s: %s", G_STRFUNC, error->message); + g_error_free (error); + return; + } + + g_variant_get (result, "(v)", &variant); + g_assert (variant); + g_variant_iter_init (&iter, variant); + size = g_variant_iter_n_children (&iter); + while (size >0 && g_variant_iter_loop (&iter, "(yv)", &type, &vtext)) { + IBusText *text = + (IBusText *)ibus_serializable_deserialize_object (vtext); + if (!IBUS_IS_TEXT (text)) { + g_warning ("%s: %s", G_STRFUNC, "text is not IBusText"); + break; + } + switch (type) { + case 'c': + g_signal_emit (context, context_signals[COMMIT_TEXT], 0, text); + break; + case 'f': { + gchar **array = NULL; + guint keyval, keycode, state; + array = g_strsplit (text->text, ",", -1); + keyval = g_ascii_strtoull (array[0], NULL, 10); + keycode = g_ascii_strtoull (array[1], NULL, 10); + state = g_ascii_strtoull (array[2], NULL, 10); + g_strfreev (array); + g_signal_emit (context, + context_signals[FORWARD_KEY_EVENT], + 0, + keyval, + keycode, + state | IBUS_FORWARD_MASK); + break; + } + default: + g_warning ("%s: Type '%c' is not supported.", G_STRFUNC, type); + } + if (g_object_is_floating (text)) { + g_object_ref_sink (text); + g_object_unref (text); + } + g_clear_pointer (&vtext, g_variant_unref); + } + + g_variant_unref (variant); + g_variant_unref (result); +} + #define DEFINE_FUNC(name, Name) \ void \ ibus_input_context_##name (IBusInputContext *context) \ diff --git a/src/ibusinputcontext.h b/src/ibusinputcontext.h index 09992148..ca604670 100644 --- a/src/ibusinputcontext.h +++ b/src/ibusinputcontext.h @@ -2,7 +2,7 @@ /* vim:set et sts=4: */ /* ibus - The Input Bus * Copyright (C) 2008-2013 Peng Huang - * Copyright (C) 2018 Takao Fujiwara + * Copyright (C) 2018-2023 Takao Fujiwara * Copyright (C) 2008-2018 Red Hat, Inc. * * This library is free software; you can redistribute it and/or @@ -298,7 +298,6 @@ gboolean ibus_input_context_process_key_event guint32 keycode, guint32 state); - /** * ibus_input_context_set_cursor_location: * @context: An IBusInputContext. @@ -519,9 +518,38 @@ void ibus_input_context_set_content_type * * See also ibus_engine_update_preedit_text_with_mode(). */ -void ibus_input_context_set_client_commit_preedit ( - IBusInputContext *context, +void ibus_input_context_set_client_commit_preedit + (IBusInputContext *context, gboolean client_commit); +/** + * ibus_input_context_set_post_process_key_event: + * @context: An #IBusInputContext. + * @enable: Can use ibus_input_context_post_process_key_event() to retrieve + * commit-text and forwar-key-event signals during + * calling ibus_input_context_process_key_event() if it's %TRUE. + * + * Since: 1.5.00 + * Stability: Unstable + */ +void ibus_input_context_set_post_process_key_event + (IBusInputContext *context, + gboolean enable); +/** + * ibus_input_context_post_process_key_event: + * @context: An #IBusInputContext. + * + * Call this API after ibus_input_context_process_key_event() returns + * to retrieve commit-text and forwar-key-event signals during + * calling ibus_input_context_process_key_event(). + * + * See also ibus_input_context_set_post_process_key_event(). + * + * Since: 1.5.00 + * Stability: Unstable + */ +void ibus_input_context_post_process_key_event + (IBusInputContext *context); + G_END_DECLS #endif -- 2.41.0 From 86d9bb9a1cbd4ffbd6bc2a4de85cb76a43bc2ced Mon Sep 17 00:00:00 2001 From: Peng Wu Date: Mon, 24 Jul 2023 14:04:12 +0800 Subject: [PATCH] client/gtk2: Update set_cursor_location function to use Gdk functions For ibus-gtk4, use the Gdk functions to get the inner cursor location. The inner cursor location is translated by XTranslateCoordinates for X Window. For ibus-gtk3, use gdk_window_get_root_coords function to translate the inner cursor location for X Window. In Wayland, the set_cursor_location_relative function is called. In X Window, the set_cursor_location function is called. Fixes: https://github.com/ibus/ibus/commit/d0a47c3 Fixes: https://github.com/ibus/ibus/commit/a823161 BUG=https://github.com/ibus/ibus/pull/2549 BUG=https://gitlab.gnome.org/GNOME/gtk/-/issues/3024#note_987835 --- client/gtk2/ibusimcontext.c | 141 +++++++++++++++--------------------- 1 file changed, 58 insertions(+), 83 deletions(-) diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c index 7ccc129d..b5a44da0 100644 --- a/client/gtk2/ibusimcontext.c +++ b/client/gtk2/ibusimcontext.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include "ibusimcontext.h" @@ -1612,8 +1613,13 @@ static gboolean _set_cursor_location_internal (IBusIMContext *ibusimcontext) { GdkRectangle area; + GdkDisplay *display = NULL; #if GTK_CHECK_VERSION (3, 98, 4) GtkWidget *root; + GtkNative *native; + graphene_point_t p; + int tx = 0, ty = 0; + double nx = 0., ny = 0.; #endif if(ibusimcontext->client_window == NULL || @@ -1623,103 +1629,72 @@ _set_cursor_location_internal (IBusIMContext *ibusimcontext) area = ibusimcontext->cursor_area; -#ifdef GDK_WINDOWING_WAYLAND #if GTK_CHECK_VERSION (3, 98, 4) root = GTK_WIDGET (gtk_widget_get_root (ibusimcontext->client_window)); - /* FIXME: GTK_STYLE_CLASS_TITLEBAR is available in GTK3 but not GTK4. - * gtk_css_boxes_get_content_rect() is available in GTK4 but it's an - * internal API and calculate the window edge 32 in GTK3. - */ - area.y += 32; - area.width = 50; /* FIXME: Why 50 meets the cursor position? */ - area.height = gtk_widget_get_height (root); - area.height += 32; - if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ())) { - ibus_input_context_set_cursor_location_relative ( - ibusimcontext->ibuscontext, - area.x, - area.y, - area.width, - area.height); - return FALSE; - } -#else - if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ())) { - gdouble px, py; - GdkWindow *parent; - GdkWindow *window = ibusimcontext->client_window; - - while ((parent = gdk_window_get_effective_parent (window)) != NULL) { - gdk_window_coords_to_parent (window, area.x, area.y, &px, &py); - area.x = px; - area.y = py; - window = parent; - } - - _set_rect_scale_factor_with_window (&area, - ibusimcontext->client_window); - ibus_input_context_set_cursor_location_relative ( - ibusimcontext->ibuscontext, - area.x, - area.y, - area.width, - area.height); - return FALSE; + /* Translates the given point in client_window coordinates to coordinates + relative to root coordinate system. */ + if (!gtk_widget_compute_point (ibusimcontext->client_window, + root, + &GRAPHENE_POINT_INIT (area.x, area.y), + &p)) { + graphene_point_init (&p, area.x, area.y); } -#endif -#endif -#if GTK_CHECK_VERSION (3, 98, 4) -#elif GTK_CHECK_VERSION (2, 91, 0) - if (area.x == -1 && area.y == -1 && area.width == 0 && area.height == 0) { - area.x = 0; - area.y += gdk_window_get_height (ibusimcontext->client_window); - } -#else - if (area.x == -1 && area.y == -1 && area.width == 0 && area.height == 0) { - gint w, h; - gdk_drawable_get_size (ibusimcontext->client_window, &w, &h); - area.y += h; - area.x = 0; - } -#endif + native = gtk_widget_get_native (ibusimcontext->client_window); + /* Translates from the surface coordinates into the widget coordinates. */ + gtk_native_get_surface_transform (native, &nx, &ny); -#if GTK_CHECK_VERSION (3, 98, 4) -#if defined(GDK_WINDOWING_X11) - GdkDisplay *display = gtk_widget_get_display (ibusimcontext->client_window); + display = gtk_widget_get_display (ibusimcontext->client_window); if (GDK_IS_X11_DISPLAY (display)) { - Display *xdisplay = gdk_x11_display_get_xdisplay (display); - Window root_window = gdk_x11_display_get_xrootwindow (display); - GtkNative *native = gtk_widget_get_native ( - ibusimcontext->client_window); - GdkSurface *surface = gtk_native_get_surface (native); - /* The window is the toplevel window but not the inner text widget. - * Unfortunatelly GTK4 cannot get the coordinate of the text widget. - */ - Window window = gdk_x11_surface_get_xid (surface); + GdkSurface *surface = gtk_native_get_surface + (gtk_widget_get_native (ibusimcontext->client_window)); Window child; - int x, y; - XTranslateCoordinates (xdisplay, window, root_window, - 0, 0, &x, &y, &child); - XWindowAttributes xwa; - XGetWindowAttributes (xdisplay, window, &xwa); - area.x = x - xwa.x + area.x; - area.y = y - xwa.y + area.y; - area.width = 50; /* FIXME: Why 50 meets the cursor position? */ - area.height = xwa.height; + int scale_factor = gtk_widget_get_scale_factor + (ibusimcontext->client_window); + + XTranslateCoordinates (GDK_DISPLAY_XDISPLAY (display), + GDK_SURFACE_XID (surface), + gdk_x11_display_get_xrootwindow (display), + 0, 0, &tx, &ty, + &child); + + tx = tx / scale_factor; + ty = ty / scale_factor; } -#endif + + area.x = p.x + nx + tx; + area.y = p.y + ny + ty; #else gdk_window_get_root_coords (ibusimcontext->client_window, area.x, area.y, &area.x, &area.y); #endif + _set_rect_scale_factor_with_window (&area, ibusimcontext->client_window); - ibus_input_context_set_cursor_location (ibusimcontext->ibuscontext, - area.x, - area.y, - area.width, - area.height); + +#ifdef GDK_WINDOWING_WAYLAND +#if !GTK_CHECK_VERSION (3, 98, 4) + display = gdk_window_get_display (ibusimcontext->client_window); +#endif + + if (GDK_IS_WAYLAND_DISPLAY (display)) { + ibus_input_context_set_cursor_location_relative (ibusimcontext->ibuscontext, + area.x, + area.y, + area.width, + area.height); + + } else { +#endif + ibus_input_context_set_cursor_location (ibusimcontext->ibuscontext, + area.x, + area.y, + area.width, + area.height); +#ifdef GDK_WINDOWING_WAYLAND + } +#endif + return FALSE; } -- 2.41.0 From f176569cf774f87b23270257da68249dcda837c9 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Wed, 18 Oct 2023 16:57:28 +0900 Subject: [PATCH] src: Add DeleteSurroundingText to PostProcessKeyEvent DeleteSurroundingText also can be delayed sending to IBus clients. Now surrounding D-Bus methods are added to PostProcessKeyEvent. Fixes: https://github.com/ibus/ibus/commit/38f09c6 BUG=https://github.com/ibus/ibus/issues/2570 --- bus/inputcontext.c | 33 +++++++++++++++++++++++++++++++-- src/ibusinputcontext.c | 23 +++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/bus/inputcontext.c b/bus/inputcontext.c index aecc64f8..64430fe4 100644 --- a/bus/inputcontext.c +++ b/bus/inputcontext.c @@ -2382,7 +2382,7 @@ _engine_forward_key_event_cb (BusEngineProxy *engine, g_assert (context->queue_during_process_key_event); if (context->processing_key_event && g_queue_get_length ( - context->queue_during_process_key_event) <= MAX_SYNC_DATA) { + context->queue_during_process_key_event) <= MAX_SYNC_DATA) { SyncForwardingData *data; IBusText *text = ibus_text_new_from_printf ("%u,%u,%u", keyval, keycode, state); @@ -2397,6 +2397,21 @@ _engine_delete_surrounding_text_cb (BusEngineProxy *engine, g_assert (context->engine == engine); + if (context->processing_key_event && g_queue_get_length ( + context->queue_during_process_key_event) <= MAX_SYNC_DATA) { + SyncForwardingData *data; + IBusText *text = ibus_text_new_from_printf ("%d,%u", + offset_from_cursor, nchars); + if (g_queue_get_length (context->queue_during_process_key_event) + == MAX_SYNC_DATA) { + g_warning ("Exceed max number of sync process_key_event data"); + } + data = g_slice_new (SyncForwardingData); + data->key = 'd'; + data->text = g_object_ref (text); + g_queue_push_tail (context->queue_during_process_key_event, data); + return; + } bus_input_context_emit_signal (context, "DeleteSurroundingText", g_variant_new ("(iu)", offset_from_cursor, nchars), @@ -2442,6 +2457,20 @@ _engine_require_surrounding_text_cb (BusEngineProxy *engine, g_assert (context->engine == engine); + if (context->processing_key_event && g_queue_get_length ( + context->queue_during_process_key_event) <= MAX_SYNC_DATA) { + SyncForwardingData *data; + IBusText *text = ibus_text_new_from_static_string (""); + if (g_queue_get_length (context->queue_during_process_key_event) + == MAX_SYNC_DATA) { + g_warning ("Exceed max number of post process_key_event data"); + } + data = g_slice_new (SyncForwardingData); + data->key = 'r'; + data->text = text; + g_queue_push_tail (context->queue_during_process_key_event, data); + return; + } bus_input_context_emit_signal (context, "RequireSurroundingText", NULL, @@ -3158,7 +3187,7 @@ bus_input_context_commit_text_use_extension (BusInputContext *context, if (use_extension && context->emoji_extension) { bus_panel_proxy_commit_text_received (context->emoji_extension, text); } else if (context->processing_key_event && g_queue_get_length ( - context->queue_during_process_key_event) <= MAX_SYNC_DATA) { + context->queue_during_process_key_event) <= MAX_SYNC_DATA) { SyncForwardingData *data; if (g_queue_get_length (context->queue_during_process_key_event) == MAX_SYNC_DATA) { diff --git a/src/ibusinputcontext.c b/src/ibusinputcontext.c index def23b25..c6a030fe 100644 --- a/src/ibusinputcontext.c +++ b/src/ibusinputcontext.c @@ -1406,6 +1406,7 @@ ibus_input_context_set_post_process_key_event (IBusInputContext *context, void ibus_input_context_post_process_key_event (IBusInputContext *context) { + IBusInputContextPrivate *priv; GVariant *cached_var_post; gboolean enable = FALSE; GVariant *result; @@ -1418,6 +1419,7 @@ ibus_input_context_post_process_key_event (IBusInputContext *context) g_assert (IBUS_IS_INPUT_CONTEXT (context)); + priv = IBUS_INPUT_CONTEXT_GET_PRIVATE (IBUS_INPUT_CONTEXT (context)); cached_var_post = g_dbus_proxy_get_cached_property ((GDBusProxy *)context, "EffectivePostProcessKeyEvent"); @@ -1479,6 +1481,25 @@ ibus_input_context_post_process_key_event (IBusInputContext *context) state | IBUS_FORWARD_MASK); break; } + case 'r': { + priv->needs_surrounding_text = TRUE; + break; + } + case 'd': { + gchar **array = NULL; + gint offset_from_cursor; + guint nchars; + array = g_strsplit (text->text, ",", -1); + offset_from_cursor = g_ascii_strtoll (array[0], NULL, 10); + nchars = g_ascii_strtoull (array[1], NULL, 10); + g_strfreev (array); + g_signal_emit (context, + context_signals[DELETE_SURROUNDING_TEXT], + 0, + offset_from_cursor, + nchars); + break; + } default: g_warning ("%s: Type '%c' is not supported.", G_STRFUNC, type); } -- 2.41.0 From ef0d29d8bf4e533c428b2cd9d3ee9fa42e9e20e7 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Wed, 25 Oct 2023 16:46:07 +0900 Subject: [PATCH] src: Add preedit D-Bus signals to PostProcessKeyEvent ibus-hangul calls UpdatePreeditText signal during pressing Enter key to hide the current preedit text and this also causes the reorder issue in sync process-key-event and need to move the preedit signals to PostProcessKeyEvent. Also refactor PostProcessKeyEvent codes. Fixes: https://github.com/ibus/ibus/commit/38f09c6 BUG=https://github.com/ibus/ibus/pull/2575 --- bus/inputcontext.c | 176 +++++++++++++++++++++++++++-------------- src/ibusinputcontext.c | 162 +++++++++++++++++++++++++++++-------- 2 files changed, 248 insertions(+), 90 deletions(-) diff --git a/bus/inputcontext.c b/bus/inputcontext.c index 64430fe4..c914fbd2 100644 --- a/bus/inputcontext.c +++ b/bus/inputcontext.c @@ -48,11 +48,25 @@ struct _SetEngineByDescData { }; typedef struct _SetEngineByDescData SetEngineByDescData; +typedef struct _DeleteSurroundingData { + gint offset; + guint nchars; +} DeleteSurroundingData; + typedef struct _SyncForwardingData { - gchar key; - IBusText *text; + char key; + IBusText *text; } SyncForwardingData; +typedef struct _SyncForwardingPreData { + char key; + IBusText *text; + union { + guint uints[3]; + DeleteSurroundingData deleting; + } u; +} SyncForwardingPreData; + struct _BusInputContext { IBusService parent; @@ -970,6 +984,7 @@ _ic_process_key_event (BusInputContext *context, } else { g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", FALSE)); + context->processing_key_event = FALSE; } } @@ -1654,6 +1670,71 @@ bus_input_context_service_set_property (IBusService *service, } +static gboolean +bus_input_context_make_post_process_key_event (BusInputContext *context, + SyncForwardingPreData *pre_data) +{ + SyncForwardingData *data; + if (context->processing_key_event && g_queue_get_length ( + context->queue_during_process_key_event) <= MAX_SYNC_DATA) { + if (g_queue_get_length (context->queue_during_process_key_event) + == MAX_SYNC_DATA) { + g_warning ("Exceed max number of post process_key_event data"); + } + data = g_slice_new (SyncForwardingData); + data->key = pre_data->key; + switch (pre_data->key) { + case 'c': + data->text = g_object_ref (pre_data->text); + break; + case 'd': + data->text = ibus_text_new_from_printf ( + "%d,%u", + pre_data->u.deleting.offset, + pre_data->u.deleting.nchars); + break; + case 'f': + data->text = ibus_text_new_from_printf ("%u,%u,%u", + pre_data->u.uints[0], + pre_data->u.uints[1], + pre_data->u.uints[2]); + break; + case 'h': + case 'r': + case 's': + data->text = ibus_text_new_from_static_string (""); + break; + case 'u': + case 'm': + data->text = g_object_ref (pre_data->text); + g_queue_push_tail (context->queue_during_process_key_event, data); + data = g_slice_new (SyncForwardingData); + data->key = pre_data->key; + if (pre_data->key == 'u') { + data->text = ibus_text_new_from_printf ( + "%u,%u", + pre_data->u.uints[0], + pre_data->u.uints[1]); + } else { + data->text = ibus_text_new_from_printf ( + "%u,%u,%u", + pre_data->u.uints[0], + pre_data->u.uints[1], + pre_data->u.uints[2]); + } + break; + default: + g_warning ("Type %c of SyncForwardingData is not supported", + pre_data->key); + g_slice_free (SyncForwardingData, data); + return FALSE; + } + g_queue_push_tail (context->queue_during_process_key_event, data); + return TRUE; + } + return FALSE; +} + gboolean bus_input_context_has_focus (BusInputContext *context) { @@ -1893,6 +1974,9 @@ bus_input_context_show_preedit_text (BusInputContext *context, } if (PREEDIT_CONDITION) { + SyncForwardingPreData pre_data = { 's', }; + if (bus_input_context_make_post_process_key_event (context, &pre_data)) + return; bus_input_context_emit_signal (context, "ShowPreeditText", NULL, @@ -1933,6 +2017,9 @@ bus_input_context_hide_preedit_text (BusInputContext *context, } if (PREEDIT_CONDITION) { + SyncForwardingPreData pre_data = { 'h', }; + if (bus_input_context_make_post_process_key_event (context, &pre_data)) + return; bus_input_context_emit_signal (context, "HidePreeditText", NULL, @@ -2254,27 +2340,18 @@ _engine_forward_key_event_cb (BusEngineProxy *engine, guint state, BusInputContext *context) { + SyncForwardingPreData pre_data = { 'f', }; + g_assert (BUS_IS_ENGINE_PROXY (engine)); g_assert (BUS_IS_INPUT_CONTEXT (context)); - g_assert (context->engine == engine); g_assert (context->queue_during_process_key_event); - if (context->processing_key_event && g_queue_get_length ( - context->queue_during_process_key_event) <= MAX_SYNC_DATA) { - SyncForwardingData *data; - IBusText *text = ibus_text_new_from_printf ("%u,%u,%u", - keyval, keycode, state); - if (g_queue_get_length (context->queue_during_process_key_event) - == MAX_SYNC_DATA) { - g_warning ("Exceed max number of post process_key_event data"); - } - data = g_slice_new (SyncForwardingData); - data->key = 'f'; - data->text = text; - g_queue_push_tail (context->queue_during_process_key_event, data); + pre_data.u.uints[0] = keyval; + pre_data.u.uints[1] = keycode; + pre_data.u.uints[2] = state; + if (bus_input_context_make_post_process_key_event (context, &pre_data)) return; - } bus_input_context_emit_signal (context, "ForwardKeyEvent", g_variant_new ("(uuu)", keyval, keycode, state), @@ -2292,26 +2369,16 @@ _engine_delete_surrounding_text_cb (BusEngineProxy *engine, guint nchars, BusInputContext *context) { + SyncForwardingPreData pre_data = { 'd', }; + g_assert (BUS_IS_ENGINE_PROXY (engine)); g_assert (BUS_IS_INPUT_CONTEXT (context)); - g_assert (context->engine == engine); - if (context->processing_key_event && g_queue_get_length ( - context->queue_during_process_key_event) <= MAX_SYNC_DATA) { - SyncForwardingData *data; - IBusText *text = ibus_text_new_from_printf ("%d,%u", - offset_from_cursor, nchars); - if (g_queue_get_length (context->queue_during_process_key_event) - == MAX_SYNC_DATA) { - g_warning ("Exceed max number of sync process_key_event data"); - } - data = g_slice_new (SyncForwardingData); - data->key = 'd'; - data->text = g_object_ref (text); - g_queue_push_tail (context->queue_during_process_key_event, data); + pre_data.u.deleting.offset = offset_from_cursor; + pre_data.u.deleting.nchars = nchars; + if (bus_input_context_make_post_process_key_event (context, &pre_data)) return; - } bus_input_context_emit_signal (context, "DeleteSurroundingText", g_variant_new ("(iu)", offset_from_cursor, nchars), @@ -2452,25 +2520,14 @@ static void _engine_require_surrounding_text_cb (BusEngineProxy *engine, BusInputContext *context) { + SyncForwardingPreData pre_data = { 'r', }; + g_assert (BUS_IS_ENGINE_PROXY (engine)); g_assert (BUS_IS_INPUT_CONTEXT (context)); - g_assert (context->engine == engine); - if (context->processing_key_event && g_queue_get_length ( - context->queue_during_process_key_event) <= MAX_SYNC_DATA) { - SyncForwardingData *data; - IBusText *text = ibus_text_new_from_static_string (""); - if (g_queue_get_length (context->queue_during_process_key_event) - == MAX_SYNC_DATA) { - g_warning ("Exceed max number of post process_key_event data"); - } - data = g_slice_new (SyncForwardingData); - data->key = 'r'; - data->text = text; - g_queue_push_tail (context->queue_during_process_key_event, data); + if (bus_input_context_make_post_process_key_event (context, &pre_data)) return; - } bus_input_context_emit_signal (context, "RequireSurroundingText", NULL, @@ -3178,6 +3235,8 @@ bus_input_context_commit_text_use_extension (BusInputContext *context, IBusText *text, gboolean use_extension) { + SyncForwardingPreData pre_data = { 'c', text, }; + g_assert (BUS_IS_INPUT_CONTEXT (context)); g_assert (context->queue_during_process_key_event); @@ -3186,17 +3245,9 @@ bus_input_context_commit_text_use_extension (BusInputContext *context, if (use_extension && context->emoji_extension) { bus_panel_proxy_commit_text_received (context->emoji_extension, text); - } else if (context->processing_key_event && g_queue_get_length ( - context->queue_during_process_key_event) <= MAX_SYNC_DATA) { - SyncForwardingData *data; - if (g_queue_get_length (context->queue_during_process_key_event) - == MAX_SYNC_DATA) { - g_warning ("Exceed max number of sync process_key_event data"); - } - data = g_slice_new (SyncForwardingData); - data->key = 'c'; - data->text = g_object_ref (text); - g_queue_push_tail (context->queue_during_process_key_event, data); + } else if (bus_input_context_make_post_process_key_event (context, + &pre_data)) { + return; } else { GVariant *variant = ibus_serializable_serialize ( (IBusSerializable *)text); @@ -3245,9 +3296,18 @@ bus_input_context_update_preedit_text (BusInputContext *context, context->preedit_cursor_pos, context->preedit_visible); } else if (PREEDIT_CONDITION) { + SyncForwardingPreData pre_data = { 'u', context->preedit_text, }; GVariant *variant = ibus_serializable_serialize ( (IBusSerializable *)context->preedit_text); - if (context->client_commit_preedit) { + pre_data.u.uints[0] = context->preedit_cursor_pos; + pre_data.u.uints[1] = extension_visible ? 1 : 0; + pre_data.u.uints[2] = context->preedit_mode; + if (context->client_commit_preedit) + pre_data.key = 'm'; + if (bus_input_context_make_post_process_key_event (context, + &pre_data)) { + return; + } else if (context->client_commit_preedit) { bus_input_context_emit_signal ( context, "UpdatePreeditTextWithMode", diff --git a/src/ibusinputcontext.c b/src/ibusinputcontext.c index c6a030fe..1b62f656 100644 --- a/src/ibusinputcontext.c +++ b/src/ibusinputcontext.c @@ -1403,10 +1403,103 @@ ibus_input_context_set_post_process_key_event (IBusInputContext *context, g_variant_unref (var_post); } + +static void +ibus_input_context_fwd_text_to_commit (IBusInputContext *context, + IBusText *text) +{ + g_signal_emit (context, context_signals[COMMIT_TEXT], 0, text); +} + + +static void +ibus_input_context_fwd_text_to_forward_key_event (IBusInputContext *context, + IBusText *text) +{ + gchar **array = NULL; + guint keyval, keycode, state; + array = g_strsplit (text->text, ",", -1); + keyval = g_ascii_strtoull (array[0], NULL, 10); + keycode = g_ascii_strtoull (array[1], NULL, 10); + state = g_ascii_strtoull (array[2], NULL, 10); + g_strfreev (array); + g_signal_emit (context, + context_signals[FORWARD_KEY_EVENT], + 0, + keyval, + keycode, + state | IBUS_FORWARD_MASK); +} + + +static void +ibus_input_context_fwd_text_to_require_surrounding (IBusInputContext *context, + IBusText *text) +{ + IBusInputContextPrivate *priv; + g_assert (IBUS_IS_INPUT_CONTEXT (context)); + priv = IBUS_INPUT_CONTEXT_GET_PRIVATE (IBUS_INPUT_CONTEXT (context)); + priv->needs_surrounding_text = TRUE; +} + + +static void +ibus_input_context_fwd_text_to_delete_surrounding (IBusInputContext *context, + IBusText *text) +{ + gchar **array = NULL; + gint offset_from_cursor; + guint nchars; + array = g_strsplit (text->text, ",", -1); + offset_from_cursor = g_ascii_strtoll (array[0], NULL, 10); + nchars = g_ascii_strtoull (array[1], NULL, 10); + g_strfreev (array); + g_signal_emit (context, + context_signals[DELETE_SURROUNDING_TEXT], + 0, + offset_from_cursor, + nchars); +} + + +static void +ibus_input_context_fwd_text_to_update_preedit (IBusInputContext *context, + IBusText *text, + IBusText *position, + char type) +{ + gchar **array = NULL; + guint32 cursor_pos; + gboolean visible; + guint mode = 0; + + array = g_strsplit (position->text, ",", -1); + cursor_pos = g_ascii_strtoull (array[0], NULL, 10); + visible = g_ascii_strtoull (array[1], NULL, 10) ? TRUE : FALSE; + if (type == 'u') { + g_signal_emit (context, + context_signals[UPDATE_PREEDIT_TEXT], + 0, + text, + cursor_pos, + visible); + } else { + mode = g_ascii_strtoull (array[2], NULL, 10); + g_signal_emit (context, + context_signals[UPDATE_PREEDIT_TEXT_WITH_MODE], + 0, + text, + cursor_pos, + visible, + mode); + } + g_strfreev (array); +} + + void ibus_input_context_post_process_key_event (IBusInputContext *context) { - IBusInputContextPrivate *priv; GVariant *cached_var_post; gboolean enable = FALSE; GVariant *result; @@ -1419,7 +1513,6 @@ ibus_input_context_post_process_key_event (IBusInputContext *context) g_assert (IBUS_IS_INPUT_CONTEXT (context)); - priv = IBUS_INPUT_CONTEXT_GET_PRIVATE (IBUS_INPUT_CONTEXT (context)); cached_var_post = g_dbus_proxy_get_cached_property ((GDBusProxy *)context, "EffectivePostProcessKeyEvent"); @@ -1454,7 +1547,7 @@ ibus_input_context_post_process_key_event (IBusInputContext *context) g_assert (variant); g_variant_iter_init (&iter, variant); size = g_variant_iter_n_children (&iter); - while (size >0 && g_variant_iter_loop (&iter, "(yv)", &type, &vtext)) { + while (size > 0 && g_variant_iter_loop (&iter, "(yv)", &type, &vtext)) { IBusText *text = (IBusText *)ibus_serializable_deserialize_object (vtext); if (!IBUS_IS_TEXT (text)) { @@ -1463,41 +1556,48 @@ ibus_input_context_post_process_key_event (IBusInputContext *context) } switch (type) { case 'c': - g_signal_emit (context, context_signals[COMMIT_TEXT], 0, text); + ibus_input_context_fwd_text_to_commit (context, text); break; case 'f': { - gchar **array = NULL; - guint keyval, keycode, state; - array = g_strsplit (text->text, ",", -1); - keyval = g_ascii_strtoull (array[0], NULL, 10); - keycode = g_ascii_strtoull (array[1], NULL, 10); - state = g_ascii_strtoull (array[2], NULL, 10); - g_strfreev (array); - g_signal_emit (context, - context_signals[FORWARD_KEY_EVENT], - 0, - keyval, - keycode, - state | IBUS_FORWARD_MASK); + ibus_input_context_fwd_text_to_forward_key_event (context, text); break; } case 'r': { - priv->needs_surrounding_text = TRUE; + ibus_input_context_fwd_text_to_require_surrounding (context, text); break; } case 'd': { - gchar **array = NULL; - gint offset_from_cursor; - guint nchars; - array = g_strsplit (text->text, ",", -1); - offset_from_cursor = g_ascii_strtoll (array[0], NULL, 10); - nchars = g_ascii_strtoull (array[1], NULL, 10); - g_strfreev (array); - g_signal_emit (context, - context_signals[DELETE_SURROUNDING_TEXT], - 0, - offset_from_cursor, - nchars); + ibus_input_context_fwd_text_to_delete_surrounding (context, text); + break; + } + case 'u': + case 'm': { + IBusText *position; + g_clear_pointer (&vtext, g_variant_unref); + if (!g_variant_iter_loop (&iter, "(yv)", &type, &vtext)) { + g_warning ("%s: %s", G_STRFUNC, + "Type 'u' requires next type 'u'"); + break; + } + if (type != 'u' && type != 'm') { + g_warning ("%s: %s", G_STRFUNC, + "The next of type 'u' should be type 'u'"); + break; + } + position = + (IBusText *)ibus_serializable_deserialize_object (vtext); + if (!IBUS_IS_TEXT (position)) { + g_warning ("%s: %s", G_STRFUNC, "text is not IBusText"); + break; + } + ibus_input_context_fwd_text_to_update_preedit (context, + text, + position, + type); + if (g_object_is_floating (position)) { + g_object_ref_sink (position); + g_object_unref (position); + } break; } default: -- 2.41.0