Blob Blame History Raw
From 71689f16ab8d19045ed44f2609c90202a6bf5db7 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Fri, 4 Mar 2011 00:04:34 +0900
Subject: [PATCH] Implement APIs for another non-Python panel.

1. Support icon and prop_list = null in ibus_property_new with GIR.
2. Add getter methods in IBusText and IBusProperty since GJS cannot access
   the members in C-Structure.
3. Add ibus_get_language_name() since GIR libxml2 does not provide the
   useful APIs.
4. Implement flags in ibus_bus_request_name() to follow DBus
   RequestName signal spec.
   http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-names
   This is needed to terminate the current IBus panel.
   E.g. IBus GTK panel is launched by ibus-daemon but another panel is
   launched by gnome-shell.
5. Support IBUS_BUS_NAME_FLAG_ALLOW_REPLACEMENT in ui/gtk/main.py
6. Fix bus_component_set_factory() not to call
   bus_component_factory_destroy_cb() twice.
7. Hide ibus_text_new_from_static_string() for GIR.
8. Add ibus_is_running_gnome_shell() for ibus-ui-gtk because
   gnome-shell runs earlier than ibus-ui-gtk.
---
 bus/component.c           |    2 +-
 bus/connection.c          |    8 +
 bus/connection.h          |   12 +-
 bus/dbusimpl.c            |  480 +++++++++++++++++++++++++++++++++++++++++++--
 bus/marshalers.list       |    3 +-
 ibus/common.py            |   24 +++-
 src/Makefile.am           |    2 +
 src/ibusbus.c             |    6 +-
 src/ibusbus.h             |    8 +-
 src/ibusproperty.c        |   24 +++
 src/ibusproperty.h        |   85 ++++++++-
 src/ibustext.c            |   18 ++
 src/ibustext.h            |   29 +++-
 src/ibustypes.h           |   33 +++
 src/ibusutil.c            |  180 ++++++++++++++++
 src/ibusutil.h            |   39 ++++
 ui/gtk/gtkpanel.xml.in.in |    2 +-
 ui/gtk/main.py            |   23 ++-
 18 files changed, 928 insertions(+), 39 deletions(-)
 create mode 100644 src/ibusutil.c
 create mode 100644 src/ibusutil.h

