Skip to content

Commit

Permalink
Implement ProcessKeyEventObject D-Bus method for X11 Korean
Browse files Browse the repository at this point in the history
Currently ibus-hangul calls ibus_engine_commit_text() during
ibus_engine_process_key_event() but sync API dos not enable such a way:
https://mail.gnome.org/archives/gtk-devel-list/2017-August/msg00045.html

X11 does not set the right order of XEvent with async APIs:
ibus#1713
So we need to use sync API for X11.
Now implement the enhanced ProcessKeyEventObject D-Bus method with
IBusInputContextEvent class and resolves the Korean issue.
  • Loading branch information
fujiwarat committed Nov 20, 2017
1 parent 88b9a93 commit 6e21323
Show file tree
Hide file tree
Showing 9 changed files with 745 additions and 45 deletions.
2 changes: 1 addition & 1 deletion bus/engineproxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,7 @@ bus_engine_proxy_process_key_event (BusEngineProxy *engine,
}

g_dbus_proxy_call ((GDBusProxy *)engine,
"ProcessKeyEvent",
"ProcessKeyEventObject",
g_variant_new ("(uuu)", keyval, keycode, state),
G_DBUS_CALL_FLAGS_NONE,
-1,
Expand Down
20 changes: 18 additions & 2 deletions bus/inputcontext.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,12 @@ static const gchar introspection_xml[] =
" <arg direction='in' type='u' name='state' />"
" <arg direction='out' type='b' name='handled' />"
" </method>"
" <method name='ProcessKeyEventObject'>"
" <arg direction='in' type='u' name='keyval' />"
" <arg direction='in' type='u' name='keycode' />"
" <arg direction='in' type='u' name='state' />"
" <arg direction='out' type='v' name='return' />"
" </method>"
" <method name='SetCursorLocation'>"
" <arg direction='in' type='i' name='x' />"
" <arg direction='in' type='i' name='y' />"
Expand Down Expand Up @@ -773,7 +779,8 @@ _ic_process_key_event_reply_cb (GObject *source,
/**
* _ic_process_key_event:
*
* Implement the "ProcessKeyEvent" method call of the org.freedesktop.IBus.InputContext interface.
* Implement the "ProcessKeyEventObject" method call of the
* org.freedesktop.IBus.InputContext interface.
*/
static void
_ic_process_key_event (BusInputContext *context,
Expand Down Expand Up @@ -834,7 +841,15 @@ _ic_process_key_event (BusInputContext *context,
invocation);
}
else {
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", FALSE));
IBusInputContextEvent *event = ibus_input_context_event_new (
"event-type", IC_EVENT_PROCESS_KEY_EVENT_RETURN,
"retval", FALSE,
NULL);
g_dbus_method_invocation_return_value (
invocation,
g_variant_new ("(v)", ibus_serializable_serialize ((
IBusSerializable *)event)));
g_object_unref (event);
}
}

Expand Down Expand Up @@ -1180,6 +1195,7 @@ bus_input_context_service_method_call (IBusService *service,
const gchar *method_name;
void (* method_callback) (BusInputContext *, GVariant *, GDBusMethodInvocation *);
} methods [] = {
{ "ProcessKeyEventObject", _ic_process_key_event },
{ "ProcessKeyEvent", _ic_process_key_event },
{ "SetCursorLocation", _ic_set_cursor_location },
{ "SetCursorLocationRelative", _ic_set_cursor_location_relative },
Expand Down
45 changes: 40 additions & 5 deletions client/x11/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -454,10 +454,13 @@ _process_key_event_done (GObject *object,
IMForwardEventStruct *pfe = (IMForwardEventStruct*) user_data;

GError *error = NULL;
gboolean retval = ibus_input_context_process_key_event_async_finish (
context,
res,
&error);
gboolean retval = FALSE;
IBusText *committed = NULL;
IBusInputContextEvent *ic_event =
ibus_input_context_process_key_event_object_async_finish (
context,
res,
&error);

if (error != NULL) {
g_warning ("Process Key Event failed: %s.", error->message);
Expand All @@ -468,9 +471,27 @@ _process_key_event_done (GObject *object,
GINT_TO_POINTER ((gint) pfe->connect_id))
== NULL) {
g_slice_free (IMForwardEventStruct, pfe);
g_object_unref (ic_event);
return;
}

if (IBUS_IS_INPUT_CONTEXT_EVENT (ic_event)) {
retval = ibus_input_context_event_get_retval (ic_event);
committed = ibus_input_context_event_get_committed_text (ic_event);
if (IBUS_IS_TEXT (committed))
g_object_ref_sink (committed);
g_object_unref (ic_event);
}
if (IBUS_IS_TEXT (committed)) {
if (*committed->text != '\0') {
X11IC x11ic = { 0, };
x11ic.icid = pfe->icid;
x11ic.connect_id = pfe->connect_id;
_context_commit_text_cb (context, committed, &x11ic);
}
g_object_unref (committed);
}

if (retval == FALSE) {
IMForwardEvent (_xims, (XPointer) pfe);
}
Expand Down Expand Up @@ -505,11 +526,25 @@ xim_forward_event (XIMS xims, IMForwardEventStruct *call_data)
}

if (_use_sync_mode) {
retval = ibus_input_context_process_key_event (
IBusInputContextEvent *ic_event;
IBusText *committed = NULL;
ic_event = ibus_input_context_process_key_event_object (
x11ic->context,
event.keyval,
event.hardware_keycode - 8,
event.state);
if (IBUS_IS_INPUT_CONTEXT_EVENT (ic_event)) {
retval = ibus_input_context_event_get_retval (ic_event);
committed = ibus_input_context_event_get_committed_text (ic_event);
if (IBUS_IS_TEXT (committed))
g_object_ref_sink (committed);
g_object_unref (ic_event);
}
if (IBUS_IS_TEXT (committed)) {
if (*committed->text != '\0')
_context_commit_text_cb (x11ic->context, committed, x11ic);
g_object_unref (committed);
}
if (retval) {
if (! x11ic->has_preedit_area) {
_xim_set_cursor_location (x11ic);
Expand Down
123 changes: 119 additions & 4 deletions src/ibusengine.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
(G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_ENGINE, IBusEnginePrivate))

enum {
PROCESS_KEY_EVENT_OBJECT,
PROCESS_KEY_EVENT,
FOCUS_IN,
FOCUS_OUT,
Expand Down Expand Up @@ -116,6 +117,12 @@ static gboolean ibus_engine_service_set_property
const gchar *property_name,
GVariant *value,
GError **error);
static IBusInputContextEvent *
ibus_engine_process_key_event_object
(IBusEngine *engine,
guint keyval,
guint keycode,
guint state);
static gboolean ibus_engine_process_key_event
(IBusEngine *engine,
guint keyval,
Expand Down Expand Up @@ -183,11 +190,11 @@ static const gchar introspection_xml[] =
"<node>"
" <interface name='org.freedesktop.IBus.Engine'>"
/* FIXME methods */
" <method name='ProcessKeyEvent'>"
" <method name='ProcessKeyEventObject'>"
" <arg direction='in' type='u' name='keyval' />"
" <arg direction='in' type='u' name='keycode' />"
" <arg direction='in' type='u' name='state' />"
" <arg direction='out' type='b' />"
" <arg direction='out' type='v' />"
" </method>"
" <method name='SetCursorLocation'>"
" <arg direction='in' type='i' name='x' />"
Expand Down Expand Up @@ -264,9 +271,33 @@ static const gchar introspection_xml[] =
" </signal>"
/* FIXME properties */
" <property name='ContentType' type='(uu)' access='write' />"
" <method name='ProcessKeyEvent'>"
" <arg direction='in' type='u' name='keyval' />"
" <arg direction='in' type='u' name='keycode' />"
" <arg direction='in' type='u' name='state' />"
" <arg direction='out' type='b' />"
" </method>"
" </interface>"
"</node>";

static gboolean
_ibus_engine_process_key_event_object_accumulator (
GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer dummy)
{
gboolean retval = TRUE;
GObject *object = g_value_get_object (handler_return);

if (object != NULL) {
g_value_copy (handler_return, return_accu);
retval = FALSE;
}

return retval;
}

static void
ibus_engine_class_init (IBusEngineClass *class)
{
Expand All @@ -284,6 +315,7 @@ ibus_engine_class_init (IBusEngineClass *class)

ibus_service_class_add_interfaces (IBUS_SERVICE_CLASS (class), introspection_xml);

class->process_key_event_object = ibus_engine_process_key_event_object;
class->process_key_event = ibus_engine_process_key_event;
class->focus_in = ibus_engine_focus_in;
class->focus_out = ibus_engine_focus_out;
Expand Down Expand Up @@ -323,6 +355,40 @@ ibus_engine_class_init (IBusEngineClass *class)
G_PARAM_STATIC_STRINGS));

/* install signals */
/**
* IBusEngine::process-key-event-object:
* @engine: An IBusEngine.
* @keyval: Key symbol of the key press.
* @keycode: KeyCode of the key press.
* @state: Key modifier flags.
*
* Emitted when a key event is received.
* Implement the member function IBusEngineClass::process_key_event_object
* in extended class to receive this signal.
* Both the key symbol and keycode are passed to the member function.
* See ibus_input_context_process_key_event() for further explanation of
* key symbol, keycode and which to use.
* The @commit will have a string if the engine returned and the string
* will be sent to the application before the returned boolean is sent.
*
* Returns: an #IBusInputContextEvent
* See also: ibus_input_context_process_key_event_object().
*
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[PROCESS_KEY_EVENT_OBJECT] =
g_signal_new (I_("process-key-event-object"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, process_key_event_object),
_ibus_engine_process_key_event_object_accumulator, NULL,
_ibus_marshal_OBJECT__UINT_UINT_UINT,
IBUS_TYPE_INPUT_CONTEXT_EVENT,
3,
G_TYPE_UINT,
G_TYPE_UINT,
G_TYPE_UINT);

/**
* IBusEngine::process-key-event:
* @engine: An IBusEngine.
Expand Down Expand Up @@ -876,18 +942,58 @@ ibus_engine_service_method_call (IBusService *service,
return;
}

if (g_strcmp0 (method_name, "ProcessKeyEvent") == 0) {
if (!g_strcmp0 (method_name, "ProcessKeyEventObject") ||
!g_strcmp0 (method_name, "ProcessKeyEvent")) {
guint keyval, keycode, state;
IBusInputContextEvent *event = NULL;
gboolean retval = FALSE;
g_variant_get (parameters, "(uuu)", &keyval, &keycode, &state);
g_signal_emit (engine,
engine_signals[PROCESS_KEY_EVENT_OBJECT],
0,
keyval,
keycode,
state,
&event);
g_signal_emit (engine,
engine_signals[PROCESS_KEY_EVENT],
0,
keyval,
keycode,
state,
&retval);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", retval));
if (!g_strcmp0 (method_name, "ProcessKeyEventObject")) {
gboolean has_event = TRUE;
if (!event) {
has_event = FALSE;
event = ibus_input_context_event_new (
"event-type", IC_EVENT_PROCESS_KEY_EVENT_RETURN,
"retval", retval,
NULL);
}
/* To register the capsulized "v" to GDBusInterfaceInfo
* instead of "a{bv}", need to run g_variant_new().
* Also the D-Bus method accepts the tuple varint only
* so g_varint_new_varint() cannot be used here.
*/
g_dbus_method_invocation_return_value (
invocation,
g_variant_new ("(v)", ibus_serializable_serialize (
(IBusSerializable *)event)));
if (!has_event)
g_object_unref (event);

} else {
g_warning ("ProcessKeyEvent D-Bus method is now deprecated. " \
"Probably your ibus-daemon is old or you don't use " \
"ibus-daemon.");
if (IBUS_IS_INPUT_CONTEXT_EVENT (event))
retval = ibus_input_context_event_get_retval (event);
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(b)",
retval));
}

return;
}

Expand Down Expand Up @@ -1110,6 +1216,15 @@ ibus_engine_service_set_property (IBusService *service,
g_return_val_if_reached (FALSE);
}

static IBusInputContextEvent *
ibus_engine_process_key_event_object (IBusEngine *engine,
guint keyval,
guint keycode,
guint state)
{
return NULL;
}

static gboolean
ibus_engine_process_key_event (IBusEngine *engine,
guint keyval,
Expand Down
9 changes: 8 additions & 1 deletion src/ibusengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

#include "ibusservice.h"
#include "ibusattribute.h"
#include "ibusinputcontext.h"
#include "ibuslookuptable.h"
#include "ibusproplist.h"

Expand Down Expand Up @@ -153,10 +154,16 @@ struct _IBusEngineClass {
(IBusEngine *engine,
guint purpose,
guint hints);
IBusInputContextEvent *
(* process_key_event_object)
(IBusEngine *engine,
guint keyval,
guint keycode,
guint state);

/*< private >*/
/* padding */
gpointer pdummy[4];
gpointer pdummy[2];
};

GType ibus_engine_get_type (void);
Expand Down
Loading

0 comments on commit 6e21323

Please sign in to comment.