Load the system registry cache prior to the user one.
[platform/upstream/ibus.git] / bus / ibusimpl.c
index 889b9fa..2cd293d 100644 (file)
@@ -1,7 +1,8 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
 /* vim:set et sts=4: */
 /* ibus - The Input Bus
- * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
- * Copyright (C) 2008-2010 Red Hat, Inc.
+ * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2008-2013 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
  * Boston, MA 02111-1307, USA.
  */
 
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <stdlib.h>
+#include "ibusimpl.h"
+
 #include <locale.h>
+#include <signal.h>
 #include <strings.h>
-#include "ibusimpl.h"
-#include "dbusimpl.h"
-#include "server.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
 #include "connection.h"
-#include "registry.h"
+#include "dbusimpl.h"
 #include "factoryproxy.h"
-#include "panelproxy.h"
+#include "global.h"
 #include "inputcontext.h"
-#include "option.h"
-
-enum {
-    LAST_SIGNAL,
-};
-
-enum {
-    PROP_0,
-};
-
-// static guint            _signals[LAST_SIGNAL] = { 0 };
-
-/* functions prototype */
-static void     bus_ibus_impl_destroy           (BusIBusImpl        *ibus);
-static gboolean bus_ibus_impl_ibus_message      (BusIBusImpl        *ibus,
-                                                 BusConnection      *connection,
-                                                 IBusMessage        *message);
-static void     bus_ibus_impl_add_factory       (BusIBusImpl        *ibus,
-                                                 BusFactoryProxy    *factory);
-static void     bus_ibus_impl_set_trigger       (BusIBusImpl        *ibus,
-                                                 GValue             *value);
-static void     bus_ibus_impl_set_preload_engines
-                                                (BusIBusImpl        *ibus,
-                                                 GValue             *value);
-static void     bus_ibus_impl_set_use_sys_layout
-                                                (BusIBusImpl        *ibus,
-                                                 GValue             *value);
-static void     bus_ibus_impl_set_embed_preedit_text
-                                                (BusIBusImpl        *ibus,
-                                                 GValue             *value);
-
-static void     bus_ibus_impl_set_use_global_engine
-                                                (BusIBusImpl        *ibus,
-                                                 GValue             *value);
-static void     bus_ibus_impl_set_global_engine (BusIBusImpl        *ibus,
-                                                 BusEngineProxy     *engine);
-
-static void     bus_ibus_impl_registry_changed  (BusIBusImpl        *ibus);
-static void     _factory_destroy_cb             (BusFactoryProxy    *factory,
-                                                 BusIBusImpl        *ibus);
-
-G_DEFINE_TYPE(BusIBusImpl, bus_ibus_impl, IBUS_TYPE_SERVICE)
-
-BusIBusImpl *
-bus_ibus_impl_get_default (void)
-{
-    static BusIBusImpl *ibus = NULL;
-
-    if (ibus == NULL) {
-        ibus = (BusIBusImpl *) g_object_new (BUS_TYPE_IBUS_IMPL,
-                                             "path", IBUS_PATH_IBUS,
-                                             NULL);
-        bus_dbus_impl_register_object (BUS_DEFAULT_DBUS,
-                                       (IBusService *)ibus);
-    }
-    return ibus;
-}
-
-static void
-bus_ibus_impl_class_init (BusIBusImplClass *klass)
-{
-    IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
-
-    ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_ibus_impl_destroy;
-
-    IBUS_SERVICE_CLASS (klass)->ibus_message = (ServiceIBusMessageFunc) bus_ibus_impl_ibus_message;
-
-}
-
-static void
-_panel_destroy_cb (BusPanelProxy *panel,
-                   BusIBusImpl   *ibus)
-{
-    g_assert (BUS_IS_PANEL_PROXY (panel));
-    g_assert (BUS_IS_IBUS_IMPL (ibus));
-
-    g_return_if_fail (ibus->panel == panel);
-
-    ibus->panel = NULL;
-    g_object_unref (panel);
-}
-
-static void
-bus_ibus_impl_set_hotkey (BusIBusImpl *ibus,
-                          GQuark       hotkey,
-                          GValue      *value)
-{
-    g_assert (BUS_IS_IBUS_IMPL (ibus));
-
-    GValueArray *array;
-    gint i;
-
-    ibus_hotkey_profile_remove_hotkey_by_event (ibus->hotkey_profile, hotkey);
-
-    if (value == NULL) {
-        return;
-    }
-
-    g_return_if_fail (G_VALUE_TYPE (value) == G_TYPE_VALUE_ARRAY);
-    array = g_value_get_boxed (value);
-
-    for (i = 0; i < array->n_values; i++) {
-       GValue *str;
-
-       str = g_value_array_get_nth (array, i);
-       g_return_if_fail (G_VALUE_TYPE (str) == G_TYPE_STRING);
-
-       ibus_hotkey_profile_add_hotkey_from_string (ibus->hotkey_profile,
-                                                   g_value_get_string (str),
-                                                   hotkey);
-   }
-
-}
-
-static void
-bus_ibus_impl_set_trigger (BusIBusImpl *ibus,
-                           GValue      *value)
-{
-    GQuark hotkey = g_quark_from_static_string ("trigger");
-    bus_ibus_impl_set_hotkey (ibus, hotkey, value);
-    if (value == NULL) {
-        ibus_hotkey_profile_add_hotkey (ibus->hotkey_profile,
-                                        IBUS_space,
-                                        IBUS_CONTROL_MASK,
-                                        hotkey);
-    }
-}
-
-static void
-bus_ibus_impl_set_next_engine (BusIBusImpl *ibus,
-                               GValue      *value)
-{
-    GQuark hotkey = g_quark_from_static_string ("next-engine");
-    bus_ibus_impl_set_hotkey (ibus, hotkey, value);
-}
-
-static void
-bus_ibus_impl_set_prev_engine (BusIBusImpl *ibus,
-                               GValue      *value)
-{
-    GQuark hotkey = g_quark_from_static_string ("prev-engine");
-    bus_ibus_impl_set_hotkey (ibus, hotkey, value);
-}
-
-static void
-bus_ibus_impl_set_preload_engines (BusIBusImpl *ibus,
-                                   GValue      *value)
-{
-    GList *engine_list = NULL;
-
-    g_list_foreach (ibus->engine_list, (GFunc) g_object_unref, NULL);
-    g_list_free (ibus->engine_list);
-
-    if (value != NULL && G_VALUE_TYPE (value) == G_TYPE_VALUE_ARRAY) {
-        GValueArray *array;
-        gint i;
-
-        array = (GValueArray *) g_value_get_boxed (value);
-        for (i = 0; array && i < array->n_values; i++) {
-            const gchar *engine_name;
-            IBusEngineDesc *engine;
-
-            if (G_VALUE_TYPE (&array->values[i]) != G_TYPE_STRING)
-                continue;
-
-            engine_name = g_value_get_string (&array->values[i]);
-
-            engine = bus_registry_find_engine_by_name (ibus->registry, engine_name);
-
-            if (engine == NULL || g_list_find (engine_list, engine) != NULL)
-                continue;
-
-            engine_list = g_list_append (engine_list, engine);
-        }
-    }
-
-    g_list_foreach (engine_list, (GFunc) g_object_ref, NULL);
-    ibus->engine_list = engine_list;
-
-    if (ibus->engine_list) {
-        IBusComponent *component;
-
-        component = ibus_component_get_from_engine ((IBusEngineDesc *) ibus->engine_list->data);
-        if (component && !ibus_component_is_running (component)) {
-            ibus_component_start (component, g_verbose);
-        }
-    }
-}
+#include "panelproxy.h"
+#include "server.h"
+#include "types.h"
 