diff --git a/bus/component.c b/bus/component.c
index c1ff85a..fdff9c3 100644
--- a/bus/component.c
+++ b/bus/component.c
@@ -256,7 +256,7 @@ bus_component_set_factory (BusComponent    *component,
     }
 
     if (component->factory) {
-        g_signal_handlers_disconnect_by_func (factory,
+        g_signal_handlers_disconnect_by_func (component->factory,
                                               bus_component_factory_destroy_cb,
                                               component);
         g_object_unref (component->factory);
diff --git a/bus/connection.c b/bus/connection.c
index a3b4c9c..9e73213 100644
--- a/bus/connection.c
+++ b/bus/connection.c
@@ -204,6 +204,14 @@ bus_connection_remove_name (BusConnection     *connection,
     return FALSE;
 }
 
+gboolean
+bus_connection_has_name (BusConnection     *connection,
+                         const gchar       *name)
+{
+    GList *list = g_list_find_custom (connection->names, name, (GCompareFunc) g_strcmp0);
+    return list != NULL;
+}
+
 GDBusConnection *
 bus_connection_get_dbus_connection (BusConnection *connection)
 {
diff --git a/bus/connection.h b/bus/connection.h
index df86036..2a34319 100644
--- a/bus/connection.h
+++ b/bus/connection.h
@@ -85,7 +85,7 @@ const gchar     *bus_connection_add_name            (BusConnection      *connect
                                                      const gchar        *name);
 
 /**
- * bus_connection_add_name:
+ * bus_connection_remove_name:
  * @name: a well-known name for the connection.
  * @returns: TRUE on success.
  *
@@ -95,6 +95,16 @@ gboolean         bus_connection_remove_name         (BusConnection      *connect
                                                      const gchar        *name);
 
 /**
+ * bus_connection_has_name:
+ * @name: a well-known name for the connection.
+ * @returns: TRUE if found the name.
+ *
+ * Lookup the well-known name from the connection.
+ */
+gboolean         bus_connection_has_name            (BusConnection      *connection,
+                                                     const gchar        *name);
+
+/**
  * bus_connection_get_dbus_connection:
  *
  * Get the underlying GDBus connection.
diff --git a/bus/dbusimpl.c b/bus/dbusimpl.c
index 48dbd42..8002bac 100644
--- a/bus/dbusimpl.c
+++ b/bus/dbusimpl.c
@@ -27,6 +27,8 @@
 
 enum {
     NAME_OWNER_CHANGED,
+    NAME_LOST,
+    NAME_ACQUIRED,
     LAST_SIGNAL,
 };
 
@@ -68,6 +70,14 @@ struct _BusDBusImplClass {
                                     gchar           *name,
                                     gchar           *old_name,
                                     gchar           *new_name);
+
+    void    (* name_lost)          (BusDBusImpl     *dbus,
+                                    BusConnection   *connection,
+                                    gchar           *name);
+
+    void    (* name_acquired)      (BusDBusImpl     *dbus,
+                                    BusConnection   *connection,
+                                    gchar           *name);
 };
 
 typedef struct _BusDispatchData BusDispatchData;
@@ -76,6 +86,19 @@ struct _BusDispatchData {
     BusConnection *skip_connection;
 };
 
+typedef struct _BusNameService BusNameService;
+struct _BusNameService {
+    gchar *name;
+    GSList *owners;
+};
+
+typedef struct _BusConnectionOwner BusConnectionOwner;
+struct _BusConnectionOwner {
+    BusConnection *conn;
+
+    guint allow_replacement : 1;
+    guint do_not_queue : 1;
+};
 
 /* functions prototype */
 static void     bus_dbus_impl_destroy           (BusDBusImpl        *dbus);
@@ -111,6 +134,14 @@ static void      bus_dbus_impl_name_owner_changed
                                                  gchar              *name,
                                                  gchar              *old_name,
                                                  gchar              *new_name);
+static void      bus_dbus_impl_name_lost
+                                                (BusDBusImpl        *dbus,
+                                                 BusConnection      *connection,
+                                                 gchar              *name);
+static void      bus_dbus_impl_name_acquired
+                                                (BusDBusImpl        *dbus,
+                                                 BusConnection      *connection,
+                                                 gchar              *name);
 static void      bus_dbus_impl_connection_destroy_cb
                                                 (BusConnection      *connection,
                                                  BusDBusImpl        *dbus);
@@ -205,6 +236,188 @@ static const gchar introspection_xml[] =
     "</node>";
 
 static void
+bus_connection_owner_set_flags (BusConnectionOwner *owner,
+                                guint32             flags)
+{
+    owner->allow_replacement =
+        (flags & IBUS_BUS_NAME_FLAG_ALLOW_REPLACEMENT) != 0;
+
+    owner->do_not_queue =
+        (flags & IBUS_BUS_NAME_FLAG_DO_NOT_QUEUE) != 0;
+}
+
+static BusConnectionOwner *
+bus_connection_owner_new (BusConnection *connection,
+                          guint32        flags)
+{
+    BusConnectionOwner *owner = NULL;
+
+    g_assert (connection != NULL);
+
+    owner = (BusConnectionOwner *) g_new0 (BusConnectionOwner, 1);
+    if (owner != NULL) {
+        owner->conn = g_object_ref (connection);
+        bus_connection_owner_set_flags (owner, flags);
+    }
+
+    return owner;
+}
+
+static void
+bus_connection_owner_free (BusConnectionOwner *owner)
+{
+    g_assert (owner != NULL);
+
+    g_object_unref (owner->conn);
+    owner->conn = NULL;
+    g_free (owner);
+}
+
+static GSList *
+_bus_name_service_find_owner_link (BusNameService *service,
+                                   BusConnection  *connection)
+{
+    GSList *owners = service->owners;
+
+    while (owners) {
+        BusConnectionOwner *owner = (BusConnectionOwner *) owners->data;
+        if (owner->conn == connection) {
+            break;
+        }
+        owners = owners->next;
+    }
+
+    return owners;
+}
+
+static BusNameService *
+bus_name_service_new (const gchar *name)
+{
+    BusNameService *service = NULL;
+
+    g_assert (name != NULL);
+
+    service = g_new0 (BusNameService, 1);
+    if (service != NULL) {
+        service->name = g_strdup (name);
+    }
+
+    return service;
+}
+
+static void
+bus_name_service_free (BusNameService *service)
+{
+    GSList *list = NULL;
+
+    g_assert (service != NULL);
+
+    list = service->owners;
+
+    while (list) {
+        bus_connection_owner_free ((BusConnectionOwner *) list->data);
+        list->data = NULL;
+        list = list->next;
+    }
+    if (service->owners) {
+        g_slist_free (service->owners);
+        service->owners = NULL;
+    }
+    g_free (service->name);
+    service->name = NULL;
+    g_free (service);
+}
+
+static void
+bus_name_service_add_primary_owner (BusNameService     *service,
+                                    BusConnectionOwner *owner,
+                                    BusDBusImpl        *dbus)
+{
+    g_assert (service != NULL);
+
+    if (dbus) {
+        g_signal_emit (dbus,
+                       dbus_signals[NAME_ACQUIRED],
+                       0,
+                       g_object_ref (owner->conn),
+                       service->name ? service->name : "");
+    }
+
+    service->owners = g_slist_prepend (service->owners, (gpointer) owner);
+}
+
+static BusConnectionOwner *
+bus_name_service_get_primary_owner (BusNameService *service)
+{
+    g_assert (service != NULL);
+
+    if (service->owners == NULL) {
+        return NULL;
+    }
+
+    return (BusConnectionOwner *) service->owners->data;
+}
+
+static void
+bus_name_service_add_owner (BusNameService     *service,
+                            BusConnectionOwner *owner,
+                            BusDBusImpl        *dbus)
+{
+    g_assert (service != NULL);
+
+    if (dbus && service->owners == NULL) {
+        g_signal_emit (dbus,
+                       dbus_signals[NAME_ACQUIRED],
+                       0,
+                       g_object_ref (owner->conn),
+                       service->name ? service->name : "");
+    }
+
+    service->owners = g_slist_append (service->owners, (gpointer) owner);
+}
+
+static void
+bus_name_service_remove_owner (BusNameService     *service,
+                               BusConnectionOwner *owner,
+                               BusDBusImpl        *dbus)
+{
+    GSList *owners;
+
+    g_assert (service != NULL);
+    g_assert (owner != NULL);
+
+    owners = _bus_name_service_find_owner_link (service, owner->conn);
+    if (owners == NULL) {
+        return;
+    }
+
+    if (dbus &&
+        owners->data == bus_name_service_get_primary_owner (service)) {
+        g_signal_emit (dbus,
+                       dbus_signals[NAME_LOST],
+                       0,
+                       g_object_ref (owner->conn),
+                       service->name ? service->name : "");
+    }
+
+    service->owners = g_slist_remove_link (service->owners, (gpointer) owners);
+}
+
+static gboolean
+bus_name_service_get_allow_replacement (BusNameService *service)
+{
+    BusConnectionOwner *owner = NULL;
+
+    g_assert (service != NULL);
+
+    owner = bus_name_service_get_primary_owner (service);
+    if (owner == NULL) {
+        return TRUE;
+    }
+    return owner->allow_replacement;
+}
+
+static void
 bus_dbus_impl_class_init (BusDBusImplClass *class)
 {
     GObjectClass *gobject_class = G_OBJECT_CLASS (class);
@@ -221,6 +434,12 @@ bus_dbus_impl_class_init (BusDBusImplClass *class)
     /* register a handler of the name-owner-changed signal below. */
     class->name_owner_changed = bus_dbus_impl_name_owner_changed;
 
+    /* register a handler of the name-lost signal below. */
+    class->name_lost = bus_dbus_impl_name_lost;
+
+    /* register a handler of the name-acquired signal below. */
+    class->name_acquired = bus_dbus_impl_name_acquired;
+
     /* install signals */
     dbus_signals[NAME_OWNER_CHANGED] =
         g_signal_new (I_("name-owner-changed"),
@@ -234,13 +453,39 @@ bus_dbus_impl_class_init (BusDBusImplClass *class)
             G_TYPE_STRING,
             G_TYPE_STRING,
             G_TYPE_STRING);
+
+    dbus_signals[NAME_LOST] =
+        g_signal_new (I_("name-lost"),
+            G_TYPE_FROM_CLASS (class),
+            G_SIGNAL_RUN_FIRST,
+            G_STRUCT_OFFSET (BusDBusImplClass, name_lost),
+            NULL, NULL,
+            bus_marshal_VOID__OBJECT_STRING,
+            G_TYPE_NONE,
+            2,
+            BUS_TYPE_CONNECTION,
+            G_TYPE_STRING);
+
+    dbus_signals[NAME_ACQUIRED] =
+        g_signal_new (I_("name-acquired"),
+            G_TYPE_FROM_CLASS (class),
+            G_SIGNAL_RUN_FIRST,
+            G_STRUCT_OFFSET (BusDBusImplClass, name_acquired),
+            NULL, NULL,
+            bus_marshal_VOID__OBJECT_STRING,
+            G_TYPE_NONE,
+            2,
+            BUS_TYPE_CONNECTION,
+            G_TYPE_STRING);
 }
 
 static void
 bus_dbus_impl_init (BusDBusImpl *dbus)
 {
     dbus->unique_names = g_hash_table_new (g_str_hash, g_str_equal);
-    dbus->names = g_hash_table_new (g_str_hash, g_str_equal);
+    dbus->names = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                         (GDestroyNotify) g_free,
+                                         (GDestroyNotify) bus_name_service_free);
 
     dbus->dispatch_lock = g_mutex_new ();
     dbus->forward_lock = g_mutex_new ();
@@ -557,10 +802,15 @@ bus_dbus_impl_request_name (BusDBusImpl           *dbus,
                             GVariant              *parameters,
                             GDBusMethodInvocation *invocation)
 {
-    /* FIXME need to handle flags defined in:
-     * http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-names */
     const gchar *name = NULL;  // e.g. "org.freedesktop.IBus.Panel"
-    guint flags = 0;
+    guint32 flags = 0;
+    guint32 retval = 0;
+    gchar *old_owner_name = NULL;
+    BusNameService *service = NULL;
+    BusConnectionOwner *primary_owner = NULL;
+    BusConnectionOwner *owner = NULL;
+    BusConnection *old_owner_conn = NULL;
+
     g_variant_get (parameters, "(&su)", &name, &flags);
 
     if (name == NULL ||
@@ -580,25 +830,82 @@ bus_dbus_impl_request_name (BusDBusImpl           *dbus,
         return;
     }
 
-    if (g_hash_table_lookup (dbus->names, name) != NULL) {
-        g_dbus_method_invocation_return_error (invocation,
-                        G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
-                        "Service name '%s' already has an owner.", name);
-        return;
+    service = (BusNameService *) g_hash_table_lookup (dbus->names, name);
+
+    if (service != NULL) {
+        primary_owner = bus_name_service_get_primary_owner (service);
+        if (primary_owner != NULL) {
+            old_owner_conn = primary_owner->conn;
+        } else {
+            old_owner_conn = NULL;
+        }
+    } else {
+        old_owner_conn = NULL;
     }
 
-    const guint retval = 1; /* DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER */
-    g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", retval));
-    g_hash_table_insert (dbus->names,
-                         (gpointer) bus_connection_add_name (connection, name),
-                         connection);
+    if (old_owner_conn == NULL) {
+        retval = IBUS_BUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
+    }
+    else if (old_owner_conn == connection) {
+        retval = IBUS_BUS_REQUEST_NAME_REPLY_ALREADY_OWNER;
+        goto out;
+    }
+    else if (((flags & IBUS_BUS_NAME_FLAG_DO_NOT_QUEUE) &&
+             !(bus_name_service_get_allow_replacement (service))) ||
+             ((flags & IBUS_BUS_NAME_FLAG_DO_NOT_QUEUE) &&
+             !(flags & IBUS_BUS_NAME_FLAG_REPLACE_EXISTING))) {
+        retval = IBUS_BUS_REQUEST_NAME_REPLY_EXISTS;
+        goto out;
+    }
+    else if (!(flags & IBUS_BUS_NAME_FLAG_DO_NOT_QUEUE) &&
+             (!(flags & IBUS_BUS_NAME_FLAG_REPLACE_EXISTING) ||
+              !(bus_name_service_get_allow_replacement (service)))) {
+        if (!bus_connection_has_name (connection, name)) {
+            bus_connection_add_name (connection, name);
+        }
+        owner = bus_connection_owner_new (connection, flags);
+        bus_name_service_add_owner (service, owner, dbus);
+        retval = IBUS_BUS_REQUEST_NAME_REPLY_IN_QUEUE;
+        goto out;
+    }
+    else {
+        if (!bus_connection_has_name (connection, name)) {
+            bus_connection_add_name (connection, name);
+        }
+        owner = bus_connection_owner_new (connection, flags);
+        old_owner_name = g_strdup (bus_connection_get_unique_name (primary_owner->conn));
+        bus_name_service_remove_owner (service, primary_owner, dbus);
+        bus_name_service_add_primary_owner (service, owner, dbus);
+        if (primary_owner->do_not_queue == 0) {
+            bus_name_service_add_owner (service, primary_owner, dbus);
+        } else {
+            if (bus_connection_has_name (primary_owner->conn, name)) {
+                bus_connection_remove_name (primary_owner->conn, name);
+            }
+            bus_connection_owner_free (primary_owner);
+        }
+        retval = IBUS_BUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
+    }
+
+    if (service == NULL) {
+        service = bus_name_service_new (name);
+        owner = bus_connection_owner_new (connection, flags);
+        bus_name_service_add_owner (service, owner, dbus);
+        g_hash_table_insert (dbus->names,
+                             (gpointer) g_strdup (bus_connection_add_name (connection, name)),
+                             service);
+    }
 
     g_signal_emit (dbus,
                    dbus_signals[NAME_OWNER_CHANGED],
                    0,
                    name,
-                   "",
+                   old_owner_name ? old_owner_name : "",
                    bus_connection_get_unique_name (connection));
+    g_free (old_owner_name);
+
+out:
+    g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", retval));
 }
 
 /**
@@ -682,6 +989,70 @@ bus_dbus_impl_name_owner_changed (BusDBusImpl   *dbus,
 }
 
 /**
+ * bus_dbus_impl_name_lost:
+ *
+ * The function is called on name-lost signal, typically when g_signal_emit (dbus, NAME_LOST)
+ * is called, and broadcasts the signal to clients.
+ */
+static void
+bus_dbus_impl_name_lost (BusDBusImpl   *dbus,
+                         BusConnection *connection,
+                         gchar         *name)
+{
+    static guint32 serial = 0;
+
+    g_assert (BUS_IS_DBUS_IMPL (dbus));
+    g_assert (name != NULL);
+
+    GDBusMessage *message = g_dbus_message_new_signal ("/org/freedesktop/DBus",
+                                                       "org.freedesktop.DBus",
+                                                       "NameLost");
+    g_dbus_message_set_sender (message, "org.freedesktop.DBus");
+    g_dbus_message_set_destination (message, bus_connection_get_unique_name (connection));
+
+    /* set a non-zero serial to make libdbus happy */
+    g_dbus_message_set_serial (message, ++serial);
+    g_dbus_message_set_body (message,
+                             g_variant_new ("(s)", name));
+
+    bus_dbus_impl_forward_message (dbus, connection, message);
+    g_object_unref (message);
+    g_object_unref (connection);
+}
+
+/**
+ * bus_dbus_impl_name_acquired:
+ *
+ * The function is called on name-acquired signal, typically when g_signal_emit (dbus, NAME_LOST)
+ * is called, and broadcasts the signal to clients.
+ */
+static void
+bus_dbus_impl_name_acquired (BusDBusImpl   *dbus,
+                             BusConnection *connection,
+                             gchar         *name)
+{
+    static guint32 serial = 0;
+
+    g_assert (BUS_IS_DBUS_IMPL (dbus));
+    g_assert (name != NULL);
+
+    GDBusMessage *message = g_dbus_message_new_signal ("/org/freedesktop/DBus",
+                                                       "org.freedesktop.DBus",
+                                                       "NameAcquired");
+    g_dbus_message_set_sender (message, "org.freedesktop.DBus");
+    g_dbus_message_set_destination (message, bus_connection_get_unique_name (connection));
+
+    /* set a non-zero serial to make libdbus happy */
+    g_dbus_message_set_serial (message, ++serial);
+    g_dbus_message_set_body (message,
+                             g_variant_new ("(s)", name));
+
+    bus_dbus_impl_forward_message (dbus, connection, message);
+    g_object_unref (message);
+    g_object_unref (connection);
+}
+
+/**
  * bus_dbus_impl_service_method_call:
  *
  * Handle a D-Bus method call from a client. This function overrides an implementation in src/ibusservice.c.
@@ -935,6 +1306,10 @@ bus_dbus_impl_connection_destroy_cb (BusConnection *connection,
                                      BusDBusImpl   *dbus)
 {
     const gchar *unique_name = bus_connection_get_unique_name (connection);
+    const GList *names = NULL;
+    BusNameService *service = NULL;
+    GSList *owners = NULL;
+
     if (unique_name != NULL) {
         g_hash_table_remove (dbus->unique_names, unique_name);
         g_signal_emit (dbus,
@@ -945,16 +1320,71 @@ bus_dbus_impl_connection_destroy_cb (BusConnection *connection,
                        "");
     }
 
-    const GList *name = bus_connection_get_names (connection);
-    while (name != NULL) {
-        g_hash_table_remove (dbus->names, name->data);
+    /* service->owners is the queue of connections.
+     * If the connection is the primary owner and
+     * bus_name_service_remove_owner() is called, the owner is removed
+     * in the queue and the next owner will become the primary owner
+     * automatically because service->owners is just a GSList.
+     * If service->owners == NULL, it's good to remove the service in
+     * dbus->names.
+     * I suppose dbus->names are global queue for every connection and
+     * connection->names are private queue of the connection.
+     */
+    names = bus_connection_get_names (connection);
+    while (names != NULL) {
+        service = (BusNameService *) g_hash_table_lookup (dbus->names,
+                                                          names->data);
+        if (service) {
+            owners = _bus_name_service_find_owner_link (service, connection);
+            if (owners) {
+                BusConnectionOwner *owner = owners->data;
+                bus_name_service_remove_owner (service, owner, dbus);
+                bus_connection_owner_free (owner);
+            }
+            if (service->owners == NULL) {
+                /* g_hash_table_remove() will call bus_name_service_free()
+                 * due to g_hash_table_new_full() */
+                g_hash_table_remove (dbus->names, names->data);
+                service = NULL;
+            }
+        }
+        /* if service == NULL, the names->data should be removed in
+         * the connection by bus_connection_remove_name().
+         * But connection->names is GSList so it cannot be removed
+         * during this while loop because names->next would not
+         * become the wrong pointer here.
+         * the next while loop can call bus_connection_remove_name().
+         */
+
         g_signal_emit (dbus,
                        dbus_signals[NAME_OWNER_CHANGED],
                        0,
-                       name->data,
+                       names->data,
                        unique_name,
                        "");
-        name = name->next;
+        names = names->next;
+    }
+
+    while ((names = bus_connection_get_names (connection)) != NULL) {
+        const gchar *name = NULL;
+        service = NULL;
+
+        while (names != NULL) {
+            name = (const gchar *) names->data;
+            service = (BusNameService *) g_hash_table_lookup (dbus->names,
+                                                              name);
+            if (service == NULL) {
+                break;
+            }
+            names = names->next;
+        }
+        if (names == NULL) {
+            break;
+        }
+        if (name == NULL) {
+            break;
+        }
+        bus_connection_remove_name (connection, name);
     }
 
     dbus->connections = g_list_remove (dbus->connections, connection);
@@ -1004,7 +1434,15 @@ bus_dbus_impl_get_connection_by_name (BusDBusImpl    *dbus,
         return (BusConnection *) g_hash_table_lookup (dbus->unique_names, name);
     }
     else {
-        return (BusConnection *) g_hash_table_lookup (dbus->names, name);
+        BusNameService *service;
+        BusConnectionOwner *owner;
+
+        service = (BusNameService *) g_hash_table_lookup (dbus->names, name);
+        if (service == NULL) {
+            return NULL;
+        }
+        owner = bus_name_service_get_primary_owner (service);
+        return owner ? owner->conn : NULL;
     }
 }
 
diff --git a/bus/marshalers.list b/bus/marshalers.list
index 15bdf02..514d6ea 100644
--- a/bus/marshalers.list
+++ b/bus/marshalers.list
@@ -4,6 +4,7 @@ VOID:INT,UINT
 VOID:INT,INT,INT,INT
 VOID:OBJECT
 VOID:OBJECT,BOOLEAN
+VOID:OBJECT,STRING
 VOID:OBJECT,UINT,BOOLEAN
 VOID:OBJECT,UINT,BOOLEAN,UINT
 VOID:STRING
diff --git a/ibus/common.py b/ibus/common.py
index cbc8d56..614d782 100644
--- a/ibus/common.py
+++ b/ibus/common.py
@@ -35,6 +35,13 @@ __all__ = (
         "ORIENTATION_SYSTEM",
         "PRELOAD_ENGINE_MODE_USER",
         "PRELOAD_ENGINE_MODE_LANG_RELATIVE",
+        "BUS_NAME_FLAG_ALLOW_REPLACEMENT",
+        "BUS_NAME_FLAG_REPLACE_EXISTING",
+        "BUS_NAME_FLAG_DO_NOT_QUEUE",
+        "BUS_REQUEST_NAME_REPLY_PRIMARY_OWNER",
+        "BUS_REQUEST_NAME_REPLY_IN_QUEUE",
+        "BUS_REQUEST_NAME_REPLY_EXISTS",
+        "BUS_REQUEST_NAME_REPLY_ALREADY_OWNER",
         "default_reply_handler",
         "default_error_handler",
         "DEFAULT_ASYNC_HANDLERS",
@@ -47,7 +54,8 @@ __all__ = (
         "main_quit",
         "main_iteration",
         "get_address",
-        "get_socket_path"
+        "get_socket_path",
+        "is_running_gnome_shell",
     )
 
 import os
@@ -106,6 +114,9 @@ get_address.restype=ctypes.c_char_p
 get_socket_path = libibus.ibus_get_socket_path
 get_socket_path.restype=ctypes.c_char_p
 
+is_running_gnome_shell = libibus.ibus_is_running_gnome_shell
+is_running_gnome_shell.restype = ctypes.c_bool
+
 # __session_id = os.getenv ("IBUS_SESSION_ID")
 # 
 # IBUS_ADDR = "unix:path=/tmp/ibus-%s%s/ibus-%s-%s" % (__username,
@@ -138,6 +149,17 @@ ORIENTATION_SYSTEM      = 2
 PRELOAD_ENGINE_MODE_USER          = 0
 PRELOAD_ENGINE_MODE_LANG_RELATIVE = 1
 
+# define bus name flag
+BUS_NAME_FLAG_ALLOW_REPLACEMENT   = (1 << 0)
+BUS_NAME_FLAG_REPLACE_EXISTING    = (1 << 1)
+BUS_NAME_FLAG_DO_NOT_QUEUE        = (1 << 2)
+
+# define bus request name reply
+BUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = 1
+BUS_REQUEST_NAME_REPLY_IN_QUEUE      = 2
+BUS_REQUEST_NAME_REPLY_EXISTS        = 3
+BUS_REQUEST_NAME_REPLY_ALREADY_OWNER = 4
+
 def default_reply_handler( *args):
     pass
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 08152a7..d422106 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -87,6 +87,7 @@ ibus_sources =              \
     ibusenginedesc.c        \
     ibusobservedpath.c      \
     ibuscomponent.c         \
+    ibusutil.c              \
 $(NULL)
 libibus_1_0_la_SOURCES =    \
     $(ibus_sources)         \
@@ -131,6 +132,7 @@ ibus_headers =              \
     ibusenginedesc.h        \
     ibusobservedpath.h      \
     ibuscomponent.h         \
+    ibusutil.h              \
     $(NULL)
 ibusincludedir = $(includedir)/ibus-@IBUS_API_VERSION@
 ibus_public_headers =       \
diff --git a/src/ibusbus.c b/src/ibusbus.c
index 5a3b978..78808a4 100644
--- a/src/ibusbus.c
+++ b/src/ibusbus.c
@@ -613,15 +613,15 @@ ibus_bus_hello (IBusBus *bus)
 #endif
 }
 
-guint
+guint32
 ibus_bus_request_name (IBusBus      *bus,
                        const gchar  *name,
-                       guint         flags)
+                       guint32       flags)
 {
     g_return_val_if_fail (IBUS_IS_BUS (bus), 0);
     g_return_val_if_fail (name != NULL, 0);
 
-    guint retval = 0;
+    guint32 retval = 0;
     GVariant *result;
     result = ibus_bus_call_sync (bus,
                                  DBUS_SERVICE_DBUS,
diff --git a/src/ibusbus.h b/src/ibusbus.h
index b1ec63c..de4cd36 100644
--- a/src/ibusbus.h
+++ b/src/ibusbus.h
@@ -122,16 +122,16 @@ const gchar *ibus_bus_hello             
  * ibus_bus_request_name:
  * @bus: the IBusBus instance to be processed.
  * @name: Name to be requested.
- * @flags: Flags (FixMe).
- * @returns: 0 if failed; positive number otherwise.
+ * @flags: IBusBusNameFlag.
+ * @returns: 0 if failed; IBusBusRequestNameReply otherwise.
  *
  * Request a name from IBus daemon synchronously.
  *
  * FIXME add an asynchronous version.
  */
-guint        ibus_bus_request_name      (IBusBus        *bus,
+guint32      ibus_bus_request_name      (IBusBus        *bus,
                                          const gchar    *name,
-                                         guint           flags);
+                                         guint32         flags);
 
 /**
  * ibus_bus_release_name:
diff --git a/src/ibusproperty.c b/src/ibusproperty.c
index bb9cc21..5a2dd78 100644
--- a/src/ibusproperty.c
+++ b/src/ibusproperty.c
@@ -217,6 +217,30 @@ ibus_property_new (const gchar   *key,
     return prop;
 }
 
+#define IBUS_PROPERTY_GET_FIELD(field, return_type) \
+return_type                                                             \
+ibus_property_get_ ## field (IBusProperty *prop)                        \
+{                                                                       \
+    return prop->field;                                                 \
+}
+
+IBUS_PROPERTY_GET_FIELD (key, const gchar *)
+IBUS_PROPERTY_GET_FIELD (icon, const gchar *)
+IBUS_PROPERTY_GET_FIELD (label, const IBusText *)
+IBUS_PROPERTY_GET_FIELD (tooltip, const IBusText *)
+IBUS_PROPERTY_GET_FIELD (sensitive, gboolean)
+IBUS_PROPERTY_GET_FIELD (visible, gboolean)
+IBUS_PROPERTY_GET_FIELD (state, IBusPropState)
+IBUS_PROPERTY_GET_FIELD (sub_props, const IBusPropList *)
+#undef IBUS_PROPERTY_GET_FIELD
+
+/* ibus_property_get_type() exists */
+IBusPropType
+ibus_property_get_prop_type (IBusProperty *prop)
+{
+    return prop->type;
+}
+
 void
 ibus_property_set_label (IBusProperty *prop,
                          IBusText     *label)
diff --git a/src/ibusproperty.h b/src/ibusproperty.h
index 0f8d427..5e76c8f 100644
--- a/src/ibusproperty.h
+++ b/src/ibusproperty.h
@@ -164,12 +164,12 @@ GType            ibus_property_get_type     ();
  * @key: Unique Identity for the IBusProperty.
  * @type: IBusPropType of IBusProperty.
  * @label: Text shown in UI.
- * @icon: Icon file for the IBusProperty.
+ * @icon: (allow-none): Icon file for the IBusProperty.
  * @tooltip: Message shown if mouse hovered the  IBusProperty.
  * @sensitive: Whether the IBusProperty is sensitive to keyboard and mouse event.
  * @visible: Whether the IBusProperty is visible.
  * @state: IBusPropState of IBusProperty.
- * @prop_list: IBusPropList that contains sub IBusProperties.
+ * @prop_list: (allow-none): IBusPropList that contains sub IBusProperties.
  * @returns: A newly allocated IBusProperty.
  *
  * New a IBusProperty.
@@ -185,6 +185,33 @@ IBusProperty    *ibus_property_new          (const gchar    *key,
                                              IBusPropList   *prop_list);
 
 /**
+ * ibus_property_get_key:
+ * @prop: An IBusProperty.
+ * @returns: the key of IBusProperty. Should not be freed.
+ *
+ * Get the key of IBusProperty.
+ */
+const gchar *    ibus_property_get_key      (IBusProperty   *prop);
+
+/**
+ * ibus_property_get_prop_type:
+ * @prop: An IBusProperty.
+ * @returns: the type of IBusProperty.
+ *
+ * Get the type of IBusProperty.
+ */
+IBusPropType     ibus_property_get_prop_type(IBusProperty   *prop);
+
+/**
+ * ibus_property_get_label:
+ * @prop: An IBusProperty.
+ * @returns: the label of IBusProperty. Should not be freed.
+ *
+ * Get the label of IBusProperty.
+ */
+const IBusText * ibus_property_get_label    (IBusProperty   *prop);
+
+/**
  * ibus_property_set_label:
  * @prop: An IBusProperty.
  * @label: Text shown in UI.
@@ -195,6 +222,15 @@ void             ibus_property_set_label    (IBusProperty   *prop,
                                              IBusText       *label);
 
 /**
+ * ibus_property_get_icon:
+ * @prop: An IBusProperty.
+ * @returns: the icon of IBusProperty. Should not be freed.
+ *
+ * Get the icon of IBusProperty.
+ */
+const gchar *    ibus_property_get_icon     (IBusProperty   *prop);
+
+/**
  * ibus_property_set_icon:
  * @prop: An IBusProperty.
  * @icon: Icon shown in UI. It could be a full path of an icon file or an icon name.
@@ -205,6 +241,15 @@ void             ibus_property_set_icon     (IBusProperty   *prop,
                                              const gchar    *icon);
 
 /**
+ * ibus_property_get_tooltip:
+ * @prop: An IBusProperty.
+ * @returns: the tooltip of IBusProperty. Should not be freed.
+ *
+ * Get the tooltip of IBusProperty.
+ */
+const IBusText * ibus_property_get_tooltip  (IBusProperty   *prop);
+
+/**
  * ibus_property_set_tooltip:
  * @prop: An IBusProperty.
  * @tooltip: Text of the tooltip.
@@ -215,6 +260,15 @@ void             ibus_property_set_tooltip  (IBusProperty   *prop,
                                              IBusText       *tooltip);
 
 /**
+ * ibus_property_get_sensitive:
+ * @prop: An IBusProperty.
+ * @returns: the sensitive of IBusProperty.
+ *
+ * Get the sensitive of IBusProperty.
+ */
+gboolean         ibus_property_get_sensitive(IBusProperty   *prop);
+
+/**
  * ibus_property_set_sensitive:
  * @prop: An IBusProperty.
  * @sensitive: Whether the IBusProperty is sensitive.
@@ -225,6 +279,15 @@ void             ibus_property_set_sensitive(IBusProperty   *prop,
                                              gboolean        sensitive);
 
 /**
+ * ibus_property_get_visible:
+ * @prop: An IBusProperty.
+ * @returns: the visible of IBusProperty.
+ *
+ * Get the visible of IBusProperty.
+ */
+gboolean         ibus_property_get_visible  (IBusProperty   *prop);
+
+/**
  * ibus_property_set_visible:
  * @prop: An IBusProperty.
  * @visible: Whether the IBusProperty is visible.
@@ -235,6 +298,15 @@ void             ibus_property_set_visible  (IBusProperty   *prop,
                                              gboolean        visible);
 
 /**
+ * ibus_property_get_state:
+ * @prop: An IBusProperty.
+ * @returns: the state of IBusProperty.
+ *
+ * Get the state of IBusProperty.
+ */
+IBusPropState    ibus_property_get_state    (IBusProperty   *prop);
+
+/**
  * ibus_property_set_state:
  * @prop: An IBusProperty.
  * @state: The state of the IBusProperty.
@@ -244,6 +316,15 @@ void             ibus_property_set_visible  (IBusProperty   *prop,
 void             ibus_property_set_state    (IBusProperty   *prop,
                                              IBusPropState   state);
 
+/**
+ * ibus_property_get_sub_props:
+ * @prop: An IBusProperty.
+ * @returns: the IBusPropList of IBusProperty. Should not be freed.
+ *
+ * Get the IBusPropList of IBusProperty.
+ */
+const IBusPropList *
+                 ibus_property_get_sub_props(IBusProperty   *prop);
 
 /**
  * ibus_property_set_sub_props:
diff --git a/src/ibustext.c b/src/ibustext.c
index b63cbc9..5889b64 100644
--- a/src/ibustext.c
+++ b/src/ibustext.c
@@ -268,3 +268,21 @@ ibus_text_get_length (IBusText *text)
 {
     return g_utf8_strlen (text->text, -1);
 }
+
+gboolean
+ibus_text_get_is_static (IBusText *text)
+{
+    return text->is_static;
+}
+
+const gchar *
+ibus_text_get_text (IBusText *text)
+{
+    return text->text;
+}
+
+const IBusAttrList *
+ibus_text_get_attributes (IBusText *text)
+{
+    return text->attrs;
+}
diff --git a/src/ibustext.h b/src/ibustext.h
index 246e5ab..6f7c505 100644
--- a/src/ibustext.h
+++ b/src/ibustext.h
@@ -110,7 +110,7 @@ IBusText        *ibus_text_new_from_string          (const gchar    *str);
 IBusText        *ibus_text_new_from_ucs4            (const gunichar *str);
 
 /**
- * ibus_text_new_from_static_string:
+ * ibus_text_new_from_static_string: (skip)
  * @str: An text string to be set.
  * @returns: A newly allocated IBusText.
  *
@@ -169,6 +169,33 @@ void             ibus_text_append_attribute         (IBusText       *text,
  */
 guint            ibus_text_get_length               (IBusText       *text);
 
+/**
+ * ibus_text_get_is_static:
+ * @text: An IBusText.
+ * @returns: the is_static in @text.
+ *
+ * Return the is_static in an IBusText.
+ */
+gboolean         ibus_text_get_is_static            (IBusText       *text);
+
+/**
+ * ibus_text_get_text:
+ * @text: An IBusText.
+ * @returns: the text in @text.
+ *
+ * Return the text in an IBusText. Should not be freed.
+ */
+const gchar *    ibus_text_get_text                 (IBusText       *text);
+
+/**
+ * ibus_text_get_attributes:
+ * @text: An IBusText.
+ * @returns: the attrs in @text.
+ *
+ * Return the attributes in an IBusText. Should not be freed.
+ */
+const IBusAttrList *
+                 ibus_text_get_attributes           (IBusText       *text);
 G_END_DECLS
 #endif
 
diff --git a/src/ibustypes.h b/src/ibustypes.h
index 035d124..6a31847 100644
--- a/src/ibustypes.h
+++ b/src/ibustypes.h
@@ -154,6 +154,39 @@ typedef enum {
 } IBusPreloadEngineMode;
 
 /**
+ * IBusBusNameFlag:
+ * @IBUS_BUS_NAME_FLAG_ALLOW_REPLACEMENT:
+ *    same as DBUS_NAME_FLAG_ALLOW_REPLACEMENT	
+ * @IBUS_BUS_NAME_FLAG_REPLACE_EXISTING:
+ *    same as DBUS_NAME_FLAG_REPLACE_EXISTING
+ * @IBUS_BUS_NAME_FLAG_DO_NOT_QUEUE:
+ *    same as DBUS_NAME_FLAG_DO_NOT_QUEUE 
+ */
+typedef enum {
+    IBUS_BUS_NAME_FLAG_ALLOW_REPLACEMENT   = (1 << 0),
+    IBUS_BUS_NAME_FLAG_REPLACE_EXISTING    = (1 << 1),
+    IBUS_BUS_NAME_FLAG_DO_NOT_QUEUE        = (1 << 2),
+} IBusBusNameFlag;
+
+/**
+ * IBusBusRequestNameReply:
+ * @IBUS_BUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
+ *    same as DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER
+ * @IBUS_BUS_REQUEST_NAME_REPLY_IN_QUEUE:
+ *    same as DBUS_REQUEST_NAME_REPLY_IN_QUEUE
+ * @IBUS_BUS_REQUEST_NAME_REPLY_EXISTS:
+ *    same as DBUS_REQUEST_NAME_REPLY_EXISTS
+ * @IBUS_BUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
+ *    same as DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER
+ */
+typedef enum {
+    IBUS_BUS_REQUEST_NAME_REPLY_PRIMARY_OWNER   = 1,
+    IBUS_BUS_REQUEST_NAME_REPLY_IN_QUEUE        = 2,
+    IBUS_BUS_REQUEST_NAME_REPLY_EXISTS          = 3,
+    IBUS_BUS_REQUEST_NAME_REPLY_ALREADY_OWNER   = 4,
+} IBusBusRequestNameReply;
+
+/**
  * IBusRectangle:
  * @x: x coordinate.
  * @y: y coordinate.
diff --git a/src/ibusutil.c b/src/ibusutil.c
new file mode 100644
index 0000000..59291f9
--- /dev/null
+++ b/src/ibusutil.c
@@ -0,0 +1,180 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* vim:set et sts=4: */
+/* bus - The Input Bus
+ * Copyright (C) 2008-2011 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2010-2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2008-2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+#include <string.h>
+#include "ibusxml.h"
+
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#endif
+
+static GHashTable *__languages_dict;
+
+static gboolean
+_iso_codes_parse_xml_node (XMLNode          *node)
+{
+    GList *p;
+    g_assert (node);
+
+    if (G_UNLIKELY (g_strcmp0 (node->name, "iso_639_entries") != 0)) {
+        return FALSE;
+    }
+
+    for (p = node->sub_nodes; p != NULL; p = p->next) {
+        XMLNode *sub_node = (XMLNode *)p->data;
+        gchar **attributes = NULL;
+        int i, j;
+        struct {
+            const gchar *key;
+            gchar *value;
+        } entries[] = {
+            { "iso_639_2B_code", NULL },
+            { "iso_639_2T_code", NULL },
+            { "iso_639_1_code", NULL },
+            { NULL, NULL },
+        };
+
+
+        if (sub_node->attributes == NULL) {
+            continue;
+        }
+        attributes = sub_node->attributes;
+        for (i = 0; attributes[i]; i+=2) {
+            if (g_strcmp0 (attributes[i], "name") == 0) {
+                for (j = 0; entries[j].key; j++) {
+                    if (entries[j].value == NULL) {
+                        continue;
+                    }
+                    g_hash_table_insert (__languages_dict,
+                                         (gpointer) entries[j].value,
+                                         (gpointer) g_strdup (attributes[i + 1]));
+                    entries[j].value = NULL;
+                }
+            } else {
+                for (j = 0; entries[j].key; j++) {
+                    if (g_strcmp0 (attributes[i], entries[j].key) == 0 &&
+                        attributes[i + 1] != NULL) {
+                        entries[j].value = g_strdup (attributes[i + 1]);
+                    }
+                }
+            }
+        }
+    }
+
+    return TRUE;
+}
+
+void
+_load_lang()
+{
+    gchar *filename;
+    XMLNode *node;
+    struct stat buf;
+
+    __languages_dict = g_hash_table_new (g_str_hash, (GEqualFunc) g_str_equal);
+    filename = g_build_filename ("/usr", "share/xml/iso-codes/iso_639.xml", NULL);
+    if (g_stat (filename, &buf) != 0) {
+        g_warning ("Can not get stat of file %s", filename);
+        g_free (filename);
+        return;
+    }
+
+    node = ibus_xml_parse_file (filename);
+    g_free (filename);
+
+    if (!node) {
+        return;
+    }
+
+    _iso_codes_parse_xml_node (node);
+    ibus_xml_free (node);
+}
+
+const gchar *
+ibus_get_language_name(const gchar *_locale) {
+    const gchar *retval;
+    gchar *p = NULL;
+    gchar *lang = NULL;
+
+    if (__languages_dict == NULL ) {
+        _load_lang();
+    }
+    if ((p = strchr (_locale, '_')) !=  NULL) {
+        p = g_strndup (_locale, p - _locale);
+    } else {
+        p = g_strdup (_locale);
+    }
+    lang = g_ascii_strdown (p, -1);
+    g_free (p);
+    retval = (const gchar *) g_hash_table_lookup (__languages_dict, lang);
+    g_free (lang);
+    if (retval != NULL) {
+#ifdef ENABLE_NLS
+        return dgettext("iso_639", retval);
+#else
+        return retval;
+#endif
+    }
+    return retval;
+}
+
+gboolean
+ibus_is_running_gnome_shell (void)
+{
+    GDBusConnection *connection = NULL;
+    GVariant *result;
+    gboolean is_running = FALSE;
+
+    connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+    if (connection == NULL) {
+        return FALSE;
+    }
+
+    result = g_dbus_connection_call_sync (connection,
+                                          "org.freedesktop.DBus",
+                                          "/org/freedesktop/DBus",
+                                          "org.freedesktop.DBus",
+                                          "GetNameOwner",
+                                          g_variant_new ("(s)", "org.gnome.Shell"),
+                                          G_VARIANT_TYPE ("(s)"),
+                                          G_DBUS_CALL_FLAGS_NONE,
+                                          -1,
+                                          NULL,
+                                          NULL);
+
+    if (result != NULL) {
+        is_running = TRUE;
+        g_variant_unref (result);
+    } else {
+        is_running = FALSE;
+    }
+    g_object_unref (connection);
+
+    return is_running;
+}
diff --git a/src/ibusutil.h b/src/ibusutil.h
new file mode 100644
index 0000000..8892996
--- /dev/null
+++ b/src/ibusutil.h
@@ -0,0 +1,39 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* vim:set et sts=4: */
+/* bus - The Input Bus
+ * Copyright (C) 2008-2011 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2010-2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2008-2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__IBUS_H_INSIDE__) && !defined (IBUS_COMPILATION)
+#error "Only <ibus.h> can be included directly"
+#endif
+
+/**
+ * ibus_get_language_name:
+ * @_locale: A const locale name.
+ * @returns: language name
+ */
+const gchar *    ibus_get_language_name         (const gchar    *_locale);
+
+/**
+ * ibus_is_running_gnome_shell:
+ * @returns: TRUE if gnome-shell is running
+ */
+gboolean         ibus_is_running_gnome_shell    (void);
diff --git a/ui/gtk/gtkpanel.xml.in.in b/ui/gtk/gtkpanel.xml.in.in
index edeed1c..1e1e656 100644
--- a/ui/gtk/gtkpanel.xml.in.in
+++ b/ui/gtk/gtkpanel.xml.in.in
@@ -3,7 +3,7 @@
 <component>
 	<name>org.freedesktop.IBus.Panel</name>
 	<description>Gtk Panel Component</description>
-	<exec>${libexecdir}/ibus-ui-gtk</exec>
+	<exec>${libexecdir}/ibus-ui-gtk -s</exec>
 	<version>@VERSION@</version>
 	<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
 	<license>GPL</license>
diff --git a/ui/gtk/main.py b/ui/gtk/main.py
index f4c901d..5e75ab7 100644
--- a/ui/gtk/main.py
+++ b/ui/gtk/main.py
@@ -50,7 +50,13 @@ class UIApplication:
         self.__bus.add_match(match_rule)
 
         self.__panel = panel.Panel(self.__bus)
-        self.__bus.request_name(ibus.IBUS_SERVICE_PANEL, 0)
+        self.__bus.request_name(ibus.IBUS_SERVICE_PANEL,
+                                ibus.BUS_NAME_FLAG_ALLOW_REPLACEMENT |
+                                ibus.BUS_NAME_FLAG_REPLACE_EXISTING)
+        self.__bus.add_match("type='signal',\
+                             sender='org.freedesktop.DBus',\
+                             member='NameLost'")
+        self.__bus.get_dbusconn().add_signal_receiver(self.__name_lost_cb, signal_name="NameLost")
         self.__notify = pynotify.Notification("IBus", \
                             _("Some input methods have been installed, removed or updated. " \
                             "Please restart ibus input platform."), \
@@ -66,6 +72,10 @@ class UIApplication:
     def __registry_changed_cb(self, bus):
         self.__notify.show()
 
+    def __name_lost_cb(self, name):
+        print "Got NameLost signal", name
+        gtk.main_quit()
+
     def run(self):
         try:
             gtk.main()
@@ -84,8 +94,9 @@ def print_help(out, v = 0):
 
 def main():
     daemonize = False
-    shortopt = "hd"
-    longopt = ["help", "daemonize"]
+    stop_by_shell = False
+    shortopt = "hds"
+    longopt = ["help", "daemonize", "stop-by-shell"]
     try:
         opts, args = getopt.getopt(sys.argv[1:], shortopt, longopt)
     except getopt.GetoptError, err:
@@ -96,10 +107,16 @@ def main():
             print_help(sys.stdout)
         elif o in("-d", "--daemonize"):
             daemonize = True
+        elif o in("-s", "--stop-by-shell"):
+            stop_by_shell = True
         else:
             print >> sys.stderr, "Unknown argument: %s" % o
             print_help(sys.stderr, 1)
 
+    if stop_by_shell and ibus.is_running_gnome_shell():
+        print "Exit because GNOME-Shell is running"
+        sys.exit()
+
     if daemonize:
         if os.fork():
             sys.exit()
-- 
1.7.4.1