/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* vim:set et sts=4: */ /* ibus - The Input Bus * Copyright (C) 2008-2014 Peng Huang * Copyright (C) 2017-2018 Takao Fujiwara * Copyright (C) 2008-2018 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA */ #include "panelproxy.h" #include "global.h" #include "marshalers.h" #include "types.h" /* panelproxy.c is a very simple proxy class for the panel component that does only the following: * * 1. Handle D-Bus signals from the panel process. For the list of the D-Bus signals, you can check the bus_panel_proxy_g_signal function, or * introspection_xml in src/ibuspanelservice.c. The bus_panel_proxy_g_signal function simply emits a corresponding glib signal for each * D-Bus signal. * 2. Handle glib signals for a BusPanelProxy object (which is usually emitted by bus_panel_proxy_g_signal.) The list of such glib signals is * in the bus_panel_proxy_class_init function. The signal handler function, e.g. bus_panel_proxy_candidate_clicked, simply calls the * corresponding function in inputcontext.c, e.g. bus_input_context_candidate_clicked, using the current focused context. * 3. Provide a way to call D-Bus methods in the panel process. For the list of the D-Bus methods, you can check the header file (panelproxy.h) * or introspection_xml in src/ibuspanelservice.c. Functions that calls g_dbus_proxy_call, e.g. bus_panel_proxy_set_cursor_location, would * fall into this category. * 4. Handle glib signals for a BusInputContext object. The list of such glib signals is in the input_context_signals[] array. The signal handler * function, e.g. _context_set_cursor_location_cb, simply invokes a D-Bus method by calling a function like bus_panel_proxy_set_cursor_location. */ enum { PAGE_UP, PAGE_DOWN, CURSOR_UP, CURSOR_DOWN, CANDIDATE_CLICKED, PROPERTY_ACTIVATE, PROPERTY_SHOW, PROPERTY_HIDE, COMMIT_TEXT, PANEL_EXTENSION, PANEL_EXTENSION_REGISTER_KEYS, UPDATE_PREEDIT_TEXT_RECEIVED, UPDATE_LOOKUP_TABLE_RECEIVED, UPDATE_AUXILIARY_TEXT_RECEIVED, LAST_SIGNAL, }; struct _BusPanelProxy { IBusProxy parent; /* instance members */ BusInputContext *focused_context; PanelType panel_type; }; struct _BusPanelProxyClass { IBusProxyClass parent; /* class members */ void (* page_up) (BusPanelProxy *panel); void (* page_down) (BusPanelProxy *panel); void (* cursor_up) (BusPanelProxy *panel); void (* cursor_down) (BusPanelProxy *panel); void (* candidate_clicked) (BusPanelProxy *panel, guint index, guint button, guint state); void (* property_activate) (BusPanelProxy *panel, const gchar *prop_name, gint prop_state); void (* commit_text) (BusPanelProxy *panel, IBusText *text); }; static guint panel_signals[LAST_SIGNAL] = { 0 }; /* functions prototype */ static void bus_panel_proxy_init (BusPanelProxy *panel); static void bus_panel_proxy_real_destroy (IBusProxy *proxy); static void bus_panel_proxy_g_signal (GDBusProxy *proxy, const gchar *sender_name, const gchar *signal_name, GVariant *parameters); static void bus_panel_proxy_page_up (BusPanelProxy *panel); static void bus_panel_proxy_page_down (BusPanelProxy *panel); static void bus_panel_proxy_cursor_up (BusPanelProxy *panel); static void bus_panel_proxy_cursor_down (BusPanelProxy *panel); static void bus_panel_proxy_candidate_clicked (BusPanelProxy *panel, guint index, guint button, guint state); static void bus_panel_proxy_property_activate (BusPanelProxy *panel, const gchar *prop_name, gint prop_state); static void bus_panel_proxy_commit_text (BusPanelProxy *panel, IBusText *text); G_DEFINE_TYPE(BusPanelProxy, bus_panel_proxy, IBUS_TYPE_PROXY) BusPanelProxy * bus_panel_proxy_new (BusConnection *connection, PanelType panel_type) { const gchar *path = NULL; GObject *obj; BusPanelProxy *panel; g_assert (BUS_IS_CONNECTION (connection)); switch (panel_type) { case PANEL_TYPE_PANEL: path = IBUS_PATH_PANEL; break; case PANEL_TYPE_EXTENSION_EMOJI: path = IBUS_PATH_PANEL_EXTENSION_EMOJI; break; default: g_return_val_if_reached (NULL); } obj = g_initable_new (BUS_TYPE_PANEL_PROXY, NULL, NULL, "g-object-path", path, "g-interface-name", IBUS_INTERFACE_PANEL, "g-connection", bus_connection_get_dbus_connection (connection), "g-default-timeout", g_gdbus_timeout, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL); panel = BUS_PANEL_PROXY (obj); panel->panel_type = panel_type; return panel; } static void bus_panel_proxy_class_init (BusPanelProxyClass *class) { IBUS_PROXY_CLASS (class)->destroy = bus_panel_proxy_real_destroy; G_DBUS_PROXY_CLASS (class)->g_signal = bus_panel_proxy_g_signal; class->page_up = bus_panel_proxy_page_up; class->page_down = bus_panel_proxy_page_down; class->cursor_up = bus_panel_proxy_cursor_up; class->cursor_down = bus_panel_proxy_cursor_down; class->candidate_clicked = bus_panel_proxy_candidate_clicked; class->property_activate = bus_panel_proxy_property_activate; class->commit_text = bus_panel_proxy_commit_text; /* install signals */ panel_signals[PAGE_UP] = g_signal_new (I_("page-up"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(BusPanelProxyClass, page_up), NULL, NULL, bus_marshal_VOID__VOID, G_TYPE_NONE, 0); panel_signals[PAGE_DOWN] = g_signal_new (I_("page-down"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(BusPanelProxyClass, page_down), NULL, NULL, bus_marshal_VOID__VOID, G_TYPE_NONE, 0); panel_signals[CURSOR_UP] = g_signal_new (I_("cursor-up"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(BusPanelProxyClass, cursor_up), NULL, NULL, bus_marshal_VOID__VOID, G_TYPE_NONE, 0); panel_signals[CURSOR_DOWN] = g_signal_new (I_("cursor-down"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(BusPanelProxyClass, cursor_down), NULL, NULL, bus_marshal_VOID__VOID, G_TYPE_NONE, 0); panel_signals[CANDIDATE_CLICKED] = g_signal_new (I_("candidate-clicked"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(BusPanelProxyClass, candidate_clicked), NULL, NULL, bus_marshal_VOID__UINT_UINT_UINT, G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); panel_signals[PROPERTY_ACTIVATE] = g_signal_new (I_("property-activate"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(BusPanelProxyClass, property_activate), NULL, NULL, bus_marshal_VOID__STRING_INT, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT); panel_signals[PROPERTY_SHOW] = g_signal_new (I_("property-show"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, bus_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); panel_signals[PROPERTY_HIDE] = g_signal_new (I_("property-hide"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, bus_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); panel_signals[COMMIT_TEXT] = g_signal_new (I_("commit-text"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(BusPanelProxyClass, commit_text), NULL, NULL, bus_marshal_VOID__OBJECT, G_TYPE_NONE, 1, IBUS_TYPE_TEXT); panel_signals[PANEL_EXTENSION] = g_signal_new (I_("panel-extension"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, bus_marshal_VOID__OBJECT, G_TYPE_NONE, 1, IBUS_TYPE_EXTENSION_EVENT); panel_signals[PANEL_EXTENSION_REGISTER_KEYS] = g_signal_new (I_("panel-extension-register-keys"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, bus_marshal_VOID__VARIANT, G_TYPE_NONE, 1, G_TYPE_VARIANT); panel_signals[UPDATE_PREEDIT_TEXT_RECEIVED] = g_signal_new (I_("update-preedit-text-received"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, bus_marshal_VOID__OBJECT_UINT_BOOLEAN, G_TYPE_NONE, 3, IBUS_TYPE_TEXT, G_TYPE_UINT, G_TYPE_BOOLEAN); panel_signals[UPDATE_LOOKUP_TABLE_RECEIVED] = g_signal_new (I_("update-lookup-table-received"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, bus_marshal_VOID__OBJECT_BOOLEAN, G_TYPE_NONE, 2, IBUS_TYPE_LOOKUP_TABLE, G_TYPE_BOOLEAN); panel_signals[UPDATE_AUXILIARY_TEXT_RECEIVED] = g_signal_new (I_("update-auxiliary-text-received"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, bus_marshal_VOID__OBJECT_BOOLEAN, G_TYPE_NONE, 2, IBUS_TYPE_TEXT, G_TYPE_BOOLEAN); } static void _g_object_unref_if_floating (gpointer instance) { if (g_object_is_floating (instance)) g_object_unref (instance); } static void bus_panel_proxy_init (BusPanelProxy *panel) { /* member variables will automatically be zero-cleared. */ } static void bus_panel_proxy_real_destroy (IBusProxy *proxy) { BusPanelProxy *panel = (BusPanelProxy *)proxy; if (panel->focused_context) { bus_panel_proxy_focus_out (panel, panel->focused_context); panel->focused_context = NULL; } IBUS_PROXY_CLASS(bus_panel_proxy_parent_class)-> destroy ((IBusProxy *)panel); } /** * bus_panel_proxy_g_signal: * * Handle all D-Bus signals from the panel process. This function emits a corresponding glib signal for each D-Bus signal. */ static void bus_panel_proxy_g_signal (GDBusProxy *proxy, const gchar *sender_name, const gchar *signal_name, GVariant *parameters) { BusPanelProxy *panel = (BusPanelProxy *)proxy; /* The list of nullary D-Bus signals. */ static const struct { const gchar *signal_name; const guint signal_id; } signals [] = { { "PageUp", PAGE_UP }, { "PageDown", PAGE_DOWN }, { "CursorUp", CURSOR_UP }, { "CursorDown", CURSOR_DOWN }, }; gint i; for (i = 0; i < G_N_ELEMENTS (signals); i++) { if (g_strcmp0 (signal_name, signals[i].signal_name) == 0) { g_signal_emit (panel, panel_signals[signals[i].signal_id], 0); return; } } /* Handle D-Bus signals with parameters. Deserialize them and emit a glib signal. */ if (g_strcmp0 ("CandidateClicked", signal_name) == 0) { guint index = 0; guint button = 0; guint state = 0; g_variant_get (parameters, "(uuu)", &index, &button, &state); g_signal_emit (panel, panel_signals[CANDIDATE_CLICKED], 0, index, button, state); return; } if (g_strcmp0 ("PropertyActivate", signal_name) == 0) { gchar *prop_name = NULL; gint prop_state = 0; g_variant_get (parameters, "(&su)", &prop_name, &prop_state); g_signal_emit (panel, panel_signals[PROPERTY_ACTIVATE], 0, prop_name, prop_state); return; } if (g_strcmp0 ("PropertyShow", signal_name) == 0) { gchar *prop_name = NULL; g_variant_get (parameters, "(&s)", &prop_name); g_signal_emit (panel, panel_signals[PROPERTY_SHOW], 0, prop_name); return; } if (g_strcmp0 ("PropertyHide", signal_name) == 0) { gchar *prop_name = NULL; g_variant_get (parameters, "(&s)", &prop_name); g_signal_emit (panel, panel_signals[PROPERTY_HIDE], 0, prop_name); return; } if (g_strcmp0 ("CommitText", signal_name) == 0) { GVariant *arg0 = NULL; g_variant_get (parameters, "(v)", &arg0); g_return_if_fail (arg0); IBusText *text = IBUS_TEXT (ibus_serializable_deserialize (arg0)); g_variant_unref (arg0); g_return_if_fail (text); g_signal_emit (panel, panel_signals[COMMIT_TEXT], 0, text); _g_object_unref_if_floating (text); return; } if (g_strcmp0 ("PanelExtension", signal_name) == 0) { GVariant *arg0 = NULL; g_variant_get (parameters, "(v)", &arg0); g_return_if_fail (arg0); IBusExtensionEvent *event = IBUS_EXTENSION_EVENT ( ibus_serializable_deserialize (arg0)); g_variant_unref (arg0); g_return_if_fail (event); g_signal_emit (panel, panel_signals[PANEL_EXTENSION], 0, event); _g_object_unref_if_floating (event); return; } if (g_strcmp0 ("PanelExtensionRegisterKeys", signal_name) == 0) { g_signal_emit (panel, panel_signals[PANEL_EXTENSION_REGISTER_KEYS], 0, parameters); return; } if (g_strcmp0 ("UpdatePreeditTextReceived", signal_name) == 0) { GVariant *variant = NULL; guint cursor_pos = 0; gboolean visible = FALSE; IBusText *text = NULL; g_variant_get (parameters, "(vub)", &variant, &cursor_pos, &visible); g_return_if_fail (variant); text = (IBusText *) ibus_serializable_deserialize (variant); g_variant_unref (variant); g_return_if_fail (text); g_signal_emit (panel, panel_signals[UPDATE_PREEDIT_TEXT_RECEIVED], 0, text, cursor_pos, visible); _g_object_unref_if_floating (text); return; } if (g_strcmp0 ("UpdateLookupTableReceived", signal_name) == 0) { GVariant *variant = NULL; gboolean visible = FALSE; IBusLookupTable *table = NULL; g_variant_get (parameters, "(vb)", &variant, &visible); g_return_if_fail (variant); table = (IBusLookupTable *) ibus_serializable_deserialize (variant); g_variant_unref (variant); g_return_if_fail (table); g_signal_emit (panel, panel_signals[UPDATE_LOOKUP_TABLE_RECEIVED], 0, table, visible); _g_object_unref_if_floating (table); return; } if (g_strcmp0 ("UpdateAuxiliaryTextReceived", signal_name) == 0) { GVariant *variant = NULL; gboolean visible = FALSE; IBusText *text = NULL; g_variant_get (parameters, "(vb)", &variant, &visible); g_return_if_fail (variant); text = (IBusText *) ibus_serializable_deserialize (variant); g_variant_unref (variant); g_return_if_fail (text); g_signal_emit (panel, panel_signals[UPDATE_AUXILIARY_TEXT_RECEIVED], 0, text, visible); _g_object_unref_if_floating (text); return; } /* shound not be reached */ g_return_if_reached (); } void bus_panel_proxy_set_cursor_location (BusPanelProxy *panel, gint x, gint y, gint w, gint h) { g_assert (BUS_IS_PANEL_PROXY (panel)); g_dbus_proxy_call ((GDBusProxy *)panel, "SetCursorLocation", g_variant_new ("(iiii)", x, y, w, h), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } void bus_panel_proxy_set_cursor_location_relative (BusPanelProxy *panel, gint x, gint y, gint w, gint h) { g_assert (BUS_IS_PANEL_PROXY (panel)); g_dbus_proxy_call ((GDBusProxy *)panel, "SetCursorLocationRelative", g_variant_new ("(iiii)", x, y, w, h), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } void bus_panel_proxy_update_preedit_text (BusPanelProxy *panel, IBusText *text, guint cursor_pos, gboolean visible) { g_assert (BUS_IS_PANEL_PROXY (panel)); g_assert (IBUS_IS_TEXT (text)); GVariant *variant = ibus_serializable_serialize ((IBusSerializable* )text); g_dbus_proxy_call ((GDBusProxy *)panel, "UpdatePreeditText", g_variant_new ("(vub)", variant, cursor_pos, visible), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } void bus_panel_proxy_update_auxiliary_text (BusPanelProxy *panel, IBusText *text, gboolean visible) { g_assert (BUS_IS_PANEL_PROXY (panel)); g_assert (IBUS_IS_TEXT (text)); GVariant *variant = ibus_serializable_serialize ((IBusSerializable* )text); g_dbus_proxy_call ((GDBusProxy *)panel, "UpdateAuxiliaryText", g_variant_new ("(vb)", variant, visible), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } void bus_panel_proxy_update_lookup_table (BusPanelProxy *panel, IBusLookupTable *table, gboolean visible) { g_assert (BUS_IS_PANEL_PROXY (panel)); g_assert (IBUS_IS_LOOKUP_TABLE (table)); GVariant *variant = ibus_serializable_serialize ((IBusSerializable* )table); g_dbus_proxy_call ((GDBusProxy *)panel, "UpdateLookupTable", g_variant_new ("(vb)", variant, visible), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } void bus_panel_proxy_register_properties (BusPanelProxy *panel, IBusPropList *prop_list) { g_assert (BUS_IS_PANEL_PROXY (panel)); g_assert (IBUS_IS_PROP_LIST (prop_list)); GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)prop_list); g_dbus_proxy_call ((GDBusProxy *)panel, "RegisterProperties", g_variant_new ("(v)", variant), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } void bus_panel_proxy_update_property (BusPanelProxy *panel, IBusProperty *prop) { g_assert (BUS_IS_PANEL_PROXY (panel)); g_assert (IBUS_IS_PROPERTY (prop)); GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)prop); g_dbus_proxy_call ((GDBusProxy *)panel, "UpdateProperty", g_variant_new ("(v)", variant), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } void bus_panel_proxy_set_content_type (BusPanelProxy *panel, guint purpose, guint hints) { g_assert (BUS_IS_PANEL_PROXY (panel)); g_dbus_proxy_call ((GDBusProxy *)panel, "ContentType", g_variant_new ("(uu)", purpose, hints), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } #define DEFINE_FUNC(name) \ static void \ bus_panel_proxy_##name (BusPanelProxy *panel) \ { \ g_assert (BUS_IS_PANEL_PROXY (panel)); \ \ if (panel->focused_context) { \ bus_input_context_##name (panel->focused_context); \ } \ } DEFINE_FUNC(page_up) DEFINE_FUNC(page_down) DEFINE_FUNC(cursor_up) DEFINE_FUNC(cursor_down) #undef DEFINE_FUNC static void bus_panel_proxy_candidate_clicked (BusPanelProxy *panel, guint index, guint button, guint state) { g_assert (BUS_IS_PANEL_PROXY (panel)); if (panel->focused_context) { bus_input_context_candidate_clicked (panel->focused_context, index, button, state); } } static void bus_panel_proxy_property_activate (BusPanelProxy *panel, const gchar *prop_name, gint prop_state) { g_assert (BUS_IS_PANEL_PROXY (panel)); if (panel->focused_context) { bus_input_context_property_activate (panel->focused_context, prop_name, prop_state); } } static void bus_panel_proxy_commit_text (BusPanelProxy *panel, IBusText *text) { gboolean use_extension = TRUE; g_assert (BUS_IS_PANEL_PROXY (panel)); g_assert (text != NULL); if (!panel->focused_context) return; if (panel->panel_type != PANEL_TYPE_PANEL) use_extension = FALSE; bus_input_context_commit_text_use_extension (panel->focused_context, text, use_extension); } #define DEFINE_FUNCTION(Name, name) \ void bus_panel_proxy_##name (BusPanelProxy *panel) \ { \ g_assert (BUS_IS_PANEL_PROXY (panel)); \ g_dbus_proxy_call ((GDBusProxy *) panel, \ #Name, \ NULL, \ G_DBUS_CALL_FLAGS_NONE, \ -1, NULL, NULL, NULL); \ } DEFINE_FUNCTION (ShowPreeditText, show_preedit_text) DEFINE_FUNCTION (HidePreeditText, hide_preedit_text) DEFINE_FUNCTION (ShowAuxiliaryText, show_auxiliary_text) DEFINE_FUNCTION (HideAuxiliaryText, hide_auxiliary_text) DEFINE_FUNCTION (ShowLookupTable, show_lookup_table) DEFINE_FUNCTION (HideLookupTable, hide_lookup_table) DEFINE_FUNCTION (PageUpLookupTable, page_up_lookup_table) DEFINE_FUNCTION (PageDownLookupTable, page_down_lookup_table) DEFINE_FUNCTION (CursorUpLookupTable, cursor_up_lookup_table) DEFINE_FUNCTION (CursorDownLookupTable, cursor_down_lookup_table) DEFINE_FUNCTION (StateChanged, state_changed) #undef DEFINE_FUNCTION static void _context_set_cursor_location_cb (BusInputContext *context, gint x, gint y, gint w, gint h, BusPanelProxy *panel) { g_assert (BUS_IS_INPUT_CONTEXT (context)); g_assert (BUS_IS_PANEL_PROXY (panel)); g_return_if_fail (panel->focused_context == context); bus_panel_proxy_set_cursor_location (panel, x, y, w, h); } static void _context_set_cursor_location_relative_cb (BusInputContext *context, gint x, gint y, gint w, gint h, BusPanelProxy *panel) { g_assert (BUS_IS_INPUT_CONTEXT (context)); g_assert (BUS_IS_PANEL_PROXY (panel)); g_return_if_fail (panel->focused_context == context); bus_panel_proxy_set_cursor_location_relative (panel, x, y, w, h); } static void _context_update_preedit_text_cb (BusInputContext *context, IBusText *text, guint cursor_pos, gboolean visible, BusPanelProxy *panel) { g_assert (BUS_IS_INPUT_CONTEXT (context)); g_assert (text != NULL); g_assert (BUS_IS_PANEL_PROXY (panel)); g_return_if_fail (panel->focused_context == context); /* The callback is called with X11 applications but * the callback is not called for extensions and panel * extensions are always calls by * bus_panel_proxy_update_preedit_text() directly * because panel extensions foward UpdatePreeditText to * UpdatePreeditTextReceived and it can be an infinite * loop. */ if (panel->panel_type != PANEL_TYPE_PANEL) return; bus_panel_proxy_update_preedit_text (panel, text, cursor_pos, visible); } static void _context_update_auxiliary_text_cb (BusInputContext *context, IBusText *text, gboolean visible, BusPanelProxy *panel) { g_assert (BUS_IS_INPUT_CONTEXT (context)); g_assert (BUS_IS_PANEL_PROXY (panel)); g_return_if_fail (panel->focused_context == context); bus_panel_proxy_update_auxiliary_text (panel, text, visible); } static void _context_update_lookup_table_cb (BusInputContext *context, IBusLookupTable *table, gboolean visible, BusPanelProxy *panel) { g_assert (BUS_IS_INPUT_CONTEXT (context)); g_assert (BUS_IS_PANEL_PROXY (panel)); g_return_if_fail (panel->focused_context == context); bus_panel_proxy_update_lookup_table (panel, table, visible); } static void _context_register_properties_cb (BusInputContext *context, IBusPropList *prop_list, BusPanelProxy *panel) { g_assert (BUS_IS_INPUT_CONTEXT (context)); g_assert (BUS_IS_PANEL_PROXY (panel)); g_return_if_fail (panel->focused_context == context); bus_panel_proxy_register_properties (panel, prop_list); } static void _context_update_property_cb (BusInputContext *context, IBusProperty *prop, BusPanelProxy *panel) { g_assert (BUS_IS_INPUT_CONTEXT (context)); g_assert (BUS_IS_PANEL_PROXY (panel)); g_return_if_fail (panel->focused_context == context); bus_panel_proxy_update_property (panel, prop); } static void _context_destroy_cb (BusInputContext *context, BusPanelProxy *panel) { g_assert (BUS_IS_INPUT_CONTEXT (context)); g_assert (BUS_IS_PANEL_PROXY (panel)); g_assert (context == panel->focused_context); bus_panel_proxy_focus_out (panel, context); } static void _context_set_content_type_cb (BusInputContext *context, guint purpose, guint hints, BusPanelProxy *panel) { g_assert (BUS_IS_INPUT_CONTEXT (context)); g_assert (BUS_IS_PANEL_PROXY (panel)); g_return_if_fail (panel->focused_context == context); bus_panel_proxy_set_content_type (panel, purpose, hints); } #define DEFINE_FUNCTION(name) \ static void _context_##name##_cb (BusInputContext *context, \ BusPanelProxy *panel) \ { \ g_assert (BUS_IS_INPUT_CONTEXT (context)); \ g_assert (BUS_IS_PANEL_PROXY (panel)); \ \ g_return_if_fail (panel->focused_context == context); \ \ bus_panel_proxy_##name (panel); \ } #define DEFINE_FUNCTION_NO_EXTENSION(name) \ static void _context_##name##_cb (BusInputContext *context, \ BusPanelProxy *panel) \ { \ g_assert (BUS_IS_INPUT_CONTEXT (context)); \ g_assert (BUS_IS_PANEL_PROXY (panel)); \ \ g_return_if_fail (panel->focused_context == context); \ \ /* The callback is called with X11 applications but \ * the callback is not called for extensions and panel \ * extensions are always calls by \ * bus_panel_proxy_update_preedit_text() directly \ * because panel extensions foward UpdatePreeditText to \ * UpdatePreeditTextReceived and it can be an infinite \ * loop. \ */ \ if (panel->panel_type != PANEL_TYPE_PANEL) \ return; \ bus_panel_proxy_##name (panel); \ } DEFINE_FUNCTION_NO_EXTENSION (show_preedit_text) DEFINE_FUNCTION_NO_EXTENSION (hide_preedit_text) DEFINE_FUNCTION (show_auxiliary_text) DEFINE_FUNCTION (hide_auxiliary_text) DEFINE_FUNCTION (show_lookup_table) DEFINE_FUNCTION (hide_lookup_table) DEFINE_FUNCTION (page_up_lookup_table) DEFINE_FUNCTION (page_down_lookup_table) DEFINE_FUNCTION (cursor_up_lookup_table) DEFINE_FUNCTION (cursor_down_lookup_table) DEFINE_FUNCTION (state_changed) #undef DEFINE_FUNCTION #undef DEFINE_FUNCTION_NO_EXTENSION static const struct { gchar *name; GCallback callback; } input_context_signals[] = { { "set-cursor-location", G_CALLBACK (_context_set_cursor_location_cb) }, { "set-cursor-location-relative", G_CALLBACK (_context_set_cursor_location_relative_cb) }, { "update-preedit-text", G_CALLBACK (_context_update_preedit_text_cb) }, { "show-preedit-text", G_CALLBACK (_context_show_preedit_text_cb) }, { "hide-preedit-text", G_CALLBACK (_context_hide_preedit_text_cb) }, { "update-auxiliary-text", G_CALLBACK (_context_update_auxiliary_text_cb) }, { "show-auxiliary-text", G_CALLBACK (_context_show_auxiliary_text_cb) }, { "hide-auxiliary-text", G_CALLBACK (_context_hide_auxiliary_text_cb) }, { "update-lookup-table", G_CALLBACK (_context_update_lookup_table_cb) }, { "show-lookup-table", G_CALLBACK (_context_show_lookup_table_cb) }, { "hide-lookup-table", G_CALLBACK (_context_hide_lookup_table_cb) }, { "page-up-lookup-table", G_CALLBACK (_context_page_up_lookup_table_cb) }, { "page-down-lookup-table", G_CALLBACK (_context_page_down_lookup_table_cb) }, { "cursor-up-lookup-table", G_CALLBACK (_context_cursor_up_lookup_table_cb) }, { "cursor-down-lookup-table", G_CALLBACK (_context_cursor_down_lookup_table_cb) }, { "register-properties", G_CALLBACK (_context_register_properties_cb) }, { "update-property", G_CALLBACK (_context_update_property_cb) }, { "engine-changed", G_CALLBACK (_context_state_changed_cb) }, { "destroy", G_CALLBACK (_context_destroy_cb) }, { "set-content-type", G_CALLBACK (_context_set_content_type_cb) }, }; void bus_panel_proxy_focus_in (BusPanelProxy *panel, BusInputContext *context) { const gchar *path; guint purpose, hints; gint i; g_assert (BUS_IS_PANEL_PROXY (panel)); g_assert (BUS_IS_INPUT_CONTEXT (context)); if (panel->focused_context == context) return; if (panel->focused_context != NULL) bus_panel_proxy_focus_out (panel, panel->focused_context); g_object_ref_sink (context); panel->focused_context = context; path = ibus_service_get_object_path ((IBusService *)context); g_dbus_proxy_call ((GDBusProxy *)panel, "FocusIn", g_variant_new ("(o)", path), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); /* install signal handlers */ for (i = 0; i < G_N_ELEMENTS (input_context_signals); i++) { g_signal_connect (context, input_context_signals[i].name, input_context_signals[i].callback, panel); } bus_input_context_get_content_type (context, &purpose, &hints); bus_panel_proxy_set_content_type (panel, purpose, hints); } void bus_panel_proxy_focus_out (BusPanelProxy *panel, BusInputContext *context) { g_assert (BUS_IS_PANEL_PROXY (panel)); g_assert (BUS_IS_INPUT_CONTEXT (context)); g_assert (panel->focused_context == context); /* uninstall signal handlers */ gint i; for (i = 0; i < G_N_ELEMENTS (input_context_signals); i++) { g_signal_handlers_disconnect_by_func (context, input_context_signals[i].callback, panel); } const gchar *path = ibus_service_get_object_path ((IBusService *)context); g_dbus_proxy_call ((GDBusProxy *)panel, "FocusOut", g_variant_new ("(o)", path), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); g_object_unref (panel->focused_context); panel->focused_context = NULL; } void bus_panel_proxy_destroy_context (BusPanelProxy *panel, BusInputContext *context) { const gchar *path; g_assert (BUS_IS_PANEL_PROXY (panel)); g_assert (BUS_IS_INPUT_CONTEXT (context)); g_object_ref_sink (context); path = ibus_service_get_object_path ((IBusService *)context); g_dbus_proxy_call ((GDBusProxy *)panel, "DestroyContext", g_variant_new ("(o)", path), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); g_object_unref (context); } PanelType bus_panel_proxy_get_panel_type (BusPanelProxy *panel) { g_assert (BUS_IS_PANEL_PROXY (panel)); return panel->panel_type; } void bus_panel_proxy_panel_extension_received (BusPanelProxy *panel, IBusExtensionEvent *event) { GVariant *data; g_assert (BUS_IS_PANEL_PROXY (panel)); g_assert (event); data = ibus_serializable_serialize (IBUS_SERIALIZABLE (event)); g_return_if_fail (data); g_dbus_proxy_call ((GDBusProxy *)panel, "PanelExtensionReceived", g_variant_new ("(v)", data), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } void bus_panel_proxy_process_key_event (BusPanelProxy *panel, guint keyval, guint keycode, guint state, GAsyncReadyCallback callback, gpointer user_data) { g_assert (BUS_IS_PANEL_PROXY (panel)); g_dbus_proxy_call ((GDBusProxy *)panel, "ProcessKeyEvent", g_variant_new ("(uuu)", keyval, keycode, state), G_DBUS_CALL_FLAGS_NONE, -1, NULL, callback, user_data); } void bus_panel_proxy_commit_text_received (BusPanelProxy *panel, IBusText *text) { GVariant *variant; g_assert (BUS_IS_PANEL_PROXY (panel)); g_assert (IBUS_IS_TEXT (text)); variant = ibus_serializable_serialize (IBUS_SERIALIZABLE (text)); g_dbus_proxy_call ((GDBusProxy *)panel, "CommitTextReceived", g_variant_new ("(v)", variant), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } void bus_panel_proxy_candidate_clicked_lookup_table (BusPanelProxy *panel, guint index, guint button, guint state) { g_assert (BUS_IS_PANEL_PROXY (panel)); g_dbus_proxy_call ((GDBusProxy *)panel, "CandidateClickedLookupTable", g_variant_new ("(uuu)", index, button, state), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); }