public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/ibus] autotool: Fix sync ibus_input_context_process_key_event()
@ 2026-05-31  2:08 Takao Fujiwara
  0 siblings, 0 replies; only message in thread
From: Takao Fujiwara @ 2026-05-31  2:08 UTC (permalink / raw)
  To: git-commits

A new commit has been pushed.

Repo   : rpms/ibus
Branch : autotool
Commit : 25ad70d2458691ff9d1210c04c910cb2a07acd49
Author : Takao Fujiwara <tfujiwar@redhat.com>
Date   : 2023-07-07T09:39:39+09:00
Stats  : +1907/-1 in 2 file(s)
URL    : https://src.fedoraproject.org/rpms/ibus/c/25ad70d2458691ff9d1210c04c910cb2a07acd49?branch=autotool

Log:
Fix sync ibus_input_context_process_key_event()

---
diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
index a691af7..9f2ae56 100644
--- a/ibus-HEAD.patch
+++ b/ibus-HEAD.patch
@@ -2336,3 +2336,1906 @@ index 68dde2f3..fee0b3ee 100644
 -- 
 2.41.0
 
+From 8a1bd5ff72b7edf47526cfa19325f7e1dab85f59 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Fri, 7 Jul 2023 08:49:49 +0900
+Subject: [PATCH 1/2] 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.
+
+BUG=https://github.com/ibus/ibus/issues/2486
+---
+ bus/inputcontext.c          | 587 +++++++++++++++++++++++++++---------
+ client/gtk2/ibusimcontext.c |  51 ++--
+ src/ibusinputcontext.c      | 162 +++++++++-
+ src/ibusinputcontext.h      |  30 +-
+ 4 files changed, 652 insertions(+), 178 deletions(-)
+
+diff --git a/bus/inputcontext.c b/bus/inputcontext.c
+index e76bbdfc..2110af87 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;
+@@ -38,13 +40,19 @@ struct _SetEngineByDescData {
+     GTask *task;
+     /* a object to cancel bus_engine_proxy_new call */
+     GCancellable *cancellable;
+-    /* a object being passed to the bus_input_context_set_engine_by_desc function. if origin_cancellable is cancelled by someone,
++    /* a object being passed to the bus_input_context_set_engine_by_desc
++     * function. if origin_cancellable is cancelled by someone,
+      * we cancel the cancellable above as well. */
+     GCancellable *origin_cancellable;
+     gulong cancelled_handler_id;
+ };
+ typedef struct _SetEngineByDescData SetEngineByDescData;
+ 
++typedef struct _SyncForwardingData {
++    gchar     key;
++    IBusText *text;
++} SyncForwardingData;
++
+ struct _BusInputContext {
+     IBusService parent;
+ 
+@@ -98,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 {
+@@ -155,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 +234,11 @@ static const gchar introspection_xml[] =
+     "<node>"
+     "  <interface name='org.freedesktop.IBus.InputContext'>"
+     /* properties */
++    "    <property name='PostProcessKeyEvent' type='(a(yv))' access='read' />\n"
+     "    <property name='ContentType' type='(uu)' access='write' />"
+     "    <property name='ClientCommitPreedit' type='(b)' access='write' />\n"
++    "    <property name='EffectivePostProcessKeyEvent' type='(b)' "
++    "                                                  access='write' />\n"
+     /* methods */
+     "    <method name='ProcessKeyEvent'>"
+     "      <arg direction='in'  type='u' name='keyval' />"
+@@ -223,6 +246,12 @@ static const gchar introspection_xml[] =
+     "      <arg direction='in'  type='u' name='state' />"
+     "      <arg direction='out' type='b' name='handled' />"
+     "    </method>"
++    "    <method name='ProcessKeyEventSync'>"
++    "      <arg direction='in'  type='u' name='keyval' />"
++    "      <arg direction='in'  type='u' name='keycode' />"
++    "      <arg direction='in'  type='u' name='state' />"
++    "      <arg direction='out' type='ba(yv)' name='handled' />"
++    "    </method>"
+     "    <method name='SetCursorLocation'>"
+     "      <arg direction='in' type='i' name='x' />"
+     "      <arg direction='in' type='i' name='y' />"
+@@ -312,11 +341,14 @@ static const gchar introspection_xml[] =
+ 
+ G_DEFINE_TYPE (BusInputContext, bus_input_context, IBUS_TYPE_SERVICE)
+ 
+-/* TRUE if we can send preedit text to client. FALSE if the panel has to handle it. Note that we check IBUS_CAP_FOCUS here since
+- * when the capability is not set, the client has to handle a preedit text regardless of the embed_preedit_text config. */
++/* TRUE if we can send preedit text to client. FALSE if the panel has to handle
++ * it. Note that we check IBUS_CAP_FOCUS here since
++ * when the capability is not set, the client has to handle a preedit text
++ * regardless of the embed_preedit_text config. */
+ #define PREEDIT_CONDITION  \
+     ((context->capabilities & IBUS_CAP_PREEDIT_TEXT) && \
+-     (bus_ibus_impl_is_embed_preedit_text (BUS_DEFAULT_IBUS) || (context->capabilities & IBUS_CAP_FOCUS) == 0))
++     (bus_ibus_impl_is_embed_preedit_text (\
++            BUS_DEFAULT_IBUS) || (context->capabilities & IBUS_CAP_FOCUS) == 0))
+ 
+ static void
+ _connection_destroy_cb (BusConnection   *connection,
+@@ -343,17 +375,23 @@ bus_input_context_class_init (BusInputContextClass *class)
+                                                        "");
+     g_object_ref_sink (class->default_engine_desc);
+ 
+-    ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_input_context_destroy;
++    ibus_object_class->destroy =
++            (IBusObjectDestroyFunc)bus_input_context_destroy;
+ 
+     /* 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'.) */
++    /* 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'.) */
+     ibus_service_class_add_interfaces (IBUS_SERVICE_CLASS (class), introspection_xml);
+ 
+-    /* install glib signals that would be handled by other classes like ibusimpl.c and panelproxy.c. */
++    /* install glib signals that would be handled by other classes like
++     * ibusimpl.c and panelproxy.c.
++     */
+     context_signals[PROCESS_KEY_EVENT] =
+         g_signal_new (I_("process-key-event"),
+             G_TYPE_FROM_CLASS (class),
+@@ -576,8 +614,11 @@ bus_input_context_class_init (BusInputContextClass *class)
+             G_TYPE_NONE,
+             0);
+ 
+-    /* This signal is not for notifying an event on this object, but is for requesting an engine as the name shows.
+-     * On the signal emission, ibusimpl.c will immediately update the context->engine variable. */
++    /* This signal is not for notifying an event on this object, but is for
++     * requesting an engine as the name shows.
++     * On the signal emission, ibusimpl.c will immediately update the
++     * context->engine variable.
++     */
+     context_signals[REQUEST_ENGINE] =
+         g_signal_new (I_("request-engine"),
+             G_TYPE_FROM_CLASS (class),
+@@ -614,7 +655,8 @@ bus_input_context_class_init (BusInputContextClass *class)
+ 
+     text_empty = ibus_text_new_from_string ("");
+     g_object_ref_sink (text_empty);
+-    lookup_table_empty = ibus_lookup_table_new (9 /* page size */, 0, FALSE, FALSE);
++    lookup_table_empty = ibus_lookup_table_new (9 /* page size */,
++                                                0, FALSE, FALSE);
+     g_object_ref_sink (lookup_table_empty);
+     props_empty = ibus_prop_list_new ();
+     g_object_ref_sink (props_empty);
+@@ -662,9 +704,10 @@ bus_input_context_destroy (BusInputContext *context)
+     }
+ 
+     if (context->connection) {
+-        g_signal_handlers_disconnect_by_func (context->connection,
+-                                         (GCallback) _connection_destroy_cb,
+-                                         context);
++        g_signal_handlers_disconnect_by_func (
++                context->connection,
++                (GCallback) _connection_destroy_cb,
++                context);
+         g_object_unref (context->connection);
+         context->connection = NULL;
+     }
+@@ -674,7 +717,8 @@ bus_input_context_destroy (BusInputContext *context)
+         context->client = NULL;
+     }
+ 
+-    IBUS_OBJECT_CLASS (bus_input_context_parent_class)->destroy (IBUS_OBJECT (context));
++    IBUS_OBJECT_CLASS (bus_input_context_parent_class)->
++            destroy (IBUS_OBJECT (context));
+ }
+ 
+ static gboolean
+@@ -689,25 +733,30 @@ bus_input_context_send_signal (BusInputContext *context,
+         return TRUE;
+     }
+ 
+-    GDBusMessage *message = g_dbus_message_new_signal (ibus_service_get_object_path ((IBusService *)context),
+-                                                       interface_name,
+-                                                       signal_name);
++    GDBusMessage *message = g_dbus_message_new_signal (
++            ibus_service_get_object_path ((IBusService *)context),
++            interface_name,
++            signal_name);
+     g_dbus_message_set_sender (message, "org.freedesktop.IBus");
+-    g_dbus_message_set_destination (message, bus_connection_get_unique_name (context->connection));
++    g_dbus_message_set_destination (
++            message,
++            bus_connection_get_unique_name (context->connection));
+     if (parameters != NULL)
+         g_dbus_message_set_body (message, parameters);
+ 
+-    gboolean retval =  g_dbus_connection_send_message (bus_connection_get_dbus_connection (context->connection),
+-                                                       message,
+-                                                       G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+-                                                       NULL, error);
++    gboolean retval =  g_dbus_connection_send_message (
++            bus_connection_get_dbus_connection (context->connection),
++            message,
++            G_DBUS_SEND_MESSAGE_FLAGS_NONE,
++            NULL, error);
+     g_object_unref (message);
+     return retval;
+ }
+ 
+ /**
+  * bus_input_context_emit_signal:
+- * @signal_name: The D-Bus signal name to emit which is in the introspection_xml.
++ * @signal_name: The D-Bus signal name to emit which is in the
++ * introspection_xml.
+  *
+  * Emit the D-Bus signal.
+  */
+@@ -763,6 +812,11 @@ bus_input_context_property_changed (BusInputContext *context,
+ }
+ 
+ 
++typedef struct _PanelProcessKeyEventData {
++    GDBusMethodInvocation *invocation;
++    BusInputContext *context;
++} PanelProcessKeyEventData;
++
+ /**
+  * _panel_process_key_event_cb:
+  *
+@@ -770,14 +824,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);
+@@ -786,6 +847,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;
+@@ -822,21 +884,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);
+@@ -850,14 +918,16 @@ _ic_process_key_event_reply_cb (GObject               *source,
+  * org.freedesktop.IBus.InputContext interface.
+  */
+ static void
+-_ic_process_key_event  (BusInputContext       *context,
+-                        GVariant              *parameters,
+-                        GDBusMethodInvocation *invocation)
++_ic_process_key_event (BusInputContext       *context,
++                       GVariant              *parameters,
++                       GDBusMethodInvocation *invocation)
+ {
+     guint keyval = IBUS_KEY_VoidSymbol;
+     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 */
+@@ -925,7 +995,8 @@ _ic_process_key_event  (BusInputContext       *context,
+ /**
+  * _ic_set_cursor_location:
+  *
+- * Implement the "SetCursorLocation" method call of the org.freedesktop.IBus.InputContext interface.
++ * Implement the "SetCursorLocation" method call of the
++ * org.freedesktop.IBus.InputContext interface.
+  */
+ static void
+ _ic_set_cursor_location (BusInputContext       *context,
+@@ -1008,7 +1079,8 @@ _ic_process_hand_writing_event (BusInputContext       *context,
+     /* do nothing if it is a fake input context */
+     if (context->has_focus &&
+         context->engine && context->fake == FALSE) {
+-        bus_engine_proxy_process_hand_writing_event (context->engine, parameters);
++        bus_engine_proxy_process_hand_writing_event (context->engine,
++                                                     parameters);
+     }
+     g_dbus_method_invocation_return_value (invocation, NULL);
+ }
+@@ -1032,7 +1104,8 @@ _ic_cancel_hand_writing (BusInputContext       *context,
+ /**
+  * _ic_focus_in:
+  *
+- * Implement the "FocusIn" method call of the org.freedesktop.IBus.InputContext interface.
++ * Implement the "FocusIn" method call of the org.freedesktop.IBus.InputContext
++ * interface.
+  */
+ static void
+ _ic_focus_in (BusInputContext       *context,
+@@ -1044,15 +1117,17 @@ _ic_focus_in (BusInputContext       *context,
+         g_dbus_method_invocation_return_value (invocation, NULL);
+     }
+     else {
+-        g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+-                        "The input context does not support focus.");
++        g_dbus_method_invocation_return_error (
++                invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
++                "The input context does not support focus.");
+     }
+ }
+ 
+ /**
+  * _ic_focus_out:
+  *
+- * Implement the "FocusOut" method call of the org.freedesktop.IBus.InputContext interface.
++ * Implement the "FocusOut" method call of the org.freedesktop.IBus.InputContext
++ * interface.
+  */
+ static void
+ _ic_focus_out (BusInputContext       *context,
+@@ -1064,15 +1139,17 @@ _ic_focus_out (BusInputContext       *context,
+         g_dbus_method_invocation_return_value (invocation, NULL);
+     }
+     else {
+-        g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+-                        "The input context does not support focus.");
++        g_dbus_method_invocation_return_error (
++                invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
++                "The input context does not support focus.");
+     }
+ }
+ 
+ /**
+  * _ic_reset:
+  *
+- * Implement the "Reset" method call of the org.freedesktop.IBus.InputContext interface.
++ * Implement the "Reset" method call of the org.freedesktop.IBus.InputContext
++ * interface.
+  */
+ static void
+ _ic_reset (BusInputContext       *context,
+@@ -1094,7 +1171,8 @@ _ic_reset (BusInputContext       *context,
+ /**
+  * _ic_set_capabilities:
+  *
+- * Implement the "SetCapabilities" method call of the org.freedesktop.IBus.InputContext interface.
++ * Implement the "SetCapabilities" method call of the
++ * org.freedesktop.IBus.InputContext interface.
+  */
+ static void
+ _ic_set_capabilities (BusInputContext       *context,
+@@ -1112,7 +1190,8 @@ _ic_set_capabilities (BusInputContext       *context,
+ /**
+  * _ic_property_activate:
+  *
+- * Implement the "PropertyActivate" method call of the org.freedesktop.IBus.InputContext interface.
++ * Implement the "PropertyActivate" method call of the
++ * org.freedesktop.IBus.InputContext interface.
+  */
+ static void
+ _ic_property_activate (BusInputContext       *context,
+@@ -1124,7 +1203,9 @@ _ic_property_activate (BusInputContext       *context,
+     g_variant_get (parameters, "(&su)", &prop_name, &prop_state);
+ 
+     if (context->engine) {
+-        bus_engine_proxy_property_activate (context->engine, prop_name, prop_state);
++        bus_engine_proxy_property_activate (context->engine,
++                                            prop_name,
++                                            prop_state);
+     }
+ 
+ #ifdef OS_CHROMEOS
+@@ -1132,9 +1213,12 @@ _ic_property_activate (BusInputContext       *context,
+      * so pass PropertyActivate signal to the focused context.
+      */
+     else if (context->fake) {
+-        BusInputContext *focused_context = bus_ibus_impl_get_focused_input_context (BUS_DEFAULT_IBUS);
++        BusInputContext *focused_context =
++                bus_ibus_impl_get_focused_input_context (BUS_DEFAULT_IBUS);
+         if (focused_context && focused_context->engine)
+-            bus_engine_proxy_property_activate (focused_context->engine, prop_name, prop_state);
++            bus_engine_proxy_property_activate (focused_context->engine,
++                                                prop_name,
++                                                prop_state);
+     }
+ #endif
+ 
+@@ -1164,7 +1248,8 @@ _ic_set_engine_done (BusInputContext       *context,
+ /**
+  * _ic_set_engine:
+  *
+- * Implement the "SetEngine" method call of the org.freedesktop.IBus.InputContext interface.
++ * Implement the "SetEngine" method call of the
++ * org.freedesktop.IBus.InputContext interface.
+  */
+ static void
+ _ic_set_engine (BusInputContext       *context,
+@@ -1205,7 +1290,8 @@ _ic_set_engine (BusInputContext       *context,
+ /**
+  * _ic_get_engine:
+  *
+- * Implement the "GetEngine" method call of the org.freedesktop.IBus.InputContext interface.
++ * Implement the "GetEngine" method call of
++ * the org.freedesktop.IBus.InputContext interface.
+  */
+ static void
+ _ic_get_engine (BusInputContext       *context,
+@@ -1218,7 +1304,9 @@ _ic_get_engine (BusInputContext       *context,
+ 
+ 
+     g_dbus_method_invocation_return_value (invocation,
+-            g_variant_new ("(v)", ibus_serializable_serialize ((IBusSerializable *)desc)));
++            g_variant_new ("(v)",
++                           ibus_serializable_serialize (
++                                   (IBusSerializable *)desc)));
+ }
+ 
+ static void
+@@ -1270,7 +1358,8 @@ bus_input_context_service_authorized_method (IBusService     *service,
+ /**
+  * bus_input_context_service_method_call:
+  *
+- * Handle a D-Bus method call whose destination and interface name are both "org.freedesktop.IBus.InputContext"
++ * Handle a D-Bus method call whose destination and interface name are both
++ * "org.freedesktop.IBus.InputContext"
+  */
+ static void
+ bus_input_context_service_method_call (IBusService            *service,
+@@ -1283,21 +1372,23 @@ bus_input_context_service_method_call (IBusService            *service,
+                                        GDBusMethodInvocation  *invocation)
+ {
+     if (g_strcmp0 (interface_name, IBUS_INTERFACE_INPUT_CONTEXT) != 0) {
+-        IBUS_SERVICE_CLASS (bus_input_context_parent_class)->service_method_call (
+-                        service,
+-                        connection,
+-                        sender,
+-                        object_path,
+-                        interface_name,
+-                        method_name,
+-                        parameters,
+-                        invocation);
++        IBUS_SERVICE_CLASS (bus_input_context_parent_class)->
++                service_method_call (service,
++                                     connection,
++                                     sender,
++                                     object_path,
++                                     interface_name,
++                                     method_name,
++                                     parameters,
++                                     invocation);
+         return;
+     }
+ 
+     static const struct {
+         const gchar *method_name;
+-        void (* method_callback) (BusInputContext *, GVariant *, GDBusMethodInvocation *);
++        void (* method_callback) (BusInputContext *,
++                                  GVariant *,
++                                  GDBusMethodInvocation *);
+     } methods [] =  {
+         { "ProcessKeyEvent",   _ic_process_key_event },
+         { "SetCursorLocation", _ic_set_cursor_location },
+@@ -1322,7 +1413,9 @@ bus_input_context_service_method_call (IBusService            *service,
+ 
+     for (i = 0; i < G_N_ELEMENTS (methods); i++) {
+         if (g_strcmp0 (method_name, methods[i].method_name) == 0) {
+-            methods[i].method_callback ((BusInputContext *)service, parameters, invocation);
++            methods[i].method_callback ((BusInputContext *)service,
++                                        parameters,
++                                        invocation);
+             return;
+         }
+     }
+@@ -1330,17 +1423,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;
+@@ -1358,24 +1543,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
+@@ -1388,6 +1579,18 @@ bus_input_context_service_set_property (IBusService     *service,
+                                         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 (error)
+         *error = NULL;
+     if (g_strcmp0 (interface_name, IBUS_INTERFACE_INPUT_CONTEXT) != 0) {
+@@ -1418,14 +1621,12 @@ bus_input_context_service_set_property (IBusService     *service,
+                      " ");
+         return 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_set_error (error,
+@@ -1465,15 +1666,23 @@ bus_input_context_focus_in (BusInputContext *context)
+                 ibus_service_get_object_path ((IBusService *)context);
+         bus_engine_proxy_focus_in (context->engine, path, context->client);
+         bus_engine_proxy_enable (context->engine);
+-        bus_engine_proxy_set_capabilities (context->engine, context->capabilities);
+-        bus_engine_proxy_set_cursor_location (context->engine, context->x, context->y, context->w, context->h);
+-        bus_engine_proxy_set_content_type (context->engine, context->purpose, context->hints);
++        bus_engine_proxy_set_capabilities (context->engine,
++                                           context->capabilities);
++        bus_engine_proxy_set_cursor_location (context->engine,
++                                              context->x,
++                                              context->y,
++                                              context->w,
++                                              context->h);
++        bus_engine_proxy_set_content_type (context->engine,
++                                           context->purpose,
++                                           context->hints);
+     }
+ 
+     if (context->capabilities & IBUS_CAP_FOCUS) {
+         g_signal_emit (context, context_signals[FOCUS_IN], 0);
+         if (context->engine) {
+-            /* if necessary, emit glib signals to the context object to update panel status. see the comment for PREEDIT_CONDITION
++            /* if necessary, emit glib signals to the context object to update
++             * panel status. see the comment for PREEDIT_CONDITION
+              * for details. */
+             if (context->preedit_visible && !PREEDIT_CONDITION) {
+                 g_signal_emit (context,
+@@ -1483,14 +1692,16 @@ bus_input_context_focus_in (BusInputContext *context)
+                                context->preedit_cursor_pos,
+                                context->preedit_visible);
+             }
+-            if (context->auxiliary_visible && (context->capabilities & IBUS_CAP_AUXILIARY_TEXT) == 0) {
++            if (context->auxiliary_visible &&
++                (context->capabilities & IBUS_CAP_AUXILIARY_TEXT) == 0) {
+                 g_signal_emit (context,
+                                context_signals[UPDATE_AUXILIARY_TEXT],
+                                0,
+                                context->auxiliary_text,
+                                context->auxiliary_visible);
+             }
+-            if (context->lookup_table_visible && (context->capabilities & IBUS_CAP_LOOKUP_TABLE) == 0) {
++            if (context->lookup_table_visible &&
++                (context->capabilities & IBUS_CAP_LOOKUP_TABLE) == 0) {
+                 g_signal_emit (context,
+                                context_signals[UPDATE_LOOKUP_TABLE],
+                                0,
+@@ -1630,14 +1841,17 @@ bus_input_context_property_activate (BusInputContext *context,
+     g_assert (BUS_IS_INPUT_CONTEXT (context));
+ 
+     if (context->engine) {
+-        bus_engine_proxy_property_activate (context->engine, prop_name, prop_state);
++        bus_engine_proxy_property_activate (context->engine,
++                                            prop_name,
++                                            prop_state);
+     }
+ }
+ 
+ /**
+  * bus_input_context_show_preedit_text:
+  *
+- * Show a preedit text. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
++ * Show a preedit text. Send D-Bus signal to update status of client or send
++ * glib signal to the panel, depending on capabilities of the client.
+  */
+ static void
+ bus_input_context_show_preedit_text (BusInputContext *context,
+@@ -1678,7 +1892,8 @@ bus_input_context_show_preedit_text (BusInputContext *context,
+ /**
+  * bus_input_context_hide_preedit_text:
+  *
+- * Hide a preedit text. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
++ * Hide a preedit text. Send D-Bus signal to update status of client or send
++ * glib signal to the panel, depending on capabilities of the client.
+  */
+ static void
+ bus_input_context_hide_preedit_text (BusInputContext *context,
+@@ -1717,7 +1932,8 @@ bus_input_context_hide_preedit_text (BusInputContext *context,
+ /**
+  * bus_input_context_update_auxiliary_text:
+  *
+- * Update an aux text. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
++ * Update an aux text. Send D-Bus signal to update status of client or send
++ * glib signal to the panel, depending on capabilities of the client.
+  */
+ static void
+ bus_input_context_update_auxiliary_text (BusInputContext *context,
+@@ -1730,11 +1946,13 @@ bus_input_context_update_auxiliary_text (BusInputContext *context,
+         g_object_unref (context->auxiliary_text);
+     }
+ 
+-    context->auxiliary_text = (IBusText *) g_object_ref_sink (text ? text : text_empty);
++    context->auxiliary_text = (IBusText *)g_object_ref_sink (
++            text ? text : text_empty);
+     context->auxiliary_visible = visible;
+ 
+     if (context->capabilities & IBUS_CAP_AUXILIARY_TEXT) {
+-        GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)text);
++        GVariant *variant =
++                ibus_serializable_serialize ((IBusSerializable *)text);
+         bus_input_context_emit_signal (context,
+                                        "UpdateAuxiliaryText",
+                                        g_variant_new ("(vb)", variant, visible),
+@@ -1752,7 +1970,8 @@ bus_input_context_update_auxiliary_text (BusInputContext *context,
+ /**
+  * bus_input_context_show_auxiliary_text:
+  *
+- * Show an aux text. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
++ * Show an aux text. Send D-Bus signal to update status of client or send glib
++ * signal to the panel, depending on capabilities of the client.
+  */
+ static void
+ bus_input_context_show_auxiliary_text (BusInputContext *context)
+@@ -1765,7 +1984,8 @@ bus_input_context_show_auxiliary_text (BusInputContext *context)
+ 
+     context->auxiliary_visible = TRUE;
+ 
+-    if ((context->capabilities & IBUS_CAP_AUXILIARY_TEXT) == IBUS_CAP_AUXILIARY_TEXT) {
++    if ((context->capabilities & IBUS_CAP_AUXILIARY_TEXT)
++        == IBUS_CAP_AUXILIARY_TEXT) {
+         bus_input_context_emit_signal (context,
+                                        "ShowAuxiliaryText",
+                                        NULL,
+@@ -1781,7 +2001,8 @@ bus_input_context_show_auxiliary_text (BusInputContext *context)
+ /**
+  * bus_input_context_hide_auxiliary_text:
+  *
+- * Hide an aux text. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
++ * Hide an aux text. Send D-Bus signal to update status of client or send glib
++ * signal to the panel, depending on capabilities of the client.
+  */
+ static void
+ bus_input_context_hide_auxiliary_text (BusInputContext *context)
+@@ -1794,7 +2015,8 @@ bus_input_context_hide_auxiliary_text (BusInputContext *context)
+ 
+     context->auxiliary_visible = FALSE;
+ 
+-    if ((context->capabilities & IBUS_CAP_AUXILIARY_TEXT) == IBUS_CAP_AUXILIARY_TEXT) {
++    if ((context->capabilities & IBUS_CAP_AUXILIARY_TEXT)
++        == IBUS_CAP_AUXILIARY_TEXT) {
+         bus_input_context_emit_signal (context,
+                                        "HideAuxiliaryText",
+                                        NULL,
+@@ -1829,11 +2051,13 @@ bus_input_context_update_lookup_table (BusInputContext *context,
+         g_object_unref (context->lookup_table);
+     }
+ 
+-    context->lookup_table = (IBusLookupTable *) g_object_ref_sink (table ? table : lookup_table_empty);
++    context->lookup_table = (IBusLookupTable *)g_object_ref_sink (
++            table ? table : lookup_table_empty);
+     context->lookup_table_visible = visible;
+ 
+     if (context->capabilities & IBUS_CAP_LOOKUP_TABLE) {
+-        GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)table);
++        GVariant *variant =
++                ibus_serializable_serialize ((IBusSerializable *)table);
+         bus_input_context_emit_signal (context,
+                                        "UpdateLookupTable",
+                                        g_variant_new ("(vb)", variant, visible),
+@@ -1851,7 +2075,8 @@ bus_input_context_update_lookup_table (BusInputContext *context,
+ /**
+  * bus_input_context_show_lookup_table:
+  *
+- * Show the lookup table. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
++ * Show the lookup table. Send D-Bus signal to update status of client or send
++ * glib signal to the panel, depending on capabilities of the client.
+  */
+ static void
+ bus_input_context_show_lookup_table (BusInputContext *context)
+@@ -1864,7 +2089,8 @@ bus_input_context_show_lookup_table (BusInputContext *context)
+ 
+     context->lookup_table_visible = TRUE;
+ 
+-    if ((context->capabilities & IBUS_CAP_LOOKUP_TABLE) == IBUS_CAP_LOOKUP_TABLE) {
++    if ((context->capabilities & IBUS_CAP_LOOKUP_TABLE)
++        == IBUS_CAP_LOOKUP_TABLE) {
+         bus_input_context_emit_signal (context,
+                                        "ShowLookupTable",
+                                        NULL,
+@@ -1880,7 +2106,8 @@ bus_input_context_show_lookup_table (BusInputContext *context)
+ /**
+  * bus_input_context_hide_lookup_table:
+  *
+- * Hide the lookup table. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
++ * Hide the lookup table. Send D-Bus signal to update status of client or send
++ * glib signal to the panel, depending on capabilities of the client.
+  */
+ static void
+ bus_input_context_hide_lookup_table (BusInputContext *context)
+@@ -1893,7 +2120,8 @@ bus_input_context_hide_lookup_table (BusInputContext *context)
+ 
+     context->lookup_table_visible = FALSE;
+ 
+-    if ((context->capabilities & IBUS_CAP_LOOKUP_TABLE) == IBUS_CAP_LOOKUP_TABLE) {
++    if ((context->capabilities & IBUS_CAP_LOOKUP_TABLE)
++        == IBUS_CAP_LOOKUP_TABLE) {
+         bus_input_context_emit_signal (context,
+                                        "HideLookupTable",
+                                        NULL,
+@@ -1909,7 +2137,8 @@ bus_input_context_hide_lookup_table (BusInputContext *context)
+ /**
+  * bus_input_context_page_up_lookup_table:
+  *
+- * Change cursor position. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
++ * Change cursor position. Send D-Bus signal to update status of client or send
++ * glib signal to the panel, depending on capabilities of the client.
+  */
+ static void
+ bus_input_context_page_up_lookup_table (BusInputContext *context)
+@@ -1920,7 +2149,8 @@ bus_input_context_page_up_lookup_table (BusInputContext *context)
+         return;
+     }
+ 
+-    if ((context->capabilities & IBUS_CAP_LOOKUP_TABLE) == IBUS_CAP_LOOKUP_TABLE) {
++    if ((context->capabilities & IBUS_CAP_LOOKUP_TABLE)
++        == IBUS_CAP_LOOKUP_TABLE) {
+         bus_input_context_emit_signal (context,
+                                        "PageUpLookupTable",
+                                        NULL,
+@@ -1936,7 +2166,8 @@ bus_input_context_page_up_lookup_table (BusInputContext *context)
+ /**
+  * bus_input_context_page_down_lookup_table:
+  *
+- * Change cursor position. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
++ * Change cursor position. Send D-Bus signal to update status of client or send
++ * glib signal to the panel, depending on capabilities of the client.
+  */
+ static void
+ bus_input_context_page_down_lookup_table (BusInputContext *context)
+@@ -1947,7 +2178,8 @@ bus_input_context_page_down_lookup_table (BusInputContext *context)
+         return;
+     }
+ 
+-    if ((context->capabilities & IBUS_CAP_LOOKUP_TABLE) == IBUS_CAP_LOOKUP_TABLE) {
++    if ((context->capabilities & IBUS_CAP_LOOKUP_TABLE)
++        == IBUS_CAP_LOOKUP_TABLE) {
+         bus_input_context_emit_signal (context,
+                                        "PageDownLookupTable",
+                                        NULL,
+@@ -1963,7 +2195,8 @@ bus_input_context_page_down_lookup_table (BusInputContext *context)
+ /**
+  * bus_input_context_cursor_up_lookup_table:
+  *
+- * Change cursor position. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
++ * Change cursor position. Send D-Bus signal to update status of client or send
++ * glib signal to the panel, depending on capabilities of the client.
+  */
+ static void
+ bus_input_context_cursor_up_lookup_table (BusInputContext *context)
+@@ -1974,7 +2207,8 @@ bus_input_context_cursor_up_lookup_table (BusInputContext *context)
+         return;
+     }
+ 
+-    if ((context->capabilities & IBUS_CAP_LOOKUP_TABLE) == IBUS_CAP_LOOKUP_TABLE) {
++    if ((context->capabilities & IBUS_CAP_LOOKUP_TABLE)
++        == IBUS_CAP_LOOKUP_TABLE) {
+         bus_input_context_emit_signal (context,
+                                        "CursorUpLookupTable",
+                                        NULL,
+@@ -1990,7 +2224,8 @@ bus_input_context_cursor_up_lookup_table (BusInputContext *context)
+ /**
+  * bus_input_context_cursor_down_lookup_table:
+  *
+- * Change cursor position. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
++ * Change cursor position. Send D-Bus signal to update status of client or send
++ * glib signal to the panel, depending on capabilities of the client.
+  */
+ static void
+ bus_input_context_cursor_down_lookup_table (BusInputContext *context)
+@@ -2001,7 +2236,8 @@ bus_input_context_cursor_down_lookup_table (BusInputContext *context)
+         return;
+     }
+ 
+-    if ((context->capabilities & IBUS_CAP_LOOKUP_TABLE) == IBUS_CAP_LOOKUP_TABLE) {
++    if ((context->capabilities & IBUS_CAP_LOOKUP_TABLE)
++        == IBUS_CAP_LOOKUP_TABLE) {
+         bus_input_context_emit_signal (context,
+                                        "CursorDownLookupTable",
+                                        NULL,
+@@ -2017,7 +2253,8 @@ bus_input_context_cursor_down_lookup_table (BusInputContext *context)
+ /**
+  * bus_input_context_register_properties:
+  *
+- * Register properties. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
++ * Register properties. Send D-Bus signal to update status of client or send
++ * glib signal to the panel, depending on capabilities of the client.
+  */
+ static void
+ bus_input_context_register_properties (BusInputContext *context,
+@@ -2027,7 +2264,8 @@ bus_input_context_register_properties (BusInputContext *context,
+     g_assert (IBUS_IS_PROP_LIST (props));
+ 
+     if (context->capabilities & IBUS_CAP_PROPERTY) {
+-        GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)props);
++        GVariant *variant =
++                ibus_serializable_serialize ((IBusSerializable *)props);
+         bus_input_context_emit_signal (context,
+                                        "RegisterProperties",
+                                        g_variant_new ("(v)", variant),
+@@ -2044,7 +2282,8 @@ bus_input_context_register_properties (BusInputContext *context,
+ /**
+  * bus_input_context_update_property:
+  *
+- * Update property. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
++ * Update property. Send D-Bus signal to update status of client or send glib
++ * signal to the panel, depending on capabilities of the client.
+  */
+ static void
+ bus_input_context_update_property (BusInputContext *context,
+@@ -2054,7 +2293,8 @@ bus_input_context_update_property (BusInputContext *context,
+     g_assert (IBUS_IS_PROPERTY (prop));
+ 
+     if (context->capabilities & IBUS_CAP_PROPERTY) {
+-        GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)prop);
++        GVariant *variant =
++                ibus_serializable_serialize ((IBusSerializable *)prop);
+         bus_input_context_emit_signal (context,
+                                        "UpdateProperty",
+                                        g_variant_new ("(v)", variant),
+@@ -2089,7 +2329,8 @@ _engine_destroy_cb (BusEngineProxy  *engine,
+ /**
+  * _engine_commit_text_cb:
+  *
+- * A function to be called when "commit-text" glib signal is sent to the engine object.
++ * A function to be called when "commit-text" glib signal is sent to the engine
++ * object.
+  */
+ static void
+ _engine_commit_text_cb (BusEngineProxy  *engine,
+@@ -2108,7 +2349,8 @@ _engine_commit_text_cb (BusEngineProxy  *engine,
+ /**
+  * _engine_forward_key_event_cb:
+  *
+- * A function to be called when "forward-key-event" glib signal is sent to the engine object.
++ * A function to be called when "forward-key-event" glib signal is sent to the
++ * engine object.
+  */
+ static void
+ _engine_forward_key_event_cb (BusEngineProxy    *engine,
+@@ -2122,16 +2364,33 @@ _engine_forward_key_event_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 ("%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),
++                                   g_variant_new ("(uuu)",
++                                                  keyval, keycode, state),
+                                    NULL);
+ }
+ 
+ /**
+  * _engine_delete_surrounding_text_cb:
+  *
+- * A function to be called when "delete-surrounding-text" glib signal is sent to the engine object.
++ * A function to be called when "delete-surrounding-text" glib signal is sent
++ * to the engine object.
+  */
+ static void
+ _engine_delete_surrounding_text_cb (BusEngineProxy    *engine,
+@@ -2146,14 +2405,16 @@ _engine_delete_surrounding_text_cb (BusEngineProxy    *engine,
+ 
+     bus_input_context_emit_signal (context,
+                                    "DeleteSurroundingText",
+-                                   g_variant_new ("(iu)", offset_from_cursor, nchars),
++                                   g_variant_new ("(iu)",
++                                                  offset_from_cursor, nchars),
+                                    NULL);
+ }
+ 
+ /**
+  * _engine_require_surrounding_text_cb:
+  *
+- * A function to be called when "require-surrounding-text" glib signal is sent to the engine object.
++ * A function to be called when "require-surrounding-text" glib signal is sent
++ * to the engine object.
+  */
+ static void
+ _engine_require_surrounding_text_cb (BusEngineProxy    *engine,
+@@ -2173,7 +2434,8 @@ _engine_require_surrounding_text_cb (BusEngineProxy    *engine,
+ /**
+  * _engine_update_preedit_text_cb:
+  *
+- * A function to be called when "update-preedit-text" glib signal is sent to the engine object.
++ * A function to be called when "update-preedit-text" glib signal is sent to
++ * the engine object.
+  */
+ static void
+ _engine_update_preedit_text_cb (BusEngineProxy  *engine,
+@@ -2197,7 +2459,8 @@ _engine_update_preedit_text_cb (BusEngineProxy  *engine,
+ /**
+  * _engine_update_auxiliary_text_cb:
+  *
+- * A function to be called when "update-auxiliary-text" glib signal is sent to the engine object.
++ * A function to be called when "update-auxiliary-text" glib signal is sent to
++ * the engine object.
+  */
+ static void
+ _engine_update_auxiliary_text_cb (BusEngineProxy   *engine,
+@@ -2217,7 +2480,8 @@ _engine_update_auxiliary_text_cb (BusEngineProxy   *engine,
+ /**
+  * _engine_update_lookup_table_cb:
+  *
+- * A function to be called when "update-lookup-table" glib signal is sent to the engine object.
++ * A function to be called when "update-lookup-table" glib signal is sent to
++ * the engine object.
+  */
+ static void
+ _engine_update_lookup_table_cb (BusEngineProxy   *engine,
+@@ -2237,7 +2501,8 @@ _engine_update_lookup_table_cb (BusEngineProxy   *engine,
+ /**
+  * _engine_register_properties_cb:
+  *
+- * A function to be called when "register-properties" glib signal is sent to the engine object.
++ * A function to be called when "register-properties" glib signal is sent to
++ * the engine object.
+  */
+ static void
+ _engine_register_properties_cb (BusEngineProxy  *engine,
+@@ -2256,7 +2521,8 @@ _engine_register_properties_cb (BusEngineProxy  *engine,
+ /**
+  * _engine_update_property_cb:
+  *
+- * A function to be called when "update-property" glib signal is sent to the engine object.
++ * A function to be called when "update-property" glib signal is sent to the
++ * engine object.
+  */
+ static void
+ _engine_update_property_cb (BusEngineProxy  *engine,
+@@ -2346,10 +2612,11 @@ bus_input_context_new (BusConnection    *connection,
+ 
+     BusInputContext *context = NULL;
+     if (connection) {
+-        context = (BusInputContext *) g_object_new (BUS_TYPE_INPUT_CONTEXT,
+-                                                    "object-path", path,
+-                                                    "connection", bus_connection_get_dbus_connection (connection),
+-                                                    NULL);
++        context = (BusInputContext *) g_object_new (
++                BUS_TYPE_INPUT_CONTEXT,
++                "object-path", path,
++                "connection", bus_connection_get_dbus_connection (connection),
++                NULL);
+     }
+     else {
+         context = (BusInputContext *) g_object_new (BUS_TYPE_INPUT_CONTEXT,
+@@ -2362,6 +2629,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);
+@@ -2439,28 +2707,37 @@ bus_input_context_disable (BusInputContext *context)
+     }
+ }
+ 
+-/* A list of signals (and their handler functions) that could be emit by the engine proxy object. */
++/* A list of signals (and their handler functions) that could be emit by the
++ * engine proxy object.
++ */
+ const static struct {
+     const gchar *name;
+     GCallback    callback;
+ } engine_signals [] = {
+     { "commit-text",              G_CALLBACK (_engine_commit_text_cb) },
+     { "forward-key-event",        G_CALLBACK (_engine_forward_key_event_cb) },
+-    { "delete-surrounding-text",  G_CALLBACK (_engine_delete_surrounding_text_cb) },
+-    { "require-surrounding-text", G_CALLBACK (_engine_require_surrounding_text_cb) },
++    { "delete-surrounding-text",
++                              G_CALLBACK (_engine_delete_surrounding_text_cb) },
++    { "require-surrounding-text",
++                             G_CALLBACK (_engine_require_surrounding_text_cb) },
+     { "update-preedit-text",      G_CALLBACK (_engine_update_preedit_text_cb) },
+     { "show-preedit-text",        G_CALLBACK (_engine_show_preedit_text_cb) },
+     { "hide-preedit-text",        G_CALLBACK (_engine_hide_preedit_text_cb) },
+-    { "update-auxiliary-text",    G_CALLBACK (_engine_update_auxiliary_text_cb) },
++    { "update-auxiliary-text",
++                                G_CALLBACK (_engine_update_auxiliary_text_cb) },
+     { "show-auxiliary-text",      G_CALLBACK (_engine_show_auxiliary_text_cb) },
+     { "hide-auxiliary-text",      G_CALLBACK (_engine_hide_auxiliary_text_cb) },
+     { "update-lookup-table",      G_CALLBACK (_engine_update_lookup_table_cb) },
+     { "show-lookup-table",        G_CALLBACK (_engine_show_lookup_table_cb) },
+     { "hide-lookup-table",        G_CALLBACK (_engine_hide_lookup_table_cb) },
+-    { "page-up-lookup-table",     G_CALLBACK (_engine_page_up_lookup_table_cb) },
+-    { "page-down-lookup-table",   G_CALLBACK (_engine_page_down_lookup_table_cb) },
+-    { "cursor-up-lookup-table",   G_CALLBACK (_engine_cursor_up_lookup_table_cb) },
+-    { "cursor-down-lookup-table", G_CALLBACK (_engine_cursor_down_lookup_table_cb) },
++    { "page-up-lookup-table",
++                                 G_CALLBACK (_engine_page_up_lookup_table_cb) },
++    { "page-down-lookup-table",
++                               G_CALLBACK (_engine_page_down_lookup_table_cb) },
++    { "cursor-up-lookup-table",
++                               G_CALLBACK (_engine_cursor_up_lookup_table_cb) },
++    { "cursor-down-lookup-table",
++                             G_CALLBACK (_engine_cursor_down_lookup_table_cb) },
+     { "register-properties",      G_CALLBACK (_engine_register_properties_cb) },
+     { "update-property",          G_CALLBACK (_engine_update_property_cb) },
+     { "panel-extension",          G_CALLBACK (_engine_panel_extension_cb) },
+@@ -2531,9 +2808,16 @@ bus_input_context_set_engine (BusInputContext *context,
+                     ibus_service_get_object_path ((IBusService *)context);
+             bus_engine_proxy_focus_in (context->engine, path, context->client);
+             bus_engine_proxy_enable (context->engine);
+-            bus_engine_proxy_set_capabilities (context->engine, context->capabilities);
+-            bus_engine_proxy_set_cursor_location (context->engine, context->x, context->y, context->w, context->h);
+-            bus_engine_proxy_set_content_type (context->engine, context->purpose, context->hints);
++            bus_engine_proxy_set_capabilities (context->engine, 
++                                               context->capabilities);
++            bus_engine_proxy_set_cursor_location (context->engine,
++                                                  context->x,
++                                                  context->y,
++                                                  context->w,
++                                                  context->h);
++            bus_engine_proxy_set_content_type (context->engine,
++                                               context->purpose,
++                                               context->hints);
+         }
+     }
+     g_signal_emit (context,
+@@ -2779,7 +3063,8 @@ bus_input_context_set_capabilities (BusInputContext    *context,
+ {
+     g_assert (BUS_IS_INPUT_CONTEXT (context));
+ 
+-    /* If the context does not support IBUS_CAP_FOCUS, then the client application have to handle all information such as
++    /* If the context does not support IBUS_CAP_FOCUS, then the client
++     * application have to handle all information such as
+      * preedit and auxiliary text. */
+     if ((capabilities & IBUS_CAP_FOCUS) == 0) {
+         capabilities |= (IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_AUXILIARY_TEXT | IBUS_CAP_LOOKUP_TABLE | IBUS_CAP_PROPERTY);
+@@ -2828,11 +3113,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);
+ }
+ 
+@@ -2842,12 +3133,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..8ee85149 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,23 @@ typedef struct {
+     gboolean  retval;
+ } ProcessKeyEventReplyData;
+ 
++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 void
+ _process_key_event_done (GObject      *object,
+                          GAsyncResult *res,
+@@ -505,10 +522,7 @@ _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: {
+@@ -519,10 +533,7 @@ _process_key_event (IBusInputContext *context,
+             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);
++            retval = _process_key_event_sync (context, keyval, keycode, state);
+             if (source)
+                 g_source_destroy (source);
+             break;
+@@ -542,17 +553,19 @@ _process_key_event (IBusInputContext *context,
+         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);
+-        }
++        /* #2498 Checking source->ref_count might cause Nautilus hang up
++         */
+         retval = data->retval;
+         g_slice_free (ProcessKeyEventReplyData, data);
+         break;
+     }
+     default: {
+         ProcessKeyEventData *data = g_slice_new0 (ProcessKeyEventData);
++        if (!data) {
++            g_warning ("Cannot allocate async data");
++            retval = _process_key_event_sync (context, keyval, keycode, state);
++            break;
++        }
+ #if GTK_CHECK_VERSION (3, 98, 4)
+         data->event = gdk_event_ref (event);
+ #else
+@@ -877,7 +890,7 @@ 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);
++    _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 +1017,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 ();
+ 
+@@ -2265,6 +2276,7 @@ _create_input_context_done (IBusBus       *bus,
+     else {
+         gboolean requested_surrounding_text = FALSE;
+         ibus_input_context_set_client_commit_preedit (context, TRUE);
++        ibus_input_context_set_post_process_key_event (context, TRUE);
+         ibusimcontext->ibuscontext = context;
+ 
+         g_signal_connect (ibusimcontext->ibuscontext,
+@@ -2489,9 +2501,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 <shawn.p.huang@gmail.com>
+- * Copyright (C) 2018-2022 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2018-2023 Takao Fujiwara <takao.fujiwara1@gmail.com>
+  * 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..1cb5126a 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 <shawn.p.huang@gmail.com>
+- * Copyright (C) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2018-2023 Takao Fujiwara <takao.fujiwara1@gmail.com>
+  * 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,32 @@ 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.
++ */
++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().
++ */
++void         ibus_input_context_post_process_key_event
++                                            (IBusInputContext   *context);
++
+ G_END_DECLS
+ #endif
+-- 
+2.41.0
+
+From 1092bfa619749a931d65f80ec49e93b7b31f60f5 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Fri, 7 Jul 2023 08:52:51 +0900
+Subject: [PATCH 2/2] client/x11: Fix sync
+ ibus_input_context_process_key_event()
+
+Fix the synchronous "ProcessKeyEvent" D-Bus method in ibus-x11 too.
+
+Fixes: https://github.com/ibus/ibus/commit/8a1bd5f
+
+BUG=https://github.com/ibus/ibus/issues/2486
+---
+ client/x11/main.c | 104 ++++++++++++++++++++++++----------------------
+ 1 file changed, 54 insertions(+), 50 deletions(-)
+
+diff --git a/client/x11/main.c b/client/x11/main.c
+index 83d95cb7..13e7a1cf 100644
+--- a/client/x11/main.c
++++ b/client/x11/main.c
+@@ -126,7 +126,7 @@ static gint     g_debug_level = 0;
+ 
+ static IBusBus *_bus = NULL;
+ 
+-static char _use_sync_mode = 2;
++static char _use_sync_mode = 1;
+ 
+ static void
+ _xim_preedit_start (XIMS xims, const X11IC *x11ic)
+@@ -380,9 +380,8 @@ xim_create_ic (XIMS xims, IMChangeICStruct *call_data)
+ 
+     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);
++    ibus_input_context_set_post_process_key_event (x11ic->context, TRUE);
+ 
+     g_hash_table_insert (_x11_ic_table,
+                          GINT_TO_POINTER (x11ic->icid), (gpointer)x11ic);
+@@ -498,6 +497,26 @@ typedef struct {
+     XEvent                event;
+ } ProcessKeyEventReplyData;
+ 
++static gboolean
++_process_key_event_sync (X11IC                *x11ic,
++                         IMForwardEventStruct *call_data,
++                         GdkEventKey          *event)
++{
++    gboolean retval = FALSE;
++
++    g_assert (x11ic);
++    g_assert (call_data);
++    g_assert (event);
++    retval = ibus_input_context_process_key_event (
++            x11ic->context,
++            event->keyval,
++            event->hardware_keycode - 8,
++            event->state);
++    ibus_input_context_post_process_key_event (x11ic->context);
++    _xim_forward_key_event_done (x11ic, &call_data->event, retval);
++    return TRUE;
++}
++
+ static void
+ _process_key_event_done (GObject      *object,
+                          GAsyncResult *res,
+@@ -609,14 +628,7 @@ xim_forward_event (XIMS xims, IMForwardEventStruct *call_data)
+ 
+     switch (_use_sync_mode) {
+     case 1: {
+-        retval = ibus_input_context_process_key_event (
+-                                      x11ic->context,
+-                                      event.keyval,
+-                                      event.hardware_keycode - 8,
+-                                      event.state);
+-        _xim_forward_key_event_done (x11ic, &call_data->event, retval);
+-        retval = 1;
+-        break;
++        return _process_key_event_sync (x11ic, call_data, &event);
+     }
+     case 2: {
+         GSource *source = g_timeout_source_new (1);
+@@ -626,44 +638,37 @@ xim_forward_event (XIMS xims, IMForwardEventStruct *call_data)
+             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);
++            retval = _process_key_event_sync (x11ic, call_data, &event);
+             if (source)
+                 g_source_destroy (source);
+-        } else {
+-            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,
+-                    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 > 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 */
+-                g_info ("Broken GSource.ref_count and maybe a timing "
+-                        "issue in %p.", source);
+-            }
+-            retval = data->retval;
+-            if (data->count == 0) {
+-                g_slice_free (ProcessKeyEventReplyData, data);
+-                return 1;
+-            }
++            break;
++        }
++        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,
++                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 > 0 && data->count < MAX_WAIT_KEY_TIME)
++            g_main_context_iteration (NULL, TRUE);
++        /* #2498 Checking source->ref_count might cause Nautilus hang up
++         */
++        retval = data->retval;
++        if (data->count == 0) {
++            g_slice_free (ProcessKeyEventReplyData, data);
++            return 1;
+         }
+ 
+         g_slice_free (ProcessKeyEventReplyData, data);
+@@ -681,8 +686,7 @@ xim_forward_event (XIMS xims, IMForwardEventStruct *call_data)
+ 
+         if (!(data = g_slice_new0 (ProcessKeyEventReplyData))) {
+             g_warning ("Cannot allocate async data");
+-            _xim_forward_key_event_done (x11ic, &call_data->event, 0);
+-            return 1;
++            return _process_key_event_sync (x11ic, call_data, &event);
+         }
+         data->connect_id = call_data->connect_id;
+         data->x11ic = x11ic;
+@@ -1186,7 +1190,7 @@ _init_ibus (void)
+                         G_CALLBACK (_bus_disconnected_cb), NULL);
+ 
+     /* https://github.com/ibus/ibus/issues/1713 */
+-    _use_sync_mode = _get_char_env ("IBUS_ENABLE_SYNC_MODE", 2);
++    _use_sync_mode = _get_char_env ("IBUS_ENABLE_SYNC_MODE", 1);
+ }
+ 
+ static void
+-- 
+2.41.0
+

diff --git a/ibus.spec b/ibus.spec
index d7a6078..c59529a 100644
--- a/ibus.spec
+++ b/ibus.spec
@@ -50,7 +50,7 @@
 
 Name:           ibus
 Version:        1.5.28
-Release:        10%{?dist}
+Release:        11%{?dist}
 Summary:        Intelligent Input Bus for Linux OS
 License:        LGPL-2.0-or-later
 URL:            https://github.com/ibus/%name/wiki
@@ -557,6 +557,9 @@ dconf update || :
 %{_datadir}/installed-tests/ibus
 
 %changelog
+* Fri Jul 07 2023 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.28-11
+- Fix sync ibus_input_context_process_key_event()
+
 * Wed Jul 05 2023 Python Maint <python-maint@redhat.com> - 1.5.28-10
 - Rebuilt for Python 3.12
 

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-05-31  2:08 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-31  2:08 [rpms/ibus] autotool: Fix sync ibus_input_context_process_key_event() Takao Fujiwara

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox