File ibus-sync-ibus_input_context_process_key_event.patch of Package ibus
From 38f09c657fd5713e39f698aae43a09a07574f1a6 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Wed, 12 Jul 2023 07:50:22 +0900
Subject: [PATCH] src: Fix sync ibus_input_context_process_key_event()
The synchronous "ProcessKeyEvent" D-Bus method cannot receive
"CommitText" and "ForwardKeyEvent" D-Bus signals during calling the method.
To resolve the issue, now
ibus_input_context_set_post_process_key_event() and
ibus_input_context_post_process_key_event() are added newly.
ibus_input_context_post_process_key_event() retries "CommitText" and
"ForwardKeyEvent" D-Bus signals during calling the "ProcessKeyEvent" D-Bus
method and ibus-daemon does not handle those signals.
--- 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;
@@ -45,6 +47,11 @@
};
typedef struct _SetEngineByDescData SetEngineByDescData;
+typedef struct _SyncForwardingData {
+ gchar key;
+ IBusText *text;
+} SyncForwardingData;
+
struct _BusInputContext {
IBusService parent;
@@ -98,6 +105,9 @@
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 +165,15 @@
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation);
+static GVariant *
+ bus_input_context_service_get_property
+ (IBusService *service,
+ GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *property_name,
+ GError **error);
static gboolean bus_input_context_service_set_property
(IBusService *service,
GDBusConnection *connection,
@@ -214,8 +233,21 @@
"<node>"
" <interface name='org.freedesktop.IBus.InputContext'>"
/* properties */
+ " <property name='PostProcessKeyEvent' type='(a(yv))' access='read'>\n"
+ " <annotation name='org.gtk.GDBus.Since'\n"
+ " value='1.5.29' />\n"
+ " <annotation name='org.gtk.GDBus.DocString'\n"
+ " value='Stability: Unstable' />\n"
+ " </property>\n"
" <property name='ContentType' type='(uu)' access='write' />"
" <property name='ClientCommitPreedit' type='(b)' access='write' />\n"
+ " <property name='EffectivePostProcessKeyEvent' type='(b)' \n"
+ " access='write'>\n"
+ " <annotation name='org.gtk.GDBus.Since'\n"
+ " value='1.5.29' />\n"
+ " <annotation name='org.gtk.GDBus.DocString'\n"
+ " value='Stability: Unstable' />\n"
+ " </property>\n"
/* methods */
" <method name='ProcessKeyEvent'>"
" <arg direction='in' type='u' name='keyval' />"
@@ -348,6 +380,8 @@
/* 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'.) */
@@ -762,6 +796,10 @@
error);
}
+typedef struct _PanelProcessKeyEventData {
+ GDBusMethodInvocation *invocation;
+ BusInputContext *context;
+} PanelProcessKeyEventData;
/**
* _panel_process_key_event_cb:
@@ -770,14 +808,21 @@
* 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 +831,7 @@
g_dbus_method_invocation_return_gerror (invocation, error);
g_error_free (error);
}
+ context->processing_key_event = FALSE;
}
typedef struct _ProcessKeyEventData ProcessKeyEventData;
@@ -822,23 +868,28 @@
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);
g_slice_free (ProcessKeyEventData, data);
}
@@ -858,6 +909,8 @@
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 */
@@ -1330,17 +1383,109 @@
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 +1503,30 @@
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 +1539,18 @@
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 +1581,12 @@
" ");
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,
@@ -2122,6 +2283,23 @@
g_assert (context->engine == engine);
+ g_assert (context->queue_during_process_key_event);
+
+ if (context->processing_key_event && g_queue_get_length (
+ context->queue_during_process_key_event) <= MAX_SYNC_DATA) {
+ SyncForwardingData *data;
+ IBusText *text = ibus_text_new_from_printf ("%u,%u,%u",
+ keyval, keycode, state);
+ if (g_queue_get_length (context->queue_during_process_key_event)
+ == MAX_SYNC_DATA) {
+ g_warning ("Exceed max number of post process_key_event data");
+ }
+ data = g_slice_new (SyncForwardingData);
+ data->key = 'f';
+ data->text = text;
+ g_queue_push_tail (context->queue_during_process_key_event, data);
+ return;
+ }
bus_input_context_emit_signal (context,
"ForwardKeyEvent",
g_variant_new ("(uuu)", keyval, keycode, state),
@@ -2362,6 +2540,7 @@
/* 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);
@@ -2828,11 +3007,17 @@
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 +3027,24 @@
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);
--- a/client/gtk2/ibusimcontext.c
+++ b/client/gtk2/ibusimcontext.c
@@ -111,7 +111,7 @@
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;
@@ -473,6 +473,97 @@
}
static gboolean
+_process_key_event_sync (IBusInputContext *context,
+ guint keyval,
+ guint keycode,
+ guint state)
+{
+ gboolean retval;
+
+ g_assert (IBUS_IS_INPUT_CONTEXT (context));
+ retval = ibus_input_context_process_key_event (context,
+ keyval,
+ keycode - 8,
+ state);
+ ibus_input_context_post_process_key_event (context);
+ return retval;
+}
+
+static gboolean
+_process_key_event_async (IBusInputContext *context,
+ guint keyval,
+ guint keycode,
+ guint state,
+ GdkEvent *event,
+ IBusIMContext *ibusimcontext)
+{
+ ProcessKeyEventData *data = g_slice_new0 (ProcessKeyEventData);
+
+ g_assert (event);
+ if (!data) {
+ g_warning ("Cannot allocate async data");
+ return _process_key_event_sync (context, keyval, keycode, state);
+ }
+#if GTK_CHECK_VERSION (3, 98, 4)
+ data->event = gdk_event_ref (event);
+#else
+ data->event = gdk_event_copy (event);
+#endif
+ data->ibusimcontext = ibusimcontext;
+ ibus_input_context_process_key_event_async (context,
+ keyval,
+ keycode - 8,
+ state,
+ -1,
+ NULL,
+ _process_key_event_done,
+ data);
+
+ return TRUE;
+}
+
+static gboolean
+_process_key_event_hybrid_async (IBusInputContext *context,
+ guint keyval,
+ guint keycode,
+ guint state)
+{
+ GSource *source = g_timeout_source_new (1);
+ ProcessKeyEventReplyData *data = NULL;
+ gboolean retval = FALSE;
+
+ if (source)
+ data = g_slice_new0 (ProcessKeyEventReplyData);
+ if (!data) {
+ g_warning ("Cannot wait for the reply of the process key event.");
+ retval = _process_key_event_sync (context, keyval, keycode, state);
+ if (source)
+ g_source_destroy (source);
+ return retval;
+ }
+ data->count = 1;
+ g_source_attach (source, NULL);
+ g_source_unref (source);
+ data->count_cb_id = g_source_get_id (source);
+ ibus_input_context_process_key_event_async (context,
+ keyval,
+ keycode - 8,
+ state,
+ -1,
+ NULL,
+ _process_key_event_reply_done,
+ data);
+ g_source_set_callback (source, _process_key_event_count_cb, data, NULL);
+ while (data->count)
+ g_main_context_iteration (NULL, TRUE);
+ /* #2498 Checking source->ref_count might cause Nautilus hang up
+ */
+ retval = data->retval;
+ g_slice_free (ProcessKeyEventReplyData, data);
+ return retval;
+}
+
+static gboolean
_process_key_event (IBusInputContext *context,
#if GTK_CHECK_VERSION (3, 98, 4)
GdkEvent *event,
@@ -505,70 +596,20 @@
switch (_use_sync_mode) {
case 1: {
- retval = ibus_input_context_process_key_event (context,
- keyval,
- keycode - 8,
- state);
+ retval = _process_key_event_sync (context, keyval, keycode, state);
break;
}
case 2: {
- GSource *source = g_timeout_source_new (1);
- ProcessKeyEventReplyData *data = NULL;
-
- if (source)
- data = g_slice_new0 (ProcessKeyEventReplyData);
- if (!data) {
- g_warning ("Cannot wait for the reply of the process key event.");
- retval = ibus_input_context_process_key_event (context,
- keyval,
- keycode - 8,
- state);
- if (source)
- g_source_destroy (source);
- break;
- }
- data->count = 1;
- g_source_attach (source, NULL);
- g_source_unref (source);
- data->count_cb_id = g_source_get_id (source);
- ibus_input_context_process_key_event_async (context,
- keyval,
- keycode - 8,
- state,
- -1,
- NULL,
- _process_key_event_reply_done,
- data);
- g_source_set_callback (source, _process_key_event_count_cb, data, NULL);
- while (data->count)
- g_main_context_iteration (NULL, TRUE);
- if (source->ref_count > 0) {
- /* g_source_get_id() could causes a SEGV */
- g_info ("Broken GSource.ref_count and maybe a timing issue in %p.",
- source);
- }
- retval = data->retval;
- g_slice_free (ProcessKeyEventReplyData, data);
+ retval = _process_key_event_hybrid_async (context,
+ keyval, keycode, state);
break;
}
default: {
- ProcessKeyEventData *data = g_slice_new0 (ProcessKeyEventData);
-#if GTK_CHECK_VERSION (3, 98, 4)
- data->event = gdk_event_ref (event);
-#else
- data->event = gdk_event_copy ((GdkEvent *)event);
-#endif
- data->ibusimcontext = ibusimcontext;
- ibus_input_context_process_key_event_async (context,
- keyval,
- keycode - 8,
- state,
- -1,
- NULL,
- _process_key_event_done,
- data);
-
- retval = TRUE;
+ retval = _process_key_event_async (context,
+ keyval, keycode, state,
+ (GdkEvent *)event,
+ ibusimcontext);
+ break;
}
}
@@ -877,7 +918,55 @@
g_assert (_signal_retrieve_surrounding_id != 0);
#if GTK_CHECK_VERSION (3, 98, 4)
- _use_sync_mode = _get_char_env ("IBUS_ENABLE_SYNC_MODE", 2);
+ /* IBus GtkIMModule, QtIMModlue, ibus-x11, ibus-wayland are called as
+ * IBus clients.
+ * Each GTK application, each QT application, Xorg server, Wayland
+ * comppsitor are called as IBus event owners here.
+ *
+ * The IBus client processes the key events between the IBus event owner
+ * and the IBus daemon and the procedure step is to:
+ *
+ * receive the key event from the IBus event owner and forward the
+ * event to the IBus daemon with the "ProcessKeyEvent" D-Bus method at
+ * first,
+ *
+ * receive the return value from the IBus daemon with the "ProessKeyEvent"
+ * D-Bus method and forward the value to the IBus event owner secondly and
+ * the return value includes if the key event is processed normally or not.
+ *
+ * The procedure behavior can be changed by the "IBUS_ENABLE_SYNC_MODE"
+ * environment variable with the synchronous procedure or asynchronous
+ * one and value is:
+ *
+ * 1: Synchronous process key event:
+ * Wait for the return of the IBus "ProcessKeyEvent" D-Bus method
+ * synchronously and forward the return value to the IBus event owner
+ * synchronously.
+ * 0: Asynchronous process key event:
+ * Return to the IBus event owner as the key event is processed normally
+ * at first as soon as the IBus client receives the event from the
+ * IBus event owner and also forward the event to the IBus daemon with
+ * the "ProcessKeyEvent" D-Bus method and wait for the return value of
+ * the D-Bus method *asynchronously*.
+ * If the return value indicates the key event is disposed by IBus,
+ * the IBus client does not perform anything. Otherwise the IBus client
+ * forwards the key event with the gdk_event_put() in GTK3,
+ * gtk_im_context_filter_key() in GTK4, IMForwardEvent() in XIM API.
+ * 2: Hybrid asynchronous process key event:
+ * Wait for the return of the IBus "ProcessKeyEvent" D-Bus method
+ * *asynchronously* with a GSource loop and forward the return value
+ * to the IBus event owner synchronously. So IBus clients perform
+ * virtually synchronously to cover problems of IBus synchronous APIs.
+ *
+ * The purpose of the asynchronous process is that each IBus input
+ * method can process the key events without D-Bus timeout and also
+ * the IBus synchronous process has a problem that the IBus
+ * "ProcessKeyEvent" D-Bus method cannot send the commit-text and
+ * forwar-key-event D-Bus signals until the D-Bus method is finished.
+ *
+ * Relative issues: #1713, #2486
+ */
+ _use_sync_mode = _get_char_env ("IBUS_ENABLE_SYNC_MODE", 1);
#else
_use_key_snooper = !_get_boolean_env ("IBUS_DISABLE_SNOOPER",
!(ENABLE_SNOOPER));
@@ -1004,8 +1093,6 @@
#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 +2352,8 @@
else {
gboolean requested_surrounding_text = FALSE;
ibus_input_context_set_client_commit_preedit (context, TRUE);
+ if (_use_sync_mode == 1)
+ ibus_input_context_set_post_process_key_event (context, TRUE);
ibusimcontext->ibuscontext = context;
g_signal_connect (ibusimcontext->ibuscontext,
@@ -2489,9 +2578,8 @@
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 */
--- a/src/ibusinputcontext.c
+++ b/src/ibusinputcontext.c
@@ -1190,14 +1190,14 @@
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 @@
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 @@
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 @@
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) \
--- a/src/ibusinputcontext.h
+++ b/src/ibusinputcontext.h
@@ -519,9 +519,37 @@
*
* See also ibus_engine_update_preedit_text_with_mode().
*/
-void ibus_input_context_set_client_commit_preedit (
- IBusInputContext *context,
+void ibus_input_context_set_client_commit_preedit
+ (IBusInputContext *context,
gboolean client_commit);
+/**
+ * ibus_input_context_set_post_process_key_event:
+ * @context: An #IBusInputContext.
+ * @enable: Can use ibus_input_context_post_process_key_event() to retrieve
+ * commit-text and forwar-key-event signals during
+ * calling ibus_input_context_process_key_event() if it's %TRUE.
+ *
+ * Since: 1.5.00
+ * Stability: Unstable
+ */
+void ibus_input_context_set_post_process_key_event
+ (IBusInputContext *context,
+ gboolean enable);
+/**
+ * ibus_input_context_post_process_key_event:
+ * @context: An #IBusInputContext.
+ *
+ * Call this API after ibus_input_context_process_key_event() returns
+ * to retrieve commit-text and forwar-key-event signals during
+ * calling ibus_input_context_process_key_event().
+ *
+ * See also ibus_input_context_set_post_process_key_event().
+ *
+ * Since: 1.5.00
+ * Stability: Unstable
+ */
+void ibus_input_context_post_process_key_event
+ (IBusInputContext *context);
G_END_DECLS
#endif