-static void
-bus_ibus_impl_set_use_sys_layout (BusIBusImpl *ibus,
-                                  GValue      *value)
-{
-    if (value != NULL && G_VALUE_TYPE (value) == G_TYPE_BOOLEAN) {
-        ibus->use_sys_layout = g_value_get_boolean (value);
-    }
-}
+struct _BusIBusImpl {
+    IBusService parent;
+    /* instance members */
+    GHashTable *factory_dict;
 
-static void
-bus_ibus_impl_set_embed_preedit_text (BusIBusImpl        *ibus,
-                                                            GValue             *value){
-    if (value != NULL && G_VALUE_TYPE (value) == G_TYPE_BOOLEAN) {
-        ibus->embed_preedit_text = g_value_get_boolean (value);
-    }
+    /* 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;
 
-static void
-bus_ibus_impl_set_use_global_engine (BusIBusImpl *ibus,
-                                     GValue      *value)
-{
-    gboolean new_value;
+    /* 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;
 
-    if (value == NULL || G_VALUE_TYPE (value) != G_TYPE_BOOLEAN) {
-        return;
-    }
+    gboolean embed_preedit_text;
 
-    new_value = g_value_get_boolean (value);
-    if (ibus->use_global_engine == new_value) {
-        return;
-    }
+    IBusRegistry    *registry;
 
-    if (new_value == TRUE) {
-        /* turn on use_global_engine option */
-        ibus->use_global_engine = TRUE;
-        if (ibus->focused_context != NULL)
-            bus_ibus_impl_set_global_engine (ibus, bus_input_context_get_engine (ibus->focused_context));
-    }
-    else {
-        /* turn off use_global_engine option */
-        bus_ibus_impl_set_global_engine (ibus, NULL);
-        ibus->use_global_engine = FALSE;
-    }
-}
+    /* a list of BusComponent objects that are created from component XML
+     * files (or from the cache of them). */
+    GList *components;
 
-static gint
-_engine_desc_cmp (IBusEngineDesc *desc1,
-                  IBusEngineDesc *desc2)
-{
-    return - ((gint) desc1->rank) + ((gint) desc2->rank);
-}
+    /* a mapping from an engine name (e.g. 'pinyin') to the corresponding
+     * IBusEngineDesc object. */
+    GHashTable *engine_table;
 
-static void
-bus_ibus_impl_set_default_preload_engines (BusIBusImpl *ibus)
-{
-    g_assert (BUS_IS_IBUS_IMPL (ibus));
+    BusInputContext *focused_context;
+    BusPanelProxy   *panel;
 
-    static gboolean done = FALSE;
-    GValue value = { 0 };
-    GList *engines, *list;
-    gchar *lang, *p;
-    GValueArray *array;
+    /* a default keymap of ibus-daemon (usually "us") which is used only
+     * when use_sys_layout is FALSE. */
+    IBusKeymap      *keymap;
 
-    if (done || ibus->config == NULL) {
-        return;
-    }
+    gboolean use_global_engine;
+    gchar *global_engine_name;
+    gchar *global_previous_engine_name;
+};
 
-    if (ibus_config_get_value (ibus->config, "general", "preload_engines", &value)) {
-        done = TRUE;
-        g_value_unset (&value);
-        return;
-    }
+struct _BusIBusImplClass {
+    IBusServiceClass parent;
 
-    done = TRUE;
-    lang = g_strdup (setlocale (LC_ALL, NULL));
-    p = index (lang, '.');
-    if (p) {
-        *p = '\0';
-    }
+    /* class members */
+};
 
-    engines = bus_registry_get_engines_by_language (ibus->registry, lang);
-    if (engines == NULL) {
-        p = index (lang, '_');
-        if (p) {
-            *p = '\0';
-            engines = bus_registry_get_engines_by_language (ibus->registry, lang);
-        }
-    }
-    g_free (lang);
+enum {
+    LAST_SIGNAL,
+};
 
-    /* sort engines by rank */
-    engines = g_list_sort (engines, (GCompareFunc) _engine_desc_cmp);
+enum {
+    PROP_0,
+};
 
