From 71689f16ab8d19045ed44f2609c90202a6bf5db7 Mon Sep 17 00:00:00 2001 From: fujiwarat 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[] = ""; 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 + * Copyright (C) 2010-2011 Takao Fujiwara + * 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 +#endif + +#include +#include +#include +#include +#include "ibusxml.h" + +#ifdef ENABLE_NLS +#include +#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 + * Copyright (C) 2010-2011 Takao Fujiwara + * 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 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 @@ org.freedesktop.IBus.Panel Gtk Panel Component - ${libexecdir}/ibus-ui-gtk + ${libexecdir}/ibus-ui-gtk -s @VERSION@ Peng Huang <shawn.p.huang@gmail.com> GPL 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