From 25ad70d2458691ff9d1210c04c910cb2a07acd49 Mon Sep 17 00:00:00 2001 From: Takao Fujiwara Date: Jul 07 2023 00:39:39 +0000 Subject: 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 +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[] = + "" + " " + /* properties */ ++ " \n" + " " + " \n" ++ " \n" + /* methods */ + " " + " " +@@ -223,6 +246,12 @@ static const gchar introspection_xml[] = + " " + " " + " " ++ " " ++ " " ++ " " ++ " " ++ " " ++ " " + " " + " " + " " +@@ -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 +- * Copyright (C) 2018-2022 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..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 +- * 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,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 +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 - 1.5.28-11 +- Fix sync ibus_input_context_process_key_event() + * Wed Jul 05 2023 Python Maint - 1.5.28-10 - Rebuilt for Python 3.12