-    g_value_init (&value, G_TYPE_VALUE_ARRAY);
-    array = g_value_array_new (5);
-    for (list = engines; list != NULL; list = list->next) {
-        IBusEngineDesc *desc;
-        GValue name = { 0 };
-        desc = (IBusEngineDesc *)list->data;
+/*
+static guint            _signals[LAST_SIGNAL] = { 0 };
+*/
 
-        /* ignore engines with rank <== 0 */
-        if (desc->rank <= 0)
-            break;
-        g_value_init (&name, G_TYPE_STRING);
-        g_value_set_string (&name, desc->name);
-        g_value_array_append (array, &name);
-    }
-    g_value_take_boxed (&value, array);
-    ibus_config_set_value (ibus->config, "general", "preload_engines", &value);
-    g_value_unset (&value);
-    g_list_free (engines);
-}
+/* 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);
+/* 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[] =
+    "<node>\n"
+    "  <interface name='org.freedesktop.IBus'>\n"
+    "    <property name='Address' type='s' access='read' />\n"
+    "    <property name='CurrentInputContext' type='o' access='read' />\n"
+    "    <property name='Engines' type='av' access='read' />\n"
+    "    <property name='GlobalEngine' type='v' access='read' />\n"
+    "    <property name='PreloadEngines' type='as' access='write'>\n"
+    "      <annotation\n"
+    "          name='org.freedesktop.DBus.Property.EmitsChangedSignal'\n"
+    "          value='true' />\n"
+    "    </property>\n"
+    "    <property name='EmbedPreeditText' type='b' access='readwrite'>\n"
+    "      <annotation\n"
+    "          name='org.freedesktop.DBus.Property.EmitsChangedSignal'\n"
+    "          value='true' />\n"
+    "    </property>\n"
+    "    <method name='CreateInputContext'>\n"
+    "      <arg direction='in'  type='s' name='client_name' />\n"
+    "      <arg direction='out' type='o' name='object_path' />\n"
+    "    </method>\n"
+    "    <method name='RegisterComponent'>\n"
+    "      <arg direction='in'  type='v' name='component' />\n"
+    "    </method>\n"
+    "    <method name='GetEnginesByNames'>\n"
+    "      <arg direction='in'  type='as' name='names' />\n"
+    "      <arg direction='out' type='av' name='engines' />\n"
+    "    </method>\n"
+    "    <method name='Exit'>\n"
+    "      <arg direction='in'  type='b' name='restart' />\n"
+    "    </method>\n"
+    "    <method name='Ping'>\n"
+    "      <arg direction='in'  type='v' name='data' />\n"
+    "      <arg direction='out' type='v' name='data' />\n"
+    "    </method>\n"
+    "    <method name='SetGlobalEngine'>\n"
+    "      <arg direction='in'  type='s' name='engine_name' />\n"
+    "    </method>\n"
+    "    <signal name='RegistryChanged'>\n"
+    "    </signal>\n"
+    "    <signal name='GlobalEngineChanged'>\n"
+    "      <arg type='s' name='engine_name' />\n"
+    "    </signal>\n"
+    "    <property name='ActiveEngines' type='av' access='read' />\n"
+    "    <method name='GetAddress'>\n"
+    "      <arg direction='out' type='s' name='address' />\n"
+    "      <annotation name='org.freedesktop.DBus.Deprecated' value='true'/>\n"
+    "    </method>\n"
+    "    <method name='CurrentInputContext'>\n"
+    "      <arg direction='out' type='o' name='object_path' />\n"
+    "      <annotation name='org.freedesktop.DBus.Deprecated' value='true'/>\n"
+    "    </method>\n"
+    "    <method name='ListEngines'>\n"
+    "      <arg direction='out' type='av' name='engines' />\n"
+    "      <annotation name='org.freedesktop.DBus.Deprecated' value='true'/>\n"
+    "    </method>\n"
+    "    <method name='ListActiveEngines'>\n"
+    "      <arg direction='out' type='av' name='engines' />\n"
+    "      <annotation name='org.freedesktop.DBus.Deprecated' value='true'/>\n"
+    "    </method>\n"
+    "    <method name='GetUseSysLayout'>\n"
+    "      <arg direction='out' type='b' name='enabled' />\n"
+    "      <annotation name='org.freedesktop.DBus.Deprecated' value='true'/>\n"
+    "    </method>\n"
+    "    <method name='GetUseGlobalEngine'>\n"
+    "      <arg direction='out' type='b' name='enabled' />\n"
+    "    </method>\n"
+    "    <method name='IsGlobalEngineEnabled'>\n"
+    "      <arg direction='out' type='b' name='enabled' />\n"
+    "      <annotation name='org.freedesktop.DBus.Deprecated' value='true'/>\n"
+    "    </method>\n"
+    "    <method name='GetGlobalEngine'>\n"
+    "      <arg direction='out' type='v' name='desc' />\n"
+    "      <annotation name='org.freedesktop.DBus.Deprecated' value='true'/>\n"
+    "    </method>\n"
+    "  </interface>\n"
+    "</node>\n";
+
+
+G_DEFINE_TYPE (BusIBusImpl, bus_ibus_impl, IBUS_TYPE_SERVICE)
 
 static void
-bus_ibus_impl_reload_config (BusIBusImpl *ibus)
+bus_ibus_impl_class_init (BusIBusImplClass *class)
 {
-    g_assert (BUS_IS_IBUS_IMPL (ibus));
-
-    gint i;
-    GValue value = { 0 };
-
-    const static struct {
-        gchar *section;
-        gchar *key;
-        void ( *func) (BusIBusImpl *, GValue *);
-    } entries [] = {
-        { "general/hotkey", "trigger", bus_ibus_impl_set_trigger },
-        { "general/hotkey", "next_engine", bus_ibus_impl_set_next_engine },
-        { "general/hotkey", "prev_engine", bus_ibus_impl_set_prev_engine },
-        { "general", "preload_engines", bus_ibus_impl_set_preload_engines },
-        { "general", "use_system_keyboard_layout", bus_ibus_impl_set_use_sys_layout },
-        { "general", "use_global_engine", bus_ibus_impl_set_use_global_engine },
-        { "general", "embed_preedit_text", bus_ibus_impl_set_embed_preedit_text },
-        { NULL, NULL, NULL },
-    };
-
-    for (i = 0; entries[i].section != NULL; i++) {
-        if (ibus->config != NULL &&
-            ibus_config_get_value (ibus->config,
-                                   entries[i].section,
-                                   entries[i].key,
-                                   &value)) {
-            entries[i].func (ibus, &value);
-            g_value_unset (&value);
-        }
-        else {
-            entries[i].func (ibus, NULL);
-        }
-    }
-}
-
-static void
-_config_value_changed_cb (IBusConfig  *config,
-                          gchar       *section,
-                          gchar       *key,
-                          GValue      *value,
-                          BusIBusImpl *ibus)
-{
-    g_assert (IBUS_IS_CONFIG (config));
-    g_assert (section);
-    g_assert (key);
-    g_assert (value);
-    g_assert (BUS_IS_IBUS_IMPL (ibus));
-
-    gint i;
-
-    const static struct {
-        gchar *section;
-        gchar *key;
-        void ( *func) (BusIBusImpl *, GValue *);
-    } entries [] = {
-        { "general/hotkey", "trigger",     bus_ibus_impl_set_trigger },
-        { "general/hotkey", "next_engine", bus_ibus_impl_set_next_engine },
-        { "general/hotkey", "prev_engine", bus_ibus_impl_set_prev_engine },
-        { "general", "preload_engines",    bus_ibus_impl_set_preload_engines },
-        { "general", "use_system_keyboard_layout", bus_ibus_impl_set_use_sys_layout },
-        { "general", "use_global_engine", bus_ibus_impl_set_use_global_engine },
-        { "general", "embed_preedit_text", bus_ibus_impl_set_embed_preedit_text },
-        { NULL, NULL, NULL },
-    };
-
-    for (i = 0; entries[i].section != NULL; i++) {
-        if (g_strcmp0 (entries[i].section, section) == 0 &&
-            g_strcmp0 (entries[i].key, key) == 0) {
-            entries[i].func (ibus, value);
-            break;
-        }
-    }
+    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
-_config_destroy_cb (IBusConfig  *config,
-                    BusIBusImpl *ibus)
+_panel_destroy_cb (BusPanelProxy *panel,
+                   BusIBusImpl   *ibus)
 {
-    g_assert (IBUS_IS_CONFIG (config));
+    g_assert (BUS_IS_PANEL_PROXY (panel));
     g_assert (BUS_IS_IBUS_IMPL (ibus));
 
-    g_assert (ibus->config == config);
+    g_return_if_fail (ibus->panel == panel);
 
-    g_object_unref (ibus->config);
-    ibus->config = NULL;
+    ibus->panel = NULL;
+    g_object_unref (panel);
 }
 
 static void
-_registry_changed_cb (BusRegistry *registry,
-                      BusIBusImpl *ibus)
+_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,
-                             const gchar *name,
-                             const gchar *old_name,
-                             const gchar *new_name,
-                             BusIBusImpl *ibus)
+_dbus_name_owner_changed_cb (BusDBusImpl   *dbus,
+                             BusConnection *orig_connection,
+                             const gchar   *name,
+                             const gchar   *old_name,
+                             const gchar   *new_name,
+                             BusIBusImpl   *ibus)
 {
     g_assert (BUS_IS_DBUS_IMPL (dbus));
     g_assert (name != NULL);
@@ -447,15 +319,15 @@ _dbus_name_owner_changed_cb (BusDBusImpl *dbus,
     g_assert (new_name != NULL);
     g_assert (BUS_IS_IBUS_IMPL (ibus));
 
-    BusFactoryProxy *factory;
-
     if (g_strcmp0 (name, IBUS_SERVICE_PANEL) == 0) {
         if (g_strcmp0 (new_name, "") != 0) {
+            /* a Panel process is started. */
             BusConnection *connection;
+            BusInputContext *context = NULL;
 
             if (ibus->panel != NULL) {
-                ibus_object_destroy (IBUS_OBJECT (ibus->panel));
-                /* panel should be NULL after destroy */
+                ibus_proxy_destroy ((IBusProxy *) ibus->panel);
+                /* panel should be NULL after destroy. See _panel_destroy_cb for details. */
                 g_assert (ibus->panel == NULL);
             }
 
@@ -463,7 +335,6 @@ _dbus_name_owner_changed_cb (BusDBusImpl *dbus,
             g_return_if_fail (connection != NULL);
 
             ibus->panel = bus_panel_proxy_new (connection);
-            g_object_ref_sink (ibus->panel);
 
             g_signal_connect (ibus->panel,
                               "destroy",
@@ -471,52 +342,36 @@ _dbus_name_owner_changed_cb (BusDBusImpl *dbus,
                               ibus);
 
             if (ibus->focused_context != NULL) {
-                bus_panel_proxy_focus_in (ibus->panel, ibus->focused_context);
+                context = ibus->focused_context;
             }
-        }
-    }
-    else if (g_strcmp0 (name, IBUS_SERVICE_CONFIG) == 0) {
-        if (g_strcmp0 (new_name, "") != 0) {
-            BusConnection *connection;
-
-            if (ibus->config != NULL) {
-                ibus_object_destroy (IBUS_OBJECT (ibus->config));
-                /* config should be NULL */
-                g_assert (ibus->config == NULL);
+            else if (ibus->use_global_engine) {
+                context = ibus->fake_context;
             }
 
-            connection = bus_dbus_impl_get_connection_by_name (BUS_DEFAULT_DBUS, new_name);
-            g_return_if_fail (connection != NULL);
-
-            ibus->config = g_object_new (IBUS_TYPE_CONFIG,
-                                         "name", NULL,
-                                         "path", IBUS_PATH_CONFIG,
-                                         "connection", connection,
-                                         NULL);
-            g_object_ref_sink (ibus->config);
-
-            g_signal_connect (ibus->config,
-                              "value-changed",
-                              G_CALLBACK (_config_value_changed_cb),
-                              ibus);
+            if (context != NULL) {
+                BusEngineProxy *engine;
 
-            g_signal_connect (ibus->config,
-                              "destroy",
-                              G_CALLBACK (_config_destroy_cb),
-                              ibus);
+                bus_panel_proxy_focus_in (ibus->panel, context);
 
-            bus_ibus_impl_set_default_preload_engines (ibus);
-            bus_ibus_impl_reload_config (ibus);
+                engine = bus_input_context_get_engine (context);
+                if (engine != NULL) {
+                    IBusPropList *prop_list =
+                        bus_engine_proxy_get_properties (engine);
+                    bus_panel_proxy_register_properties (ibus->panel,
+                                                         prop_list);
+                }
+            }
         }
     }
 
-    factory = bus_registry_name_owner_changed (ibus->registry, name, old_name, new_name);
-
-    if (factory) {
-        bus_ibus_impl_add_factory (ibus, factory);
-    }
+    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)
 {
@@ -526,42 +381,50 @@ bus_ibus_impl_init (BusIBusImpl *ibus)
                             NULL,
                             (GDestroyNotify) g_object_unref);
 
