/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* vim:set et sts=4: */ /* ibus - The Input Bus * Copyright (C) 2008-2013 Peng Huang * Copyright (C) 2011-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 "ibusimpl.h" #include #include #include #include #include #include #include "connection.h" #include "dbusimpl.h" #include "factoryproxy.h" #include "global.h" #include "inputcontext.h" #include "panelproxy.h" #include "server.h" #include "types.h" struct _BusIBusImpl { IBusService parent; /* instance members */ GHashTable *factory_dict; /* registered components */ GList *registered_components; GList *contexts; /* a fake input context for global engine support */ BusInputContext *fake_context; /* a list of engines that are started by a user (without the --ibus * command line flag.) */ GList *register_engine_list; /* if TRUE, ibus-daemon uses a keysym translated by the system * (i.e. XKB) as-is. otherwise, ibus-daemon itself converts keycode * into keysym. */ gboolean use_sys_layout; gboolean embed_preedit_text; IBusRegistry *registry; /* a list of BusComponent objects that are created from component XML * files (or from the cache of them). */ GList *components; /* a mapping from an engine name (e.g. 'pinyin') to the corresponding * IBusEngineDesc object. */ GHashTable *engine_table; BusInputContext *focused_context; BusPanelProxy *panel; BusPanelProxy *emoji_extension; gboolean enable_emoji_extension; /* a default keymap of ibus-daemon (usually "us") which is used only * when use_sys_layout is FALSE. */ IBusKeymap *keymap; gboolean use_global_engine; gchar *global_engine_name; gchar *global_previous_engine_name; GVariant *extension_register_keys; }; struct _BusIBusImplClass { IBusServiceClass parent; /* class members */ }; enum { LAST_SIGNAL, }; enum { PROP_0, }; /* static guint _signals[LAST_SIGNAL] = { 0 }; */ /* functions prototype */ static void bus_ibus_impl_destroy (BusIBusImpl *ibus); static void bus_ibus_impl_service_method_call (IBusService *service, GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation); static GVariant * bus_ibus_impl_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_ibus_impl_service_set_property (IBusService *service, GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GVariant *value, GError **error); static void bus_ibus_impl_registry_init (BusIBusImpl *ibus); static void bus_ibus_impl_registry_changed (BusIBusImpl *ibus); static void bus_ibus_impl_registry_destroy (BusIBusImpl *ibus); static void bus_ibus_impl_component_name_owner_changed (BusIBusImpl *ibus, const gchar *name, const gchar *old_name, const gchar *new_name); static void bus_ibus_impl_global_engine_changed (BusIBusImpl *ibus); static void bus_ibus_impl_set_context_engine_from_desc (BusIBusImpl *ibus, BusInputContext *context, IBusEngineDesc *desc); static BusInputContext *bus_ibus_impl_create_input_context (BusIBusImpl *ibus, BusConnection *connection, const gchar *client); static IBusEngineDesc *bus_ibus_impl_get_engine_desc (BusIBusImpl *ibus, const gchar *engine_name); static void bus_ibus_impl_set_focused_context (BusIBusImpl *ibus, BusInputContext *context); static void bus_ibus_impl_property_changed (BusIBusImpl *service, const gchar *property_name, GVariant *value); /* some callback functions */ static void _context_engine_changed_cb (BusInputContext *context, BusIBusImpl *ibus); /* The interfaces available in this class, which consists of a list of * methods this class implements and a list of signals this class may emit. * Method calls to the interface that are not defined in this XML will * be automatically rejected by the GDBus library (see src/ibusservice.c * for details.) * Implement org.freedesktop.DBus.Properties * http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties */ static const gchar introspection_xml[] = "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n"; G_DEFINE_TYPE (BusIBusImpl, bus_ibus_impl, IBUS_TYPE_SERVICE) static void bus_ibus_impl_class_init (BusIBusImplClass *class) { IBUS_OBJECT_CLASS (class)->destroy = (IBusObjectDestroyFunc) bus_ibus_impl_destroy; /* override the parent class's implementation. */ IBUS_SERVICE_CLASS (class)->service_method_call = bus_ibus_impl_service_method_call; IBUS_SERVICE_CLASS (class)->service_get_property = bus_ibus_impl_service_get_property; IBUS_SERVICE_CLASS (class)->service_set_property = bus_ibus_impl_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. 'GetAddress'.) */ ibus_service_class_add_interfaces (IBUS_SERVICE_CLASS (class), introspection_xml); } /** * _panel_destroy_cb: * * A callback function which is called when (1) the connection to the panel process is terminated, * or (2) ibus_proxy_destroy (ibus->panel); is called. See src/ibusproxy.c for details. */ static void _panel_destroy_cb (BusPanelProxy *panel, BusIBusImpl *ibus) { g_assert (BUS_IS_PANEL_PROXY (panel)); g_assert (BUS_IS_IBUS_IMPL (ibus)); if (ibus->panel == panel) ibus->panel = NULL; else if (ibus->emoji_extension == panel) ibus->emoji_extension = NULL; else g_return_if_reached (); g_object_unref (panel); } static void bus_ibus_impl_set_panel_extension_mode (BusIBusImpl *ibus, IBusExtensionEvent *event) { gboolean is_extension = FALSE; g_return_if_fail (BUS_IS_IBUS_IMPL (ibus)); g_return_if_fail (IBUS_IS_EXTENSION_EVENT (event)); if (!ibus->emoji_extension) { g_warning ("Panel extension is not running."); return; } g_return_if_fail (BUS_IS_PANEL_PROXY (ibus->emoji_extension)); ibus->enable_emoji_extension = ibus_extension_event_is_enabled (event); is_extension = ibus_extension_event_is_extension (event); if (ibus->focused_context != NULL) { if (ibus->enable_emoji_extension) { bus_input_context_set_emoji_extension (ibus->focused_context, ibus->emoji_extension); } else { bus_input_context_set_emoji_extension (ibus->focused_context, NULL); } if (is_extension) bus_input_context_panel_extension_received (ibus->focused_context, event); } if (is_extension) return; /* Use the DBus method because it seems any DBus signal, * g_dbus_message_new_signal(), cannot be reached to the server. */ bus_panel_proxy_panel_extension_received (ibus->emoji_extension, event); } static void bus_ibus_impl_set_panel_extension_keys (BusIBusImpl *ibus, GVariant *parameters) { BusEngineProxy *engine = NULL; g_return_if_fail (BUS_IS_IBUS_IMPL (ibus)); g_return_if_fail (parameters); if (!ibus->emoji_extension) { g_warning ("Panel extension is not running."); return; } if (ibus->extension_register_keys) g_variant_unref (ibus->extension_register_keys); ibus->extension_register_keys = g_variant_ref_sink (parameters); if (ibus->focused_context != NULL) { engine = bus_input_context_get_engine (ibus->focused_context); } if (!engine) return; bus_engine_proxy_panel_extension_register_keys (engine, parameters); } static void _panel_panel_extension_cb (BusPanelProxy *panel, IBusExtensionEvent *event, BusIBusImpl *ibus) { bus_ibus_impl_set_panel_extension_mode (ibus, event); } static void _panel_panel_extension_register_keys_cb (BusInputContext *context, GVariant *parameters, BusIBusImpl *ibus) { bus_ibus_impl_set_panel_extension_keys (ibus, parameters); } static void _panel_update_preedit_text_received_cb (BusPanelProxy *panel, IBusText *text, guint cursor_pos, gboolean visible, BusIBusImpl *ibus) { g_return_if_fail (BUS_IS_IBUS_IMPL (ibus)); if (!ibus->focused_context) return; bus_input_context_update_preedit_text (ibus->focused_context, text, cursor_pos, visible, IBUS_ENGINE_PREEDIT_CLEAR, FALSE); } static void _panel_update_lookup_table_received_cb (BusPanelProxy *panel, IBusLookupTable *table, gboolean visible, BusIBusImpl *ibus) { g_return_if_fail (BUS_IS_IBUS_IMPL (ibus)); g_return_if_fail (IBUS_IS_LOOKUP_TABLE (table)); if (!ibus->focused_context) return; /* Call bus_input_context_update_lookup_table() instead of * bus_panel_proxy_update_lookup_table() for panel extensions because * bus_input_context_page_up() can call bus_panel_proxy_page_up_received(). */ bus_input_context_update_lookup_table ( ibus->focused_context, table, visible, TRUE); } static void _panel_update_auxiliary_text_received_cb (BusPanelProxy *panel, IBusText *text, gboolean visible, BusIBusImpl *ibus) { g_return_if_fail (BUS_IS_IBUS_IMPL (ibus)); g_return_if_fail (IBUS_IS_TEXT (text)); if (!ibus->panel) return; bus_panel_proxy_update_auxiliary_text ( ibus->panel, text, visible); } static void _registry_changed_cb (IBusRegistry *registry, BusIBusImpl *ibus) { bus_ibus_impl_registry_changed (ibus); } /* * _dbus_name_owner_changed_cb: * * A callback function to be called when the name-owner-changed signal is sent to the dbus object. * This usually means a client (e.g. a panel/config/engine process or an application) is connected/disconnected to/from the bus. */ static void _dbus_name_owner_changed_cb (BusDBusImpl *dbus, BusConnection *orig_connection, const gchar *name, const gchar *old_name, const gchar *new_name, BusIBusImpl *ibus) { PanelType panel_type = PANEL_TYPE_NONE; g_assert (BUS_IS_DBUS_IMPL (dbus)); g_assert (name != NULL); g_assert (old_name != NULL); g_assert (new_name != NULL); g_assert (BUS_IS_IBUS_IMPL (ibus)); if (!g_strcmp0 (name, IBUS_SERVICE_PANEL)) panel_type = PANEL_TYPE_PANEL; else if (!g_strcmp0 (name, IBUS_SERVICE_PANEL_EXTENSION_EMOJI)) panel_type = PANEL_TYPE_EXTENSION_EMOJI; do { if (panel_type == PANEL_TYPE_NONE) break; if (g_strcmp0 (new_name, "") != 0) { /* a Panel process is started. */ BusConnection *connection; BusInputContext *context = NULL; BusPanelProxy **panel = (panel_type == PANEL_TYPE_PANEL) ? &ibus->panel : &ibus->emoji_extension; GDBusConnection *dbus_connection = NULL; if (*panel != NULL) { ibus_proxy_destroy ((IBusProxy *)(*panel)); /* panel should be NULL after destroy. See _panel_destroy_cb * for details. */ g_assert (*panel == NULL); } connection = bus_dbus_impl_get_connection_by_name (BUS_DEFAULT_DBUS, new_name); g_return_if_fail (connection != NULL); dbus_connection = bus_connection_get_dbus_connection (connection); /* rhbz#1349148 rhbz#1385349 * Avoid SEGV of BUS_IS_PANEL_PROXY (ibus->panel) * This function is called during destroying the connection * in this case? */ if (dbus_connection == NULL || g_dbus_connection_is_closed (dbus_connection)) { new_name = ""; break; } *panel = bus_panel_proxy_new (connection, panel_type); if (panel_type == PANEL_TYPE_EXTENSION_EMOJI) ibus->enable_emoji_extension = FALSE; g_signal_connect (*panel, "destroy", G_CALLBACK (_panel_destroy_cb), ibus); g_signal_connect (*panel, "panel-extension", G_CALLBACK (_panel_panel_extension_cb), ibus); g_signal_connect (*panel, "panel-extension-register-keys", G_CALLBACK ( _panel_panel_extension_register_keys_cb), ibus); g_signal_connect ( *panel, "update-preedit-text-received", G_CALLBACK (_panel_update_preedit_text_received_cb), ibus); g_signal_connect ( *panel, "update-lookup-table-received", G_CALLBACK (_panel_update_lookup_table_received_cb), ibus); g_signal_connect ( *panel, "update-auxiliary-text-received", G_CALLBACK (_panel_update_auxiliary_text_received_cb), ibus); if (ibus->focused_context != NULL) { context = ibus->focused_context; } else if (ibus->use_global_engine) { context = ibus->fake_context; } if (context != NULL) { BusEngineProxy *engine; bus_panel_proxy_focus_in (*panel, context); engine = bus_input_context_get_engine (context); if (engine != NULL) { IBusPropList *prop_list = bus_engine_proxy_get_properties (engine); bus_panel_proxy_register_properties (*panel, prop_list); } } } } while (0); bus_ibus_impl_component_name_owner_changed (ibus, name, old_name, new_name); } /** * bus_ibus_impl_init: * * The constructor of BusIBusImpl. Initialize all member variables of a BusIBusImpl object. */ static void bus_ibus_impl_init (BusIBusImpl *ibus) { ibus->factory_dict = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_object_unref); ibus->fake_context = bus_input_context_new (NULL, "fake"); g_object_ref_sink (ibus->fake_context); bus_dbus_impl_register_object (BUS_DEFAULT_DBUS, (IBusService *) ibus->fake_context); bus_input_context_set_capabilities (ibus->fake_context, IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS | IBUS_CAP_SURROUNDING_TEXT); g_signal_connect (ibus->fake_context, "engine-changed", G_CALLBACK (_context_engine_changed_cb), ibus); bus_input_context_focus_in (ibus->fake_context); ibus->register_engine_list = NULL; ibus->contexts = NULL; ibus->focused_context = NULL; ibus->panel = NULL; ibus->emoji_extension = NULL; ibus->keymap = ibus_keymap_get ("us"); ibus->use_sys_layout = TRUE; ibus->embed_preedit_text = TRUE; ibus->use_global_engine = TRUE; ibus->global_engine_name = NULL; ibus->global_previous_engine_name = NULL; /* focus the fake_context, if use_global_engine is enabled. */ if (ibus->use_global_engine) bus_ibus_impl_set_focused_context (ibus, ibus->fake_context); g_signal_connect (BUS_DEFAULT_DBUS, "name-owner-changed", G_CALLBACK (_dbus_name_owner_changed_cb), ibus); bus_ibus_impl_registry_init (ibus); } /** * bus_ibus_impl_destroy: * * The destructor of BusIBusImpl. */ static void bus_ibus_impl_destroy (BusIBusImpl *ibus) { pid_t pid; glong timeout; gint status; gboolean flag; g_list_foreach (ibus->components, (GFunc) bus_component_stop, NULL); pid = 0; timeout = 0; flag = FALSE; while (1) { while ((pid = waitpid (0, &status, WNOHANG)) > 0); if (pid == -1) { /* all children finished */ break; } if (pid == 0) { /* no child status changed */ g_usleep (1000); timeout += 1000; if (timeout >= G_USEC_PER_SEC) { if (flag == FALSE) { gpointer old; old = signal (SIGTERM, SIG_IGN); /* send TERM signal to the whole process group (i.e. engines, panel, and config daemon.) */ kill (-getpid (), SIGTERM); signal (SIGTERM, old); flag = TRUE; } else { g_warning ("Not every child processes exited!"); break; } } } } g_list_free_full (ibus->register_engine_list, g_object_unref); ibus->register_engine_list = NULL; if (ibus->factory_dict != NULL) { g_hash_table_destroy (ibus->factory_dict); ibus->factory_dict = NULL; } if (ibus->keymap != NULL) { g_object_unref (ibus->keymap); ibus->keymap = NULL; } g_free (ibus->global_engine_name); ibus->global_engine_name = NULL; g_free (ibus->global_previous_engine_name); ibus->global_previous_engine_name = NULL; if (ibus->fake_context) { g_object_unref (ibus->fake_context); ibus->fake_context = NULL; } bus_ibus_impl_registry_destroy (ibus); IBUS_OBJECT_CLASS (bus_ibus_impl_parent_class)->destroy (IBUS_OBJECT (ibus)); } /** * _ibus_get_address: * * Implement the "Address" method call of the org.freedesktop.IBus interface. */ static GVariant * _ibus_get_address (BusIBusImpl *ibus, GDBusConnection *connection, GError **error) { if (error) { *error = NULL; } return g_variant_new_string (bus_server_get_address ()); } static void _ibus_get_address_depre (BusIBusImpl *ibus, GVariant *parameters, GDBusMethodInvocation *invocation) { GDBusConnection *connection = g_dbus_method_invocation_get_connection (invocation); GError *error = NULL; GVariant *variant = NULL; g_warning ("org.freedesktop.IBus.GetAddress() is deprecated!"); variant = _ibus_get_address (ibus, connection, &error); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", g_variant_get_string (variant, NULL))); g_variant_unref (variant); } static IBusEngineDesc * _find_engine_desc_by_name (BusIBusImpl *ibus, const gchar *engine_name) { IBusEngineDesc *desc = NULL; GList *p; /* find engine in registered engine list */ for (p = ibus->register_engine_list; p != NULL; p = p->next) { desc = (IBusEngineDesc *) p->data; if (g_strcmp0 (ibus_engine_desc_get_name (desc), engine_name) == 0) return desc; } return NULL; } /** * _context_request_engine_cb: * * A callback function to be called when the "request-engine" signal is sent to the context. */ static IBusEngineDesc * _context_request_engine_cb (BusInputContext *context, const gchar *engine_name, BusIBusImpl *ibus) { if (engine_name == NULL || engine_name[0] == '\0') engine_name = DEFAULT_ENGINE; return bus_ibus_impl_get_engine_desc (ibus, engine_name); } /** * bus_ibus_impl_get_engine_desc: * * Get the IBusEngineDesc by engine_name. If the engine_name is NULL, return * a default engine desc. */ static IBusEngineDesc * bus_ibus_impl_get_engine_desc (BusIBusImpl *ibus, const gchar *engine_name) { g_return_val_if_fail (engine_name != NULL, NULL); g_return_val_if_fail (engine_name[0] != '\0', NULL); IBusEngineDesc *desc = _find_engine_desc_by_name (ibus, engine_name); if (desc == NULL) { desc = (IBusEngineDesc *) g_hash_table_lookup (ibus->engine_table, engine_name); } return desc; } static void bus_ibus_impl_set_context_engine_from_desc (BusIBusImpl *ibus, BusInputContext *context, IBusEngineDesc *desc) { bus_input_context_set_engine_by_desc (context, desc, g_gdbus_timeout, /* timeout in msec. */ NULL, /* we do not cancel the call. */ NULL, /* use the default callback function. */ NULL); } static void _context_panel_extension_cb (BusInputContext *context, IBusExtensionEvent *event, BusIBusImpl *ibus) { bus_ibus_impl_set_panel_extension_mode (ibus, event); } const static struct { const gchar *name; GCallback callback; } context_signals [] = { { "panel-extension", G_CALLBACK (_context_panel_extension_cb) } }; /** * bus_ibus_impl_set_focused_context: * * Set the current focused context. */ static void bus_ibus_impl_set_focused_context (BusIBusImpl *ibus, BusInputContext *context) { gint i; BusEngineProxy *engine = NULL; guint purpose = 0; guint hints = 0; g_assert (BUS_IS_IBUS_IMPL (ibus)); g_assert (context == NULL || BUS_IS_INPUT_CONTEXT (context)); g_assert (context == NULL || bus_input_context_get_capabilities (context) & IBUS_CAP_FOCUS); /* Do noting if it is focused context. */ if (ibus->focused_context == context) { return; } if (ibus->focused_context) { if (ibus->use_global_engine) { /* dettach engine from the focused context */ engine = bus_input_context_get_engine (ibus->focused_context); if (engine) { g_object_ref (engine); /* _ic_focus_in() can be called before _ic_focus_out() is * called under the async processes of two ibus clients. * E.g. gedit is a little slower v.s. a simple GtkTextView * application is the fastest when you click a Hangul * preedit text between the applications. * preedit will be committed with focus-out in the ibus client * likes ibus-im.so * so do not commit preedit here in focus-in event. */ bus_input_context_clear_preedit_text (ibus->focused_context, FALSE); bus_input_context_set_engine (ibus->focused_context, NULL); bus_input_context_set_emoji_extension (ibus->focused_context, NULL); } } if (ibus->panel != NULL) bus_panel_proxy_focus_out (ibus->panel, ibus->focused_context); if (ibus->emoji_extension != NULL) { bus_panel_proxy_focus_out (ibus->emoji_extension, ibus->focused_context); } bus_input_context_set_emoji_extension (ibus->focused_context, NULL); bus_input_context_get_content_type (ibus->focused_context, &purpose, &hints); for (i = 0; i < G_N_ELEMENTS(context_signals); i++) { g_signal_handlers_disconnect_by_func (ibus->focused_context, context_signals[i].callback, ibus); } g_object_unref (ibus->focused_context); ibus->focused_context = NULL; } if (context == NULL && ibus->use_global_engine) { context = ibus->fake_context; if (context) bus_input_context_set_content_type (context, purpose, hints); } if (context) { ibus->focused_context = (BusInputContext *) g_object_ref (context); /* attach engine to the focused context */ if (engine != NULL) { bus_input_context_set_engine (context, engine); bus_input_context_enable (context); if (ibus->enable_emoji_extension) { bus_input_context_set_emoji_extension (context, ibus->emoji_extension); } else { bus_input_context_set_emoji_extension (context, NULL); } } for (i = 0; i < G_N_ELEMENTS(context_signals); i++) { g_signal_connect (ibus->focused_context, context_signals[i].name, context_signals[i].callback, ibus); } if (ibus->panel != NULL) bus_panel_proxy_focus_in (ibus->panel, context); if (ibus->emoji_extension != NULL) bus_panel_proxy_focus_in (ibus->emoji_extension, context); } if (engine != NULL) g_object_unref (engine); } static void bus_ibus_impl_set_global_engine (BusIBusImpl *ibus, BusEngineProxy *engine) { if (!ibus->use_global_engine) return; if (ibus->focused_context) { bus_input_context_set_engine (ibus->focused_context, engine); if (ibus->enable_emoji_extension) { bus_input_context_set_emoji_extension (ibus->focused_context, ibus->emoji_extension); } else { bus_input_context_set_emoji_extension (ibus->focused_context, NULL); } } else if (ibus->fake_context) { bus_input_context_set_engine (ibus->fake_context, engine); } } static void bus_ibus_impl_set_global_engine_by_name (BusIBusImpl *ibus, const gchar *name) { if (!ibus->use_global_engine) return; BusInputContext *context = ibus->focused_context != NULL ? ibus->focused_context : ibus->fake_context; if (context == NULL) { return; } if (g_strcmp0 (name, ibus->global_engine_name) == 0) { /* If the user requested the same global engine, then we just enable the * original one. */ bus_input_context_enable (context); return; } /* If there is a focused input context, then we just change the engine of * the focused context, which will then change the global engine * automatically. Otherwise, we need to change the global engine directly. */ IBusEngineDesc *desc = NULL; desc = bus_ibus_impl_get_engine_desc (ibus, name); if (desc != NULL) { bus_ibus_impl_set_context_engine_from_desc (ibus, context, desc); } } /* When preload_engines and register_engiens are changed, this function * will check the global engine. If necessay, it will change the global engine. */ static void bus_ibus_impl_check_global_engine (BusIBusImpl *ibus) { GList *engine_list = NULL; /* do nothing */ if (!ibus->use_global_engine) return; /* The current global engine is not removed, so do nothing. */ if (ibus->global_engine_name != NULL && _find_engine_desc_by_name (ibus, ibus->global_engine_name)) { return; } /* If the previous engine is available, then just switch to it. */ if (ibus->global_previous_engine_name != NULL && _find_engine_desc_by_name (ibus, ibus->global_previous_engine_name)) { bus_ibus_impl_set_global_engine_by_name ( ibus, ibus->global_previous_engine_name); return; } /* Just switch to the fist engine in the list. */ engine_list = ibus->register_engine_list; if (engine_list) { IBusEngineDesc *engine_desc = (IBusEngineDesc *)engine_list->data; bus_ibus_impl_set_global_engine_by_name (ibus, ibus_engine_desc_get_name (engine_desc)); return; } /* No engine available? Just disable global engine. */ bus_ibus_impl_set_global_engine (ibus, NULL); } /** * _context_engine_changed_cb: * * A callback function to be called when the "engine-changed" signal is sent to the context. * Update global engine as well if necessary. */ static void _context_engine_changed_cb (BusInputContext *context, BusIBusImpl *ibus) { if (!ibus->use_global_engine) return; if ((context == ibus->focused_context) || (ibus->focused_context == NULL && context == ibus->fake_context)) { BusEngineProxy *engine = bus_input_context_get_engine (context); if (engine != NULL) { /* only set global engine if engine is not NULL */ const gchar *name = ibus_engine_desc_get_name (bus_engine_proxy_get_desc (engine)); if (g_strcmp0 (name, ibus->global_engine_name) == 0) return; g_free (ibus->global_previous_engine_name); ibus->global_previous_engine_name = ibus->global_engine_name; ibus->global_engine_name = g_strdup (name); bus_ibus_impl_global_engine_changed (ibus); } } } /** * _context_focus_in_cb: * * A callback function to be called when the "focus-in" signal is sent to the context. * If necessary, enables the global engine on the context and update ibus->focused_context. */ static void _context_focus_in_cb (BusInputContext *context, BusIBusImpl *ibus) { g_assert (BUS_IS_IBUS_IMPL (ibus)); g_assert (BUS_IS_INPUT_CONTEXT (context)); /* Do nothing if context does not support focus. * The global engine shoule be detached from the focused context. */ if ((bus_input_context_get_capabilities (context) & IBUS_CAP_FOCUS) == 0) { return; } bus_ibus_impl_set_focused_context (ibus, context); } /** * _context_focus_out_cb: * * A callback function to be called when the "focus-out" signal is sent to the context. */ static void _context_focus_out_cb (BusInputContext *context, BusIBusImpl *ibus) { g_assert (BUS_IS_IBUS_IMPL (ibus)); g_assert (BUS_IS_INPUT_CONTEXT (context)); /* Do noting if context does not support focus. * Actually, the context should emit focus signals, if it does not support focus */ if ((bus_input_context_get_capabilities (context) & IBUS_CAP_FOCUS) == 0) { return; } /* Do noting if it is not focused context. */ if (ibus->focused_context != context) { return; } bus_ibus_impl_set_focused_context (ibus, NULL); } /** * _context_destroy_cb: * * A callback function to be called when the "destroy" signal is sent to the * context. */ static void _context_destroy_cb (BusInputContext *context, BusIBusImpl *ibus) { g_assert (BUS_IS_IBUS_IMPL (ibus)); g_assert (BUS_IS_INPUT_CONTEXT (context)); if (context == ibus->focused_context) bus_ibus_impl_set_focused_context (ibus, NULL); if (ibus->panel && bus_input_context_get_capabilities (context) & IBUS_CAP_FOCUS) { bus_panel_proxy_destroy_context (ibus->panel, context); } if (ibus->emoji_extension && bus_input_context_get_capabilities (context) & IBUS_CAP_FOCUS) { bus_panel_proxy_destroy_context (ibus->emoji_extension, context); } ibus->contexts = g_list_remove (ibus->contexts, context); g_object_unref (context); } /** * bus_ibus_impl_create_input_context: * @client: A name of a client. e.g. "gtk-im" * @returns: A BusInputContext object. * * Create a new BusInputContext object for the client. */ static BusInputContext * bus_ibus_impl_create_input_context (BusIBusImpl *ibus, BusConnection *connection, const gchar *client) { BusInputContext *context = bus_input_context_new (connection, client); g_object_ref_sink (context); ibus->contexts = g_list_append (ibus->contexts, context); /* Installs glib signal handlers so that the ibus object could be notified when e.g. an IBus.InputContext D-Bus method is called. */ static const struct { gchar *name; GCallback callback; } signals [] = { { "request-engine", G_CALLBACK (_context_request_engine_cb) }, { "engine-changed", G_CALLBACK (_context_engine_changed_cb) }, { "focus-in", G_CALLBACK (_context_focus_in_cb) }, { "focus-out", G_CALLBACK (_context_focus_out_cb) }, { "destroy", G_CALLBACK (_context_destroy_cb) }, }; gint i; for (i = 0; i < G_N_ELEMENTS (signals); i++) { g_signal_connect (context, signals[i].name, signals[i].callback, ibus); } bus_input_context_enable (context); /* register the context object so that the object could handle IBus.InputContext method calls. */ bus_dbus_impl_register_object (BUS_DEFAULT_DBUS, (IBusService *) context); g_object_ref (context); return context; } /** * _ibus_create_input_context: * * Implement the "CreateInputContext" method call of the org.freedesktop.IBus interface. */ static void _ibus_create_input_context (BusIBusImpl *ibus, GVariant *parameters, GDBusMethodInvocation *invocation) { const gchar *client_name = NULL; // e.g. "gtk-im" g_variant_get (parameters, "(&s)", &client_name); BusConnection *connection = bus_connection_lookup (g_dbus_method_invocation_get_connection (invocation)); BusInputContext *context = bus_ibus_impl_create_input_context (ibus, connection, client_name); if (context) { const gchar *path = ibus_service_get_object_path ((IBusService *) context); /* the format-string 'o' is for a D-Bus object path. */ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(o)", path)); g_object_unref (context); } else { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Create input context failed!"); } } /** * _ibus_get_current_input_context: * * Implement the "CurrentInputContext" method call of the * org.freedesktop.IBus interface. */ static GVariant * _ibus_get_current_input_context (BusIBusImpl *ibus, GDBusConnection *connection, GError **error) { if (error) { *error = NULL; } if (!ibus->focused_context) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No focused input context"); return NULL; } else { const gchar *path = ibus_service_get_object_path ( (IBusService *) ibus->focused_context); /* the format-string 'o' is for a D-Bus object path. */ return g_variant_new_object_path (path); } } static void _ibus_current_input_context_depre (BusIBusImpl *ibus, GVariant *parameters, GDBusMethodInvocation *invocation) { GDBusConnection *connection = g_dbus_method_invocation_get_connection (invocation); GVariant *variant = NULL; GError *error = NULL; g_warning ("org.freedesktop.IBus.CurrentInputContext() is deprecated!"); variant = _ibus_get_current_input_context (ibus, connection, &error); if (variant == NULL) { g_dbus_method_invocation_return_error ( invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "%s", error->message); g_error_free (error); } else { const gchar *path = g_variant_get_string (variant, NULL); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(o)", path)); g_variant_unref (variant); } } static void _component_destroy_cb (BusComponent *component, BusIBusImpl *ibus) { g_assert (BUS_IS_IBUS_IMPL (ibus)); g_assert (BUS_IS_COMPONENT (component)); ibus->registered_components = g_list_remove (ibus->registered_components, component); /* remove engines from engine_list */ GList *engines = bus_component_get_engines (component); GList *p; for (p = engines; p != NULL; p = p->next) { if (g_list_find (ibus->register_engine_list, p->data)) { ibus->register_engine_list = g_list_remove (ibus->register_engine_list, p->data); g_object_unref (p->data); } } g_list_free (engines); g_object_unref (component); bus_ibus_impl_check_global_engine (ibus); } /** * _ibus_register_component: * * Implement the "RegisterComponent" method call of the * org.freedesktop.IBus interface. */ static void _ibus_register_component (BusIBusImpl *ibus, GVariant *parameters, GDBusMethodInvocation *invocation) { GVariant *variant = g_variant_get_child_value (parameters, 0); IBusComponent *component = (IBusComponent *) ibus_serializable_deserialize (variant); if (!IBUS_IS_COMPONENT (component)) { if (component) g_object_unref (component); g_dbus_method_invocation_return_error ( invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "The first argument should be an IBusComponent."); return; } BusConnection *connection = bus_connection_lookup ( g_dbus_method_invocation_get_connection (invocation)); BusFactoryProxy *factory = bus_factory_proxy_new (connection); if (factory == NULL) { g_object_unref (component); g_dbus_method_invocation_return_error ( invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Create factory failed."); return; } g_object_ref_sink (component); BusComponent *buscomp = bus_component_new (component, factory); bus_component_set_destroy_with_factory (buscomp, TRUE); g_object_unref (component); g_object_unref (factory); ibus->registered_components = g_list_append (ibus->registered_components, g_object_ref_sink (buscomp)); GList *engines = bus_component_get_engines (buscomp); g_list_foreach (engines, (GFunc) g_object_ref, NULL); ibus->register_engine_list = g_list_concat (ibus->register_engine_list, engines); g_signal_connect (buscomp, "destroy", G_CALLBACK (_component_destroy_cb), ibus); g_dbus_method_invocation_return_value (invocation, NULL); } /** * _ibus_get_engines: * * Implement the "Engines" method call of the org.freedesktop.IBus interface. */ static GVariant * _ibus_get_engines (BusIBusImpl *ibus, GDBusConnection *connection, GError **error) { GVariantBuilder builder; GList *engines = NULL; GList *p; if (error) { *error = NULL; } g_variant_builder_init (&builder, G_VARIANT_TYPE ("av")); engines = g_hash_table_get_values (ibus->engine_table); for (p = engines; p != NULL; p = p->next) { g_variant_builder_add ( &builder, "v", ibus_serializable_serialize ((IBusSerializable *) p->data)); } g_list_free (engines); return g_variant_builder_end (&builder); } static void _ibus_list_engines_depre (BusIBusImpl *ibus, GVariant *parameters, GDBusMethodInvocation *invocation) { GDBusConnection *connection = g_dbus_method_invocation_get_connection (invocation); GError *error = NULL; GVariant *variant = NULL; g_warning ("org.freedesktop.IBus.ListEngines() is deprecated!"); variant = _ibus_get_engines (ibus, connection, &error); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@av)", variant)); } /** * _ibus_get_engines_by_names: * * Implement the "GetEnginesByNames" method call of the org.freedesktop.IBus interface. */ static void _ibus_get_engines_by_names (BusIBusImpl *ibus, GVariant *parameters, GDBusMethodInvocation *invocation) { const gchar **names = NULL; g_variant_get (parameters, "(^a&s)", &names); g_assert (names != NULL); gint i = 0; GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("av")); while (names[i] != NULL) { IBusEngineDesc *desc = (IBusEngineDesc *) g_hash_table_lookup ( ibus->engine_table, names[i++]); if (desc == NULL) continue; g_variant_builder_add ( &builder, "v", ibus_serializable_serialize ((IBusSerializable *)desc)); } g_dbus_method_invocation_return_value (invocation, g_variant_new ("(av)", &builder)); } /** * _ibus_get_active_engines: * * Implement the "ActiveEngines" method call of the * org.freedesktop.IBus interface. */ static GVariant * _ibus_get_active_engines (BusIBusImpl *ibus, GDBusConnection *connection, GError **error) { GVariantBuilder builder; GList *p; if (error) { *error = NULL; } g_variant_builder_init (&builder, G_VARIANT_TYPE ("av")); for (p = ibus->register_engine_list; p != NULL; p = p->next) { g_variant_builder_add ( &builder, "v", ibus_serializable_serialize ((IBusSerializable *) p->data)); } return g_variant_builder_end (&builder); } static void _ibus_list_active_engines_depre (BusIBusImpl *ibus, GVariant *parameters, GDBusMethodInvocation *invocation) { GDBusConnection *connection = g_dbus_method_invocation_get_connection (invocation); GError *error = NULL; GVariant *variant = NULL; g_warning ("org.freedesktop.IBus.ListActiveEngines() is deprecated!"); variant = _ibus_get_active_engines (ibus, connection, &error); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@av)", variant)); } /** * _ibus_exit: * * Implement the "Exit" method call of the org.freedesktop.IBus interface. */ static void _ibus_exit (BusIBusImpl *ibus, GVariant *parameters, GDBusMethodInvocation *invocation) { gboolean restart = FALSE; g_variant_get (parameters, "(b)", &restart); g_dbus_method_invocation_return_value (invocation, NULL); /* Make sure the reply has been sent out before exit */ g_dbus_connection_flush_sync ( g_dbus_method_invocation_get_connection (invocation), NULL, NULL); bus_server_quit (restart); } /** * _ibus_ping: * * Implement the "Ping" method call of the org.freedesktop.IBus interface. */ static void _ibus_ping (BusIBusImpl *ibus, GVariant *parameters, GDBusMethodInvocation *invocation) { g_dbus_method_invocation_return_value (invocation, parameters); } /** * _ibus_get_use_sys_layout: * * Implement the "UseSysLayout" method call of the * org.freedesktop.IBus interface. */ static GVariant * _ibus_get_use_sys_layout (BusIBusImpl *ibus, GDBusConnection *connection, GError **error) { if (error) { *error = NULL; } return g_variant_new_boolean (ibus->use_sys_layout); } static void _ibus_get_use_sys_layout_depre (BusIBusImpl *ibus, GVariant *parameters, GDBusMethodInvocation *invocation) { GDBusConnection *connection = g_dbus_method_invocation_get_connection (invocation); GError *error = NULL; GVariant *variant = NULL; g_warning ("org.freedesktop.IBus.GetUseSysLayout() is deprecated!"); variant = _ibus_get_use_sys_layout (ibus, connection, &error); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", g_variant_get_boolean (variant))); g_variant_unref (variant); } /** * _ibus_get_use_global_engine: * * Implement the "UseGlobalEngine" method call of the * org.freedesktop.IBus interface. */ static GVariant * _ibus_get_use_global_engine (BusIBusImpl *ibus, GDBusConnection *connection, GError **error) { if (error) { *error = NULL; } return g_variant_new_boolean (ibus->use_global_engine); } static void _ibus_get_use_global_engine_depre (BusIBusImpl *ibus, GVariant *parameters, GDBusMethodInvocation *invocation) { GDBusConnection *connection = g_dbus_method_invocation_get_connection (invocation); GError *error = NULL; GVariant *variant = NULL; g_warning ("org.freedesktop.IBus.GetUseGlobalEngine() is deprecated!"); variant = _ibus_get_use_global_engine (ibus, connection, &error); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", g_variant_get_boolean (variant))); g_variant_unref (variant); } /** * _ibus_get_global_engine: * * Implement the "GlobalEngine" method call of the * org.freedesktop.IBus interface. */ static GVariant * _ibus_get_global_engine (BusIBusImpl *ibus, GDBusConnection *connection, GError **error) { IBusEngineDesc *desc = NULL; if (error) { *error = NULL; } do { if (!ibus->use_global_engine) break; BusInputContext *context = ibus->focused_context; if (context == NULL) context = ibus->fake_context; desc = bus_input_context_get_engine_desc (context); if (desc == NULL) break; GVariant *variant = ibus_serializable_serialize ( (IBusSerializable *) desc); // Set type "v" for introspection_xml. return g_variant_new_variant (variant); } while (0); g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No global engine."); return NULL; } static void _ibus_get_global_engine_depre (BusIBusImpl *ibus, GVariant *parameters, GDBusMethodInvocation *invocation) { GDBusConnection *connection = g_dbus_method_invocation_get_connection (invocation); GError *error = NULL; GVariant *variant = NULL; g_warning ("org.freedesktop.IBus.GetGlobalEngine() is deprecated!"); variant = _ibus_get_global_engine (ibus, connection, &error); if (variant == NULL) { g_dbus_method_invocation_return_error ( invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "%s", error->message); g_error_free (error); } else { GVariant *retval = g_variant_get_variant (variant); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(v)", retval)); g_variant_unref (variant); } } struct _SetGlobalEngineData { BusIBusImpl *ibus; GDBusMethodInvocation *invocation; }; typedef struct _SetGlobalEngineData SetGlobalEngineData; static void _ibus_set_global_engine_ready_cb (BusInputContext *context, GAsyncResult *res, SetGlobalEngineData *data) { BusIBusImpl *ibus = data->ibus; GError *error = NULL; if (!bus_input_context_set_engine_by_desc_finish (context, res, &error)) { g_dbus_method_invocation_return_error (data->invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Set global engine failed: %s", error->message); g_error_free (error); } else { g_dbus_method_invocation_return_value (data->invocation, NULL); BusEngineProxy *engine = bus_input_context_get_engine (context); if (ibus->use_global_engine && (context != ibus->focused_context)) { /* context and ibus->focused_context don't match. This means that * the focus is moved before _ibus_set_global_engine() asynchronous * call finishes. In this case, the engine for the context currently * being focused hasn't been updated. Update the engine here so that * subsequent _ibus_get_global_engine() call could return a * consistent engine name. */ if (engine && ibus->focused_context != NULL) { g_object_ref (engine); bus_input_context_set_engine (context, NULL); bus_input_context_set_emoji_extension (context, NULL); bus_input_context_set_engine (ibus->focused_context, engine); if (ibus->enable_emoji_extension) { bus_input_context_set_emoji_extension ( ibus->focused_context, ibus->emoji_extension); } else { bus_input_context_set_emoji_extension ( ibus->focused_context, NULL); } g_object_unref (engine); } } if (engine && ibus->extension_register_keys) { bus_engine_proxy_panel_extension_register_keys ( engine, ibus->extension_register_keys); } } g_object_unref (ibus); g_slice_free (SetGlobalEngineData, data); } /** * _ibus_set_global_engine: * * Implement the "SetGlobalEngine" method call of the * org.freedesktop.IBus interface. */ static void _ibus_set_global_engine (BusIBusImpl *ibus, GVariant *parameters, GDBusMethodInvocation *invocation) { if (!ibus->use_global_engine) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Global engine feature is disabled."); return; } BusInputContext *context = ibus->focused_context; if (context == NULL) context = ibus->fake_context; const gchar *engine_name = NULL; g_variant_get (parameters, "(&s)", &engine_name); IBusEngineDesc *desc = bus_ibus_impl_get_engine_desc(ibus, engine_name); if (desc == NULL) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Cannot find engine %s.", engine_name); return; } SetGlobalEngineData *data = g_slice_new0 (SetGlobalEngineData); data->ibus = g_object_ref (ibus); data->invocation = invocation; bus_input_context_set_engine_by_desc (context, desc, g_gdbus_timeout, /* timeout in msec. */ NULL, /* we do not cancel the call. */ (GAsyncReadyCallback) _ibus_set_global_engine_ready_cb, data); } /** * _ibus_get_global_engine_enabled: * * Implement the "GlobalEngineEnabled" method call of the * org.freedesktop.IBus interface. */ static GVariant * _ibus_get_global_engine_enabled (BusIBusImpl *ibus, GDBusConnection *connection, GError **error) { gboolean enabled = FALSE; if (error) { *error = NULL; } do { if (!ibus->use_global_engine) break; BusInputContext *context = ibus->focused_context; if (context == NULL) context = ibus->fake_context; if (context == NULL) break; enabled = TRUE; } while (0); return g_variant_new_boolean (enabled); } static void _ibus_is_global_engine_enabled_depre (BusIBusImpl *ibus, GVariant *parameters, GDBusMethodInvocation *invocation) { GDBusConnection *connection = g_dbus_method_invocation_get_connection (invocation); GError *error = NULL; GVariant *variant = NULL; g_warning ("org.freedesktop.IBus.IsGlobalEngineEnabled() is deprecated!"); variant = _ibus_get_global_engine_enabled (ibus, connection, &error); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", g_variant_get_boolean (variant))); g_variant_unref (variant); } /** * _ibus_set_preload_engines: * * Implement the "PreloadEngines" method call of the * org.freedesktop.IBus interface. */ static gboolean _ibus_set_preload_engines (BusIBusImpl *ibus, GDBusConnection *connection, GVariant *value, GError **error) { int i, j; const gchar **names = NULL; IBusEngineDesc *desc = NULL; BusComponent *component = NULL; BusFactoryProxy *factory = NULL; GPtrArray *array = g_ptr_array_new (); if (error) { *error = NULL; } g_variant_get (value, "^a&s", &names); for (i = 0; names[i] != NULL; i++) { gboolean has_component = FALSE; desc = bus_ibus_impl_get_engine_desc(ibus, names[i]); if (desc == NULL) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Cannot find engine %s.", names[i]); g_ptr_array_free (array, FALSE); return FALSE; } component = bus_component_from_engine_desc (desc); factory = bus_component_get_factory (component); if (factory != NULL) { continue; } for (j = 0; j < array->len; j++) { if (component == (BusComponent *) g_ptr_array_index (array, j)) { has_component = TRUE; break; } } if (!has_component) { g_ptr_array_add (array, component); } } for (j = 0; j < array->len; j++) { bus_component_start ((BusComponent *) g_ptr_array_index (array, j), g_verbose); } g_ptr_array_free (array, FALSE); bus_ibus_impl_property_changed (ibus, "PreloadEngines", value); return TRUE; } /** * _ibus_get_embed_preedit_text: * * Implement the "EmbedPreeditText" method call of * the org.freedesktop.IBus interface. */ static GVariant * _ibus_get_embed_preedit_text (BusIBusImpl *ibus, GDBusConnection *connection, GError **error) { if (error) { *error = NULL; } return g_variant_new_boolean (ibus->embed_preedit_text); } /** * _ibus_set_embed_preedit_text: * * Implement the "EmbedPreeditText" method call of * the org.freedesktop.IBus interface. */ static gboolean _ibus_set_embed_preedit_text (BusIBusImpl *ibus, GDBusConnection *connection, GVariant *value, GError **error) { if (error) { *error = NULL; } gboolean embed_preedit_text = g_variant_get_boolean (value); if (embed_preedit_text != ibus->embed_preedit_text) { ibus->embed_preedit_text = embed_preedit_text; bus_ibus_impl_property_changed (ibus, "EmbedPreeditText", value); } return TRUE; } /** * bus_ibus_impl_service_method_call: * * Handle a D-Bus method call whose destination and interface name are * both "org.freedesktop.IBus" */ static void bus_ibus_impl_service_method_call (IBusService *service, GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation) { if (g_strcmp0 (interface_name, IBUS_INTERFACE_IBUS) != 0) { IBUS_SERVICE_CLASS (bus_ibus_impl_parent_class)->service_method_call ( service, connection, sender, object_path, interface_name, method_name, parameters, invocation); return; } /* all methods in the xml definition above should be listed here. */ static const struct { const gchar *method_name; void (* method_callback) (BusIBusImpl *, GVariant *, GDBusMethodInvocation *); } methods [] = { /* IBus interface */ { "CreateInputContext", _ibus_create_input_context }, { "RegisterComponent", _ibus_register_component }, { "GetEnginesByNames", _ibus_get_engines_by_names }, { "Exit", _ibus_exit }, { "Ping", _ibus_ping }, { "SetGlobalEngine", _ibus_set_global_engine }, /* Start of deprecated methods */ { "GetAddress", _ibus_get_address_depre }, { "CurrentInputContext", _ibus_current_input_context_depre }, { "ListEngines", _ibus_list_engines_depre }, { "ListActiveEngines", _ibus_list_active_engines_depre }, { "GetUseSysLayout", _ibus_get_use_sys_layout_depre }, { "GetUseGlobalEngine", _ibus_get_use_global_engine_depre }, { "GetGlobalEngine", _ibus_get_global_engine_depre }, { "IsGlobalEngineEnabled", _ibus_is_global_engine_enabled_depre }, /* End of deprecated methods */ }; gint i; for (i = 0; i < G_N_ELEMENTS (methods); i++) { if (g_strcmp0 (methods[i].method_name, method_name) == 0) { methods[i].method_callback ((BusIBusImpl *) service, parameters, invocation); return; } } g_warning ("service_method_call received an unknown method: %s", method_name ? method_name : "(null)"); } /** * bus_ibus_impl_service_get_property: * * Handle org.freedesktop.DBus.Properties.Get */ static GVariant * bus_ibus_impl_service_get_property (IBusService *service, GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error) { gint i; static const struct { const gchar *method_name; GVariant * (* method_callback) (BusIBusImpl *, GDBusConnection *, GError **); } methods [] = { { "Address", _ibus_get_address }, { "CurrentInputContext", _ibus_get_current_input_context }, { "Engines", _ibus_get_engines }, { "ActiveEngines", _ibus_get_active_engines }, { "GlobalEngine", _ibus_get_global_engine }, { "EmbedPreeditText", _ibus_get_embed_preedit_text }, }; if (g_strcmp0 (interface_name, IBUS_INTERFACE_IBUS) != 0) { return IBUS_SERVICE_CLASS ( bus_ibus_impl_parent_class)->service_get_property ( service, connection, sender, object_path, interface_name, property_name, error); } for (i = 0; i < G_N_ELEMENTS (methods); i++) { if (g_strcmp0 (methods[i].method_name, property_name) == 0) { return methods[i].method_callback ((BusIBusImpl *) service, connection, error); } } g_warning ("service_get_property received an unknown property: %s", property_name ? property_name : "(null)"); return NULL; } /** * bus_ibus_impl_service_set_property: * * Handle org.freedesktop.DBus.Properties.Set */ static gboolean bus_ibus_impl_service_set_property (IBusService *service, GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GVariant *value, GError **error) { gint i; static const struct { const gchar *method_name; gboolean (* method_callback) (BusIBusImpl *, GDBusConnection *, GVariant *, GError **); } methods [] = { { "PreloadEngines", _ibus_set_preload_engines }, { "EmbedPreeditText", _ibus_set_embed_preedit_text }, }; if (g_strcmp0 (interface_name, IBUS_INTERFACE_IBUS) != 0) { return IBUS_SERVICE_CLASS ( bus_ibus_impl_parent_class)->service_set_property ( service, connection, sender, object_path, interface_name, property_name, value, error); } for (i = 0; i < G_N_ELEMENTS (methods); i++) { if (g_strcmp0 (methods[i].method_name, property_name) == 0) { return methods[i].method_callback ((BusIBusImpl *) service, connection, value, error); } } g_warning ("service_set_property received an unknown property: %s", property_name ? property_name : "(null)"); return FALSE; } BusIBusImpl * bus_ibus_impl_get_default (void) { static BusIBusImpl *ibus = NULL; if (ibus == NULL) { ibus = (BusIBusImpl *) g_object_new (BUS_TYPE_IBUS_IMPL, "object-path", IBUS_PATH_IBUS, NULL); } return ibus; } BusFactoryProxy * bus_ibus_impl_lookup_factory (BusIBusImpl *ibus, const gchar *path) { g_assert (BUS_IS_IBUS_IMPL (ibus)); BusFactoryProxy *factory; factory = (BusFactoryProxy *) g_hash_table_lookup (ibus->factory_dict, path); return factory; } IBusKeymap * bus_ibus_impl_get_keymap (BusIBusImpl *ibus) { g_assert (BUS_IS_IBUS_IMPL (ibus)); return ibus->keymap; } /** * bus_ibus_impl_registry_init: * * Initialize IBusRegistry. */ static void bus_ibus_impl_registry_init (BusIBusImpl *ibus) { GList *p; GList *components; IBusRegistry *registry = ibus_registry_new (); ibus->registry = NULL; ibus->components = NULL; ibus->engine_table = g_hash_table_new (g_str_hash, g_str_equal); if (g_strcmp0 (g_cache, "none") == 0) { /* Only load registry, but not read and write cache. */ ibus_registry_load (registry); } else if (g_strcmp0 (g_cache, "refresh") == 0) { /* Load registry and overwrite the cache. */ ibus_registry_load (registry); ibus_registry_save_cache (registry, TRUE); } else if (g_strcmp0 (g_cache, "auto") == 0) { /* Load registry from cache. If the cache does not exist or * it is outdated, then generate it. */ if (ibus_registry_load_cache (registry, FALSE) == FALSE || ibus_registry_check_modification (registry)) { ibus_object_destroy (IBUS_OBJECT (registry)); registry = ibus_registry_new (); if (ibus_registry_load_cache (registry, TRUE) == FALSE || ibus_registry_check_modification (registry)) { ibus_object_destroy (IBUS_OBJECT (registry)); registry = ibus_registry_new (); ibus_registry_load (registry); ibus_registry_save_cache (registry, TRUE); } } } ibus->registry = registry; components = ibus_registry_get_components (registry); for (p = components; p != NULL; p = p->next) { IBusComponent *component = (IBusComponent *) p->data; BusComponent *buscomp = bus_component_new (component, NULL /* factory */); GList *engines = NULL; GList *p1; g_object_ref_sink (buscomp); ibus->components = g_list_append (ibus->components, buscomp); engines = bus_component_get_engines (buscomp); for (p1 = engines; p1 != NULL; p1 = p1->next) { IBusEngineDesc *desc = (IBusEngineDesc *) p1->data; const gchar *name = ibus_engine_desc_get_name (desc); if (g_hash_table_lookup (ibus->engine_table, name) == NULL) { g_hash_table_insert (ibus->engine_table, (gpointer) name, desc); } else { g_message ("Engine %s is already registered by other component", name); } } g_list_free (engines); } g_list_free (components); g_signal_connect (ibus->registry, "changed", G_CALLBACK (_registry_changed_cb), ibus); ibus_registry_start_monitor_changes (ibus->registry); } static void bus_ibus_impl_registry_destroy (BusIBusImpl *ibus) { g_list_free_full (ibus->components, g_object_unref); ibus->components = NULL; g_clear_pointer (&ibus->engine_table, g_hash_table_destroy); g_clear_pointer (&ibus->registry, ibus_object_destroy); if (ibus->extension_register_keys) g_clear_pointer (&ibus->extension_register_keys, g_variant_unref); } static gint _component_is_name_cb (BusComponent *component, const gchar *name) { g_assert (BUS_IS_COMPONENT (component)); g_assert (name); return g_strcmp0 (bus_component_get_name (component), name); } static void bus_ibus_impl_component_name_owner_changed (BusIBusImpl *ibus, const gchar *name, const gchar *old_name, const gchar *new_name) { BusComponent *component; BusFactoryProxy *factory; g_assert (BUS_IS_IBUS_IMPL (ibus)); g_assert (name); g_assert (old_name); g_assert (new_name); component = bus_ibus_impl_lookup_component_by_name (ibus, name); if (component == NULL) { /* name is a unique name, or a well-known name we don't know. */ return; } if (g_strcmp0 (old_name, "") != 0) { /* the component is stopped. */ factory = bus_component_get_factory (component); if (factory != NULL) { ibus_proxy_destroy ((IBusProxy *) factory); } } if (g_strcmp0 (new_name, "") != 0) { /* the component is started. */ BusConnection *connection = bus_dbus_impl_get_connection_by_name (BUS_DEFAULT_DBUS, new_name); if (connection == NULL) return; factory = bus_factory_proxy_new (connection); if (factory == NULL) return; bus_component_set_factory (component, factory); g_object_unref (factory); } } BusComponent * bus_ibus_impl_lookup_component_by_name (BusIBusImpl *ibus, const gchar *name) { GList *p; g_assert (BUS_IS_IBUS_IMPL (ibus)); g_assert (name); p = g_list_find_custom (ibus->components, name, (GCompareFunc) _component_is_name_cb); if (p) { return (BusComponent *) p->data; } else { return NULL; } } /** * bus_ibus_impl_property_changed * * Send a "PropertiesChanged" D-Bus signal for a property to buses * (connections) that are listening to the signal. */ static void bus_ibus_impl_property_changed (BusIBusImpl *service, const gchar *property_name, GVariant *value) { GDBusMessage *message = g_dbus_message_new_signal ("/org/freedesktop/IBus", "org.freedesktop.DBus.Properties", "PropertiesChanged"); /* set a non-zero serial to make libdbus happy */ g_dbus_message_set_serial (message, 1); g_dbus_message_set_sender (message, "org.freedesktop.IBus"); GVariantBuilder *builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); g_variant_builder_add (builder, "{sv}", property_name, value); g_dbus_message_set_body (message, g_variant_new ("(sa{sv}as)", "org.freedesktop.IBus", builder, NULL)); g_variant_builder_unref (builder); bus_dbus_impl_dispatch_message_by_rule (BUS_DEFAULT_DBUS, message, NULL); g_object_unref (message); } /** * bus_ibus_impl_emit_signal: * * Send a D-Bus signal to buses (connections) that are listening to the signal. */ static void bus_ibus_impl_emit_signal (BusIBusImpl *ibus, const gchar *signal_name, GVariant *parameters) { GDBusMessage *message = g_dbus_message_new_signal ("/org/freedesktop/IBus", "org.freedesktop.IBus", signal_name); /* set a non-zero serial to make libdbus happy */ g_dbus_message_set_serial (message, 1); g_dbus_message_set_sender (message, "org.freedesktop.IBus"); if (parameters) g_dbus_message_set_body (message, parameters); bus_dbus_impl_dispatch_message_by_rule (BUS_DEFAULT_DBUS, message, NULL); g_object_unref (message); } static void bus_ibus_impl_registry_changed (BusIBusImpl *ibus) { bus_ibus_impl_emit_signal (ibus, "RegistryChanged", NULL); } static void bus_ibus_impl_global_engine_changed (BusIBusImpl *ibus) { const gchar *name = ibus->global_engine_name ? ibus->global_engine_name : ""; bus_ibus_impl_emit_signal (ibus, "GlobalEngineChanged", g_variant_new ("(s)", name)); } gboolean bus_ibus_impl_is_use_sys_layout (BusIBusImpl *ibus) { g_assert (BUS_IS_IBUS_IMPL (ibus)); return ibus->use_sys_layout; } gboolean bus_ibus_impl_is_embed_preedit_text (BusIBusImpl *ibus) { g_assert (BUS_IS_IBUS_IMPL (ibus)); return ibus->embed_preedit_text; } BusInputContext * bus_ibus_impl_get_focused_input_context (BusIBusImpl *ibus) { g_assert (BUS_IS_IBUS_IMPL (ibus)); return ibus->focused_context; }