-    ibus->engine_list = NULL;
+    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->config = NULL;
 
-    ibus->registry = bus_registry_new ();
-
-    g_signal_connect (ibus->registry,
-                      "changed",
-                      G_CALLBACK (_registry_changed_cb),
-                      ibus);
-#ifdef G_THREADS_ENABLED
-    extern gint g_monitor_timeout;
-    if (g_monitor_timeout != 0) {
-        bus_registry_start_monitor_changes (ibus->registry);
-    }
-#endif
-
-    ibus->hotkey_profile = ibus_hotkey_profile_new ();
     ibus->keymap = ibus_keymap_get ("us");
 
-    ibus->use_sys_layout = FALSE;
+    ibus->use_sys_layout = TRUE;
     ibus->embed_preedit_text = TRUE;
-    ibus->use_global_engine = FALSE;
-    ibus->global_engine = NULL;
+    ibus->use_global_engine = TRUE;
+    ibus->global_engine_name = NULL;
+    ibus->global_previous_engine_name = NULL;
 
-    bus_ibus_impl_reload_config (ibus);
+    /* 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)
 {
@@ -570,7 +433,7 @@ bus_ibus_impl_destroy (BusIBusImpl *ibus)
     gint status;
     gboolean flag;
 
-    bus_registry_stop_all_components (ibus->registry);
+    g_list_foreach (ibus->components, (GFunc) bus_component_stop, NULL);
 
     pid = 0;
     timeout = 0;
@@ -588,6 +451,7 @@ bus_ibus_impl_destroy (BusIBusImpl *ibus)
                 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;
@@ -598,14 +462,9 @@ bus_ibus_impl_destroy (BusIBusImpl *ibus)
                 }
             }
         }
-    };
-
-    g_list_foreach (ibus->engine_list, (GFunc) g_object_unref, NULL);
-    g_list_free (ibus->engine_list);
-    ibus->engine_list = NULL;
+    }
 
-    g_list_foreach (ibus->register_engine_list, (GFunc) g_object_unref, NULL);
-    g_list_free (ibus->register_engine_list);
+    g_list_free_full (ibus->register_engine_list, g_object_unref);
     ibus->register_engine_list = NULL;
 
     if (ibus->factory_dict != NULL) {
@@ -613,391 +472,358 @@ bus_ibus_impl_destroy (BusIBusImpl *ibus)
         ibus->factory_dict = NULL;
     }
 
-    if (ibus->hotkey_profile != NULL) {
-        g_object_unref (ibus->hotkey_profile);
-        ibus->hotkey_profile = NULL;
-    }
-
     if (ibus->keymap != NULL) {
         g_object_unref (ibus->keymap);
         ibus->keymap = NULL;
     }
 
-    if (ibus->global_engine) {
-        g_object_unref (ibus->global_engine);
-        ibus->global_engine = NULL;
-    }
-
-    bus_server_quit (BUS_DEFAULT_SERVER);
-    ibus_object_destroy ((IBusObject *) BUS_DEFAULT_SERVER);
-    IBUS_OBJECT_CLASS(bus_ibus_impl_parent_class)->destroy (IBUS_OBJECT (ibus));
-}
-
-/* introspectable interface */
-static IBusMessage *
-_ibus_introspect (BusIBusImpl     *ibus,
-                  IBusMessage     *message,
-                  BusConnection   *connection)
-{
-    static const gchar *introspect =
-        DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
-        "<node>\n"
-        "  <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
-        "    <method name=\"Introspect\">\n"
-        "      <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
-        "    </method>\n"
-        "  </interface>\n"
-        "  <interface name=\"org.freedesktop.IBus\">\n"
-        "    <method name=\"GetAddress\">\n"
-        "      <arg name=\"address\" direction=\"out\" type=\"s\"/>\n"
-        "    </method>\n"
-        "    <method name=\"CreateInputContext\">\n"
-        "      <arg name=\"name\" direction=\"in\" type=\"s\"/>\n"
-        "      <arg name=\"context\" direction=\"out\" type=\"o\"/>\n"
-        "    </method>\n"
-        "    <method name=\"CurrentInputContext\">\n"
-        "      <arg name=\"name\" direction=\"out\" type=\"s\"/>\n"
-        "    </method>\n"
-        "    <method name=\"RegisterComponent\">\n"
-        "      <arg name=\"components\" direction=\"in\" type=\"v\"/>\n"
-        "    </method>\n"
-        "    <method name=\"ListEngines\">\n"
-        "      <arg name=\"engines\" direction=\"out\" type=\"av\"/>\n"
-        "    </method>\n"
-        "    <method name=\"ListActiveEngines\">\n"
-        "      <arg name=\"engines\" direction=\"out\" type=\"av\"/>\n"
-        "    </method>\n"
-        "    <method name=\"Exit\">\n"
-        "      <arg name=\"restart\" direction=\"in\" type=\"b\"/>\n"
-        "    </method>\n"
-        "    <method name=\"Ping\">\n"
-        "      <arg name=\"data\" direction=\"in\" type=\"v\"/>\n"
-        "      <arg name=\"data\" direction=\"out\" type=\"v\"/>\n"
-        "    </method>\n"
-        "    <signal name=\"RegistryChanged\">\n"
-        "    </signal>\n"
-        "  </interface>\n"
-        "</node>\n";
-
-    IBusMessage *reply_message;
-    reply_message = ibus_message_new_method_return (message);
-    ibus_message_append_args (reply_message,
-                              G_TYPE_STRING, &introspect,
-                              G_TYPE_INVALID);
-
-    return reply_message;
-}
-
-
-
-static IBusMessage *
-_ibus_get_address (BusIBusImpl     *ibus,
-                   IBusMessage     *message,
-                   BusConnection   *connection)
-{
-    const gchar *address;
-    IBusMessage *reply;
+    g_free (ibus->global_engine_name);
+    ibus->global_engine_name = NULL;
+
+    g_free (ibus->global_previous_engine_name);
+    ibus->global_previous_engine_name = NULL;
 
-    address = ibus_server_get_address (IBUS_SERVER (BUS_DEFAULT_SERVER));
+    if (ibus->fake_context) {
+        g_object_unref (ibus->fake_context);
+        ibus->fake_context = NULL;
+    }
 
-    reply = ibus_message_new_method_return (message);
-    ibus_message_append_args (reply,
-                              G_TYPE_STRING, &address,
-                              G_TYPE_INVALID);
+    bus_ibus_impl_registry_destroy (ibus);
 
-    return reply;
+    IBUS_OBJECT_CLASS (bus_ibus_impl_parent_class)->destroy (IBUS_OBJECT (ibus));
 }
 
-static BusEngineProxy *
-bus_ibus_impl_create_engine (IBusEngineDesc *engine_desc)
+/**
+ * _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)
 {
-    IBusComponent *comp;
-    BusFactoryProxy *factory;
-    BusEngineProxy *engine;
-    GTimeVal t1, t2;
+    if (error) {
+        *error = NULL;
+    }
 
-    factory = bus_factory_proxy_get_from_engine (engine_desc);
+    return g_variant_new_string (bus_server_get_address ());
+}
 
-    if (factory == NULL) {
-        /* try to execute the engine */
-        comp = ibus_component_get_from_engine (engine_desc);
-        g_assert (comp);
-
-        if (!ibus_component_is_running (comp)) {
-            ibus_component_start (comp, g_verbose);
-            g_get_current_time (&t1);
-            while (1) {
-                if (g_main_context_pending (NULL)) {
-                    g_main_context_iteration (NULL, FALSE);
-                    factory = bus_factory_proxy_get_from_engine (engine_desc);
-                    if (factory != NULL) {
-                        break;
-                    }
-                }
-                g_get_current_time (&t2);
-                if (t2.tv_sec - t1.tv_sec >= 5)
-                    break;
-            }
-        }
-        else {
-            factory = bus_factory_proxy_get_from_engine (engine_desc);
-        }
-    }
+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;
 
-    if (factory == NULL) {
-        return NULL;
-    }
+    g_warning ("org.freedesktop.IBus.GetAddress() is deprecated!");
 
-    g_object_ref (factory);
-    engine = bus_factory_proxy_create_engine (factory, engine_desc);
-    g_object_unref (factory);
+    variant = _ibus_get_address (ibus, connection, &error);
 
-    return engine;
+    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,
-                          gchar *engine_name)
+_find_engine_desc_by_name (BusIBusImpl *ibus,
+                           const gchar *engine_name)
 {
-    IBusEngineDesc *engine_desc = NULL;
+    IBusEngineDesc *desc = NULL;
     GList *p;
 
     /* find engine in registered engine list */
     for (p = ibus->register_engine_list; p != NULL; p = p->next) {
-        engine_desc = (IBusEngineDesc *)p->data;
-        if (g_strcmp0 (engine_desc->name, engine_name) == 0)
-            return engine_desc;
-    }
-
-    /* find engine in preload engine list */
-    for (p = ibus->engine_list; p != NULL; p = p->next) {
-        engine_desc = (IBusEngineDesc *)p->data;
-        if (g_strcmp0 (engine_desc->name, engine_name) == 0)
-            return engine_desc;
+        desc = (IBusEngineDesc *) p->data;
+        if (g_strcmp0 (ibus_engine_desc_get_name (desc), engine_name) == 0)
+            return desc;
     }
 
     return NULL;
 }
 
-static void
+/**
+ * _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,
-                            gchar           *engine_name,
+                            const gchar     *engine_name,
                             BusIBusImpl     *ibus)
 {
-    IBusEngineDesc *engine_desc = NULL;
-    BusEngineProxy *engine;
+    if (engine_name == NULL || engine_name[0] == '\0')
+        engine_name = DEFAULT_ENGINE;
 
-    /* context should has focus before request an engine */
-    g_return_if_fail (bus_input_context_has_focus (context));
+    return bus_ibus_impl_get_engine_desc (ibus, engine_name);
+}
 
-    if (engine_name == NULL || engine_name[0] == '\0') {
-        /* request default engine */
-        if (ibus->register_engine_list) {
-            engine_desc = (IBusEngineDesc *)ibus->register_engine_list->data;
-        }
-        else if (ibus->engine_list) {
-            engine_desc = (IBusEngineDesc *)ibus->engine_list->data;
-        }
-    }
-    else {
-        /* request engine by name */
-        engine_desc = _find_engine_desc_by_name (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);
 
-    if (engine_desc != NULL) {
-        engine = bus_ibus_impl_create_engine (engine_desc);
-        if (engine != NULL) {
-            bus_input_context_set_engine (context, engine);
-        }
+    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
-_context_request_next_engine_cb (BusInputContext *context,
-                                 BusIBusImpl     *ibus)
+bus_ibus_impl_set_context_engine_from_desc (BusIBusImpl     *ibus,
+                                            BusInputContext *context,
+                                            IBusEngineDesc  *desc)
 {
-    BusEngineProxy *engine;
-    IBusEngineDesc *desc;
-    IBusEngineDesc *next_desc = NULL;
-    GList *p;
+    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);
+}
 
-    engine = bus_input_context_get_engine (context);
-    if (engine == NULL) {
-        _context_request_engine_cb (context, NULL, ibus);
+/**
+ * bus_ibus_impl_set_focused_context:
+ *
+ * Set the current focused context.
+ */
+static void
+bus_ibus_impl_set_focused_context (BusIBusImpl     *ibus,
+                                   BusInputContext *context)
+{
+    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;
     }
 
-    desc = bus_engine_proxy_get_desc (engine);
+    BusEngineProxy *engine = NULL;
 
-    p = g_list_find (ibus->register_engine_list, desc);
-    if (p != NULL) {
-        p = p->next;
-    }
-    if (p == NULL) {
-        p = g_list_find (ibus->engine_list, desc);
-        if (p != NULL) {
-            p = p->next;
+    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);
+                bus_input_context_set_engine (ibus->focused_context, NULL);
+            }
         }
-    }
 
-    if (p != NULL) {
-        next_desc = (IBusEngineDesc*) p->data;
+        if (ibus->panel != NULL)
+            bus_panel_proxy_focus_out (ibus->panel, ibus->focused_context);
+
+        g_object_unref (ibus->focused_context);
+        ibus->focused_context = NULL;
     }
-    else {
-        if (ibus->register_engine_list) {
-            next_desc = (IBusEngineDesc *)ibus->register_engine_list->data;
-        }
-        else if (ibus->engine_list) {
-            next_desc = (IBusEngineDesc *)ibus->engine_list->data;
-        }
+
+    if (context == NULL && ibus->use_global_engine) {
+        context = ibus->fake_context;
     }
 
-    if (next_desc != NULL) {
-        engine = bus_ibus_impl_create_engine (next_desc);
+    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->panel != NULL)
+            bus_panel_proxy_focus_in (ibus->panel, context);
     }
+
+    if (engine != NULL)
+        g_object_unref (engine);
 }
 
 static void
-_context_request_prev_engine_cb (BusInputContext *context,
-                                 BusIBusImpl     *ibus)
+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);
+    } else if (ibus->fake_context) {
+        bus_input_context_set_engine (ibus->fake_context, engine);
+    }
 }
 
 static void
-_global_engine_destroy_cb (BusEngineProxy *engine,
-                           BusIBusImpl    *ibus)
+bus_ibus_impl_set_global_engine_by_name (BusIBusImpl *ibus,
+                                         const gchar *name)
 {
-    if (ibus->global_engine != engine) {
+    if (!ibus->use_global_engine)
+        return;
+
+    BusInputContext *context =
+            ibus->focused_context != NULL ? ibus->focused_context : ibus->fake_context;
+
+    if (context == NULL) {
         return;
     }
 
-    g_signal_handlers_disconnect_by_func (ibus->global_engine,
-                G_CALLBACK (_global_engine_destroy_cb), ibus);
-    g_object_unref (ibus->global_engine);
-    ibus->global_engine = NULL;
+    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_set_global_engine (BusIBusImpl    *ibus,
-                                 BusEngineProxy *engine)
+bus_ibus_impl_check_global_engine (BusIBusImpl *ibus)
 {
-    g_assert (ibus->use_global_engine == TRUE);
+    GList *engine_list = NULL;
 
-    if (ibus->global_engine == engine) {
+    /* 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 (ibus->global_engine) {
-        ibus_object_destroy ((IBusObject *)ibus->global_engine);
-        /* global_engine should be NULL */
-        g_assert (ibus->global_engine == NULL);
+    /* 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;
     }
 
-    if (engine != NULL && !IBUS_OBJECT_DESTROYED (engine)) {
-        g_object_ref (engine);
-        ibus->global_engine = engine;
-        g_signal_connect (ibus->global_engine, "destroy",
-                G_CALLBACK (_global_engine_destroy_cb), ibus);
+    /* 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)
 {
-    BusEngineProxy *engine;
-
-    if (context != ibus->focused_context ||
-        ibus->use_global_engine != TRUE) {
+    if (!ibus->use_global_engine)
         return;
-    }
 
-    engine = bus_input_context_get_engine (context);
-    bus_ibus_impl_set_global_engine (ibus, engine);
+    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_out_cb (BusInputContext    *context,
-                       BusIBusImpl        *ibus)
+_context_focus_in_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 */
+    /* 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;
     }
 
-    /* Do noting if it is not focused context. */
-    if (ibus->focused_context != context) {
-        return;
-    }
-
-    ibus->focused_context = NULL;
-
-    if (ibus->panel != NULL) {
-        bus_panel_proxy_focus_out (ibus->panel, context);
-    }
-
-    /* If the use_global_engine option is enabled,
-     * the global engine shoule be detached from the focused context. */
-    if (ibus->use_global_engine) {
-        bus_input_context_set_engine (context, NULL);
-    }
-
-    g_object_unref (context);
+    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_in_cb (BusInputContext *context,
-                      BusIBusImpl     *ibus)
+_context_focus_out_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. */
+    /* 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 nothing if it is focused context already. */
-    if (ibus->focused_context == context) {
+    /* Do noting if it is not focused context. */
+    if (ibus->focused_context != context) {
         return;
     }
 
-    if (ibus->focused_context) {
-        /* focus out context */
-        bus_input_context_focus_out (ibus->focused_context);
-        g_assert (ibus->focused_context == NULL);
-    }
-
-    g_object_ref (context);
-    ibus->focused_context = context;
-
-    /* If the use_global_engine option is enabled, then we need:
-     * - Switch the context to use the global engine or save the context's
-     *   existing engine as global engine.
-     * - Set the context's enabled state according to the saved state.
-     * Note: we get this signal only if the context supports IBUS_CAP_FOCUS. */
-    if (ibus->use_global_engine) {
-        bus_input_context_set_engine (context, ibus->global_engine);
-        if (ibus->global_engine &&
-            bus_engine_proxy_is_enabled (ibus->global_engine)) {
-            bus_input_context_enable (context);
-        }
-    }
-
-    if (ibus->panel != NULL) {
-        bus_panel_proxy_focus_in (ibus->panel, ibus->focused_context);
-    }
+    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)
@@ -1006,77 +832,42 @@ _context_destroy_cb (BusInputContext    *context,
     g_assert (BUS_IS_INPUT_CONTEXT (context));
 
     if (context == ibus->focused_context) {
-        /* focus out context */
-        bus_input_context_focus_out (ibus->focused_context);
-        g_assert (ibus->focused_context == NULL);
+        bus_ibus_impl_set_focused_context (ibus, NULL);
     }
 
     ibus->contexts = g_list_remove (ibus->contexts, context);
     g_object_unref (context);
 }
 
-#if 0
-static void
-_context_enabled_cb (BusInputContext    *context,
-                     BusIBusImpl        *ibus)
-{
-}
-
-static void
-_context_disabled_cb (BusInputContext    *context,
-                      BusIBusImpl        *ibus)
-{
-}
-#endif
-
-static IBusMessage *
-_ibus_create_input_context (BusIBusImpl     *ibus,
-                            IBusMessage     *message,
-                            BusConnection   *connection)
+/**
+ * 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)
 {
-    g_assert (BUS_IS_IBUS_IMPL (ibus));
-    g_assert (message != NULL);
-    g_assert (BUS_IS_CONNECTION (connection));
-
-    gint i;
-    gchar *client;
-    IBusError *error;
-    IBusMessage *reply;
-    BusInputContext *context;
-    const gchar *path;
-
-    if (!ibus_message_get_args (message,
-                                &error,
-                                G_TYPE_STRING, &client,
-                                G_TYPE_INVALID)) {
-        reply = ibus_message_new_error (message,
-                                        DBUS_ERROR_INVALID_ARGS,
-                                        "Argument 1 of CreateInputContext should be an string");
-        ibus_error_free (error);
-        return reply;
-    }
-
-    context = bus_input_context_new (connection, 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) },
-        { "request-next-engine", G_CALLBACK (_context_request_next_engine_cb) },
-        { "request-prev-engine", G_CALLBACK (_context_request_prev_engine_cb) },
+        { "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) },
-    #if 0
-        { "enabled",        G_CALLBACK (_context_enabled_cb) },
-        { "disabled",       G_CALLBACK (_context_disabled_cb) },
-    #endif
     };
 
+    gint i;
     for (i = 0; i < G_N_ELEMENTS (signals); i++) {
         g_signal_connect (context,
                           signals[i].name,
@@ -1084,322 +875,980 @@ _ibus_create_input_context (BusIBusImpl     *ibus,
                           ibus);
     }
 
-    path = ibus_service_get_path ((IBusService *) context);
-    reply = ibus_message_new_method_return (message);
-    ibus_message_append_args (reply,
-                              IBUS_TYPE_OBJECT_PATH, &path,
-                              G_TYPE_INVALID);
+    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);
-    return reply;
+                                   (IBusService *) context);
+    g_object_ref (context);
+    return context;
 }
 
-static IBusMessage *
-_ibus_current_input_context (BusIBusImpl     *ibus,
-                            IBusMessage     *message,
-                            BusConnection   *connection)
+/**
+ * _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)
 {
-    g_assert (BUS_IS_IBUS_IMPL (ibus));
-    g_assert (message != NULL);
-    g_assert (BUS_IS_CONNECTION (connection));
+    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!");
+    }
+}
 
-    IBusMessage *reply;
-    const gchar *path;
+/**
+ * _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)
     {
-        reply = ibus_message_new_error (message,
-                                        DBUS_ERROR_FAILED,
-                                        "No input context focused");
-        return reply;
+        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);
+    }
+}
 
-    reply = ibus_message_new_method_return (message);
-    path = ibus_service_get_path((IBusService *)ibus->focused_context);
-    ibus_message_append_args (reply,
-                              G_TYPE_STRING, &path,
-                              G_TYPE_INVALID);
-
-    return reply;
+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,
+                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
-_factory_destroy_cb (BusFactoryProxy    *factory,
-                     BusIBusImpl        *ibus)
+_component_destroy_cb (BusComponent *component,
+                       BusIBusImpl  *ibus)
 {
     g_assert (BUS_IS_IBUS_IMPL (ibus));
-    g_assert (BUS_IS_FACTORY_PROXY (factory));
+    g_assert (BUS_IS_COMPONENT (component));
 
-    IBusComponent *component;
-    GList *engines, *p;
+    ibus->registered_components = g_list_remove (ibus->registered_components, component);
 
-    ibus->factory_list = g_list_remove (ibus->factory_list, factory);
+    /* 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);
 
-    component = bus_factory_proxy_get_component (factory);
+    g_object_unref (component);
 
-    if (component != NULL) {
-        p = engines = ibus_component_get_engines (component);
-        for (; 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);
+    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);
 }
 
-static void
-bus_ibus_impl_add_factory (BusIBusImpl     *ibus,
-                           BusFactoryProxy *factory)
+/**
+ * _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)
 {
-    g_assert (BUS_IS_IBUS_IMPL (ibus));
-    g_assert (BUS_IS_FACTORY_PROXY (factory));
+    GVariantBuilder builder;
+    GList *engines = NULL;
+    GList *p;
+
+    if (error) {
+        *error = NULL;
+    }
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
 
-    g_object_ref_sink (factory);
-    ibus->factory_list = g_list_append (ibus->factory_list, factory);
+    engines = g_hash_table_get_values (ibus->engine_table);
 
-    g_signal_connect (factory, "destroy", G_CALLBACK (_factory_destroy_cb), ibus);
+    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;
 
-static IBusMessage *
-_ibus_register_component (BusIBusImpl     *ibus,
-                          IBusMessage     *message,
-                          BusConnection   *connection)
+    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)
 {
-    IBusMessage *reply;
-    IBusError *error;
-    gboolean retval;
-    GList *engines;
-    IBusComponent *component;
-    BusFactoryProxy *factory;
+    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));
+}
 
-    retval = ibus_message_get_args (message, &error,
-                                    IBUS_TYPE_COMPONENT, &component,
-                                    G_TYPE_INVALID);
+/**
+ * _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 (!retval) {
-        reply = ibus_message_new_error_printf (message,
-                                               DBUS_ERROR_INVALID_ARGS,
-                                               "1st Argument must be IBusComponent: %s",
-                                               error->message);
-        ibus_error_free (error);
-        return reply;
+    if (error) {
+        *error = NULL;
     }
 
-    g_object_ref_sink (component);
-    factory = bus_factory_proxy_new (component, connection);
+    g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
 
-    if (factory == NULL) {
-        reply = ibus_message_new_error (message,
-                                        DBUS_ERROR_FAILED,
-                                        "Can not create factory");
-        return reply;
+    for (p = ibus->register_engine_list; p != NULL; p = p->next) {
+        g_variant_builder_add (
+                &builder, "v",
+                ibus_serializable_serialize ((IBusSerializable *) p->data));
     }
 
-    bus_ibus_impl_add_factory (ibus, factory);
+    return g_variant_builder_end (&builder);
+}
 
-    engines = ibus_component_get_engines (component);
+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_list_foreach (engines, (GFunc) g_object_ref, NULL);
-    ibus->register_engine_list = g_list_concat (ibus->register_engine_list, engines);
-    g_object_unref (component);
+    g_warning ("org.freedesktop.IBus.ListActiveEngines() is deprecated!");
 
-    reply = ibus_message_new_method_return (message);
-    return reply;
+    variant = _ibus_get_active_engines (ibus, connection, &error);
+
+    g_dbus_method_invocation_return_value (invocation,
+                                           g_variant_new ("(@av)", variant));
 }
 
-static IBusMessage *
-_ibus_list_engines (BusIBusImpl   *ibus,
-                    IBusMessage   *message,
-                    BusConnection *connection)
+/**
+ * _ibus_exit:
+ *
+ * Implement the "Exit" method call of the org.freedesktop.IBus interface.
+ */
+static void
+_ibus_exit (BusIBusImpl           *ibus,
+            GVariant              *parameters,
+            GDBusMethodInvocation *invocation)
 {
-    IBusMessage *reply;
-    IBusMessageIter iter, sub_iter;
-    GList *engines, *p;
+    gboolean restart = FALSE;
+    g_variant_get (parameters, "(b)", &restart);
 
-    reply = ibus_message_new_method_return (message);
+    g_dbus_method_invocation_return_value (invocation, NULL);
 
-    ibus_message_iter_init_append (reply, &iter);
-    ibus_message_iter_open_container (&iter, IBUS_TYPE_ARRAY, "v", &sub_iter);
+    /* Make sure the reply has been sent out before exit */
+    g_dbus_connection_flush_sync (
+            g_dbus_method_invocation_get_connection (invocation),
+            NULL,
+            NULL);
 
-    engines = bus_registry_get_engines (ibus->registry);
-    for (p = engines; p != NULL; p = p->next) {
-        ibus_message_iter_append (&sub_iter, IBUS_TYPE_ENGINE_DESC, &(p->data));
+    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;
     }
-    g_list_free (engines);
-    ibus_message_iter_close_container (&iter, &sub_iter);
 
-    return reply;
+    return g_variant_new_boolean (ibus->use_sys_layout);
 }
 
-static IBusMessage *
-_ibus_list_active_engines (BusIBusImpl   *ibus,
-                           IBusMessage   *message,
-                           BusConnection *connection)
+static void
+_ibus_get_use_sys_layout_depre (BusIBusImpl           *ibus,
+                                GVariant              *parameters,
+                                GDBusMethodInvocation *invocation)
 {
-    IBusMessage *reply;
-    IBusMessageIter iter, sub_iter;
-    GList *p;
+    GDBusConnection *connection =
+            g_dbus_method_invocation_get_connection (invocation);
+    GError *error = NULL;
+    GVariant *variant = NULL;
 
-    reply = ibus_message_new_method_return (message);
+    g_warning ("org.freedesktop.IBus.GetUseSysLayout() is deprecated!");
 
-    ibus_message_iter_init_append (reply, &iter);
-    ibus_message_iter_open_container (&iter, IBUS_TYPE_ARRAY, "v", &sub_iter);
+    variant = _ibus_get_use_sys_layout (ibus, connection, &error);
 
-    for (p = ibus->engine_list; p != NULL; p = p->next) {
-        ibus_message_iter_append (&sub_iter, IBUS_TYPE_ENGINE_DESC, &(p->data));
-    }
+    g_dbus_method_invocation_return_value (invocation,
+            g_variant_new ("(b)", g_variant_get_boolean (variant)));
 
-    for (p = ibus->register_engine_list; p != NULL; p = p->next) {
-        ibus_message_iter_append (&sub_iter, IBUS_TYPE_ENGINE_DESC, &(p->data));
+    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;
     }
-    ibus_message_iter_close_container (&iter, &sub_iter);
 
-    return reply;
+    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!");
 
-static IBusMessage *
-_ibus_exit (BusIBusImpl     *ibus,
-            IBusMessage     *message,
-            BusConnection   *connection)
+    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)
 {
-    IBusMessage *reply;
-    IBusError *error;
-    gboolean restart;
+    IBusEngineDesc *desc = NULL;
 
-    if (!ibus_message_get_args (message,
-                                &error,
-                                G_TYPE_BOOLEAN, &restart,
-                                G_TYPE_INVALID)) {
-        reply = ibus_message_new_error (message,
-                                        DBUS_ERROR_INVALID_ARGS,
-                                        "Argument 1 of Exit should be an boolean");
-        ibus_error_free (error);
-        return reply;
+    if (error) {
+        *error = NULL;
     }
 
-    reply = ibus_message_new_method_return (message);
-    ibus_connection_send ((IBusConnection *) connection, reply);
-    ibus_connection_flush ((IBusConnection *) connection);
-    ibus_message_unref (reply);
+    do {
+        if (!ibus->use_global_engine)
+            break;
+        BusInputContext *context = ibus->focused_context;
+        if (context == NULL)
+            context = ibus->fake_context;
 
-    ibus_object_destroy ((IBusObject *) BUS_DEFAULT_SERVER);
+        desc = bus_input_context_get_engine_desc (context);
 
-    if (!restart) {
-        exit (0);
+        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,
+                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);
     }
-    else {
-        extern gchar **g_argv;
-        gchar *exe;
-        gint fd;
+}
 
-        exe = g_strdup_printf ("/proc/%d/exe", getpid ());
-        exe = g_file_read_link (exe, NULL);
+struct _SetGlobalEngineData {
+    BusIBusImpl *ibus;
+    GDBusMethodInvocation *invocation;
+};
+typedef struct _SetGlobalEngineData SetGlobalEngineData;
 
-        if (exe == NULL)
-            exe = BINDIR"/ibus-daemon";
+static void
+_ibus_set_global_engine_ready_cb (BusInputContext       *context,
+                                  GAsyncResult          *res,
+                                  SetGlobalEngineData   *data)
+{
+    BusIBusImpl *ibus = data->ibus;
 
-        /* close all fds except stdin, stdout, stderr */
-        for (fd = 3; fd <= sysconf (_SC_OPEN_MAX); fd ++) {
-            close (fd);
+    GError *error = NULL;
+    if (!bus_input_context_set_engine_by_desc_finish (context, res, &error)) {
+        g_error_free (error);
+        g_dbus_method_invocation_return_error (data->invocation,
+                                               G_DBUS_ERROR,
+                                               G_DBUS_ERROR_FAILED,
+                                               "Set global engine failed.");
+    }
+    else {
+        g_dbus_method_invocation_return_value (data->invocation, NULL);
+
+        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. */
+            BusEngineProxy *engine = bus_input_context_get_engine (context);
+            if (engine && ibus->focused_context != NULL) {
+                g_object_ref (engine);
+                bus_input_context_set_engine (context, NULL);
+                bus_input_context_set_engine (ibus->focused_context, engine);
+                g_object_unref (engine);
+            }
         }
+    }
 
-        execv (exe, g_argv);
-        g_warning ("execv %s failed!", g_argv[0]);
-        exit (-1);
+    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;
     }
 
-    /* should not reach here */
-    g_assert_not_reached ();
+    BusInputContext *context = ibus->focused_context;
+    if (context == NULL)
+        context = ibus->fake_context;
 
-    return NULL;
+    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,
+                                               "Can not 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 IBusMessage *
-_ibus_ping (BusIBusImpl     *ibus,
-            IBusMessage     *message,
-            BusConnection   *connection)
+static void
+_ibus_is_global_engine_enabled_depre (BusIBusImpl           *ibus,
+                                      GVariant              *parameters,
+                                      GDBusMethodInvocation *invocation)
 {
-    IBusMessage *reply;
-    IBusMessageIter src, dst;
+    GDBusConnection *connection =
+            g_dbus_method_invocation_get_connection (invocation);
+    GError *error = NULL;
+    GVariant *variant = NULL;
 
-    reply = ibus_message_new_method_return (message);
+    g_warning ("org.freedesktop.IBus.IsGlobalEngineEnabled() is deprecated!");
 
-    ibus_message_iter_init (message, &src);
-    ibus_message_iter_init_append (reply, &dst);
+    variant = _ibus_get_global_engine_enabled (ibus, connection, &error);
 
-    ibus_message_iter_copy_data (&dst, &src);
+    g_dbus_method_invocation_return_value (invocation,
+            g_variant_new ("(b)", g_variant_get_boolean (variant)));
 
-    return reply;
+    g_variant_unref (variant);
 }
 
+/**
+ * _ibus_set_preload_engines:
+ *
+ * Implement the "PreloadEngines" method call of the
+ * org.freedesktop.IBus interface.
+ */
 static gboolean
-bus_ibus_impl_ibus_message (BusIBusImpl     *ibus,
-                            BusConnection   *connection,
-                            IBusMessage     *message)
+_ibus_set_preload_engines (BusIBusImpl     *ibus,
+                           GDBusConnection *connection,
+                           GVariant        *value,
+                           GError         **error)
 {
-    g_assert (BUS_IS_IBUS_IMPL (ibus));
-    g_assert (BUS_IS_CONNECTION (connection));
-    g_assert (message != NULL);
+    int i, j;
+    const gchar **names = NULL;
+    IBusEngineDesc *desc = NULL;
+    BusComponent *component = NULL;
+    BusFactoryProxy *factory = NULL;
+    GPtrArray *array = g_ptr_array_new ();
 
-    gint i;
-    IBusMessage *reply_message = NULL;
+    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,
+                         "Can not 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);
+
+    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;
+    }
+
+    ibus->embed_preedit_text = g_variant_get_boolean (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 *interface;
-        const gchar *name;
-        IBusMessage *(* handler) (BusIBusImpl *, IBusMessage *, BusConnection *);
-    } handlers[] =  {
-        /* Introspectable interface */
-        { DBUS_INTERFACE_INTROSPECTABLE,
-                               "Introspect",            _ibus_introspect },
+        const gchar *method_name;
+        void (* method_callback) (BusIBusImpl *,
+                                  GVariant *,
+                                  GDBusMethodInvocation *);
+    } methods [] =  {
         /* IBus interface */
-        { IBUS_INTERFACE_IBUS, "GetAddress",            _ibus_get_address },
-        { IBUS_INTERFACE_IBUS, "CreateInputContext",    _ibus_create_input_context },
-        { IBUS_INTERFACE_IBUS, "CurrentInputContext",   _ibus_current_input_context },
-        { IBUS_INTERFACE_IBUS, "RegisterComponent",     _ibus_register_component },
-        { IBUS_INTERFACE_IBUS, "ListEngines",           _ibus_list_engines },
-        { IBUS_INTERFACE_IBUS, "ListActiveEngines",     _ibus_list_active_engines },
-        { IBUS_INTERFACE_IBUS, "Exit",                  _ibus_exit },
-        { IBUS_INTERFACE_IBUS, "Ping",                  _ibus_ping },
+        { "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 },
     };
 
-    ibus_message_set_sender (message, bus_connection_get_unique_name (connection));
-    ibus_message_set_destination (message, DBUS_SERVICE_DBUS);
+    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);
+    }
 
-    if (ibus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_CALL) {
-        for (i = 0; i < G_N_ELEMENTS (handlers); i++) {
-            if (ibus_message_is_method_call (message,
-                                             handlers[i].interface,
-                                             handlers[i].name)) {
+    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);
+        }
+    }
 
-                reply_message = handlers[i].handler (ibus, message, connection);
-                if (reply_message) {
+    g_warning ("service_get_property received an unknown property: %s",
+               property_name ? property_name : "(null)");
+    return NULL;
+}
 
-                    ibus_message_set_sender (reply_message, DBUS_SERVICE_DBUS);
-                    ibus_message_set_destination (reply_message, bus_connection_get_unique_name (connection));
-                    ibus_message_set_no_reply (reply_message, TRUE);
+static void
+_emit_properties_changed (BusIBusImpl     *service,
+                          GDBusConnection *connection,
+                          const gchar     *object_path,
+                          const gchar     *interface_name,
+                          const gchar     *property_name,
+                          GVariant        *value,
+                          gboolean         is_changed)
+{
+    GVariantBuilder *builder;
+    GVariantBuilder *invalidated_builder;
+    GError *error = NULL;
+
+    builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
+    invalidated_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
+
+    if (is_changed) {
+        g_variant_builder_add (builder, "{sv}", property_name, value);
+    } else {
+        g_variant_builder_add (invalidated_builder, "s", property_name);
+    }
+
+    g_dbus_connection_emit_signal (connection,
+                                   NULL,
+                                   object_path,
+                                   "org.freedesktop.DBus.Properties",
+                                   "PropertiesChanged",
+                                   g_variant_new ("(sa{sv}as)",
+                                                  interface_name,
+                                                  builder,
+                                                  invalidated_builder),
+                                   &error);
+
+    if (error) {
+        g_warning ("Failed to emit property %s in %s.%s: %s",
+                   property_name,
+                   "org.freedesktop.DBus.Properties",
+                   "PropertiesChanged",
+                   error->message);
+        g_error_free (error);
+    }
+}
 
-                    ibus_connection_send ((IBusConnection *) connection, reply_message);
-                    ibus_message_unref (reply_message);
-                }
+/**
+ * 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;
 
-                g_signal_stop_emission_by_name (ibus, "ibus-message");
-                return TRUE;
-            }
+    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) {
+            gboolean retval = methods[i].method_callback (
+                    (BusIBusImpl *) service,
+                    connection,
+                    value,
+                    error);
+
+            _emit_properties_changed ((BusIBusImpl *) service,
+                                      connection,
+                                      object_path,
+                                      interface_name,
+                                      property_name,
+                                      value,
+                                      retval);
+
+            return retval;
         }
     }
 
-    return IBUS_SERVICE_CLASS(bus_ibus_impl_parent_class)->ibus_message (
-                                    (IBusService *) ibus,
-                                    (IBusConnection *) connection,
-                                    message);
+    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 *
@@ -1415,48 +1864,244 @@ bus_ibus_impl_lookup_factory (BusIBusImpl *ibus,
     return factory;
 }
 
-IBusHotkeyProfile *
-bus_ibus_impl_get_hotkey_profile (BusIBusImpl *ibus)
+IBusKeymap *
+bus_ibus_impl_get_keymap (BusIBusImpl *ibus)
 {
+
     g_assert (BUS_IS_IBUS_IMPL (ibus));
 
-    return ibus->hotkey_profile;
+    return ibus->keymap;
 }
 
-IBusKeymap *
-bus_ibus_impl_get_keymap (BusIBusImpl *ibus)
+/**
+ * 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_hash_table_destroy (ibus->engine_table);
+    ibus->engine_table = NULL;
+
+    ibus_object_destroy (IBUS_OBJECT (ibus->registry));
+    ibus->registry = NULL;
+}
+
+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);
 
-    return ibus->keymap;
+    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);
+    }
 }
 
-BusRegistry *
-bus_ibus_impl_get_registry (BusIBusImpl *ibus)
+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;
+    }
+}
 
-    return ibus->registry;
+/**
+ * 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));
 
-    IBusMessage *message;
+    return ibus->use_sys_layout;
+}
 
-    message = ibus_message_new_signal (IBUS_PATH_IBUS,
-                                       IBUS_INTERFACE_IBUS,
-                                       "RegistryChanged");
-    ibus_message_append_args (message,
-                              G_TYPE_INVALID);
-    ibus_message_set_sender (message, IBUS_SERVICE_IBUS);
+gboolean
+bus_ibus_impl_is_embed_preedit_text (BusIBusImpl *ibus)
+{
+    g_assert (BUS_IS_IBUS_IMPL (ibus));
 
-    bus_dbus_impl_dispatch_message_by_rule (BUS_DEFAULT_DBUS, message, NULL);
+    return ibus->embed_preedit_text;
+}
 
-    ibus_message_unref (message);
+BusInputContext *
+bus_ibus_impl_get_focused_input_context (BusIBusImpl *ibus)
+{
+    g_assert (BUS_IS_IBUS_IMPL (ibus));
 
+    return ibus->focused_context;
 }