diff options
| author | Huang Peng <shawn.p.huang@gmail.com> | 2009-02-05 10:39:56 +0800 |
|---|---|---|
| committer | Huang Peng <shawn.p.huang@gmail.com> | 2009-02-05 10:39:56 +0800 |
| commit | aedad1ea0a7fef604aa27f4b58433fd8f2ece29e (patch) | |
| tree | ffcb531d8474bde18b90341bcd4eb639edd74525 /bus | |
| parent | 41ad46305a88637dd99f00a2d2a3f455505d357b (diff) | |
re-implement ibus in c language.
Diffstat (limited to 'bus')
| -rw-r--r-- | bus/.gitignore | 2 | ||||
| -rw-r--r-- | bus/Makefile.am | 99 | ||||
| -rw-r--r-- | bus/connection.c | 261 | ||||
| -rw-r--r-- | bus/connection.h | 71 | ||||
| -rw-r--r-- | bus/dbusimpl.c | 1174 | ||||
| -rw-r--r-- | bus/dbusimpl.h | 87 | ||||
| -rw-r--r-- | bus/engineproxy.c | 716 | ||||
| -rw-r--r-- | bus/engineproxy.h | 95 | ||||
| -rw-r--r-- | bus/factoryproxy.c | 220 | ||||
| -rw-r--r-- | bus/factoryproxy.h | 84 | ||||
| -rw-r--r-- | bus/ibusimpl.c | 935 | ||||
| -rw-r--r-- | bus/ibusimpl.h | 102 | ||||
| -rw-r--r-- | bus/inputcontext.c | 1559 | ||||
| -rw-r--r-- | bus/inputcontext.h | 82 | ||||
| -rw-r--r-- | bus/main.c | 148 | ||||
| -rw-r--r-- | bus/matchrule.c | 672 | ||||
| -rw-r--r-- | bus/matchrule.h | 124 | ||||
| -rw-r--r-- | bus/panelproxy.c | 757 | ||||
| -rw-r--r-- | bus/panelproxy.h | 112 | ||||
| -rw-r--r-- | bus/registry.c | 447 | ||||
| -rw-r--r-- | bus/registry.h | 88 | ||||
| -rw-r--r-- | bus/server.c | 152 | ||||
| -rw-r--r-- | bus/server.h | 72 | ||||
| -rw-r--r-- | bus/test-matchrule.c | 40 | ||||
| -rw-r--r-- | bus/test-registry.c | 9 |
25 files changed, 8108 insertions, 0 deletions
diff --git a/bus/.gitignore b/bus/.gitignore new file mode 100644 index 0000000..847558d --- /dev/null +++ b/bus/.gitignore @@ -0,0 +1,2 @@ +ibus-daemon +test-matchrule diff --git a/bus/Makefile.am b/bus/Makefile.am new file mode 100644 index 0000000..d0f883a --- /dev/null +++ b/bus/Makefile.am @@ -0,0 +1,99 @@ +# vim:set noet ts=4: +# +# ibus - The Input Bus +# +# Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com> +# +# 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 program; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA + +libibus = $(top_builddir)/src/libibus.la + +INCLUDES = \ + -I$(top_srcdir)/src \ + $(NULL) + +AM_CFLAGS = \ + @GLIB2_CFLAGS@ \ + @DBUS_CFLAGS@ \ + -DG_LOG_DOMAIN=\"IBUS\" \ + -DPKGDATADIR=\"$(pkgdatadir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + $(INCLUDES) \ + $(NULL) +AM_LDFLAGS = \ + @GLIB2_LIBS@ \ + @DBUS_LIBS@ \ + $(libibus) \ + $(NULL) + +TESTS = \ + test-matchrule \ + $(NULL) + +noinst_PROGRAMS = $(TESTS) +bin_PROGRAMS = ibus-daemon +ibus_daemon_DEPENDENCIES = \ + $(libibus) \ + $(NULL) +ibus_daemon_SOURCES = \ + main.c \ + dbusimpl.c \ + dbusimpl.h \ + ibusimpl.c \ + ibusimpl.h \ + inputcontext.c \ + inputcontext.h \ + engineproxy.c \ + engineproxy.h \ + panelproxy.c \ + panelproxy.h \ + factoryproxy.c \ + factoryproxy.h \ + server.c \ + server.h \ + connection.c \ + connection.h \ + matchrule.c \ + matchrule.h \ + registry.c \ + registry.h \ + $(NULL) +ibus_daemon_CFLAGS = \ + $(AM_CFLAGS) \ + $(NULL) +ibus_daemon_LDADD = \ + $(AM_LDFLAGS) \ + $(NULL) + +test_registry_SOURCES = \ + registry.c \ + registry.h \ + factoryproxy.c \ + factoryproxy.h \ + test-registry.c \ + $(NULL) + +test_matchrule_SOURCES = \ + connection.c \ + matchrule.c \ + test-matchrule.c \ + $(NULL) + +$(libibus): + $(MAKE) -C $(top_builddir)/src + +test: ibus-daemon + $(builddir)/ibus-daemon diff --git a/bus/connection.c b/bus/connection.c new file mode 100644 index 0000000..fd05ab1 --- /dev/null +++ b/bus/connection.c @@ -0,0 +1,261 @@ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ + +#include "connection.h" +#include "matchrule.h" + +#define BUS_CONNECTION_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), BUS_TYPE_CONNECTION, BusConnectionPrivate)) + +/* BusConnectionPriv */ +struct _BusConnectionPrivate { + gchar *unique_name; + /* list for well known names */ + GList *names; + GList *rules; +}; +typedef struct _BusConnectionPrivate BusConnectionPrivate; + +// static guint _signals[LAST_SIGNAL] = { 0 }; + +/* functions prototype */ +static void bus_connection_class_init (BusConnectionClass *klass); +static void bus_connection_init (BusConnection *connection); +static void bus_connection_destroy (BusConnection *connection); +static gboolean bus_connection_ibus_message (BusConnection *connection, + IBusMessage *message); +#if 0 +static gboolean bus_connection_dbus_signal (BusConnection *connection, + DBusMessage *message); +#endif + +static IBusObjectClass *parent_class = NULL; + +GType +bus_connection_get_type (void) +{ + static GType type = 0; + + static const GTypeInfo type_info = { + sizeof (BusConnectionClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) bus_connection_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (IBusConnection), + 0, + (GInstanceInitFunc) bus_connection_init, + }; + + if (type == 0) { + type = g_type_register_static (IBUS_TYPE_CONNECTION, + "BusConnection", + &type_info, + (GTypeFlags)0); + } + + return type; +} + +BusConnection * +bus_connection_new (void) +{ + BusConnection *connection = BUS_CONNECTION (g_object_new (BUS_TYPE_CONNECTION, NULL)); + return connection; +} + +static void +bus_connection_class_init (BusConnectionClass *klass) +{ + IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass); + IBusConnectionClass *ibus_connection_class = IBUS_CONNECTION_CLASS (klass); + + parent_class = (IBusObjectClass *) g_type_class_peek_parent (klass); + + g_type_class_add_private (klass, sizeof (BusConnectionPrivate)); + + ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_connection_destroy; + + ibus_connection_class->ibus_message = + (IBusIBusMessageFunc) bus_connection_ibus_message; + +} + +static void +bus_connection_init (BusConnection *connection) +{ + BusConnectionPrivate *priv; + + priv = BUS_CONNECTION_GET_PRIVATE (connection); + + priv->unique_name = NULL; + priv->names = NULL; +} + +static void +bus_connection_destroy (BusConnection *connection) +{ + GList *name; + BusConnectionPrivate *priv; + + IBUS_OBJECT_CLASS(parent_class)->destroy (IBUS_OBJECT (connection)); + + priv = BUS_CONNECTION_GET_PRIVATE (connection); + + if (priv->unique_name) { + g_free (priv->unique_name); + priv->unique_name = NULL; + } + + for (name = priv->names; name != NULL; name = name->next) { + g_free (name->data); + } + g_list_free (priv->names); + priv->names = NULL; +} + +static gboolean +bus_connection_ibus_message (BusConnection *connection, + IBusMessage *message) +{ + gboolean retval; + +#if 0 + gchar *str = ibus_message_to_string (message); + g_debug ("%s", str); + g_free(str); +#endif + + retval = IBUS_CONNECTION_CLASS (parent_class)->ibus_message ( + (IBusConnection *)connection, + message); + return retval; +} + +#if 0 +static gboolean +bus_connection_dbus_signal (BusConnection *connection, + DBusMessage *message) +{ + gboolean retval; + retval = IBUS_CONNECTION_CLASS (parent_class)->dbus_signal ( + IBUS_CONNECTION (connection), message); + return retval; +} +#endif + +const gchar * +bus_connection_get_unique_name (BusConnection *connection) +{ + BusConnectionPrivate *priv; + + priv = BUS_CONNECTION_GET_PRIVATE (connection); + return priv->unique_name; +} + +void +bus_connection_set_unique_name (BusConnection *connection, + const gchar *name) +{ + BusConnectionPrivate *priv; + priv = BUS_CONNECTION_GET_PRIVATE (connection); + g_assert (priv->unique_name == NULL); + priv->unique_name = g_strdup (name); +} + +const GList * +bus_connection_get_names (BusConnection *connection) +{ + BusConnectionPrivate *priv; + + priv = BUS_CONNECTION_GET_PRIVATE (connection); + return priv->names; +} + +const gchar * +bus_connection_add_name (BusConnection *connection, + const gchar *name) +{ + gchar *new_name; + BusConnectionPrivate *priv; + + priv = BUS_CONNECTION_GET_PRIVATE (connection); + new_name = g_strdup (name); + priv->names = g_list_append (priv->names, new_name); + + return new_name; +} + +gboolean +bus_connection_remove_name (BusConnection *connection, + const gchar *name) +{ + BusConnectionPrivate *priv; + GList *link; + + priv = BUS_CONNECTION_GET_PRIVATE (connection); + + link = g_list_find_custom (priv->names, name, (GCompareFunc) g_strcmp0); + + if (link) { + g_free (link->data); + priv->names = g_list_delete_link (priv->names, link); + return TRUE; + } + return FALSE; +} + +gboolean +bus_connection_add_match (BusConnection *connection, + const gchar *rule) +{ + g_assert (BUS_IS_CONNECTION (connection)); + g_assert (rule != NULL); + + BusMatchRule *p; + GList *link; + BusConnectionPrivate *priv; + + priv = BUS_CONNECTION_GET_PRIVATE (connection); + + p = bus_match_rule_new (rule); + if (p == NULL) + return FALSE; + + for (link = priv->rules; link != NULL; link = link->next) { + if (bus_match_rule_is_equal (p, (BusMatchRule *)link->data)) { + g_object_unref (p); + return TRUE; + } + } + + priv->rules = g_list_append (priv->rules, p); + return TRUE; + +} + +gboolean +bus_connection_remove_match (BusConnection *connection, + const gchar *rule) +{ + return FALSE; +} + diff --git a/bus/connection.h b/bus/connection.h new file mode 100644 index 0000000..1c0624d --- /dev/null +++ b/bus/connection.h @@ -0,0 +1,71 @@ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ +#ifndef __CONNECTION_H_ +#define __CONNECTION_H_ + +#include <ibus.h> + +/* + * Type macros. + */ + +/* define GOBJECT macros */ +#define BUS_TYPE_CONNECTION \ + (bus_connection_get_type ()) +#define BUS_CONNECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUS_TYPE_CONNECTION, BusConnection)) +#define BUS_CONNECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), BUS_TYPE_CONNECTION, BusConnectionClass)) +#define BUS_IS_CONNECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUS_TYPE_CONNECTION)) +#define BUS_IS_CONNECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), BUS_TYPE_CONNECTION)) +#define BUS_CONNECTION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), BUS_TYPE_CONNECTION, BusConnectionClass)) + +G_BEGIN_DECLS + +typedef struct _BusConnection BusConnection; +typedef struct _BusConnectionClass BusConnectionClass; + +struct _BusConnection { + IBusConnection parent; + /* instance members */ +}; + +struct _BusConnectionClass { + IBusConnectionClass parent; + + /* class members */ +}; + +GType bus_connection_get_type (void); +BusConnection *bus_connection_new (void); +const gchar *bus_connection_get_unique_name (BusConnection *connection); +void bus_connection_set_unique_name (BusConnection *connection, + const gchar *name); +const GList *bus_connection_get_names (BusConnection *connection); +const gchar *bus_connection_add_name (BusConnection *connection, + const gchar *name); +gboolean bus_connection_remove_name (BusConnection *connection, + const gchar *name); +G_END_DECLS +#endif + diff --git a/bus/dbusimpl.c b/bus/dbusimpl.c new file mode 100644 index 0000000..6a70f89 --- /dev/null +++ b/bus/dbusimpl.c @@ -0,0 +1,1174 @@ +/* vim:set et sts=4: */ +/* ibus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ + +#include <string.h> +#include <ibusinternal.h> +#include <ibusmarshalers.h> +#include "dbusimpl.h" +#include "connection.h" +#include "matchrule.h" + +#define BUS_DBUS_IMPL_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), BUS_TYPE_DBUS_IMPL, BusDBusImplPrivate)) + +enum { + NAME_ACQUIRED, + NAME_LOST, + NAME_OWNER_CHANGED, + LAST_SIGNAL, +}; + +enum { + PROP_0, +}; + + +/* IBusDBusImplPriv */ +struct _BusDBusImplPrivate { + GHashTable *unique_names; + GHashTable *names; + GHashTable *objects; + GList *connections; + GList *rules; + gint id; +}; + +typedef struct _BusDBusImplPrivate BusDBusImplPrivate; + +static guint dbus_signals[LAST_SIGNAL] = { 0 }; + +/* functions prototype */ +static void bus_dbus_impl_class_init (BusDBusImplClass *klass); +static void bus_dbus_impl_init (BusDBusImpl *dbus); +static void bus_dbus_impl_dispose (BusDBusImpl *dbus); +static gboolean bus_dbus_impl_ibus_message (BusDBusImpl *dbus, + BusConnection *connection, + IBusMessage *message); +static void bus_dbus_impl_name_owner_changed + (BusDBusImpl *dbus, + gchar *name, + gchar *old_name, + gchar *new_name); + +static IBusServiceClass *parent_class = NULL; + +GType +bus_dbus_impl_get_type (void) +{ + static GType type = 0; + + static const GTypeInfo type_info = { + sizeof (BusDBusImplClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) bus_dbus_impl_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (BusDBusImpl), + 0, + (GInstanceInitFunc) bus_dbus_impl_init, + }; + + if (type == 0) { + type = g_type_register_static (IBUS_TYPE_SERVICE, + "BusDBusImpl", + &type_info, + (GTypeFlags) 0); + } + return type; +} + +BusDBusImpl * +bus_dbus_impl_get_default (void) +{ + // BusDBusImplPrivate *priv; + static BusDBusImpl *dbus = NULL; + + if (dbus == NULL) { + dbus = (BusDBusImpl *) g_object_new (BUS_TYPE_DBUS_IMPL, + "path", DBUS_PATH_DBUS, + NULL); + } + + return dbus; +} + +static void +bus_dbus_impl_class_init (BusDBusImplClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + IBusServiceClass *service_class = IBUS_SERVICE_CLASS (klass); + + parent_class = (IBusServiceClass *) g_type_class_peek_parent (klass); + + g_type_class_add_private (klass, sizeof (BusDBusImplPrivate)); + + gobject_class->dispose = (GObjectFinalizeFunc) bus_dbus_impl_dispose; + + service_class->ibus_message = (ServiceIBusMessageFunc) bus_dbus_impl_ibus_message; + + klass->name_owner_changed = bus_dbus_impl_name_owner_changed; + + /* install signals */ + dbus_signals[NAME_OWNER_CHANGED] = + g_signal_new (I_("name-owner-changed"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (BusDBusImplClass, name_owner_changed), + NULL, NULL, + ibus_marshal_VOID__STRING_STRING_STRING, + G_TYPE_NONE, + 3, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); + +} + +static void +bus_dbus_impl_init (BusDBusImpl *dbus) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + priv->unique_names = g_hash_table_new (g_str_hash, g_str_equal); + priv->names = g_hash_table_new (g_str_hash, g_str_equal); + priv->objects = g_hash_table_new (g_str_hash, g_str_equal); + priv->connections = NULL; + priv->rules = NULL; + priv->id = 1; + + g_object_ref (dbus); + g_hash_table_insert (priv->objects, DBUS_PATH_DBUS, dbus); +} + +static void +bus_dbus_impl_dispose (BusDBusImpl *dbus) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + G_OBJECT_CLASS(parent_class)->dispose (G_OBJECT (dbus)); +} + + +/* introspectable interface */ +static IBusMessage * +_dbus_introspect (BusDBusImpl *dbus, + 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.DBus\">\n" + " <method name=\"Hello\">\n" + " <arg direction=\"out\" type=\"s\"/>\n" + " </method>\n" + " <method name=\"RequestName\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"in\" type=\"u\"/>\n" + " <arg direction=\"out\" type=\"u\"/>\n" + " </method>\n" + " <method name=\"ReleaseName\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"u\"/>\n" + " </method>\n" + " <method name=\"StartServiceByName\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"in\" type=\"u\"/>\n" + " <arg direction=\"out\" type=\"u\"/>\n" + " </method>\n" + " <method name=\"UpdateActivationEnvironment\">\n" + " <arg direction=\"in\" type=\"a{ss}\"/>\n" + " </method>\n" + " <method name=\"NameHasOwner\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"b\"/>\n" + " </method>\n" + " <method name=\"ListNames\">\n" + " <arg direction=\"out\" type=\"as\"/>\n" + " </method>\n" + " <method name=\"ListActivatableNames\">\n" + " <arg direction=\"out\" type=\"as\"/>\n" + " </method>\n" + " <method name=\"AddMatch\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " </method>\n" + " <method name=\"RemoveMatch\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " </method>\n" + " <method name=\"GetNameOwner\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"s\"/>\n" + " </method>\n" + " <method name=\"ListQueuedOwners\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"as\"/>\n" + " </method>\n" + " <method name=\"GetConnectionUnixUser\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"u\"/>\n" + " </method>\n" + " <method name=\"GetConnectionUnixProcessID\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"u\"/>\n" + " </method>\n" + " <method name=\"GetAdtAuditSessionData\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"ay\"/>\n" + " </method>\n" + " <method name=\"GetConnectionSELinuxSecurityContext\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"ay\"/>\n" + " </method>\n" + " <method name=\"ReloadConfig\">\n" + " </method>\n" + " <method name=\"GetId\">\n" + " <arg direction=\"out\" type=\"s\"/>\n" + " </method>\n" + " <signal name=\"NameOwnerChanged\">\n" + " <arg type=\"s\"/>\n" + " <arg type=\"s\"/>\n" + " <arg type=\"s\"/>\n" + " </signal>\n" + " <signal name=\"NameLost\">\n" + " <arg type=\"s\"/>\n" + " </signal>\n" + " <signal name=\"NameAcquired\">\n" + " <arg type=\"s\"/>\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; +} + + +/* dbus interface */ +static IBusMessage * +_dbus_no_implement (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + IBusMessage *reply_message; + reply_message = ibus_message_new_error_printf (message, + DBUS_ERROR_UNKNOWN_METHOD, + "IBus does not support %s.", + ibus_message_get_member (message)); + return reply_message; +} + + +static IBusMessage * +_dbus_hello (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + IBusMessage *reply_message; + + if (bus_connection_get_unique_name (connection) != NULL) { + reply_message = ibus_message_new_error (message, + DBUS_ERROR_FAILED, + "Already handled an Hello message"); + } + else { + gchar *name; + + name = g_strdup_printf (":1.%d", priv->id ++); + bus_connection_set_unique_name (connection, name); + g_free (name); + + name = (gchar *) bus_connection_get_unique_name (connection); + g_hash_table_insert (priv->unique_names, name, connection); + + reply_message = ibus_message_new_method_return (message); + ibus_message_append_args (reply_message, + G_TYPE_STRING, &name, + G_TYPE_INVALID); + + ibus_connection_send ((IBusConnection *) connection, reply_message); + ibus_message_unref (reply_message); + ibus_connection_flush ((IBusConnection *) connection); + reply_message = NULL; + + g_signal_emit (dbus, + dbus_signals[NAME_OWNER_CHANGED], + 0, + name, + "", + name); + + } + + return reply_message; +} + +static IBusMessage * +_dbus_list_names (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + IBusMessage *reply_message; + IBusMessageIter iter, sub_iter; + GList *name, *names; + + reply_message = ibus_message_new_method_return (message); + + ibus_message_iter_init_append (message, &iter); + ibus_message_iter_open_container (&iter, IBUS_TYPE_ARRAY, "s", &sub_iter); + + // append unique names + names = g_hash_table_get_keys (priv->unique_names); + + names = g_list_sort (names, (GCompareFunc) g_strcmp0); + for (name = names; name != NULL; name = name->next) { + ibus_message_iter_append (&sub_iter, G_TYPE_STRING, &(name->data)); + } + g_list_free (names); + + // append well-known names + names = g_hash_table_get_keys (priv->names); + names = g_list_sort (names, (GCompareFunc) g_strcmp0); + for (name = names; name != NULL; name = name->next) { + ibus_message_iter_append (&sub_iter, G_TYPE_STRING, &(name->data)); + } + g_list_free (names); + + ibus_message_iter_close_container (&iter, &sub_iter); + + return reply_message; +} + +static IBusMessage * +_dbus_name_has_owner (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + gchar *name; + gboolean retval; + gboolean has_owner; + IBusMessage *reply_message; + IBusError *error; + + retval = ibus_message_get_args (message, + &error, + G_TYPE_STRING, &name, + G_TYPE_INVALID); + + if (! retval) { + reply_message = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply_message; + } + + if (name[0] == ':') { + has_owner = g_hash_table_lookup (priv->unique_names, name) != NULL; + } + else { + has_owner = g_hash_table_lookup (priv->names, name) != NULL; + } + + reply_message = ibus_message_new_method_return (message); + ibus_message_append_args (reply_message, + G_TYPE_BOOLEAN, &has_owner, + G_TYPE_INVALID); + + return reply_message; +} + + +static IBusMessage * +_dbus_get_name_owner (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + gchar *name; + BusConnection *owner; + gboolean retval; + const gchar *owner_name = NULL; + IBusMessage *reply_message; + IBusError *error; + + retval = ibus_message_get_args (message, + &error, + G_TYPE_STRING, &name, + G_TYPE_INVALID); + + if (! retval) { + reply_message = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply_message; + } + + if (g_strcmp0 (name, DBUS_SERVICE_DBUS) == 0 || + g_strcmp0 (name, IBUS_SERVICE_IBUS) == 0) { + owner_name = name; + } + else { + owner = bus_dbus_impl_get_connection_by_name (dbus, name); + if (owner != NULL) { + owner_name = bus_connection_get_unique_name (owner); + } + } + + if (owner_name != NULL) { + reply_message = ibus_message_new_method_return (message); + ibus_message_append_args (reply_message, + G_TYPE_STRING, &owner_name, + G_TYPE_INVALID); + } + else { + reply_message = ibus_message_new_error_printf (message, + DBUS_ERROR_NAME_HAS_NO_OWNER, + "Name '%s' does have owner", + name); + } + + return reply_message; +} + +static IBusMessage * +_dbus_get_id (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + IBusMessage *reply_message; + const gchar *name; + + name = bus_connection_get_unique_name (connection); + + if (name == NULL) { + reply_message = ibus_message_new_error (message, + DBUS_ERROR_FAILED, + "Can not GetId before Hello"); + return reply_message; + } + + reply_message = ibus_message_new_method_return (message); + ibus_message_append_args (reply_message, + G_TYPE_STRING, &name, + G_TYPE_INVALID); + return reply_message; +} + +static void +_rule_destroy_cb (BusMatchRule *rule, + BusDBusImpl *dbus) +{ + g_assert (BUS_IS_MATCH_RULE (rule)); + g_assert (BUS_IS_DBUS_IMPL (dbus)); + + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + priv->rules = g_list_remove (priv->rules, rule); + g_object_unref (rule); +} + +static IBusMessage * +_dbus_add_match (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + IBusMessage *reply_message; + IBusError *error; + gboolean retval; + gchar *rule_text; + BusMatchRule *rule; + GList *link; + + retval = ibus_message_get_args (message, + &error, + G_TYPE_STRING, &rule_text, + G_TYPE_INVALID); + + if (!retval) { + reply_message = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply_message; + } + + rule = bus_match_rule_new (rule_text); + + if (rule == NULL) { + reply_message = ibus_message_new_error_printf (message, + DBUS_ERROR_MATCH_RULE_INVALID, + "Parse rule [%s] failed", + rule_text); + return reply_message; + } + + for (link = priv->rules; link != NULL; link = link->next) { + if (bus_match_rule_is_equal (rule, BUS_MATCH_RULE (link->data))) { + bus_match_rule_add_recipient (BUS_MATCH_RULE (link->data), connection); + g_object_unref (rule); + rule = NULL; + break; + } + } + + if (rule) { + bus_match_rule_add_recipient (rule, connection); + priv->rules = g_list_append (priv->rules, rule); + g_signal_connect (rule, "destroy", G_CALLBACK (_rule_destroy_cb), dbus); + } + + reply_message = ibus_message_new_method_return (message); + return reply_message; +} + +static IBusMessage * +_dbus_remove_match (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + IBusMessage *reply_message; + IBusError *error; + gchar *rule_text; + BusMatchRule *rule; + GList *link; + + if (!ibus_message_get_args (message, + &error, + G_TYPE_STRING, &rule_text, + G_TYPE_INVALID)) { + reply_message = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply_message; + } + + rule = bus_match_rule_new (rule_text); + + if (rule == NULL ) { + reply_message = ibus_message_new_error_printf (message, + DBUS_ERROR_MATCH_RULE_INVALID, + "Parse rule [%s] failed", + rule_text); + return reply_message; + } + + for (link = priv->rules; link != NULL; link = link->next) { + if (bus_match_rule_is_equal (rule, BUS_MATCH_RULE (link->data))) { + bus_match_rule_remove_recipient (BUS_MATCH_RULE (link->data), connection); + break; + } + } + + g_object_unref (rule); + + reply_message = ibus_message_new_method_return (message); + return reply_message; +} + +static IBusMessage * +_dbus_request_name (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + IBusMessage *reply_message; + IBusError *error; + gchar *name; + guint flags; + guint retval; + + if (!ibus_message_get_args (message, + &error, + G_TYPE_STRING, &name, + G_TYPE_UINT, &flags, + G_TYPE_INVALID)) { + reply_message = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply_message; + } + + if (g_hash_table_lookup (priv->names, name) != NULL) { + reply_message = ibus_message_new_error_printf (message, + DBUS_ERROR_FAILED, + "Name %s has owner", + name); + return reply_message; + } + + retval = 1; + g_hash_table_insert (priv->names, + (gpointer )bus_connection_add_name (connection, name), + connection); + reply_message = ibus_message_new_method_return (message); + ibus_message_append_args (reply_message, + G_TYPE_UINT, &retval, + G_TYPE_INVALID); + + ibus_connection_send ((IBusConnection *) connection, reply_message); + ibus_message_unref (reply_message); + ibus_connection_flush ((IBusConnection *) connection); + + g_signal_emit (dbus, + dbus_signals[NAME_OWNER_CHANGED], + 0, + name, + "", + bus_connection_get_unique_name (connection)); + + return NULL; +} + +static IBusMessage * +_dbus_release_name (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + IBusMessage *reply_message; + IBusError *error; + gchar *name; + guint retval; + + if (!ibus_message_get_args (message, + &error, + G_TYPE_STRING, &name, + G_TYPE_INVALID)) { + reply_message = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply_message; + } + + reply_message = ibus_message_new_method_return (message); + if (bus_connection_remove_name (connection, name)) { + retval = 1; + } + else { + retval = 2; + } + + ibus_message_append_args (message, + G_TYPE_UINT, &retval, + G_TYPE_INVALID); + + return reply_message; +} + + +static gboolean +bus_dbus_impl_ibus_message (BusDBusImpl *dbus, + BusConnection *connection, + IBusMessage *message) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (BUS_IS_CONNECTION (connection)); + g_assert (message != NULL); + + gint i; + IBusMessage *reply_message = NULL; + + static const struct { + const gchar *interface; + const gchar *name; + IBusMessage *(* handler) (BusDBusImpl *, IBusMessage *, BusConnection *); + } handlers[] = { + /* Introspectable interface */ + { DBUS_INTERFACE_INTROSPECTABLE, + "Introspect", _dbus_introspect }, + /* DBus interface */ + { DBUS_INTERFACE_DBUS, "Hello", _dbus_hello }, + { DBUS_INTERFACE_DBUS, "ListNames", _dbus_list_names }, + { DBUS_INTERFACE_DBUS, "ListActivatableNames", + _dbus_no_implement }, + { DBUS_INTERFACE_DBUS, "NameHasOwner", + _dbus_name_has_owner }, + { DBUS_INTERFACE_DBUS, "StartServiceByName", + _dbus_no_implement }, + { DBUS_INTERFACE_DBUS, "GetNameOwner", + _dbus_get_name_owner }, + { DBUS_INTERFACE_DBUS, "GetConnectionUnixUser", + _dbus_no_implement }, + { DBUS_INTERFACE_DBUS, "AddMatch", _dbus_add_match }, + { DBUS_INTERFACE_DBUS, "RemoveMatch", + _dbus_remove_match }, + { DBUS_INTERFACE_DBUS, "GetId", _dbus_get_id }, + { DBUS_INTERFACE_DBUS, "RequestName", _dbus_request_name }, + { DBUS_INTERFACE_DBUS, "ReleaseName", _dbus_release_name }, + { NULL, NULL, NULL } + }; + + ibus_message_set_destination (message, DBUS_SERVICE_DBUS); + + for (i = 0; handlers[i].interface != NULL; i++) { + if (ibus_message_is_method_call (message, + handlers[i].interface, + handlers[i].name)) { + + reply_message = handlers[i].handler (dbus, message, connection); + if (reply_message) { + + 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); + + ibus_connection_send (IBUS_CONNECTION (connection), reply_message); + ibus_message_unref (reply_message); + } + + g_signal_stop_emission_by_name (dbus, "ibus-message"); + return TRUE; + } + } + + return parent_class->ibus_message ((IBusService *) dbus, + (IBusConnection *) connection, + message); +} + +static void +bus_dbus_impl_name_owner_changed (BusDBusImpl *dbus, + gchar *name, + gchar *old_name, + gchar *new_name) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (name != NULL); + g_assert (old_name != NULL); + g_assert (new_name != NULL); + + IBusMessage *message; + + message = ibus_message_new_signal (DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "NameOwnerChanged"); + ibus_message_append_args (message, + G_TYPE_STRING, &name, + G_TYPE_STRING, &old_name, + G_TYPE_STRING, &new_name, + G_TYPE_INVALID); + ibus_message_set_sender (message, DBUS_SERVICE_DBUS); + + bus_dbus_impl_dispatch_message_by_rule (dbus, message, NULL); + + ibus_message_unref (message); + +} + +static gboolean +_connection_ibus_message_cb (BusConnection *connection, + IBusMessage *message, + BusDBusImpl *dbus) +{ + g_assert (BUS_IS_CONNECTION (connection)); + g_assert (message != NULL); + g_assert (BUS_IS_DBUS_IMPL (dbus)); + + const gchar *dest; + + if (ibus_message_is_signal (message, + DBUS_INTERFACE_LOCAL, + "Disconnected")) { + /* ignore signal from local interface */ + return FALSE; + } + + ibus_message_set_sender (message, bus_connection_get_unique_name (connection)); + + switch (ibus_message_get_type (message)) { + case DBUS_MESSAGE_TYPE_ERROR: + g_debug ("From :%s to %s, Error: %s : %s", + ibus_message_get_sender (message), + ibus_message_get_destination (message), + ibus_message_get_error_name (message), + ibus_message_get_error_message (message)); + break; +#if 0 + case DBUS_MESSAGE_TYPE_METHOD_CALL: + g_debug("From %s to %s, Method %s on %s", + ibus_message_get_sender (message), + ibus_message_get_destination (message), + ibus_message_get_path (message), + ibus_message_get_member (message)); + break; +#endif + } + + dest = ibus_message_get_destination (message); + + if (dest == NULL || + strcmp ((gchar *)dest, IBUS_SERVICE_IBUS) == 0 || + strcmp ((gchar *)dest, DBUS_SERVICE_DBUS) == 0) { + /* this message is sent to ibus-daemon */ + + switch (ibus_message_get_type (message)) { + case DBUS_MESSAGE_TYPE_SIGNAL: + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + case DBUS_MESSAGE_TYPE_ERROR: + bus_dbus_impl_dispatch_message_by_rule (dbus, message, NULL); + return FALSE; + case DBUS_MESSAGE_TYPE_METHOD_CALL: + { + const gchar *path; + IBusService *object; + + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + path = ibus_message_get_path (message); + + object = g_hash_table_lookup (priv->objects, path); + + if (object == NULL || + ibus_service_handle_message (object, + (IBusConnection *) connection, + message) == FALSE) { + IBusMessage *error; + error = ibus_message_new_error_printf (message, + DBUS_ERROR_UNKNOWN_METHOD, + "Unknown method %s on %s", + ibus_message_get_member (message), + path); + ibus_connection_send ((IBusConnection *) connection, error); + ibus_message_unref (error); + } + + /* dispatch message */ + bus_dbus_impl_dispatch_message_by_rule (dbus, message, NULL); + } + break; + default: + g_assert (FALSE); + } + } + else { + /* If the destination is not IBus or DBus, the message will be forwanded. */ + bus_dbus_impl_dispatch_message (dbus, message); + } + + g_signal_stop_emission_by_name (connection, "ibus-message"); + return TRUE; +} + +static void +_connection_ibus_message_sent_cb (BusConnection *connection, + IBusMessage *message, + BusDBusImpl *dbus) +{ + g_assert (BUS_IS_CONNECTION (connection)); + g_assert (message != NULL); + g_assert (BUS_IS_DBUS_IMPL (dbus)); + + bus_dbus_impl_dispatch_message_by_rule (dbus, message, connection); +} + +static void +_connection_destroy_cb (BusConnection *connection, + BusDBusImpl *dbus) +{ + g_assert (BUS_IS_CONNECTION (connection)); + g_assert (BUS_IS_DBUS_IMPL (dbus)); + + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + /* + ibus_service_remove_from_connection ( + IBUS_SERVICE (dbus), + IBUS_CONNECTION (connection)); + */ + + const gchar *unique_name = bus_connection_get_unique_name (connection); + if (unique_name != NULL) { + g_hash_table_remove (priv->unique_names, unique_name); + g_signal_emit (dbus, + dbus_signals[NAME_OWNER_CHANGED], + 0, + unique_name, + unique_name, + ""); + } + + const GList *name = bus_connection_get_names (connection); + + while (name != NULL) { + g_hash_table_remove (priv->names, name->data); + g_signal_emit (dbus, + dbus_signals[NAME_OWNER_CHANGED], + 0, + name->data, + unique_name, + ""); + name = name->next; + } + + priv->connections = g_list_remove (priv->connections, connection); + g_object_unref (connection); +} + + +gboolean +bus_dbus_impl_new_connection (BusDBusImpl *dbus, + BusConnection *connection) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (BUS_IS_CONNECTION (connection)); + + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + g_assert (g_list_find (priv->connections, connection) == NULL); + + g_object_ref (connection); + priv->connections = g_list_append (priv->connections, connection); + + g_signal_connect (connection, + "ibus-message", + G_CALLBACK (_connection_ibus_message_cb), + dbus); + + g_signal_connect (connection, + "ibus-message-sent", + G_CALLBACK (_connection_ibus_message_sent_cb), + dbus); + + + g_signal_connect (connection, + "destroy", + G_CALLBACK (_connection_destroy_cb), + dbus); + return TRUE; +} + + +BusConnection * +bus_dbus_impl_get_connection_by_name (BusDBusImpl *dbus, + const gchar *name) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (name != NULL); + + BusConnection *connection = NULL; + + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + if (name[0] == ':') { + connection = BUS_CONNECTION (g_hash_table_lookup ( + priv->unique_names, + name)); + } + else { + connection = BUS_CONNECTION (g_hash_table_lookup ( + priv->names, + name)); + } + + return connection; +} + + +void +bus_dbus_impl_dispatch_message (BusDBusImpl *dbus, + IBusMessage *message) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (message != NULL); + + const gchar *destination; + BusConnection *dest_connection = NULL; + + destination = ibus_message_get_destination (message); + + if (destination != NULL) { + dest_connection = bus_dbus_impl_get_connection_by_name (dbus, destination); + + if (dest_connection != NULL) { + ibus_connection_send (IBUS_CONNECTION (dest_connection), message); + } + else { + IBusMessage *reply_message; + reply_message = ibus_message_new_error_printf (message, + DBUS_ERROR_SERVICE_UNKNOWN, + "Can not find service %s", + destination); + bus_dbus_impl_dispatch_message (dbus, reply_message); + ibus_message_unref (reply_message); + } + } + + bus_dbus_impl_dispatch_message_by_rule (dbus, message, dest_connection); +} + +void +bus_dbus_impl_dispatch_message_by_rule (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *skip_connection) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (message != NULL); + g_assert (BUS_IS_CONNECTION (skip_connection) || skip_connection == NULL); + + GList *recipients = NULL; + GList *link = NULL; + + static gint32 data_slot = -1; + + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + if (data_slot == -1) { + dbus_message_allocate_data_slot (&data_slot); + } + + /* If this message has been dispatched by rule, it will be ignored. */ + if (dbus_message_get_data (message, data_slot) != NULL) + return; + + dbus_message_set_data (message, data_slot, (gpointer) TRUE, NULL); + +#if 0 + if (g_strcmp0 (ibus_message_get_member (message), "ValueChanged") == 0) { + g_debug ("Dispatch ValueChanged"); + } +#endif + + for (link = priv->rules; link != NULL; link = link->next) { + if (bus_match_rule_get_recipients (BUS_MATCH_RULE (link->data), + message, + &recipients)) { + break; + } + } + + for (link = recipients; link != NULL; link = link->next) { + BusConnection *connection = BUS_CONNECTION (link->data); + if (connection != skip_connection) { + ibus_connection_send (IBUS_CONNECTION (connection), message); + } + g_object_unref (link->data); + } + g_list_free (recipients); +} + + +static void +_object_destroy_cb (IBusService *object, + BusDBusImpl *dbus) +{ + gboolean retval; + + retval = bus_dbus_impl_unregister_object (dbus, object); + + g_assert (retval); +} + +gboolean +bus_dbus_impl_register_object (BusDBusImpl *dbus, + IBusService *object) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (IBUS_IS_SERVICE (object)); + + const gchar *path; + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + path = ibus_service_get_path (object); + + g_return_val_if_fail (path, FALSE); + + g_return_val_if_fail (g_hash_table_lookup (priv->objects, path) == NULL, FALSE); + + g_object_ref (object); + g_hash_table_insert (priv->objects, (gpointer)path, object); + + g_signal_connect (object, "destroy", G_CALLBACK (_object_destroy_cb), dbus); + + return TRUE; +} + +gboolean +bus_dbus_impl_unregister_object (BusDBusImpl *dbus, + IBusService *object) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (IBUS_IS_SERVICE (object)); + + const gchar *path; + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + path = ibus_service_get_path (object); + g_return_val_if_fail (path, FALSE); + + g_return_val_if_fail (g_hash_table_lookup (priv->objects, path) == object, FALSE); + + g_signal_handlers_disconnect_by_func (object, G_CALLBACK (_object_destroy_cb), dbus); + + g_hash_table_remove (priv->objects, path); + g_object_unref (object); + + return TRUE; +} + diff --git a/bus/dbusimpl.h b/bus/dbusimpl.h new file mode 100644 index 0000000..d04326e --- /dev/null +++ b/bus/dbusimpl.h @@ -0,0 +1,87 @@ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ +#ifndef __DBUS_IMPL_H_ +#define __DBUS_IMPL_H_ + +#include <ibus.h> +#include "connection.h" + +/* + * Type macros. + */ + +/* define GOBJECT macros */ +#define BUS_TYPE_DBUS_IMPL \ + (bus_dbus_impl_get_type ()) +#define BUS_DBUS_IMPL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUS_TYPE_DBUS_IMPL, BusDBusImpl)) +#define BUS_DBUS_IMPL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), BUS_TYPE_DBUS_IMPL, BusDBusImplClass)) +#define BUS_IS_DBUS_IMPL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUS_TYPE_DBUS_IMPL)) +#define BUS_IS_DBUS_IMPL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), BUS_TYPE_DBUS_IMPL)) +#define BUS_DBUS_IMPL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), BUS_TYPE_DBUS_IMPL, BusDBusImplClass)) + +#define BUS_DEFAULT_DBUS \ + (bus_dbus_impl_get_default ()) + +G_BEGIN_DECLS + +typedef struct _BusDBusImpl BusDBusImpl; +typedef struct _BusDBusImplClass BusDBusImplClass; + +struct _BusDBusImpl { + IBusService parent; + /* instance members */ +}; + +struct _BusDBusImplClass { + IBusServiceClass parent; + + /* class members */ + void (* name_owner_changed) (BusDBusImpl *dbus, + gchar *name, + gchar *old_name, + gchar *new_name); +}; + +GType bus_dbus_impl_get_type (void); +BusDBusImpl *bus_dbus_impl_get_default (void); +gboolean bus_dbus_impl_new_connection (BusDBusImpl *dbus, + BusConnection *connection); +BusConnection *bus_dbus_impl_get_connection_by_name + (BusDBusImpl *dbus, + const gchar *name); +void bus_dbus_impl_dispatch_message (BusDBusImpl *dbus, + DBusMessage *message); +void bus_dbus_impl_dispatch_message_by_rule + (BusDBusImpl *dbus, + DBusMessage *message, + BusConnection *skip_connection); +gboolean bus_dbus_impl_register_object (BusDBusImpl *dbus, + IBusService *object); +gboolean bus_dbus_impl_unregister_object(BusDBusImpl *dbus, + IBusService *object); + +G_END_DECLS +#endif + diff --git a/bus/engineproxy.c b/bus/engineproxy.c new file mode 100644 index 0000000..963dfe2 --- /dev/null +++ b/bus/engineproxy.c @@ -0,0 +1,716 @@ +/* vim:set et sts=4: */ +/* ibus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ + +#include <ibusinternal.h> +#include <ibusmarshalers.h> +#include "engineproxy.h" + +#define BUS_ENGINE_PROXY_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), BUS_TYPE_ENGINE_PROXY, BusEngineProxyPrivate)) + +enum { + COMMIT_TEXT, + FORWARD_KEY_EVENT, + UPDATE_PREEDIT_TEXT, + SHOW_PREEDIT_TEXT, + HIDE_PREEDIT_TEXT, + UPDATE_AUXILIARY_TEXT, + SHOW_AUXILIARY_TEXT, + HIDE_AUXILIARY_TEXT, + UPDATE_LOOKUP_TABLE, + SHOW_LOOKUP_TABLE, + HIDE_LOOKUP_TABLE, + PAGE_UP_LOOKUP_TABLE, + PAGE_DOWN_LOOKUP_TABLE, + CURSOR_UP_LOOKUP_TABLE, + CURSOR_DOWN_LOOKUP_TABLE, + REGISTER_PROPERTIES, + UPDATE_PROPERTY, + LAST_SIGNAL, +}; + + +/* BusEngineProxyPriv */ +struct _BusEngineProxyPrivate { + gboolean enabled; + IBusEngineDesc *desc; + + IBusPropList *prop_list; +}; +typedef struct _BusEngineProxyPrivate BusEngineProxyPrivate; + +static guint engine_signals[LAST_SIGNAL] = { 0 }; +// static guint engine_signals[LAST_SIGNAL] = { 0 }; + +/* functions prototype */ +static void bus_engine_proxy_class_init (BusEngineProxyClass *klass); +static void bus_engine_proxy_init (BusEngineProxy *engine); +static void bus_engine_proxy_real_destroy (BusEngineProxy *engine); + +static gboolean bus_engine_proxy_ibus_signal (IBusProxy *proxy, + IBusMessage *message); + +static IBusProxyClass *parent_class = NULL; + +GType +bus_engine_proxy_get_type (void) +{ + static GType type = 0; + + static const GTypeInfo type_info = { + sizeof (BusEngineProxyClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) bus_engine_proxy_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (BusEngineProxy), + 0, + (GInstanceInitFunc) bus_engine_proxy_init, + }; + + if (type == 0) { + type = g_type_register_static (IBUS_TYPE_PROXY, + "BusEngineProxy", + &type_info, + (GTypeFlags)0); + } + return type; +} + +BusEngineProxy * +bus_engine_proxy_new (const gchar *path, + IBusEngineDesc *desc, + BusConnection *connection) +{ + g_assert (path); + g_assert (IBUS_IS_ENGINE_DESC (desc)); + g_assert (BUS_IS_CONNECTION (connection)); + + BusEngineProxy *engine; + + engine = (BusEngineProxy *) g_object_new (BUS_TYPE_ENGINE_PROXY, + "name", NULL, + "path", path, + "connection", connection, + NULL); + + BusEngineProxyPrivate *priv; + priv = BUS_ENGINE_PROXY_GET_PRIVATE (engine); + priv->desc = desc; + g_object_ref (desc); + + return engine; +} + +static void +bus_engine_proxy_class_init (BusEngineProxyClass *klass) +{ + IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass); + IBusProxyClass *proxy_class = IBUS_PROXY_CLASS (klass); + + + parent_class = (IBusProxyClass *) g_type_class_peek_parent (klass); + + g_type_class_add_private (klass, sizeof (BusEngineProxyPrivate)); + + ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_engine_proxy_real_destroy; + + proxy_class->ibus_signal = bus_engine_proxy_ibus_signal; + + /* install signals */ + engine_signals[COMMIT_TEXT] = + g_signal_new (I_("commit-text"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + IBUS_TYPE_TEXT); + + engine_signals[FORWARD_KEY_EVENT] = + g_signal_new (I_("forward-key-event"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__UINT_UINT, + G_TYPE_NONE, + 2, + G_TYPE_UINT, + G_TYPE_UINT); + + engine_signals[UPDATE_PREEDIT_TEXT] = + g_signal_new (I_("update-preedit-text"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__OBJECT_UINT_BOOLEAN, + G_TYPE_NONE, + 3, + IBUS_TYPE_TEXT, + G_TYPE_UINT, + G_TYPE_BOOLEAN); + + engine_signals[SHOW_PREEDIT_TEXT] = + g_signal_new (I_("show-preedit-text"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + engine_signals[HIDE_PREEDIT_TEXT] = + g_signal_new (I_("hide-preedit-text"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + engine_signals[UPDATE_AUXILIARY_TEXT] = + g_signal_new (I_("update-auxiliary-text"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__OBJECT_BOOLEAN, + G_TYPE_NONE, + 2, + IBUS_TYPE_TEXT, + G_TYPE_BOOLEAN); + + engine_signals[SHOW_AUXILIARY_TEXT] = + g_signal_new (I_("show-auxiliary-text"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + engine_signals[HIDE_AUXILIARY_TEXT] = + g_signal_new (I_("hide-auxiliary-text"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + engine_signals[UPDATE_LOOKUP_TABLE] = + g_signal_new (I_("update-lookup-table"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__BOXED_BOOLEAN, + G_TYPE_NONE, + 2, + IBUS_TYPE_LOOKUP_TABLE, + G_TYPE_BOOLEAN); + + engine_signals[SHOW_LOOKUP_TABLE] = + g_signal_new (I_("show-lookup-table"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + engine_signals[HIDE_LOOKUP_TABLE] = + g_signal_new (I_("hide-lookup-table"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + engine_signals[PAGE_UP_LOOKUP_TABLE] = + g_signal_new (I_("page-up-lookup-table"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + engine_signals[PAGE_DOWN_LOOKUP_TABLE] = + g_signal_new (I_("page-down-lookup-table"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + engine_signals[CURSOR_UP_LOOKUP_TABLE] = + g_signal_new (I_("cursor-up-lookup-table"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + engine_signals[CURSOR_DOWN_LOOKUP_TABLE] = + g_signal_new (I_("cursor-down-lookup-table"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + engine_signals[REGISTER_PROPERTIES] = + g_signal_new (I_("register-properties"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + IBUS_TYPE_PROP_LIST); + + engine_signals[UPDATE_PROPERTY] = + g_signal_new (I_("update-property"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + IBUS_TYPE_PROPERTY); + +} + +static void +bus_engine_proxy_init (BusEngineProxy *engine) +{ + BusEngineProxyPrivate *priv; + priv = BUS_ENGINE_PROXY_GET_PRIVATE (engine); + + priv->enabled = FALSE; + priv->prop_list = NULL; + priv->desc = NULL; +} + +static void +bus_engine_proxy_real_destroy (BusEngineProxy *engine) +{ + BusEngineProxyPrivate *priv; + priv = BUS_ENGINE_PROXY_GET_PRIVATE (engine); + + if (priv->prop_list) { + g_object_unref (priv->prop_list); + priv->prop_list = NULL; + } + + if (ibus_proxy_get_connection ((IBusProxy *) engine)) { + ibus_proxy_call ((IBusProxy *) engine, + "Destroy", + DBUS_TYPE_INVALID); + } + + if (priv->desc) { + g_object_unref (priv->desc); + priv->desc = NULL; + } + + IBUS_OBJECT_CLASS(parent_class)->destroy (IBUS_OBJECT (engine)); +} + +static gboolean +bus_engine_proxy_ibus_signal (IBusProxy *proxy, + IBusMessage *message) +{ + g_assert (BUS_IS_ENGINE_PROXY (proxy)); + g_assert (message != NULL); + + BusEngineProxy *engine; + BusEngineProxyPrivate *priv; + IBusError *error; + gint i; + + static const struct { + const gchar *member; + const guint signal_id; + } signals [] = { + { "ShowPreeditText", SHOW_PREEDIT_TEXT }, + { "HidePreeditText", HIDE_PREEDIT_TEXT }, + { "ShowAuxiliaryText", SHOW_AUXILIARY_TEXT }, + { "HideAuxiliaryText", HIDE_AUXILIARY_TEXT }, + { "ShowLookupTable", SHOW_LOOKUP_TABLE }, + { "HideLookupTable", HIDE_LOOKUP_TABLE }, + { "PageUpLookupTable", PAGE_UP_LOOKUP_TABLE }, + { "PageDownLookupTable", PAGE_DOWN_LOOKUP_TABLE }, + { "CursorUpLookupTable", CURSOR_UP_LOOKUP_TABLE }, + { "CursorDownLookupTable", CURSOR_DOWN_LOOKUP_TABLE }, + { NULL, 0}, + }; + + engine = BUS_ENGINE_PROXY (proxy); + priv = BUS_ENGINE_PROXY_GET_PRIVATE (engine); + + for (i = 0; ; i++) { + if (signals[i].member == NULL) + break; + if (ibus_message_is_signal (message, IBUS_INTERFACE_ENGINE, signals[i].member)) { + g_signal_emit (engine, engine_signals[signals[i].signal_id], 0); + goto handled; + } + } + + if (ibus_message_is_signal (message, IBUS_INTERFACE_ENGINE, "CommitText")) { + IBusText *text; + gboolean retval; + + retval = ibus_message_get_args (message, + &error, + IBUS_TYPE_TEXT, &text, + G_TYPE_INVALID); + if (!retval) + goto failed; + g_signal_emit (engine, engine_signals[COMMIT_TEXT], 0, text); + g_object_unref (text); + } + else if (ibus_message_is_signal (message, IBUS_INTERFACE_ENGINE, "ForwardKeyEvent")) { + guint keyval; + guint states; + gboolean retval; + + retval = ibus_message_get_args (message, + &error, + G_TYPE_UINT, &keyval, + G_TYPE_UINT, &states, + G_TYPE_INVALID); + + if (!retval) + goto failed; + g_signal_emit (engine, engine_signals[FORWARD_KEY_EVENT], keyval, states); + } + else if (ibus_message_is_signal (message, IBUS_INTERFACE_ENGINE, "UpdatePreeditText")) { + IBusText *text; + gint cursor_pos; + gboolean visible; + gboolean retval; + + retval = ibus_message_get_args (message, + &error, + IBUS_TYPE_TEXT, &text, + G_TYPE_UINT, &cursor_pos, + G_TYPE_BOOLEAN, &visible, + G_TYPE_INVALID); + + if (!retval) + goto failed; + + g_signal_emit (engine, engine_signals[UPDATE_PREEDIT_TEXT], 0, + text, cursor_pos, visible); + g_object_unref (text); + } + else if (ibus_message_is_signal (message, IBUS_INTERFACE_ENGINE, "UpdateAuxiliaryText")) { + IBusText *text; + gboolean visible; + gboolean retval; + + retval = ibus_message_get_args (message, + &error, + IBUS_TYPE_TEXT, &text, + G_TYPE_BOOLEAN, &visible, + G_TYPE_INVALID); + + if (!retval) + goto failed; + + g_signal_emit (engine, engine_signals[UPDATE_AUXILIARY_TEXT], 0, text, visible); + g_object_unref (text); + } + else if (ibus_message_is_signal (message, IBUS_INTERFACE_ENGINE, "UpdateLookupTable")) { + IBusLookupTable *table; + gboolean visible; + gboolean retval; + + retval = ibus_message_get_args (message, + &error, + IBUS_TYPE_LOOKUP_TABLE, &table, + G_TYPE_BOOLEAN, &visible, + G_TYPE_INVALID); + + if (!retval) + goto failed; + + g_signal_emit (engine, engine_signals[UPDATE_LOOKUP_TABLE], 0, table, visible); + g_object_unref (table); + } + else if (ibus_message_is_signal (message, IBUS_INTERFACE_ENGINE, "RegisterProperties")) { + gboolean retval; + + if (priv->prop_list) { + g_object_unref (priv->prop_list); + priv->prop_list = NULL; + } + + retval = ibus_message_get_args (message, + &error, + IBUS_TYPE_PROP_LIST, &priv->prop_list, + G_TYPE_INVALID); + if (!retval) { + priv->prop_list = NULL; + goto failed; + } + g_signal_emit (engine, engine_signals[REGISTER_PROPERTIES], 0, priv->prop_list); + } + else if (ibus_message_is_signal (message, IBUS_INTERFACE_ENGINE, "UpdateProperty")) { + IBusProperty *prop; + gboolean retval; + + retval = ibus_message_get_args (message, + &error, + IBUS_TYPE_PROPERTY, &prop, + G_TYPE_INVALID); + + if (!retval) + goto failed; + + g_signal_emit (engine, engine_signals[UPDATE_PROPERTY], 0, prop); + g_object_unref (prop); + } + else { + return FALSE; + } + +handled: + g_signal_stop_emission_by_name (engine, "ibus-signal"); + return TRUE; + +failed: + g_warning ("%s: %s", error->name, error->message); + ibus_error_free (error); + return FALSE; +} + +typedef struct { + GFunc func; + gpointer user_data; +}CallData; + +static void +bus_engine_proxy_process_key_event_reply_cb (IBusPendingCall *pending, + CallData *call_data) +{ + IBusMessage *reply_message; + IBusError *error; + gboolean retval; + + reply_message = dbus_pending_call_steal_reply (pending); + + if ((error = ibus_error_new_from_message (reply_message)) != NULL) { + g_warning ("%s: %s", error->name, error->message); + ibus_message_unref (reply_message); + ibus_error_free (error); + call_data->func(FALSE, call_data->user_data); + return; + } + + if (!ibus_message_get_args (reply_message, + &error, + G_TYPE_BOOLEAN, &retval, + G_TYPE_INVALID)) { + g_warning ("%s: %s", error->name, error->message); + ibus_message_unref (reply_message); + ibus_error_free (error); + call_data->func(FALSE, call_data->user_data); + return; + } + + call_data->func((gpointer *)retval, call_data->user_data); + g_slice_free (CallData, call_data); +} + +void +bus_engine_proxy_process_key_event (BusEngineProxy *engine, + guint keyval, + guint state, + GFunc return_cb, + gpointer user_data) +{ + g_assert (BUS_IS_ENGINE_PROXY (engine)); + g_assert (return_cb); + + IBusPendingCall *pending = NULL; + CallData *call_data; + IBusError *error; + gboolean retval; + + retval = ibus_proxy_call_with_reply ((IBusProxy *) engine, + "ProcessKeyEvent", + &pending, + -1, + &error, + G_TYPE_UINT, &keyval, + G_TYPE_UINT, &state, + G_TYPE_INVALID); + if (!retval) { + g_warning ("%s: %s", error->name, error->message); + ibus_error_free (error); + return_cb (FALSE, user_data); + return; + } + + call_data = g_slice_new0 (CallData); + call_data->func = return_cb; + call_data->user_data = user_data; + + retval = ibus_pending_call_set_notify (pending, + (IBusPendingCallNotifyFunction) bus_engine_proxy_process_key_event_reply_cb, + call_data, + NULL); + ibus_pending_call_unref (pending); + + if (!retval) { + g_warning ("%s : ProcessKeyEvent", DBUS_ERROR_NO_MEMORY); + return_cb (FALSE, user_data); + return; + } +} + +void +bus_engine_proxy_set_cursor_location (BusEngineProxy *engine, + gint x, + gint y, + gint w, + gint h) +{ + g_assert (BUS_IS_ENGINE_PROXY (engine)); + + ibus_proxy_call ((IBusProxy *) engine, + "SetCursorLocation", + G_TYPE_INT, &x, + G_TYPE_INT, &y, + G_TYPE_INT, &w, + G_TYPE_INT, &h, + G_TYPE_INVALID); +} + +void +bus_engine_proxy_set_capabilities (BusEngineProxy *engine, + guint caps) +{ + g_assert (BUS_IS_ENGINE_PROXY (engine)); + + ibus_proxy_call ((IBusProxy *) engine, + "SetCapabilites", + G_TYPE_UINT, &caps, + G_TYPE_INVALID); + +} + +void +bus_engine_proxy_property_activate (BusEngineProxy *engine, + const gchar *prop_name, + guint prop_state) +{ + g_assert (BUS_IS_ENGINE_PROXY (engine)); + g_assert (prop_name != NULL); + + ibus_proxy_call ((IBusProxy *) engine, + "PropertyActivate", + G_TYPE_STRING, &prop_name, + G_TYPE_UINT, &prop_state, + G_TYPE_INVALID); +} + +void +bus_engine_proxy_property_show (BusEngineProxy *engine, + const gchar *prop_name) +{ + g_assert (BUS_IS_ENGINE_PROXY (engine)); + g_assert (prop_name != NULL); + + ibus_proxy_call ((IBusProxy *) engine, + "PropertyShow", + G_TYPE_STRING, &prop_name, + G_TYPE_INVALID); +} + +void bus_engine_proxy_property_hide (BusEngineProxy *engine, + const gchar *prop_name) +{ + g_assert (BUS_IS_ENGINE_PROXY (engine)); + g_assert (prop_name != NULL); + + ibus_proxy_call ((IBusProxy *) engine, + "PropertyHide", + G_TYPE_STRING, &prop_name, + G_TYPE_INVALID); +} + +#define DEFINE_FUNCTION(Name, name) \ + void bus_engine_proxy_##name (BusEngineProxy *engine) \ + { \ + g_assert (BUS_IS_ENGINE_PROXY (engine)); \ + ibus_proxy_call ((IBusProxy *) engine, \ + #Name, \ + DBUS_TYPE_INVALID); \ + } + +DEFINE_FUNCTION (FocusIn, focus_in) +DEFINE_FUNCTION (FocusOut, focus_out) +DEFINE_FUNCTION (Reset, reset) +DEFINE_FUNCTION (PageUp, page_up) +DEFINE_FUNCTION (PageDown, page_down) +DEFINE_FUNCTION (CursorUp, cursor_up) +DEFINE_FUNCTION (CursorDown, cursor_down) +DEFINE_FUNCTION (Enable, enable) +DEFINE_FUNCTION (Disable, disable) + +#undef DEFINE_FUNCTION + + +IBusEngineDesc * +bus_engine_proxy_get_desc (BusEngineProxy *engine) +{ + g_assert (BUS_IS_ENGINE_PROXY (engine)); + + BusEngineProxyPrivate *priv; + priv = BUS_ENGINE_PROXY_GET_PRIVATE (engine); + + return priv->desc; +} diff --git a/bus/engineproxy.h b/bus/engineproxy.h new file mode 100644 index 0000000..2206c9a --- /dev/null +++ b/bus/engineproxy.h @@ -0,0 +1,95 @@ +/* vim:set et sts=4: */ +/* ibus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ +#ifndef __ENGINE_PROXY_H_ +#define __ENGINE_PROXY_H_ + +#include <ibus.h> +#include "connection.h" + +/* + * Type macros. + */ + +/* define GOBJECT macros */ +#define BUS_TYPE_ENGINE_PROXY \ + (bus_engine_proxy_get_type ()) +#define BUS_ENGINE_PROXY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUS_TYPE_ENGINE_PROXY, BusEngineProxy)) +#define BUS_ENGINE_PROXY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), BUS_TYPE_ENGINE_PROXY, BusEngineProxyClass)) +#define BUS_IS_ENGINE_PROXY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUS_TYPE_ENGINE_PROXY)) +#define BUS_IS_ENGINE_PROXY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), BUS_TYPE_ENGINE_PROXY)) +#define BUS_ENGINE_PROXY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), BUS_TYPE_ENGINE_PROXY, BusEngineProxyClass)) + +G_BEGIN_DECLS + +typedef struct _BusEngineProxy BusEngineProxy; +typedef struct _BusEngineProxyClass BusEngineProxyClass; + +struct _BusEngineProxy { + IBusProxy parent; + /* instance members */ +}; + +struct _BusEngineProxyClass { + IBusProxyClass parent; + /* class members */ +}; + +GType bus_engine_proxy_get_type (void); +BusEngineProxy *bus_engine_proxy_new (const gchar *path, + IBusEngineDesc *desc, + BusConnection *connection); +IBusEngineDesc *bus_engine_proxy_get_desc (BusEngineProxy *engine); +void bus_engine_proxy_process_key_event (BusEngineProxy *engine, + guint keyval, + guint state, + GFunc return_cn, + gpointer user_data); +void bus_engine_proxy_set_cursor_location + (BusEngineProxy *engine, + gint x, + gint y, + gint w, + gint h); +void bus_engine_proxy_focus_in (BusEngineProxy *engine); +void bus_engine_proxy_focus_out (BusEngineProxy *engine); +void bus_engine_proxy_reset (BusEngineProxy *engine); +void bus_engine_proxy_set_capabilities (BusEngineProxy *engine, + guint caps); +void bus_engine_proxy_page_up (BusEngineProxy *engine); +void bus_engine_proxy_page_down (BusEngineProxy *engine); +void bus_engine_proxy_cursor_up (BusEngineProxy *engine); +void bus_engine_proxy_cursor_down (BusEngineProxy *engine); +void bus_engine_proxy_enable (BusEngineProxy *engine); +void bus_engine_proxy_disable (BusEngineProxy *engine); +void bus_engine_proxy_property_activate (BusEngineProxy *engine, + const gchar *prop_name, + guint state); +void bus_engine_proxy_property_show (BusEngineProxy *engine, + const gchar *prop_name); +void bus_engine_proxy_property_hide (BusEngineProxy *engine, + const gchar *prop_name); +G_END_DECLS +#endif + diff --git a/bus/factoryproxy.c b/bus/factoryproxy.c new file mode 100644 index 0000000..38357b4 --- /dev/null +++ b/bus/factoryproxy.c @@ -0,0 +1,220 @@ +/* vim:set et sts=4: */ +/* ibus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ + +#include <ibusinternal.h> +#include <ibusmarshalers.h> +#include "dbusimpl.h" +#include "factoryproxy.h" + +/* functions prototype */ +static void bus_factory_proxy_class_init (BusFactoryProxyClass *klass); +static void bus_factory_proxy_init (BusFactoryProxy *factory); +static void bus_factory_proxy_destroy (BusFactoryProxy *factory); + + +static IBusProxyClass *parent_class = NULL; + +GType +bus_factory_proxy_get_type (void) +{ + static GType type = 0; + + static const GTypeInfo type_info = { + sizeof (BusFactoryProxyClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) bus_factory_proxy_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (BusFactoryProxy), + 0, + (GInstanceInitFunc) bus_factory_proxy_init, + }; + + if (type == 0) { + type = g_type_register_static (IBUS_TYPE_PROXY, + "BusFactoryProxy", + &type_info, + (GTypeFlags)0); + } + return type; +} + +BusFactoryProxy * +bus_factory_proxy_new (IBusComponent *component, + BusConnection *connection) +{ + g_assert (IBUS_IS_COMPONENT (component)); + + BusFactoryProxy *factory; + GList *p; + + if (connection == NULL) { + connection = bus_dbus_impl_get_connection_by_name (BUS_DEFAULT_DBUS, component->name); + } + + if (connection == NULL) { + return NULL; + } + + factory = g_object_new (BUS_TYPE_FACTORY_PROXY, + "name", NULL, + "path", "/org/freedesktop/IBus/Factory", + "connection", connection, + NULL); + + g_object_ref (component); + factory->component = component; + g_object_set_data ((GObject *)factory->component, "factory", factory); + + factory->engine_list = ibus_component_get_engines (factory->component); + + for (p = factory->engine_list; p != NULL; p = p->next) { + IBusEngineDesc *desc = (IBusEngineDesc *)p->data; + g_object_ref (desc); + g_object_set_data ((GObject *)desc, "factory", factory); + } + + return factory; +} + +static void +bus_factory_proxy_class_init (BusFactoryProxyClass *klass) +{ + IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass); + + parent_class = (IBusProxyClass *) g_type_class_peek_parent (klass); + + ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_factory_proxy_destroy; + +} + +static void +bus_factory_proxy_init (BusFactoryProxy *factory) +{ + factory->component = NULL; +} + +static void +bus_factory_proxy_destroy (BusFactoryProxy *factory) +{ + GList *p; + + for (p = factory->engine_list; p != NULL ; p = p->next) { + IBusEngineDesc *desc = (IBusEngineDesc *)p->data; + g_object_steal_data ((GObject *)desc, "factory"); + g_object_unref (desc); + } + g_list_free (factory->engine_list); + factory->engine_list = NULL; + + if (factory->component) { + g_object_steal_data ((GObject *)factory->component, "factory"); + g_object_unref (factory->component); + factory->component = NULL; + } + + IBUS_OBJECT_CLASS(parent_class)->destroy (IBUS_OBJECT (factory)); +} + +IBusComponent * +bus_factory_proxy_get_component (BusFactoryProxy *factory) +{ + return factory->component; +} + +BusFactoryProxy * +bus_factory_proxy_get_from_component (IBusComponent *component) +{ + IBUS_IS_COMPONENT (component); + + BusFactoryProxy *factory; + + factory = (BusFactoryProxy *) g_object_get_data ((GObject *)component, "factory"); + + return factory; +} + +BusFactoryProxy * +bus_factory_proxy_get_from_engine (IBusEngineDesc *desc) +{ + + IBUS_IS_ENGINE_DESC (desc); + + BusFactoryProxy *factory; + + factory = (BusFactoryProxy *) g_object_get_data ((GObject *)desc, "factory"); + + return factory; +} + +BusEngineProxy * +bus_factory_proxy_create_engine (BusFactoryProxy *factory, + IBusEngineDesc *desc) +{ + g_assert (BUS_IS_FACTORY_PROXY (factory)); + g_assert (IBUS_IS_ENGINE_DESC (desc)); + + IBusMessage *reply_message; + IBusError *error; + BusEngineProxy *engine; + gchar *object_path; + + if (g_list_find (factory->component->engines, desc) == NULL) { + return NULL; + } + + reply_message = ibus_proxy_call_with_reply_and_block ((IBusProxy *) factory, + "CreateEngine", + -1, + &error, + G_TYPE_STRING, &(desc->name), + DBUS_TYPE_INVALID); + if (reply_message == NULL) { + g_warning ("%s: %s", error->name, error->message); + ibus_error_free (error); + return NULL; + } + + if ((error = ibus_error_new_from_message (reply_message)) != NULL) { + g_warning ("%s: %s", error->name, error->message); + ibus_error_free (error); + ibus_message_unref (reply_message); + return NULL; + } + + if (!ibus_message_get_args (reply_message, + &error, + IBUS_TYPE_OBJECT_PATH, &object_path, + G_TYPE_INVALID)) { + g_warning ("%s: %s", error->name, error->message); + ibus_error_free (error); + ibus_message_unref (reply_message); + + return NULL; + } + + IBusConnection *connection = ibus_proxy_get_connection ((IBusProxy *) factory); + engine = bus_engine_proxy_new (object_path, desc, (BusConnection *) connection); + ibus_message_unref (reply_message); + + return engine; +} + diff --git a/bus/factoryproxy.h b/bus/factoryproxy.h new file mode 100644 index 0000000..198b458 --- /dev/null +++ b/bus/factoryproxy.h @@ -0,0 +1,84 @@ +/* vim:set et sts=4: */ +/* ibus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ +#ifndef __FACTORY_PROXY_H_ +#define __FACTORY_PROXY_H_ + +#include <ibus.h> +#include "connection.h" +#include "engineproxy.h" + +/* + * Type macros. + */ + +/* define GOBJECT macros */ +#define BUS_TYPE_FACTORY_PROXY \ + (bus_factory_proxy_get_type ()) +#define BUS_FACTORY_PROXY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUS_TYPE_FACTORY_PROXY, BusFactoryProxy)) +#define BUS_FACTORY_PROXY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), BUS_TYPE_FACTORY_PROXY, BusFactoryProxyClass)) +#define BUS_IS_FACTORY_PROXY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUS_TYPE_FACTORY_PROXY)) +#define BUS_IS_FACTORY_PROXY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), BUS_TYPE_FACTORY_PROXY)) +#define BUS_FACTORY_PROXY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), BUS_TYPE_FACTORY_PROXY, BusFactoryProxyClass)) + +G_BEGIN_DECLS + +typedef struct _BusFactoryProxy BusFactoryProxy; +typedef struct _BusFactoryProxyClass BusFactoryProxyClass; + +struct _BusFactoryProxy { + IBusProxy parent; + /* instance members */ + + IBusComponent *component; + GList *engine_list; +}; + +struct _BusFactoryProxyClass { + IBusProxyClass parent; + /* class members */ +}; + +GType bus_factory_proxy_get_type (void); +BusFactoryProxy *bus_factory_proxy_new (IBusComponent *component, + BusConnection *connection); +IBusComponent *bus_factory_proxy_get_component(BusFactoryProxy *factory); +BusEngineProxy *bus_factory_proxy_create_engine(BusFactoryProxy *factory, + IBusEngineDesc *desc); +BusFactoryProxy *bus_factory_proxy_get_from_component + (IBusComponent *component); +BusFactoryProxy *bus_factory_proxy_get_from_engine + (IBusEngineDesc *desc); + +#if 0 +const gchar *bus_factory_proxy_get_name (BusFactoryProxy *factory); +const gchar *bus_factory_proxy_get_lang (BusFactoryProxy *factory); +const gchar *bus_factory_proxy_get_icon (BusFactoryProxy *factory); +const gchar *bus_factory_proxy_get_authors (BusFactoryProxy *factory); +const gchar *bus_factory_proxy_get_credits (BusFactoryProxy *factory); +#endif + +G_END_DECLS +#endif + diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c new file mode 100644 index 0000000..31799d6 --- /dev/null +++ b/bus/ibusimpl.c @@ -0,0 +1,935 @@ +/* vim:set et sts=4: */ +/* ibus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ + +#include "ibusimpl.h" +#include "dbusimpl.h" +#include "server.h" +#include "connection.h" +#include "registry.h" +#include "factoryproxy.h" +#include "panelproxy.h" +#include "inputcontext.h" + + +enum { + LAST_SIGNAL, +}; + +enum { + PROP_0, +}; + +// static guint _signals[LAST_SIGNAL] = { 0 }; + +/* functions prototype */ +static void bus_ibus_impl_class_init (BusIBusImplClass *klass); +static void bus_ibus_impl_init (BusIBusImpl *ibus); +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 _factory_destroy_cb (BusFactoryProxy *factory, + BusIBusImpl *ibus); + +static IBusServiceClass *parent_class = NULL; + +GType +bus_ibus_impl_get_type (void) +{ + static GType type = 0; + + static const GTypeInfo type_info = { + sizeof (BusIBusImplClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) bus_ibus_impl_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (BusIBusImpl), + 0, + (GInstanceInitFunc) bus_ibus_impl_init, + }; + + if (type == 0) { + type = g_type_register_static (IBUS_TYPE_SERVICE, + "BusIBusImpl", + &type_info, + (GTypeFlags) 0); + } + return type; +} + +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); + + parent_class = (IBusServiceClass *) g_type_class_peek_parent (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_trigger (BusIBusImpl *ibus, + GValue *value) +{ + g_assert (BUS_IS_IBUS_IMPL (ibus)); + + GValueArray *array; + gint i; + + ibus_hotkey_profile_remove_hotkey_by_event (ibus->hotkey_profile, + g_quark_from_static_string ("trigger")); + + if (value == NULL) { + ibus_hotkey_profile_add_hotkey (ibus->hotkey_profile, + IBUS_space, + IBUS_CONTROL_MASK, + g_quark_from_static_string ("trigger")); + 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), + g_quark_from_static_string ("trigger")); + } +} + +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; +} + +static void +bus_ibus_impl_reload_config (BusIBusImpl *ibus) +{ + 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", "preload_engines", bus_ibus_impl_set_preload_engines }, + { 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", "preload_engines", bus_ibus_impl_set_preload_engines }, + { 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; + } + } +} + +static void +_config_destroy_cb (IBusConfig *config, + BusIBusImpl *ibus) +{ + g_assert (IBUS_IS_CONFIG (config)); + g_assert (BUS_IS_IBUS_IMPL (ibus)); + + g_assert (ibus->config == config); + + ibus->config = NULL; + g_object_unref (config); +} + +static void +_dbus_name_owner_changed_cb (BusDBusImpl *dbus, + const gchar *name, + const gchar *old_name, + const gchar *new_name, + BusIBusImpl *ibus) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (name != NULL); + g_assert (old_name != NULL); + g_assert (new_name != NULL); + g_assert (BUS_IS_IBUS_IMPL (ibus)); + + BusFactoryProxy *factory; + + if (g_strcmp0 (name, IBUS_SERVICE_PANEL) == 0) { + if (g_strcmp0 (new_name, "") != 0) { + BusConnection *connection; + + if (ibus->panel != NULL) { + ibus_object_destroy (IBUS_OBJECT (ibus->panel)); + ibus->panel = NULL; + } + + connection = bus_dbus_impl_get_connection_by_name (BUS_DEFAULT_DBUS, new_name); + g_return_if_fail (connection != NULL); + + ibus->panel = bus_panel_proxy_new (connection); + + g_signal_connect (ibus->panel, + "destroy", + G_CALLBACK (_panel_destroy_cb), + ibus); + + if (ibus->focused_context != NULL) { + bus_panel_proxy_focus_in (ibus->panel, 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)); + ibus->config = NULL; + } + + 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_signal_connect (ibus->config, + "value-changed", + G_CALLBACK (_config_value_changed_cb), + ibus); + + g_signal_connect (ibus->config, + "destroy", + G_CALLBACK (_config_destroy_cb), + ibus); + + bus_ibus_impl_reload_config (ibus); + } + } + + factory = bus_registry_name_owner_changed (ibus->registry, name, old_name, new_name); + + if (factory) { + bus_ibus_impl_add_factory (ibus, factory); + g_object_unref (factory); + } +} + +static void +bus_ibus_impl_init (BusIBusImpl *ibus) +{ + ibus->factory_dict = g_hash_table_new_full ( + g_str_hash, + g_str_equal, + NULL, + (GDestroyNotify) g_object_unref); + + ibus->registry = bus_registry_new (); + ibus->engine_list = NULL; + ibus->register_engine_list = NULL; + ibus->contexts = NULL; + ibus->focused_context = NULL; + ibus->panel = NULL; + ibus->config = NULL; + + ibus->hotkey_profile = ibus_hotkey_profile_new (); + + bus_ibus_impl_reload_config (ibus); + + g_signal_connect (BUS_DEFAULT_DBUS, + "name-owner-changed", + G_CALLBACK (_dbus_name_owner_changed_cb), + ibus); +} + +static void +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); + ibus->register_engine_list = NULL; + + if (ibus->factory_dict != NULL) { + g_hash_table_destroy (ibus->factory_dict); + ibus->factory_dict = NULL; + } + + if (ibus->hotkey_profile != NULL) { + g_object_unref (ibus->hotkey_profile); + ibus->hotkey_profile = NULL; + } + + bus_server_quit (BUS_DEFAULT_SERVER); + + IBUS_OBJECT_CLASS(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.DBus\">\n" + " <method name=\"RequestName\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"in\" type=\"u\"/>\n" + " <arg direction=\"out\" type=\"u\"/>\n" + " </method>\n" + " <signal name=\"NameOwnerChanged\">\n" + " <arg type=\"s\"/>\n" + " <arg type=\"s\"/>\n" + " <arg type=\"s\"/>\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; + + address = ibus_server_get_address (IBUS_SERVER (BUS_DEFAULT_SERVER)); + + reply = ibus_message_new_method_return (message); + ibus_message_append_args (message, + G_TYPE_STRING, &address, + G_TYPE_INVALID); + + return reply; +} + +static void +_context_request_engine_cb (BusInputContext *context, + gchar *engine_name, + BusIBusImpl *ibus) +{ + IBusEngineDesc *engine_desc = NULL; + IBusComponent *comp; + BusFactoryProxy *factory; + BusEngineProxy *engine; + + 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 */ + GList *p; + gboolean found = FALSE; + + /* 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) { + found = TRUE; + break; + } + } + + if (!found) { + /* 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) { + found = TRUE; + break; + } + } + } + + if (!found) { + engine_desc = NULL; + } + } + + if (engine_desc == NULL) + return; + + factory = bus_factory_proxy_get_from_engine (engine_desc); + + 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); + + gint time = 0; + while (time < G_USEC_PER_SEC * 3) { + if (g_main_context_pending (NULL)) { + g_main_context_iteration (NULL, FALSE); + } + else { + g_usleep (50 * 1000); + time += 50 * 1000; + } + factory = bus_factory_proxy_get_from_engine (engine_desc); + if (factory != NULL) { + break; + } + } + } + factory = bus_factory_proxy_get_from_engine (engine_desc); + } + + if (factory == NULL) + return; + + engine = bus_factory_proxy_create_engine (factory, engine_desc); + + if (engine == NULL) + return; + + bus_input_context_set_engine (context, engine); +} + +static void +_context_request_next_engine_cb (BusInputContext *context, + BusIBusImpl *ibus) +{ + +} + +static void +_context_request_prev_engine_cb (BusInputContext *context, + BusIBusImpl *ibus) +{ + +} + +static void +_context_focus_out_cb (BusInputContext *context, + BusIBusImpl *ibus) +{ + g_assert (BUS_IS_IBUS_IMPL (ibus)); + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + if (ibus->focused_context != context) + return; + + if (ibus->panel != NULL) { + bus_panel_proxy_focus_out (ibus->panel, context); + } + + if (context) { + g_object_unref (context); + ibus->focused_context = NULL; + } +} + +static void +_context_focus_in_cb (BusInputContext *context, + BusIBusImpl *ibus) +{ + g_assert (BUS_IS_IBUS_IMPL (ibus)); + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + 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 (ibus->panel != NULL) { + bus_panel_proxy_focus_in (ibus->panel, ibus->focused_context); + } + +} + +static void +_context_destroy_cb (BusInputContext *context, + BusIBusImpl *ibus) +{ + g_assert (BUS_IS_IBUS_IMPL (ibus)); + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + if (context == ibus->focused_context) { + /* focus out context */ + bus_input_context_focus_out (ibus->focused_context); + g_assert (ibus->focused_context == NULL); + } + + ibus->contexts = g_list_remove (ibus->contexts, context); + g_object_unref (context); +} + +static IBusMessage * +_ibus_create_input_context (BusIBusImpl *ibus, + IBusMessage *message, + BusConnection *connection) +{ + 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); + ibus->contexts = g_list_append (ibus->contexts, context); + + 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) }, + { "focus-in", G_CALLBACK (_context_focus_in_cb) }, + { "focus-out", G_CALLBACK (_context_focus_out_cb) }, + { "destroy", G_CALLBACK (_context_destroy_cb) }, + { NULL, NULL } + }; + + for (i = 0; signals[i].name != NULL; i++) { + g_signal_connect (context, + signals[i].name, + signals[i].callback, + 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_dbus_impl_register_object (BUS_DEFAULT_DBUS, + (IBusService *)context); + return reply; +} + +static void +_factory_destroy_cb (BusFactoryProxy *factory, + BusIBusImpl *ibus) +{ + g_assert (BUS_IS_IBUS_IMPL (ibus)); + g_assert (BUS_IS_FACTORY_PROXY (factory)); + + IBusComponent *component; + GList *engines, *p; + + ibus->factory_list = g_list_remove (ibus->factory_list, factory); + + component = bus_factory_proxy_get_component (factory); + + 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); + } + + g_object_unref (factory); +} + +static void +bus_ibus_impl_add_factory (BusIBusImpl *ibus, + BusFactoryProxy *factory) +{ + g_assert (BUS_IS_IBUS_IMPL (ibus)); + g_assert (BUS_IS_FACTORY_PROXY (factory)); + + g_object_ref (factory); + ibus->factory_list = g_list_append (ibus->factory_list, factory); + + g_signal_connect (factory, "destroy", G_CALLBACK (_factory_destroy_cb), ibus); +} + + +static IBusMessage * +_ibus_register_component (BusIBusImpl *ibus, + IBusMessage *message, + BusConnection *connection) +{ + IBusMessage *reply; + IBusError *error; + gboolean retval; + GList *engines; + IBusComponent *component; + BusFactoryProxy *factory; + + retval = ibus_message_get_args (message, &error, + IBUS_TYPE_COMPONENT, &component, + G_TYPE_INVALID); + + 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; + } + + factory = bus_factory_proxy_new (component, connection); + + if (factory == NULL) { + reply = ibus_message_new_error (message, + DBUS_ERROR_FAILED, + "Can not create factory"); + return reply; + } + + bus_ibus_impl_add_factory (ibus, factory); + g_object_unref (factory); + + engines = ibus_component_get_engines (component); + + 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); + + reply = ibus_message_new_method_return (message); + return reply; +} + +static IBusMessage * +_ibus_list_engines (BusIBusImpl *ibus, + IBusMessage *message, + BusConnection *connection) +{ + IBusMessage *reply; + IBusMessageIter iter, sub_iter; + GList *engines, *p; + + reply = ibus_message_new_method_return (message); + + ibus_message_iter_init_append (reply, &iter); + ibus_message_iter_open_container (&iter, IBUS_TYPE_ARRAY, "v", &sub_iter); + + 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)); + } + g_list_free (engines); + ibus_message_iter_close_container (&iter, &sub_iter); + + return reply; +} + +static IBusMessage * +_ibus_list_active_engines (BusIBusImpl *ibus, + IBusMessage *message, + BusConnection *connection) +{ + IBusMessage *reply; + IBusMessageIter iter, sub_iter; + GList *p; + + reply = ibus_message_new_method_return (message); + + ibus_message_iter_init_append (reply, &iter); + ibus_message_iter_open_container (&iter, IBUS_TYPE_ARRAY, "v", &sub_iter); + + for (p = ibus->engine_list; p != NULL; p = p->next) { + ibus_message_iter_append (&sub_iter, IBUS_TYPE_ENGINE_DESC, &(p->data)); + } + + for (p = ibus->register_engine_list; p != NULL; p = p->next) { + ibus_message_iter_append (&sub_iter, IBUS_TYPE_ENGINE_DESC, &(p->data)); + } + ibus_message_iter_close_container (&iter, &sub_iter); + + return reply; +} + +static IBusMessage * +_ibus_kill (BusIBusImpl *ibus, + IBusMessage *message, + BusConnection *connection) +{ + IBusMessage *reply; + + reply = ibus_message_new_method_return (message); + ibus_connection_send ((IBusConnection *) connection, reply); + ibus_connection_flush ((IBusConnection *) connection); + ibus_message_unref (reply); + + ibus_object_destroy (IBUS_OBJECT (ibus)); + return NULL; +} + +static gboolean +bus_ibus_impl_ibus_message (BusIBusImpl *ibus, + BusConnection *connection, + IBusMessage *message) +{ + g_assert (BUS_IS_IBUS_IMPL (ibus)); + g_assert (BUS_IS_CONNECTION (connection)); + g_assert (message != NULL); + + gint i; + IBusMessage *reply_message = NULL; + + static const struct { + const gchar *interface; + const gchar *name; + IBusMessage *(* handler) (BusIBusImpl *, IBusMessage *, BusConnection *); + } handlers[] = { + /* Introspectable interface */ + { DBUS_INTERFACE_INTROSPECTABLE, + "Introspect", _ibus_introspect }, + /* IBus interface */ + { IBUS_INTERFACE_IBUS, "GetAddress", _ibus_get_address }, + { IBUS_INTERFACE_IBUS, "CreateInputContext", _ibus_create_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, "Kill", _ibus_kill }, + { NULL, NULL, NULL } + }; + + ibus_message_set_sender (message, bus_connection_get_unique_name (connection)); + ibus_message_set_destination (message, DBUS_SERVICE_DBUS); + + for (i = 0; handlers[i].interface != NULL; i++) { + if (ibus_message_is_method_call (message, + handlers[i].interface, + handlers[i].name)) { + + reply_message = handlers[i].handler (ibus, message, connection); + if (reply_message) { + + 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); + + ibus_connection_send ((IBusConnection *) connection, reply_message); + ibus_message_unref (reply_message); + } + + g_signal_stop_emission_by_name (ibus, "ibus-message"); + return TRUE; + } + } + + return parent_class->ibus_message ((IBusService *) ibus, + (IBusConnection *) connection, + message); +} + +BusFactoryProxy * +bus_ibus_impl_lookup_factory (BusIBusImpl *ibus, + const gchar *path) +{ + g_assert (BUS_IS_IBUS_IMPL (ibus)); + + BusFactoryProxy *factory; + + factory = (BusFactoryProxy *) g_hash_table_lookup (ibus->factory_dict, path); + + return factory; +} + +IBusHotkeyProfile * +bus_ibus_impl_get_hotkey_profile (BusIBusImpl *ibus) +{ + g_assert (BUS_IS_IBUS_IMPL (ibus)); + + return ibus->hotkey_profile; +} + +BusRegistry * +bus_ibus_impl_get_registry (BusIBusImpl *ibus) +{ + + g_assert (BUS_IS_IBUS_IMPL (ibus)); + + return ibus->registry; +} + diff --git a/bus/ibusimpl.h b/bus/ibusimpl.h new file mode 100644 index 0000000..fd85138 --- /dev/null +++ b/bus/ibusimpl.h @@ -0,0 +1,102 @@ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ +#ifndef __IBUS_IMPL_H_ +#define __IBUS_IMPL_H_ + +#include <ibus.h> +#include "connection.h" +#include "inputcontext.h" +#include "registry.h" +#include "factoryproxy.h" +#include "panelproxy.h" + +/* + * Type macros. + */ + +/* define GOBJECT macros */ +#define BUS_TYPE_IBUS_IMPL \ + (bus_ibus_impl_get_type ()) +#define BUS_IBUS_IMPL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUS_TYPE_IBUS_IMPL, BusIBusImpl)) +#define BUS_IBUS_IMPL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), BUS_TYPE_IBUS_IMPL, BusIBusImplClass)) +#define BUS_IS_IBUS_IMPL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUS_TYPE_IBUS_IMPL)) +#define BUS_IS_IBUS_IMPL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), BUS_TYPE_IBUS_IMPL)) +#define BUS_IBUS_IMPL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), BUS_TYPE_IBUS_IMPL, BusIBusImplClass)) + +#define BUS_DEFAULT_IBUS \ + (bus_ibus_impl_get_default ()) +#define BUS_DEFAULT_HOTKEY_PROFILE \ + (bus_ibus_impl_get_hotkey_profile (BUS_DEFAULT_IBUS)) +#define BUS_DEFAULT_REGISTRY \ + (bus_ibus_impl_get_registry (BUS_DEFAULT_IBUS)) + +G_BEGIN_DECLS + +typedef struct _BusIBusImpl BusIBusImpl; +typedef struct _BusIBusImplClass BusIBusImplClass; + +struct _BusIBusImpl { + IBusService parent; + /* instance members */ + + GHashTable *factory_dict; + GList *factory_list; + GList *contexts; + + GList *engine_list; + GList *register_engine_list; + GList *component_list; + + BusRegistry *registry; + + BusInputContext *focused_context; + BusPanelProxy *panel; + IBusConfig *config; + IBusHotkeyProfile *hotkey_profile; + +}; + +struct _BusIBusImplClass { + IBusServiceClass parent; + + /* class members */ +}; + +GType bus_ibus_impl_get_type (void); +BusIBusImpl *bus_ibus_impl_get_default (void); +BusFactoryProxy *bus_ibus_impl_get_default_factory (BusIBusImpl *ibus); +BusFactoryProxy *bus_ibus_impl_get_next_factory (BusIBusImpl *ibus, + BusFactoryProxy *factory); +BusFactoryProxy *bus_ibus_impl_get_previous_factory (BusIBusImpl *ibus, + BusFactoryProxy *factory); +BusFactoryProxy *bus_ibus_impl_lookup_factory (BusIBusImpl *ibus, + const gchar *path); +IBusHotkeyProfile + *bus_ibus_impl_get_hotkey_profile (BusIBusImpl *ibus); +BusRegistry *bus_ibus_impl_get_registry (BusIBusImpl *ibus); + +G_END_DECLS +#endif + diff --git a/bus/inputcontext.c b/bus/inputcontext.c new file mode 100644 index 0000000..a4e3359 --- /dev/null +++ b/bus/inputcontext.c @@ -0,0 +1,1559 @@ +/* vim:set et sts=4: */ +/* ibus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ + +#include <ibusinternal.h> +#include <ibusmarshalers.h> +#include "ibusimpl.h" +#include "inputcontext.h" +#include "engineproxy.h" +#include "factoryproxy.h" + +#define BUS_INPUT_CONTEXT_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), BUS_TYPE_INPUT_CONTEXT, BusInputContextPrivate)) + +enum { + PROCESS_KEY_EVENT, + SET_CURSOR_LOCATION, + FOCUS_IN, + FOCUS_OUT, + UPDATE_PREEDIT_TEXT, + SHOW_PREEDIT_TEXT, + HIDE_PREEDIT_TEXT, + UPDATE_AUXILIARY_TEXT, + SHOW_AUXILIARY_TEXT, + HIDE_AUXILIARY_TEXT, + UPDATE_LOOKUP_TABLE, + SHOW_LOOKUP_TABLE, + HIDE_LOOKUP_TABLE, + PAGE_UP_LOOKUP_TABLE, + PAGE_DOWN_LOOKUP_TABLE, + CURSOR_UP_LOOKUP_TABLE, + CURSOR_DOWN_LOOKUP_TABLE, + REGISTER_PROPERTIES, + UPDATE_PROPERTY, + ENABLED, + DISABLED, + ENGINE_CHANGED, + REQUEST_ENGINE, + REQUEST_NEXT_ENGINE, + REQUEST_PREV_ENGINE, + LAST_SIGNAL, +}; + +enum { + PROP_0, +}; + + +/* IBusInputContextPriv */ +struct _BusInputContextPrivate { + BusConnection *connection; + BusEngineProxy *engine; + gchar *client; + + gboolean has_focus; + gboolean enabled; + + /* capabilities */ + guint capabilities; + + /* cursor location */ + gint x; + gint y; + gint w; + gint h; +}; + +typedef struct _BusInputContextPrivate BusInputContextPrivate; + +static guint context_signals[LAST_SIGNAL] = { 0 }; + +/* functions prototype */ +static void bus_input_context_class_init (BusInputContextClass *klass); +static void bus_input_context_init (BusInputContext *context); +static void bus_input_context_destroy (BusInputContext *context); +static gboolean bus_input_context_ibus_message (BusInputContext *context, + BusConnection *connection, + IBusMessage *message); +static gboolean bus_input_context_filter_keyboard_shortcuts + (BusInputContext *context, + guint keyval, + guint modifiers); +static gboolean bus_input_context_send_signal (BusInputContext *context, + const gchar *signal_name, + GType first_arg_type, + ...); +static void _engine_destroy_cb (BusEngineProxy *factory, + BusInputContext *context); + +static IBusServiceClass *parent_class = NULL; +static guint id = 0; + +GType +bus_input_context_get_type (void) +{ + static GType type = 0; + + static const GTypeInfo type_info = { + sizeof (BusInputContextClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) bus_input_context_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (BusInputContext), + 0, + (GInstanceInitFunc) bus_input_context_init, + }; + + if (type == 0) { + type = g_type_register_static (IBUS_TYPE_SERVICE, + "BusInputContext", + &type_info, + (GTypeFlags) 0); + } + return type; +} + +static void +_connection_destroy_cb (BusConnection *connection, + BusInputContext *context) +{ + BUS_IS_CONNECTION (connection); + BUS_IS_INPUT_CONTEXT (context); + + ibus_object_destroy (IBUS_OBJECT (context)); +} + + +BusInputContext * +bus_input_context_new (BusConnection *connection, + const gchar *client) +{ + g_assert (BUS_IS_CONNECTION (connection)); + g_assert (client != NULL); + + BusInputContext *context; + gchar *path; + BusInputContextPrivate *priv; + + path = g_strdup_printf (IBUS_PATH_INPUT_CONTEXT, ++id); + + context = (BusInputContext *) g_object_new (BUS_TYPE_INPUT_CONTEXT, + "path", path, + NULL); + g_free (path); + + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + +#if 0 + ibus_service_add_to_connection (IBUS_SERVICE (context), + IBUS_CONNECTION (connection)); +#endif + + g_object_ref (connection); + priv->connection = connection; + priv->client = g_strdup (client); + + g_signal_connect (priv->connection, + "destroy", + (GCallback) _connection_destroy_cb, + context); + + return context; +} + +static void +bus_input_context_class_init (BusInputContextClass *klass) +{ + IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass); + + parent_class = (IBusServiceClass *) g_type_class_peek_parent (klass); + + g_type_class_add_private (klass, sizeof (BusInputContextPrivate)); + + ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_input_context_destroy; + + IBUS_SERVICE_CLASS (klass)->ibus_message = (ServiceIBusMessageFunc) bus_input_context_ibus_message; + + /* install signals */ + context_signals[PROCESS_KEY_EVENT] = + g_signal_new (I_("process-key-event"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_BOOL__UINT_UINT, + G_TYPE_BOOLEAN, + 2, + G_TYPE_UINT, + G_TYPE_UINT); + + context_signals[SET_CURSOR_LOCATION] = + g_signal_new (I_("set-cursor-location"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__INT_INT_INT_INT, + G_TYPE_NONE, + 4, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT); + + context_signals[FOCUS_IN] = + g_signal_new (I_("focus-in"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + context_signals[FOCUS_OUT] = + g_signal_new (I_("focus-out"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + context_signals[UPDATE_PREEDIT_TEXT] = + g_signal_new (I_("update-preedit-text"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__OBJECT_UINT_BOOLEAN, + G_TYPE_NONE, + 3, + IBUS_TYPE_TEXT, + G_TYPE_UINT, + G_TYPE_BOOLEAN); + + context_signals[SHOW_PREEDIT_TEXT] = + g_signal_new (I_("show-preedit-text"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + context_signals[HIDE_PREEDIT_TEXT] = + g_signal_new (I_("hide-preedit-text"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + context_signals[UPDATE_AUXILIARY_TEXT] = + g_signal_new (I_("update-auxiliary-text"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__OBJECT_BOOLEAN, + G_TYPE_NONE, + 2, + IBUS_TYPE_TEXT, + G_TYPE_BOOLEAN); + + context_signals[SHOW_AUXILIARY_TEXT] = + g_signal_new (I_("show-auxiliary-text"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + context_signals[HIDE_AUXILIARY_TEXT] = + g_signal_new (I_("hide-auxiliary-text"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + context_signals[UPDATE_LOOKUP_TABLE] = + g_signal_new (I_("update-lookup-table"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__OBJECT_BOOLEAN, + G_TYPE_NONE, + 2, + IBUS_TYPE_LOOKUP_TABLE, + G_TYPE_BOOLEAN); + + context_signals[SHOW_LOOKUP_TABLE] = + g_signal_new (I_("show-lookup-table"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + context_signals[HIDE_LOOKUP_TABLE] = + g_signal_new (I_("hide-lookup-table"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + context_signals[PAGE_UP_LOOKUP_TABLE] = + g_signal_new (I_("page-up-lookup-table"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + context_signals[PAGE_DOWN_LOOKUP_TABLE] = + g_signal_new (I_("page-down-lookup-table"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + context_signals[CURSOR_UP_LOOKUP_TABLE] = + g_signal_new (I_("cursor-up-lookup-table"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + context_signals[CURSOR_DOWN_LOOKUP_TABLE] = + g_signal_new (I_("cursor-down-lookup-table"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + context_signals[REGISTER_PROPERTIES] = + g_signal_new (I_("register-properties"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + IBUS_TYPE_PROP_LIST); + + context_signals[UPDATE_PROPERTY] = + g_signal_new (I_("update-property"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + IBUS_TYPE_PROPERTY); + + context_signals[ENABLED] = + g_signal_new (I_("enabled"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + context_signals[DISABLED] = + g_signal_new (I_("disabled"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + context_signals[ENGINE_CHANGED] = + g_signal_new (I_("factory-changed"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + context_signals[REQUEST_ENGINE] = + g_signal_new (I_("request-engine"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + context_signals[REQUEST_NEXT_ENGINE] = + g_signal_new (I_("request-next-engine"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + context_signals[REQUEST_PREV_ENGINE] = + g_signal_new (I_("request-prev-engine"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + +static void +bus_input_context_init (BusInputContext *context) +{ + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + priv->connection = NULL; + priv->client = NULL; + priv->engine = NULL; + priv->has_focus = FALSE; + priv->enabled = FALSE; + + priv->capabilities = 0; + + priv->x = 0; + priv->y = 0; + priv->w = 0; + priv->h = 0; + +} + +static void +bus_input_context_destroy (BusInputContext *context) +{ + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + if (priv->has_focus) { + bus_input_context_focus_out (context); + priv->has_focus = FALSE; + } + + if (priv->engine) { + g_signal_handlers_disconnect_by_func (priv->engine, + G_CALLBACK (_engine_destroy_cb), + context); + g_object_unref (priv->engine); + priv->engine = NULL; + } + + if (priv->connection) { + g_signal_handlers_disconnect_by_func (priv->connection, + (GCallback) _connection_destroy_cb, + context); + g_object_unref (priv->connection); + priv->connection = NULL; + } + + if (priv->client) { + g_free (priv->client); + priv->client = NULL; + } + + IBUS_OBJECT_CLASS(parent_class)->destroy (IBUS_OBJECT (context)); +} + +/* introspectable interface */ +static IBusMessage * +_ibus_introspect (BusInputContext *context, + IBusMessage *message, + BusConnection *connection) +{ + static const gchar *introspect = + "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" + "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" + "<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=\"RequestName\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"in\" type=\"u\"/>\n" + " <arg direction=\"out\" type=\"u\"/>\n" + " </method>\n" + " <signal name=\"NameOwnerChanged\">\n" + " <arg type=\"s\"/>\n" + " <arg type=\"s\"/>\n" + " <arg type=\"s\"/>\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; +} + +typedef struct { + BusInputContext *context; + IBusMessage *message; +}CallData; + +static void +_ic_process_key_event_reply_cb (gboolean retval, + CallData *call_data) +{ + IBusMessage *reply; + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (call_data->context); + + reply = ibus_message_new_method_return (call_data->message); + ibus_message_append_args (reply, + G_TYPE_BOOLEAN, &retval, + G_TYPE_INVALID); + ibus_connection_send ((IBusConnection *)priv->connection, reply); + + g_object_unref (call_data->context); + ibus_message_unref (call_data->message); + ibus_message_unref (reply); + g_slice_free (CallData, call_data); +} + +static IBusMessage * +_ic_process_key_event (BusInputContext *context, + IBusMessage *message, + BusConnection *connection) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (message != NULL); + g_assert (BUS_IS_CONNECTION (connection)); + + IBusMessage *reply = NULL; + guint keyval, modifiers; + gboolean retval; + IBusError *error; + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + + error = ibus_error_new (); + retval = ibus_message_get_args (message, + &error, + G_TYPE_UINT, &keyval, + G_TYPE_UINT, &modifiers, + G_TYPE_INVALID); + + if (!retval) { + reply = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply; + } + + ibus_error_free (error); + + retval = bus_input_context_filter_keyboard_shortcuts (context, keyval, modifiers); + + if (retval) { + reply = ibus_message_new_method_return (message); + ibus_message_append_args (reply, + G_TYPE_BOOLEAN, &retval, + G_TYPE_INVALID); + } + else if (priv->enabled && priv->engine) { + CallData *call_data = g_slice_new (CallData); + call_data->context = context; + call_data->message = message; + g_object_ref (context); + ibus_message_ref (message); + + bus_engine_proxy_process_key_event (priv->engine, + keyval, + modifiers, + (GFunc) _ic_process_key_event_reply_cb, + call_data); + } + else { + reply = ibus_message_new_method_return (message); + ibus_message_append_args (reply, + G_TYPE_BOOLEAN, &retval, + G_TYPE_INVALID); + } + return reply; +} + +static IBusMessage * +_ic_set_cursor_location (BusInputContext *context, + IBusMessage *message, + BusConnection *connection) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (message != NULL); + g_assert (BUS_IS_CONNECTION (connection)); + + IBusMessage *reply; + guint x, y, w, h; + gboolean retval; + IBusError *error; + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + retval = ibus_message_get_args (message, &error, + G_TYPE_INT, &x, + G_TYPE_INT, &y, + G_TYPE_INT, &w, + G_TYPE_INT, &h, + G_TYPE_INVALID); + + if (!retval) { + reply = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply; + } + + priv->x = x; + priv->y = y; + priv->h = h; + priv->w = w; + + if (priv->engine) { + bus_engine_proxy_set_cursor_location (priv->engine, x, y, w, h); + } + + if (priv->capabilities & IBUS_CAP_FOCUS) { + g_signal_emit (context, + context_signals[SET_CURSOR_LOCATION], + 0, + x, + y, + w, + h); + } + + reply = ibus_message_new_method_return (message); + return reply; +} + +static IBusMessage * +_ic_focus_in (BusInputContext *context, + IBusMessage *message, + BusConnection *connection) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (message != NULL); + g_assert (BUS_IS_CONNECTION (connection)); + + IBusMessage *reply; + + bus_input_context_focus_in (context); + + reply = ibus_message_new_method_return (message); + + return reply; +} + +static IBusMessage * +_ic_focus_out (BusInputContext *context, + IBusMessage *message, + BusConnection *connection) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (message != NULL); + g_assert (BUS_IS_CONNECTION (connection)); + + IBusMessage *reply; + + bus_input_context_focus_out (context); + + reply = ibus_message_new_method_return (message); + + return reply; +} + +static IBusMessage * +_ic_reset (BusInputContext *context, + IBusMessage *message, + BusConnection *connection) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (message != NULL); + g_assert (BUS_IS_CONNECTION (connection)); + + IBusMessage *reply; + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + + if (priv->engine) { + bus_engine_proxy_reset (priv->engine); + } + + reply = ibus_message_new_method_return (message); + return reply; +} + +static IBusMessage * +_ic_set_capabilities (BusInputContext *context, + IBusMessage *message, + BusConnection *connection) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (message != NULL); + g_assert (BUS_IS_CONNECTION (connection)); + + IBusMessage *reply; + guint caps; + gboolean retval; + IBusError *error; + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + + retval = ibus_message_get_args (message, + &error, + G_TYPE_UINT, &caps, + G_TYPE_INVALID); + + if (!retval) { + reply = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply; + } + + if (priv->capabilities != caps) { + priv->capabilities = caps; + + if (priv->engine) { + bus_engine_proxy_set_capabilities (priv->engine, caps); + } + } + + reply = ibus_message_new_method_return (message); + return reply; +} + +static IBusMessage * +_ic_is_enabled (BusInputContext *context, + IBusMessage *message, + BusConnection *connection) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (message != NULL); + g_assert (BUS_IS_CONNECTION (connection)); + + IBusMessage *reply; + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + reply = ibus_message_new_method_return (message); + ibus_message_append_args (reply, + G_TYPE_BOOLEAN, &priv->enabled, + G_TYPE_INVALID); + + return reply; +} + +static IBusMessage * +_ic_set_engine (BusInputContext *context, + IBusMessage *message, + BusConnection *connection) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (message != NULL); + g_assert (BUS_IS_CONNECTION (connection)); + + gboolean retval; + IBusMessage *reply; + IBusError *error; + gchar *engine_name; + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + retval = ibus_message_get_args (message, + &error, + G_TYPE_STRING, &engine_name, + G_TYPE_INVALID); + if (!retval) { + reply = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply; + } + + g_signal_emit (context, context_signals[REQUEST_ENGINE], 0, engine_name); + + if (priv->engine == NULL) { + reply = ibus_message_new_error_printf (message, + "org.freedesktop.IBus.NoEngine", + "can not find engine with name %s", + engine_name); + return reply; + } + + bus_input_context_enable (context); + + reply = ibus_message_new_method_return (message); + return reply; +} + +static IBusMessage * +_ic_get_engine (BusInputContext *context, + IBusMessage *message, + BusConnection *connection) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (message != NULL); + g_assert (BUS_IS_CONNECTION (connection)); + + IBusMessage *reply; + IBusEngineDesc *desc; + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + if (priv->engine) { + desc = bus_engine_proxy_get_desc (priv->engine); + if (desc != NULL) { + reply = ibus_message_new_method_return (message); + ibus_message_append_args (reply, + IBUS_TYPE_ENGINE_DESC, &desc, + G_TYPE_INVALID); + return reply; + } + } + + reply = ibus_message_new_error (message, + DBUS_ERROR_FAILED, + "InputContext does not have factory."); + return reply; +} + +static IBusMessage * +_ic_destroy (BusInputContext *context, + IBusMessage *message, + BusConnection *connection) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (message != NULL); + g_assert (BUS_IS_CONNECTION (connection)); + + IBusMessage *reply; + reply = ibus_message_new_method_return (message); + + ibus_connection_send ((IBusConnection *) connection, reply); + ibus_connection_flush ((IBusConnection *) connection); + ibus_message_unref (reply); + + ibus_object_destroy ((IBusObject *) context); + + return NULL; +} + +static gboolean +bus_input_context_ibus_message (BusInputContext *context, + BusConnection *connection, + IBusMessage *message) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (BUS_IS_CONNECTION (connection)); + g_assert (message != NULL); + + gint i; + IBusMessage *reply_message = NULL; + + static const struct { + const gchar *interface; + const gchar *name; + IBusMessage *(* handler) (BusInputContext *, IBusMessage *, BusConnection *); + } handlers[] = { + /* Introspectable interface */ + { DBUS_INTERFACE_INTROSPECTABLE, + "Introspect", _ibus_introspect }, + /* IBus interface */ + { IBUS_INTERFACE_INPUT_CONTEXT, "ProcessKeyEvent", _ic_process_key_event }, + { IBUS_INTERFACE_INPUT_CONTEXT, "SetCursorLocation", _ic_set_cursor_location }, + { IBUS_INTERFACE_INPUT_CONTEXT, "FocusIn", _ic_focus_in }, + { IBUS_INTERFACE_INPUT_CONTEXT, "FocusOut", _ic_focus_out }, + { IBUS_INTERFACE_INPUT_CONTEXT, "Reset", _ic_reset }, + { IBUS_INTERFACE_INPUT_CONTEXT, "SetCapabilities", _ic_set_capabilities }, + { IBUS_INTERFACE_INPUT_CONTEXT, "IsEnabled", _ic_is_enabled }, + { IBUS_INTERFACE_INPUT_CONTEXT, "SetEngine", _ic_set_engine }, + { IBUS_INTERFACE_INPUT_CONTEXT, "GetEngine", _ic_get_engine }, + { IBUS_INTERFACE_INPUT_CONTEXT, "Destroy", _ic_destroy }, + + { NULL, NULL, NULL } + }; + + ibus_message_set_sender (message, bus_connection_get_unique_name (connection)); + ibus_message_set_destination (message, DBUS_SERVICE_DBUS); + + for (i = 0; handlers[i].interface != NULL; i++) { + if (ibus_message_is_method_call (message, + handlers[i].interface, + handlers[i].name)) { + + reply_message = handlers[i].handler (context, message, connection); + if (reply_message) { + + 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); + + ibus_connection_send (IBUS_CONNECTION (connection), reply_message); + ibus_message_unref (reply_message); + } + + g_signal_stop_emission_by_name (context, "ibus-message"); + return TRUE; + } + } + + return parent_class->ibus_message ((IBusService *)context, + (IBusConnection *)connection, + message); +} + + +gboolean +bus_input_context_is_focus (BusInputContext *context) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + return priv->has_focus; +} + +void +bus_input_context_focus_in (BusInputContext *context) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + if (priv->has_focus) + return; + + priv->has_focus = TRUE; + + if (priv->engine) { + bus_engine_proxy_focus_in (priv->engine); + } + + if (priv->capabilities & IBUS_CAP_FOCUS) { + g_signal_emit (context, context_signals[FOCUS_IN], 0); + } +} + +void +bus_input_context_focus_out (BusInputContext *context) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + if (!priv->has_focus) + return; + + priv->has_focus = FALSE; + + if (priv->engine) { + bus_engine_proxy_focus_out (priv->engine); + } + + if (priv->capabilities & IBUS_CAP_FOCUS) { + g_signal_emit (context, context_signals[FOCUS_OUT], 0); + } +} + +void +bus_input_context_page_up (BusInputContext *context) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + if (priv->engine) { + bus_engine_proxy_page_up (priv->engine); + } +} + +void +bus_input_context_page_down (BusInputContext *context) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + if (priv->engine) { + bus_engine_proxy_page_down (priv->engine); + } +} + +void +bus_input_context_cursor_up (BusInputContext *context) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + if (priv->engine) { + bus_engine_proxy_cursor_up (priv->engine); + } +} + +void +bus_input_context_cursor_down (BusInputContext *context) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + if (priv->engine) { + bus_engine_proxy_cursor_down (priv->engine); + } +} + +void +bus_input_context_property_activate (BusInputContext *context, + const gchar *prop_name, + gint prop_state) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + if (priv->engine) { + bus_engine_proxy_property_activate (priv->engine, prop_name, prop_state); + } +} + +static void +_engine_destroy_cb (BusEngineProxy *engine, + BusInputContext *context) +{ + g_assert (BUS_IS_ENGINE_PROXY (engine)); + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + g_assert (priv->engine == engine); + + bus_input_context_set_engine (context, NULL); +} + +static void +_engine_commit_text_cb (BusEngineProxy *engine, + IBusText *text, + BusInputContext *context) +{ + g_assert (BUS_IS_ENGINE_PROXY (engine)); + g_assert (text != NULL); + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + g_assert (priv->engine == engine); + + bus_input_context_send_signal (context, + "CommitText", + IBUS_TYPE_TEXT, &text, + G_TYPE_INVALID); + +} + +static void +_engine_forward_key_event_cb (BusEngineProxy *engine, + guint keyval, + guint state, + BusInputContext *context) +{ + g_assert (BUS_IS_ENGINE_PROXY (engine)); + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + g_assert (priv->engine == engine); + + bus_input_context_send_signal (context, + "ForwardKeyEvent", + G_TYPE_UINT, &keyval, + G_TYPE_UINT, &state, + G_TYPE_INVALID); + +} + +static void +_engine_update_preedit_text_cb (BusEngineProxy *engine, + IBusText *text, + guint cursor_pos, + gboolean visible, + BusInputContext *context) +{ + g_assert (BUS_IS_ENGINE_PROXY (engine)); + g_assert (IBUS_IS_TEXT (text)); + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + g_assert (priv->engine == engine); + + if (priv->capabilities & IBUS_CAP_PREEDIT_TEXT) { + bus_input_context_send_signal (context, + "UpdatePreeditText", + IBUS_TYPE_TEXT, &text, + G_TYPE_UINT, &cursor_pos, + G_TYPE_BOOLEAN, &visible, + G_TYPE_INVALID); + } + else { + g_signal_emit (context, + context_signals[UPDATE_PREEDIT_TEXT], + 0, + text, + cursor_pos, + visible); + } +} + +static void +_engine_update_auxiliary_text_cb (BusEngineProxy *engine, + IBusText *text, + gboolean visible, + BusInputContext *context) +{ + g_assert (BUS_IS_ENGINE_PROXY (engine)); + g_assert (IBUS_IS_TEXT (text)); + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + g_assert (priv->engine == engine); + + if (priv->capabilities & IBUS_CAP_AUXILIARY_TEXT) { + bus_input_context_send_signal (context, + "UpdateAuxiliaryText", + IBUS_TYPE_TEXT, &text, + G_TYPE_BOOLEAN, &visible, + G_TYPE_INVALID); + } + else { + g_signal_emit (context, + context_signals[UPDATE_AUXILIARY_TEXT], + 0, + text, + visible); + } +} + +static void +_engine_update_lookup_table_cb (BusEngineProxy *engine, + IBusLookupTable *table, + gboolean visible, + BusInputContext *context) +{ + g_assert (BUS_IS_ENGINE_PROXY (engine)); + g_assert (IBUS_IS_LOOKUP_TABLE (table)); + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + g_assert (priv->engine == engine); + + if (priv->capabilities & IBUS_CAP_LOOKUP_TABLE) { + bus_input_context_send_signal (context, + "UpdateLookupTable", + IBUS_TYPE_LOOKUP_TABLE, &table, + G_TYPE_BOOLEAN, &visible, + G_TYPE_INVALID); + } + else { + g_signal_emit (context, + context_signals[UPDATE_LOOKUP_TABLE], + 0, + table, + visible); + } +} + +static void +_engine_register_properties_cb (BusEngineProxy *engine, + IBusPropList *prop_list, + BusInputContext *context) +{ + g_assert (BUS_IS_ENGINE_PROXY (engine)); + g_assert (IBUS_IS_PROP_LIST (prop_list)); + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + g_assert (priv->engine == engine); + + if (priv->capabilities & IBUS_CAP_PROPERTY) { + bus_input_context_send_signal (context, + "RegisterProperties", + IBUS_TYPE_PROP_LIST, &prop_list, + G_TYPE_INVALID); + } + else { + g_signal_emit (context, + context_signals[REGISTER_PROPERTIES], + 0, + prop_list); + } +} + +static void +_engine_update_property_cb (BusEngineProxy *engine, + IBusProperty *prop, + BusInputContext *context) +{ + g_assert (BUS_IS_ENGINE_PROXY (engine)); + g_assert (IBUS_IS_PROPERTY (prop)); + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + g_assert (priv->engine == engine); + + if (priv->capabilities & IBUS_CAP_PROPERTY) { + bus_input_context_send_signal (context, + "UpdateProperty", + IBUS_TYPE_PROPERTY, &prop, + G_TYPE_INVALID); + } + else { + g_signal_emit (context, + context_signals[UPDATE_PROPERTY], + 0, + prop); + } +} + +#define DEFINE_FUNCTION(name, Name, signal_name, cap) \ + static void \ + _engine_##name##_cb (BusEngineProxy *engine, \ + BusInputContext *context) \ + { \ + g_assert (BUS_IS_ENGINE_PROXY (engine)); \ + g_assert (BUS_IS_INPUT_CONTEXT (context)); \ + \ + BusInputContextPrivate *priv; \ + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); \ + \ + g_assert (priv->engine == engine); \ + \ + if ((priv->capabilities & (cap)) == (cap)) { \ + bus_input_context_send_signal (context, \ + #Name, \ + G_TYPE_INVALID); \ + } \ + else { \ + g_signal_emit (context, \ + context_signals[signal_name], \ + 0); \ + } \ +} + +DEFINE_FUNCTION (show_preedit_text, ShowPreeditText, SHOW_PREEDIT_TEXT, IBUS_CAP_PREEDIT_TEXT) +DEFINE_FUNCTION (hide_preedit_text, HidePreeditText, HIDE_PREEDIT_TEXT, IBUS_CAP_PREEDIT_TEXT) +DEFINE_FUNCTION (show_auxiliary_text, ShowAuxiliaryText, SHOW_AUXILIARY_TEXT, IBUS_CAP_AUXILIARY_TEXT) +DEFINE_FUNCTION (hide_auxiliary_text, HideAuxiliaryText, HIDE_AUXILIARY_TEXT, IBUS_CAP_AUXILIARY_TEXT) +DEFINE_FUNCTION (show_lookup_table, ShowLookupTable, SHOW_LOOKUP_TABLE, IBUS_CAP_LOOKUP_TABLE) +DEFINE_FUNCTION (hide_lookup_table, HideLookupTable, HIDE_LOOKUP_TABLE, IBUS_CAP_LOOKUP_TABLE) +DEFINE_FUNCTION (page_up_lookup_table, PageUpLookupTable, PAGE_UP_LOOKUP_TABLE, IBUS_CAP_LOOKUP_TABLE) +DEFINE_FUNCTION (page_down_lookup_table, PageDownLookupTable, PAGE_DOWN_LOOKUP_TABLE, IBUS_CAP_LOOKUP_TABLE) +DEFINE_FUNCTION (cursor_up_lookup_table, CursorUpLookupTable, CURSOR_UP_LOOKUP_TABLE, IBUS_CAP_LOOKUP_TABLE) +DEFINE_FUNCTION (cursor_down_lookup_table, CursorDownLookupTable, CURSOR_DOWN_LOOKUP_TABLE, IBUS_CAP_LOOKUP_TABLE) + +#undef DEFINE_FUNCTION + +void +bus_input_context_enable (BusInputContext *context) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + if (priv->engine == NULL) + return; + + priv->enabled = TRUE; + + bus_engine_proxy_enable (priv->engine); + bus_input_context_send_signal (context, + "Enabled", + G_TYPE_INVALID); + g_signal_emit (context, + context_signals[ENABLED], + 0); + if (priv->has_focus) { + bus_engine_proxy_focus_in (priv->engine); + } +} + +void +bus_input_context_disable (BusInputContext *context) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + priv->enabled = FALSE; + + if (priv->engine) { + if (priv->has_focus) { + bus_engine_proxy_focus_out (priv->engine); + } + bus_engine_proxy_disable (priv->engine); + } + + bus_input_context_send_signal (context, + "Disabled", + G_TYPE_INVALID); + g_signal_emit (context, + context_signals[DISABLED], + 0); +} + + +void +bus_input_context_set_engine (BusInputContext *context, + BusEngineProxy *engine) +{ + + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + if (priv->engine != NULL) { + g_signal_handlers_disconnect_by_func (priv->engine, _engine_destroy_cb, context); + ibus_object_destroy ((IBusObject *) priv->engine); + g_object_unref (priv->engine); + priv->engine = NULL; + } + + if (engine == NULL) { + bus_input_context_disable (context); + g_signal_emit (context, + context_signals[ENGINE_CHANGED], + 0); + return; + } + + priv->engine = engine; + g_object_ref (priv->engine); + + gint i; + const static struct { + const gchar *name; + GCallback callback; + } signals [] = { + { "commit-text", G_CALLBACK (_engine_commit_text_cb) }, + { "forward-key-event", G_CALLBACK (_engine_forward_key_event_cb) }, + { "update-preedit-text", G_CALLBACK (_engine_update_preedit_text_cb) }, + { "show-preedit-text", G_CALLBACK (_engine_show_preedit_text_cb) }, + { "hide-preedit-text", G_CALLBACK (_engine_hide_preedit_text_cb) }, + { "update-auxiliary-text", G_CALLBACK (_engine_update_auxiliary_text_cb) }, + { "show-auxiliary-text", G_CALLBACK (_engine_show_auxiliary_text_cb) }, + { "hide-auxiliary-text", G_CALLBACK (_engine_hide_auxiliary_text_cb) }, + { "update-lookup-table", G_CALLBACK (_engine_update_lookup_table_cb) }, + { "show-lookup-table", G_CALLBACK (_engine_show_lookup_table_cb) }, + { "hide-lookup-table", G_CALLBACK (_engine_hide_lookup_table_cb) }, + { "page-up-lookup-table", G_CALLBACK (_engine_page_up_lookup_table_cb) }, + { "page-down-lookup-table", G_CALLBACK (_engine_page_down_lookup_table_cb) }, + { "cursor-up-lookup-table", G_CALLBACK (_engine_cursor_up_lookup_table_cb) }, + { "cursor-down-lookup-table", G_CALLBACK (_engine_cursor_down_lookup_table_cb) }, + { "register-properties", G_CALLBACK (_engine_register_properties_cb) }, + { "update-property", G_CALLBACK (_engine_update_property_cb) }, + { "destroy", G_CALLBACK (_engine_destroy_cb) }, + { NULL, 0 } + }; + + for (i = 0; signals[i].name != NULL; i++) { + g_signal_connect (priv->engine, + signals[i].name, + signals[i].callback, + context); + } + + g_signal_emit (context, + context_signals[ENGINE_CHANGED], + 0); +} + +static gboolean +bus_input_context_filter_keyboard_shortcuts (BusInputContext *context, + guint keyval, + guint modifiers) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusInputContextPrivate *priv; + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + static GQuark trigger; + static GQuark next_factory; + static GQuark prev_factory; + + GQuark event; + + if (trigger == 0) { + trigger = g_quark_from_static_string ("trigger"); + next_factory = g_quark_from_static_string ("next-engine"); + prev_factory = g_quark_from_static_string ("prev-engine"); + } + + event = ibus_hotkey_profile_filter_key_event (BUS_DEFAULT_HOTKEY_PROFILE, + keyval, + modifiers, + 0); + + if (event == trigger) { + if (priv->engine == NULL) { + g_signal_emit (context, context_signals[REQUEST_ENGINE], 0, NULL); + } + + if (priv->engine == NULL) { + return FALSE; + } + + if (priv->enabled) { + bus_input_context_disable (context); + } + else { + bus_input_context_enable (context); + } + + return TRUE; + } + else if (event == next_factory) { + return TRUE; + } + else if (event == prev_factory) { + return TRUE; + } + else + return FALSE; +} + + +static gboolean +bus_input_context_send_signal (BusInputContext *context, + const gchar *signal_name, + GType first_arg_type, + ...) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (signal_name != NULL); + + va_list args; + gboolean retval; + IBusMessage *message; + BusInputContextPrivate *priv; + + priv = BUS_INPUT_CONTEXT_GET_PRIVATE (context); + + g_assert (priv->connection != NULL); + + message = ibus_message_new_signal (ibus_service_get_path ((IBusService *)context), + IBUS_INTERFACE_INPUT_CONTEXT, + signal_name); + + ibus_message_set_sender (message, IBUS_SERVICE_IBUS); + + va_start (args, first_arg_type); + ibus_message_append_args_valist (message, first_arg_type, args); + va_end (args); + + retval = ibus_connection_send ((IBusConnection *)priv->connection, message); + ibus_message_unref (message); + + return retval; +} diff --git a/bus/inputcontext.h b/bus/inputcontext.h new file mode 100644 index 0000000..edebdc0 --- /dev/null +++ b/bus/inputcontext.h @@ -0,0 +1,82 @@ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ +#ifndef __INPUT_CONTEXT_H_ +#define __INPUT_CONTEXT_H_ + +#include <ibus.h> +#include "connection.h" +#include "factoryproxy.h" + +/* + * Type macros. + */ + +/* define GOBJECT macros */ +#define BUS_TYPE_INPUT_CONTEXT \ + (bus_input_context_get_type ()) +#define BUS_INPUT_CONTEXT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUS_TYPE_INPUT_CONTEXT, BusInputContext)) +#define BUS_INPUT_CONTEXT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), BUS_TYPE_INPUT_CONTEXT, BusInputContextClass)) +#define BUS_IS_INPUT_CONTEXT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUS_TYPE_INPUT_CONTEXT)) +#define BUS_IS_INPUT_CONTEXT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), BUS_TYPE_INPUT_CONTEXT)) +#define BUS_INPUT_CONTEXT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), BUS_TYPE_INPUT_CONTEXT, BusInputContextClass)) + +G_BEGIN_DECLS + +typedef struct _BusInputContext BusInputContext; +typedef struct _BusInputContextClass BusInputContextClass; + +struct _BusInputContext { + IBusService parent; + /* instance members */ +}; + +struct _BusInputContextClass { + IBusServiceClass parent; + + /* class members */ +}; + +GType bus_input_context_get_type (void); +BusInputContext *bus_input_context_new (BusConnection *connection, + const gchar *client); +gboolean bus_input_context_is_focus (BusInputContext *context); +void bus_input_context_focus_in (BusInputContext *context); +void bus_input_context_focus_out (BusInputContext *context); +void bus_input_context_enable_or_disable(BusInputContext *context); +void bus_input_context_enable (BusInputContext *context); +void bus_input_context_disable (BusInputContext *context); +void bus_input_context_page_up (BusInputContext *context); +void bus_input_context_page_down (BusInputContext *context); +void bus_input_context_cursor_up (BusInputContext *context); +void bus_input_context_cursor_down (BusInputContext *context); +void bus_input_context_set_engine (BusInputContext *context, + BusEngineProxy *factory); +void bus_input_context_property_activate(BusInputContext *context, + const gchar *prop_name, + gint prop_state); + +G_END_DECLS +#endif + diff --git a/bus/main.c b/bus/main.c new file mode 100644 index 0000000..74e05d8 --- /dev/null +++ b/bus/main.c @@ -0,0 +1,148 @@ +/* vim:set et sts=4: */ +/* ibus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <locale.h> +#include "server.h" +#include "ibusimpl.h" + +static gboolean daemonize = FALSE; +static gboolean single = FALSE; +static gboolean xim = FALSE; +static gchar *panel = "default"; +static gchar *config = "default"; +static gchar *desktop = "gnome"; +static gboolean verbose = FALSE; + +static const GOptionEntry entries[] = +{ + { "daemonize", 'd', 0, G_OPTION_ARG_NONE, &daemonize, "run ibus as background process.", NULL }, + { "single", 's', 0, G_OPTION_ARG_NONE, &single, "do not execute panel and config module.", NULL }, + { "xim", 'x', 0, G_OPTION_ARG_NONE, &xim, "execute ibus XIM server.", NULL }, + { "desktop", 'n', 0, G_OPTION_ARG_STRING, &desktop, "specify the name of desktop session. [default=gnome]", "name" }, + { "panel", 'p', 0, G_OPTION_ARG_STRING, &panel, "specify the cmdline of panel program.", "cmdline" }, + { "config", 'c', 0, G_OPTION_ARG_STRING, &config, "specify the cmdline of config program.", "cmdline" }, + { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "verbose.", NULL }, + { NULL }, +}; + +static gboolean +execute_cmdline (const gchar *cmdline) +{ + g_assert (cmdline); + + gint argc; + gchar **argv; + gboolean retval; + GError *error; + + error = NULL; + if (!g_shell_parse_argv (cmdline, &argc, &argv, &error)) { + g_warning ("Can not parse cmdline `%s` exec: %s", cmdline, error->message); + g_error_free (error); + return FALSE; + } + + error = NULL; + retval = g_spawn_async (NULL, argv, NULL, + G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, + NULL, NULL, + NULL, &error); + g_strfreev (argv); + + if (!retval) { + g_warning ("Can not execute cmdline `%s`: %s", cmdline, error->message); + g_error_free (error); + return FALSE; + } + + return TRUE; +} + +gint +main (gint argc, gchar **argv) +{ + GOptionContext *context; + BusServer *server; + + GError *error = NULL; + + setlocale (LC_ALL, ""); + + context = g_option_context_new ("- ibus daemon"); + + g_option_context_add_main_entries (context, entries, "ibus-daemon"); + + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_printerr ("Option parsing failed: %s\n", error->message); + exit (-1); + } + + if (daemonize) { + if (daemon (1, 0) != 0) { + g_printerr ("Can not daemonize ibus.\n"); + exit (-1); + } + } + + g_type_init (); + + server = bus_server_get_default (); + bus_server_listen (server); + + if (!single) { + /* execute config component */ + if (g_strcmp0 (config, "default") == 0) { + IBusComponent *component; + component = bus_registry_lookup_component_by_name (BUS_DEFAULT_REGISTRY, IBUS_SERVICE_CONFIG); + if (component == NULL || !ibus_component_start (component)) { + g_printerr ("Can not execute default config program\n"); + exit (-1); + } + } else if (g_strcmp0 (config, "disable") != 0 && g_strcmp0 (config, "") != 0) { + if (!execute_cmdline (config)) + exit (-1); + } + + /* execut panel component */ + if (g_strcmp0 (panel, "default") == 0) { + IBusComponent *component; + component = bus_registry_lookup_component_by_name (BUS_DEFAULT_REGISTRY, IBUS_SERVICE_PANEL); + if (component == NULL || !ibus_component_start (component)) { + g_printerr ("Can not execute default panel program\n"); + exit (-1); + } + } else if (g_strcmp0 (panel, "disable") != 0 && g_strcmp0 (panel, "") != 0) { + if (!execute_cmdline (panel)) + exit (-1); + } + } + + /* execute ibus xim server */ + if (xim) { + if (!execute_cmdline (LIBEXECDIR"/ibus-x11 --kill-daemon")) + exit (-1); + } + + bus_server_run (server); + + return 0; +} diff --git a/bus/matchrule.c b/bus/matchrule.c new file mode 100644 index 0000000..6884fd6 --- /dev/null +++ b/bus/matchrule.c @@ -0,0 +1,672 @@ +/* vim:set et sts=4: */ +/* IBus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ +#include <string.h> +#include "matchrule.h" + +#define BUS_CONFIG_PROXY_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), BUS_TYPE_CONFIG_PROXY, BusMatchRulePrivate)) + + +static void bus_match_rule_class_init (BusMatchRuleClass *klass); +static void bus_match_rule_init (BusMatchRule *rule); +static void bus_match_rule_destroy (BusMatchRule *rule); + +static IBusObjectClass *parent_class = NULL; + +GType +bus_match_rule_get_type (void) +{ + static GType type = 0; + + static const GTypeInfo type_info = { + sizeof (BusMatchRuleClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) bus_match_rule_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (BusMatchRule), + 0, + (GInstanceInitFunc) bus_match_rule_init, + }; + + if (type == 0) { + type = g_type_register_static (IBUS_TYPE_OBJECT, + "BusMatchRule", + &type_info, + (GTypeFlags)0); + } + return type; +} + +static void +bus_match_rule_class_init (BusMatchRuleClass *klass) +{ + IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass); + + parent_class = (IBusObjectClass *) g_type_class_peek_parent (klass); + + ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_match_rule_destroy; +} + +static void +bus_match_rule_init (BusMatchRule *rule) +{ + rule->flags = 0; + rule->message_type = DBUS_MESSAGE_TYPE_INVALID; + rule->interface = NULL; + rule->member = NULL; + rule->sender = NULL; + rule->destination = NULL; + rule->path = NULL; + rule->args = g_array_new (TRUE, TRUE, sizeof (gchar *)); +} + +static void +bus_match_rule_destroy (BusMatchRule *rule) +{ + g_free (rule->interface); + g_free (rule->member); + g_free (rule->sender); + g_free (rule->destination); + g_free (rule->path); + + gint i; + GList *link; + + for (i = 0; i < rule->args->len; i++) { + g_free (g_array_index (rule->args, gchar *, i)); + } + g_array_free (rule->args, TRUE); + + for (link = rule->recipients; link != NULL; link = link->next) { + BusRecipient *recipient = (BusRecipient *) link->data; + g_object_unref (recipient->connection); + g_slice_free (BusRecipient, recipient); + } + g_list_free (rule->recipients); + + IBUS_OBJECT_CLASS(parent_class)->destroy (IBUS_OBJECT (rule)); +} + + +typedef struct _Token { + gchar *key; + gchar *value; +} Token; + +#define SKIP_WHITE(a) \ + while (*(a) == ' ' || *(a) == '\t') { (a)++; } +#define IS_ALPHA(a) \ + ((*(a) >= 'a' && *(a) <= 'z') || (*(a) >= 'A' && *(a) <= 'Z')) +#define IS_NUMBER(a) \ + (*(a) >= '0' && *(a) <= '9') + +static gchar * +find_key (const gchar **p) +{ + GString *text; + + text = g_string_new (""); + + SKIP_WHITE(*p) + if (!IS_ALPHA (*p)) + goto failed; + + g_string_append_c (text, **p); + (*p) ++; + + while (IS_ALPHA (*p) || IS_NUMBER (*p)) { + g_string_append_c (text, **p); + (*p) ++; + } + + return g_string_free (text, FALSE); + +failed: + g_string_free (text, TRUE); + return NULL; + +} + +static gchar * +find_value (const gchar **p) +{ + GString *text; + + text = g_string_new (""); + + SKIP_WHITE (*p); + + if (**p != '\'') + goto failed; + (*p) ++; + + while (**p != '\'') { + if (**p == '\0') + goto failed; + if (**p == '\\') + (*p) ++; + g_string_append_c (text, **p); + (*p) ++; + } + (*p) ++; + + return g_string_free (text, FALSE); + +failed: + g_string_free (text, TRUE); + return NULL; +} + +static Token * +tokenize_rule (const gchar *text) +{ + GArray *tokens; + Token token; + const gchar *p; + gint i; + + tokens = g_array_new (TRUE, TRUE, sizeof (Token)); + + p = text; + + while (*p != '\0') { + gchar *key; + gchar *value; + + SKIP_WHITE (p); + key = find_key (&p); + if (key == NULL) + goto failed; + SKIP_WHITE (p); + if (*p != '=') + goto failed; + p ++; + SKIP_WHITE (p); + value = find_value (&p); + if (value == NULL) { + g_free (key); + goto failed; + } + SKIP_WHITE (p); + if (*p != ',' && *p != '\0') { + g_free (key); + g_free (value); + goto failed; + } + + if (*p == ',') + p ++; + token.key = key; + token.value = value; + g_array_append_val (tokens, token); + } + + return (Token *)g_array_free (tokens, FALSE); + +failed: + + for (i = 0; i < tokens->len; i++) { + Token *p = &g_array_index (tokens, Token, i); + g_free (p->key); + g_free (p->value); + } + + g_array_free (tokens, TRUE); + return NULL; +} + +static void +tokens_free (Token *tokens) +{ + Token *p; + p = tokens; + + while (p != NULL && p->key != NULL) { + g_free (p->key); + g_free (p->value); + p ++; + } + g_free (tokens); +} + +static gboolean +_atoi (const gchar *text, gint *i) +{ + const gchar *p = text; + *i = 0; + while (*p != '\0') { + if (!IS_NUMBER(p)) + return FALSE; + *i = (*i) * 10 - '0' + *p; + p ++; + } + return TRUE; +} + +BusMatchRule * +bus_match_rule_new (const gchar *text) +{ + g_assert (text != NULL); + + Token *tokens, *p; + BusMatchRule *rule; + + rule = BUS_MATCH_RULE (g_object_new (BUS_TYPE_MATCH_RULE, NULL)); + + /* parse rule */ + tokens = tokenize_rule (text); + + for (p = tokens; p != NULL && p->key != 0; p++) { + if (g_strcmp0 (p->key, "type") == 0) { + if (g_strcmp0 (p->value, "signal") == 0) { + bus_match_rule_set_message_type (rule, DBUS_MESSAGE_TYPE_SIGNAL); + } + else if (g_strcmp0 (p->value, "method_call") == 0) { + bus_match_rule_set_message_type (rule, DBUS_MESSAGE_TYPE_METHOD_CALL); + } + else if (g_strcmp0 (p->value, "method_return") == 0) { + bus_match_rule_set_message_type (rule, DBUS_MESSAGE_TYPE_METHOD_RETURN); + } + else if (g_strcmp0 (p->value, "error") == 0) { + bus_match_rule_set_message_type (rule, DBUS_MESSAGE_TYPE_ERROR); + } + else + goto failed; + } + else if (g_strcmp0 (p->key, "sender") == 0) { + bus_match_rule_set_sender (rule, p->value); + } + else if (g_strcmp0 (p->key, "interface") == 0) { + bus_match_rule_set_interface (rule, p->value); + } + else if (g_strcmp0 (p->key, "member") == 0) { + bus_match_rule_set_member (rule, p->value); + } + else if (g_strcmp0 (p->key, "path") == 0) { + bus_match_rule_set_path (rule, p->value); + } + else if (g_strcmp0 (p->key, "destination") == 0) { + bus_match_rule_set_destination (rule, p->value); + } + else if (strncmp (p->key, "arg", 3) == 0) { + gint i; + if (! _atoi (p->key + 3, &i)) + goto failed; + bus_match_rule_set_arg (rule, i, p->value); + } + else + goto failed; + } + + tokens_free (tokens); + return rule; + +failed: + tokens_free (tokens); + g_object_unref (rule); + return NULL; +} + +gboolean +bus_match_rule_set_message_type (BusMatchRule *rule, + gint type) +{ + g_assert (rule != NULL); + g_assert (type == DBUS_MESSAGE_TYPE_SIGNAL || + type == DBUS_MESSAGE_TYPE_METHOD_CALL || + type == DBUS_MESSAGE_TYPE_METHOD_RETURN || + type == DBUS_MESSAGE_TYPE_ERROR); + + rule->flags |= MATCH_TYPE; + rule->message_type = type; + + return TRUE; +} + +gboolean +bus_match_rule_set_sender (BusMatchRule *rule, + const gchar *sender) +{ + g_assert (rule != NULL); + g_assert (sender != NULL); + + rule->flags |= MATCH_SENDER; + + g_free (rule->sender); + rule->sender = g_strdup (sender); + + return TRUE; +} + +gboolean +bus_match_rule_set_interface (BusMatchRule *rule, + const gchar *interface) +{ + g_assert (rule != NULL); + g_assert (interface != NULL); + + rule->flags |= MATCH_INTERFACE; + + g_free (rule->interface); + rule->interface = g_strdup (interface); + return TRUE; +} + +gboolean +bus_match_rule_set_member (BusMatchRule *rule, + const gchar *member) +{ + g_assert (rule != NULL); + g_assert (member != NULL); + + rule->flags |= MATCH_MEMBER; + + g_free (rule->member); + rule->member = g_strdup (member); + + return TRUE; +} + +gboolean +bus_match_rule_set_path (BusMatchRule *rule, + const gchar *path) +{ + g_assert (rule != NULL); + g_assert (path != NULL); + + rule->flags |= MATCH_PATH; + + g_free (rule->path); + rule->path = g_strdup (path); + + return TRUE; +} + +gboolean +bus_match_rule_set_destination (BusMatchRule *rule, + const gchar *dest) +{ + g_assert (rule != NULL); + g_assert (dest != NULL); + + rule->flags |= MATCH_DESTINATION; + + g_free (rule->destination); + rule->destination = g_strdup (dest); + + return TRUE; +} + +gboolean +bus_match_rule_set_arg (BusMatchRule *rule, + guint arg_i, + const gchar *arg) +{ + g_assert (rule != NULL); + g_assert (arg != NULL); + + rule->flags |= MATCH_ARGS; + + if (arg_i >= rule->args->len) { + g_array_set_size (rule->args, arg_i + 1); + } + + g_free (g_array_index (rule->args, gchar *, arg_i)); + g_array_index (rule->args, gchar *, arg_i) = g_strdup (arg); + return TRUE; +} + +gboolean +bus_match_rule_match (BusMatchRule *rule, + DBusMessage *message) +{ + g_assert (rule != NULL); + g_assert (message != NULL); + + if (rule->flags & MATCH_TYPE) { + if (ibus_message_get_type (message) != rule->message_type) + return FALSE; + } + + if (rule->flags & MATCH_INTERFACE) { + if (g_strcmp0 (ibus_message_get_interface (message), rule->interface) != 0) + return FALSE; + } + + if (rule->flags & MATCH_MEMBER) { + if (g_strcmp0 (ibus_message_get_member (message), rule->member) != 0) + return FALSE; + } + + if (rule->flags & MATCH_SENDER) { + if (g_strcmp0 (ibus_message_get_sender (message), rule->sender) != 0) + return FALSE; + } + + if (rule->flags & MATCH_DESTINATION) { + if (g_strcmp0 (ibus_message_get_destination (message), rule->destination) != 0) + return FALSE; + } + + if (rule->flags & MATCH_PATH) { + if (g_strcmp0 (ibus_message_get_path (message), rule->path) != 0) + return FALSE; + } + + if (rule->flags & MATCH_ARGS) { + guint i; + DBusMessageIter iter; + + ibus_message_iter_init (message, &iter); + + for (i = 0; i < rule->args->len; i++) { + gchar *arg = g_array_index (rule->args, gchar *, i); + if (arg != NULL) { + gint type; + gchar *value; + + type = ibus_message_iter_get_arg_type (&iter); + if (type != G_TYPE_STRING && type != IBUS_TYPE_OBJECT_PATH) + return FALSE; + + ibus_message_iter_get_basic (&iter, &value); + + if (g_strcmp0 (arg, value) != 0) + return FALSE; + } + ibus_message_iter_next (&iter); + } + } + return TRUE; +} + +gboolean +bus_match_rule_is_equal (BusMatchRule *a, + BusMatchRule *b) +{ + g_assert (a != NULL); + g_assert (b != NULL); + + if (a->flags != b->flags) + return FALSE; + + if (a->flags & MATCH_TYPE) { + if (a->message_type != b->message_type) + return FALSE; + } + + if (a->flags & MATCH_INTERFACE) { + if (g_strcmp0 (a->interface, b->interface) != 0) + return FALSE; + } + + if (a->flags & MATCH_MEMBER) { + if (g_strcmp0 (a->member, b->member) != 0) + return FALSE; + } + + if (a->flags & MATCH_SENDER) { + if (g_strcmp0 (a->sender, b->sender) != 0) + return FALSE; + } + + if (a->flags & MATCH_DESTINATION) { + if (g_strcmp0 (a->destination, b->destination) != 0) + return FALSE; + } + + if (a->flags & MATCH_PATH) { + if (g_strcmp0 (a->path, b->path) != 0) + return FALSE; + } + + if (a->flags & MATCH_ARGS) { + if (a->args->len != b->args->len) + return FALSE; + + gint i; + + for (i = 0; i < a->args->len; i++) { + if (g_strcmp0 (g_array_index (a->args, gchar *, i), + g_array_index (b->args, gchar *, i)) != 0) + return FALSE; + } + } + + return TRUE; +} + +static void +_connection_destroy_cb (BusConnection *connection, + BusMatchRule *rule) +{ + g_assert (BUS_IS_MATCH_RULE (rule)); + g_assert (BUS_IS_CONNECTION (connection)); + + GList *link; + BusRecipient *recipient; + + for (link = rule->recipients; link != NULL; link = link->next) { + recipient = (BusRecipient *)link->data; + + if (recipient->connection == connection) { + rule->recipients = g_list_remove_link (rule->recipients, link); + g_object_unref (connection); + g_slice_free (BusRecipient, recipient); + return; + } + + if (rule->recipients == NULL) { + ibus_object_destroy (IBUS_OBJECT (rule)); + } + } + g_assert_not_reached (); +} + +void +bus_match_rule_add_recipient (BusMatchRule *rule, + BusConnection *connection) +{ + g_assert (BUS_IS_MATCH_RULE (rule)); + g_assert (BUS_IS_CONNECTION (connection)); + + GList *link; + BusRecipient *recipient; + + for (link = rule->recipients; link != NULL; link = link->next) { + recipient = (BusRecipient *) link->data; + if (connection == recipient->connection) { + recipient->refcount ++; + return; + } + } + + recipient = g_slice_new (BusRecipient); + + g_object_ref (connection); + recipient->connection = connection; + recipient->refcount = 1; + + rule->recipients = g_list_append (rule->recipients, recipient); + g_signal_connect (connection, + "destroy", + G_CALLBACK (_connection_destroy_cb), + rule); +} + +void +bus_match_rule_remove_recipient (BusMatchRule *rule, + BusConnection *connection) +{ + g_assert (BUS_IS_MATCH_RULE (rule)); + g_assert (BUS_IS_CONNECTION (connection)); + + GList *link; + BusRecipient *recipient; + + for (link = rule->recipients; link != NULL; link = link->next) { + recipient = (BusRecipient *) link->data; + if (connection == recipient->connection) { + recipient->refcount --; + if (recipient->refcount == 0) { + rule->recipients = g_list_remove_link (rule->recipients, link); + g_slice_free (BusRecipient, recipient); + g_signal_handlers_disconnect_by_func (connection, + G_CALLBACK (_connection_destroy_cb), + rule); + g_object_unref (connection); + } + + if (rule->recipients == NULL ) { + ibus_object_destroy (IBUS_OBJECT(rule)); + } + return; + } + } + + g_warning ("Remove recipient failed"); +} + +gboolean +bus_match_rule_get_recipients (BusMatchRule *rule, + DBusMessage *message, + GList **recipients) +{ + g_assert (BUS_IS_MATCH_RULE (rule)); + g_assert (message != NULL); + g_assert (recipients != NULL); + + GList *link; + + if (!bus_match_rule_match (rule, message)) + return FALSE; + + for (link = rule->recipients; link != NULL; link = link->next) { + BusRecipient *recipient = (BusRecipient *) link->data; + + g_object_ref (recipient->connection); + *recipients = g_list_append (*recipients, recipient->connection); + } + + return TRUE; +} + diff --git a/bus/matchrule.h b/bus/matchrule.h new file mode 100644 index 0000000..e8aaf39 --- /dev/null +++ b/bus/matchrule.h @@ -0,0 +1,124 @@ +/* vim:set et sts=4: */ +/* IBus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ +#ifndef __MATCH_RULE_H_ +#define __MATCH_RULE_H_ + +#include <ibus.h> +#include "connection.h" + +/* + * Type macros. + */ + +/* define GOBJECT macros */ +#define BUS_TYPE_MATCH_RULE \ + (bus_match_rule_get_type ()) +#define BUS_MATCH_RULE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUS_TYPE_MATCH_RULE, BusMatchRule)) +#define BUS_MATCH_RULE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), BUS_TYPE_MATCH_RULE, BusMatchRuleClass)) +#define BUS_IS_MATCH_RULE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUS_TYPE_MATCH_RULE)) +#define BUS_IS_MATCH_RULE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), BUS_TYPE_MATCH_RULE)) +#define BUS_MATCH_RULE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), BUS_TYPE_MATCH_RULE, BusMatchRuleClass)) + +G_BEGIN_DECLS + +typedef struct _BusMatchRule BusMatchRule; +typedef struct _BusMatchRuleClass BusMatchRuleClass; + +typedef enum { + MATCH_TYPE = 1 << 0, + MATCH_INTERFACE = 1 << 1, + MATCH_MEMBER = 1 << 2, + MATCH_SENDER = 1 << 3, + MATCH_DESTINATION = 1 << 4, + MATCH_PATH = 1 << 5, + MATCH_ARGS = 1 << 6, +} BusMatchFlags; + +typedef struct _BusRecipient BusRecipient; +struct _BusRecipient { + BusConnection *connection; + gint refcount; +}; + +struct _BusMatchRule { + IBusProxy parent; + /* instance members */ + gint flags; + gint message_type; + gchar *interface; + gchar *member; + gchar *sender; + gchar *destination; + gchar *path; + GArray *args; + GList *recipients; +}; + +struct _BusMatchRuleClass { + IBusProxyClass parent; + /* class members */ +}; + +GType bus_match_rule_get_type (void); +BusMatchRule *bus_match_rule_new (const gchar *text); +BusMatchRule *bus_match_rule_ref (BusMatchRule *rule); +void bus_match_rule_unref (BusMatchRule *rule); +void bus_match_rule_free (BusMatchRule *rule); +gboolean bus_match_rule_set_message_type + (BusMatchRule *rule, + gint type); +gboolean bus_match_rule_set_sender (BusMatchRule *rule, + const gchar *sender); +gboolean bus_match_rule_set_interface + (BusMatchRule *rule, + const gchar *interface); +gboolean bus_match_rule_set_member (BusMatchRule *rule, + const gchar *member); +gboolean bus_match_rule_set_path (BusMatchRule *rule, + const gchar *path); +gboolean bus_match_rule_set_destination + (BusMatchRule *rule, + const gchar *dest); +gboolean bus_match_rule_set_arg (BusMatchRule *rule, + guint arg_i, + const gchar *arg); +gboolean bus_match_rule_match (BusMatchRule *rule, + DBusMessage *message); +gboolean bus_match_rule_is_equal (BusMatchRule *a, + BusMatchRule *b); +void bus_match_rule_add_recipient + (BusMatchRule *rule, + BusConnection *connection); +void bus_match_rule_remove_recipient + (BusMatchRule *rule, + BusConnection *connection); +gboolean bus_match_rule_get_recipients + (BusMatchRule *rule, + DBusMessage *message, + GList **recipients); + +G_END_DECLS +#endif + diff --git a/bus/panelproxy.c b/bus/panelproxy.c new file mode 100644 index 0000000..7d99f67 --- /dev/null +++ b/bus/panelproxy.c @@ -0,0 +1,757 @@ +/* vim:set et sts=4: */ +/* ibus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ + +#include <ibusinternal.h> +#include <ibusmarshalers.h> +#include "panelproxy.h" + +#define BUS_PANEL_PROXY_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), BUS_TYPE_PANEL_PROXY, BusPanelProxyPrivate)) + +enum { + PAGE_UP, + PAGE_DOWN, + CURSOR_UP, + CURSOR_DOWN, + PROPERTY_ACTIVATE, + PROPERTY_SHOW, + PROPERTY_HIDE, + LAST_SIGNAL, +}; + + +/* BusPanelProxyPriv */ +struct _BusPanelProxyPrivate { + BusInputContext *focused_context; +}; +typedef struct _BusPanelProxyPrivate BusPanelProxyPrivate; + +static guint panel_signals[LAST_SIGNAL] = { 0 }; +// static guint engine_signals[LAST_SIGNAL] = { 0 }; + +/* functions prototype */ +static void bus_panel_proxy_class_init (BusPanelProxyClass *klass); +static void bus_panel_proxy_init (BusPanelProxy *panel); +static void bus_panel_proxy_real_destroy (BusPanelProxy *panel); + +static gboolean bus_panel_proxy_ibus_signal (IBusProxy *proxy, + IBusMessage *message); +static void bus_panel_proxy_page_up (BusPanelProxy *panel); +static void bus_panel_proxy_page_down (BusPanelProxy *panel); +static void bus_panel_proxy_cursor_up (BusPanelProxy *panel); +static void bus_panel_proxy_cursor_down (BusPanelProxy *panel); +static void bus_panel_proxy_property_activate + (BusPanelProxy *panel, + const gchar *prop_name, + gint prop_state); + +static IBusProxyClass *parent_class = NULL; + +struct _SignalCallbackTable { + gchar *name; + GCallback callback; +} ; + +static const struct _SignalCallbackTable __signals[]; + +GType +bus_panel_proxy_get_type (void) +{ + static GType type = 0; + + static const GTypeInfo type_info = { + sizeof (BusPanelProxyClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) bus_panel_proxy_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (BusPanelProxy), + 0, + (GInstanceInitFunc) bus_panel_proxy_init, + }; + + if (type == 0) { + type = g_type_register_static (IBUS_TYPE_PROXY, + "BusPanelProxy", + &type_info, + (GTypeFlags)0); + } + return type; +} + +BusPanelProxy * +bus_panel_proxy_new (BusConnection *connection) +{ + g_assert (BUS_IS_CONNECTION (connection)); + + GObject *obj; + obj = g_object_new (BUS_TYPE_PANEL_PROXY, + "name", NULL, + "path", IBUS_PATH_PANEL, + "connection", connection, + NULL); + + return BUS_PANEL_PROXY (obj); +} + +static void +bus_panel_proxy_class_init (BusPanelProxyClass *klass) +{ + IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass); + IBusProxyClass *proxy_class = IBUS_PROXY_CLASS (klass); + + + parent_class = (IBusProxyClass *) g_type_class_peek_parent (klass); + + g_type_class_add_private (klass, sizeof (BusPanelProxyPrivate)); + + klass->page_up = bus_panel_proxy_page_up; + klass->page_down = bus_panel_proxy_page_down; + klass->cursor_up = bus_panel_proxy_cursor_up; + klass->cursor_down = bus_panel_proxy_cursor_down; + + klass->property_activate = bus_panel_proxy_property_activate; + + ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_panel_proxy_real_destroy; + + proxy_class->ibus_signal = bus_panel_proxy_ibus_signal; + + /* install signals */ + panel_signals[PAGE_UP] = + g_signal_new (I_("page-up"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(BusPanelProxyClass, page_up), + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + panel_signals[PAGE_DOWN] = + g_signal_new (I_("page-down"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(BusPanelProxyClass, page_down), + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + panel_signals[CURSOR_UP] = + g_signal_new (I_("cursor-up"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(BusPanelProxyClass, cursor_up), + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + panel_signals[CURSOR_DOWN] = + g_signal_new (I_("cursor-down"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(BusPanelProxyClass, cursor_down), + NULL, NULL, + ibus_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + panel_signals[PROPERTY_ACTIVATE] = + g_signal_new (I_("property-activate"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(BusPanelProxyClass, property_activate), + NULL, NULL, + ibus_marshal_VOID__STRING_INT, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_INT); + + panel_signals[PROPERTY_SHOW] = + g_signal_new (I_("property-show"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + + panel_signals[PROPERTY_HIDE] = + g_signal_new (I_("property-hide"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + ibus_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + +} + +static void +bus_panel_proxy_init (BusPanelProxy *panel) +{ + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + priv->focused_context = NULL; +} + +static void +bus_panel_proxy_real_destroy (BusPanelProxy *panel) +{ + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + if (ibus_proxy_get_connection ((IBusProxy *)panel) != NULL) { + ibus_proxy_call ((IBusProxy *) panel, + "Destroy", + DBUS_TYPE_INVALID); + } + + if (priv->focused_context) { + bus_panel_proxy_focus_out (panel, priv->focused_context); + priv->focused_context = NULL; + } + + IBUS_OBJECT_CLASS(parent_class)->destroy (IBUS_OBJECT (panel)); +} + +static gboolean +bus_panel_proxy_ibus_signal (IBusProxy *proxy, + IBusMessage *message) +{ + g_assert (BUS_IS_PANEL_PROXY (proxy)); + g_assert (message != NULL); + + BusPanelProxy *panel; + IBusError *error; + gint i; + + static const struct { + const gchar *member; + const guint signal_id; + } signals [] = { + { "PageUp", PAGE_UP }, + { "PageDown", PAGE_DOWN }, + { "CursorUp", CURSOR_UP }, + { "CursorDown", CURSOR_DOWN }, + { NULL, 0}, + }; + + panel = BUS_PANEL_PROXY (proxy); + + for (i = 0; ; i++) { + if (signals[i].member == NULL) + break; + if (ibus_message_is_signal (message, IBUS_INTERFACE_PANEL, signals[i].member)) { + g_signal_emit (panel, panel_signals[signals[i].signal_id], 0); + goto handled; + } + } + + if (ibus_message_is_signal (message, IBUS_INTERFACE_PANEL, "PropertyActivate")) { + gchar *prop_name; + gint prop_state; + gboolean retval; + + retval = ibus_message_get_args (message, + &error, + G_TYPE_STRING, &prop_name, + G_TYPE_INT, &prop_state, + G_TYPE_INVALID); + if (!retval) + goto failed; + + g_signal_emit (panel, panel_signals[PROPERTY_ACTIVATE], 0, prop_name, prop_state); + } + else if (ibus_message_is_signal (message, IBUS_INTERFACE_PANEL, "PropertyShow")) { + gchar *prop_name; + gboolean retval; + + retval = ibus_message_get_args (message, + &error, + G_TYPE_STRING, &prop_name, + G_TYPE_INVALID); + if (!retval) + goto failed; + g_signal_emit (panel, panel_signals[PROPERTY_SHOW], 0, prop_name); + } + else if (ibus_message_is_signal (message, IBUS_INTERFACE_PANEL, "PropertyHide")) { + gchar *prop_name; + gboolean retval; + + retval = ibus_message_get_args (message, + &error, + G_TYPE_STRING, &prop_name, + G_TYPE_INVALID); + if (!retval) + goto failed; + g_signal_emit (panel, panel_signals[PROPERTY_HIDE], 0, prop_name); + } + +handled: + g_signal_stop_emission_by_name (panel, "ibus-signal"); + return TRUE; + +failed: + g_warning ("%s: %s", error->name, error->message); + ibus_error_free (error); + return FALSE; +} + + +void +bus_panel_proxy_focus_in (BusPanelProxy *panel, + BusInputContext *context) +{ + g_assert (BUS_IS_PANEL_PROXY (panel)); + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + if (priv->focused_context == context) + return; + + if (priv->focused_context != NULL) + bus_panel_proxy_focus_out (panel, priv->focused_context); + + g_object_ref (context); + priv->focused_context = context; + + const gchar *path = ibus_service_get_path ((IBusService *)context); + + ibus_proxy_call ((IBusProxy *) panel, + "FocusIn", + IBUS_TYPE_OBJECT_PATH, &path, + G_TYPE_INVALID); + + /* install signal handlers */ + gint i; + for (i = 0; __signals[i].name != NULL; i++) { + g_signal_connect (context, + __signals[i].name, + __signals[i].callback, + panel); + } +} + +void +bus_panel_proxy_focus_out (BusPanelProxy *panel, + BusInputContext *context) +{ + g_assert (BUS_IS_PANEL_PROXY (panel)); + g_assert (BUS_IS_INPUT_CONTEXT (context)); + + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + g_assert (priv->focused_context == context); + + /* uninstall signal handlers */ + gint i; + for (i = 0; __signals[i].name != NULL; i++) { + g_signal_handlers_disconnect_by_func (context, + __signals[i].callback, + panel); + } + + const gchar *path = ibus_service_get_path ((IBusService *)context); + + ibus_proxy_call ((IBusProxy *) panel, + "FocusOut", + IBUS_TYPE_OBJECT_PATH, &path, + G_TYPE_INVALID); + + g_object_unref (priv->focused_context); + priv->focused_context = NULL; +} + +void +bus_panel_proxy_set_cursor_location (BusPanelProxy *panel, + gint x, + gint y, + gint w, + gint h) +{ + g_assert (BUS_IS_PANEL_PROXY (panel)); + + ibus_proxy_call ((IBusProxy *) panel, + "SetCursorLocation", + G_TYPE_INT, &x, + G_TYPE_INT, &y, + G_TYPE_INT, &w, + G_TYPE_INT, &h, + G_TYPE_INVALID); +} + +void +bus_panel_proxy_update_preedit_text (BusPanelProxy *panel, + IBusText *text, + guint cursor_pos, + gboolean visible) +{ + g_assert (BUS_IS_PANEL_PROXY (panel)); + g_assert (text != NULL); + + ibus_proxy_call ((IBusProxy *) panel, + "UpdatePreeditText", + IBUS_TYPE_TEXT, &text, + G_TYPE_UINT, &cursor_pos, + G_TYPE_BOOLEAN, &visible, + G_TYPE_INVALID); +} + +void +bus_panel_proxy_update_auxiliary_text (BusPanelProxy *panel, + IBusText *text, + gboolean visible) +{ + g_assert (BUS_IS_PANEL_PROXY (panel)); + g_assert (text != NULL); + + ibus_proxy_call ((IBusProxy *) panel, + "UpdateAuxiliaryText", + IBUS_TYPE_TEXT, &text, + G_TYPE_BOOLEAN, &visible, + G_TYPE_INVALID); +} + +void +bus_panel_proxy_update_lookup_table (BusPanelProxy *panel, + IBusLookupTable *table, + gboolean visible) +{ + g_assert (BUS_IS_PANEL_PROXY (panel)); + g_assert (table != NULL); + + ibus_proxy_call ((IBusProxy *) panel, + "UpdateLookupTable", + IBUS_TYPE_LOOKUP_TABLE, &table, + G_TYPE_BOOLEAN, &visible, + G_TYPE_INVALID); +} + +void +bus_panel_proxy_register_properties (BusPanelProxy *panel, + IBusPropList *prop_list) +{ + g_assert (BUS_IS_PANEL_PROXY (panel)); + g_assert (prop_list != NULL); + + ibus_proxy_call ((IBusProxy *) panel, + "RegisterProperties", + IBUS_TYPE_PROP_LIST, &prop_list, + G_TYPE_INVALID); + ibus_connection_flush (ibus_proxy_get_connection((IBusProxy *)panel)); +} + +void +bus_panel_proxy_update_property (BusPanelProxy *panel, + IBusProperty *prop) +{ + g_assert (BUS_IS_PANEL_PROXY (panel)); + g_assert (prop != NULL); + + ibus_proxy_call ((IBusProxy *) panel, + "UpdateProperty", + IBUS_TYPE_PROPERTY, &prop, + G_TYPE_INVALID); +} + +static void +bus_panel_proxy_page_up (BusPanelProxy *panel) +{ + g_assert (BUS_IS_PANEL_PROXY (panel)); + + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + if (priv->focused_context) { + bus_input_context_page_up (priv->focused_context); + } +} + +static void +bus_panel_proxy_page_down (BusPanelProxy *panel) +{ + g_assert (BUS_IS_PANEL_PROXY (panel)); + + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + if (priv->focused_context) { + bus_input_context_page_down (priv->focused_context); + } +} +static void +bus_panel_proxy_cursor_up (BusPanelProxy *panel) +{ + g_assert (BUS_IS_PANEL_PROXY (panel)); + + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + if (priv->focused_context) { + bus_input_context_cursor_up (priv->focused_context); + } +} + +static void +bus_panel_proxy_cursor_down (BusPanelProxy *panel) +{ + g_assert (BUS_IS_PANEL_PROXY (panel)); + + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + if (priv->focused_context) { + bus_input_context_cursor_down (priv->focused_context); + } +} + +static void +bus_panel_proxy_property_activate (BusPanelProxy *panel, + const gchar *prop_name, + gint prop_state) +{ + g_assert (BUS_IS_PANEL_PROXY (panel)); + + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + if (priv->focused_context) { + bus_input_context_property_activate (priv->focused_context, prop_name, prop_state); + } +} + +#define DEFINE_FUNCTION(Name, name) \ + void bus_panel_proxy_##name (BusPanelProxy *panel) \ + { \ + g_assert (BUS_IS_PANEL_PROXY (panel)); \ + ibus_proxy_call ((IBusProxy *) panel, \ + #Name, \ + G_TYPE_INVALID); \ + } + +DEFINE_FUNCTION (ShowPreeditText, show_preedit_text) +DEFINE_FUNCTION (HidePreeditText, hide_preedit_text) +DEFINE_FUNCTION (ShowAuxiliaryText, show_auxiliary_text) +DEFINE_FUNCTION (HideAuxiliaryText, hide_auxiliary_text) +DEFINE_FUNCTION (ShowLookupTable, show_lookup_table) +DEFINE_FUNCTION (HideLookupTable, hide_lookup_table) +DEFINE_FUNCTION (PageUpLookupTable, page_up_lookup_table) +DEFINE_FUNCTION (PageDownLookupTable, page_down_lookup_table) +DEFINE_FUNCTION (CursorUpLookupTable, cursor_up_lookup_table) +DEFINE_FUNCTION (CursorDownLookupTable, cursor_down_lookup_table) +DEFINE_FUNCTION (StateChanged, state_changed) + +#undef DEFINE_FUNCTION + +static void +_context_set_cursor_location_cb (BusInputContext *context, + gint x, + gint y, + gint w, + gint h, + BusPanelProxy *panel) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (BUS_IS_PANEL_PROXY (panel)); + + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + g_return_if_fail (priv->focused_context == context); + + bus_panel_proxy_set_cursor_location (panel, x, y, w, h); +} + +static void +_context_update_preedit_text_cb (BusInputContext *context, + IBusText *text, + guint cursor_pos, + gboolean visible, + BusPanelProxy *panel) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (text != NULL); + g_assert (BUS_IS_PANEL_PROXY (panel)); + + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + g_return_if_fail (priv->focused_context == context); + + bus_panel_proxy_update_preedit_text (panel, + text, + cursor_pos, + visible); +} + +static void +_context_update_auxiliary_text_cb (BusInputContext *context, + IBusText *text, + gboolean visible, + BusPanelProxy *panel) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (BUS_IS_PANEL_PROXY (panel)); + + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + g_return_if_fail (priv->focused_context == context); + + bus_panel_proxy_update_auxiliary_text (panel, + text, + visible); +} + +static void +_context_update_lookup_table_cb (BusInputContext *context, + IBusLookupTable *table, + gboolean visible, + BusPanelProxy *panel) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (BUS_IS_PANEL_PROXY (panel)); + + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + g_return_if_fail (priv->focused_context == context); + + bus_panel_proxy_update_lookup_table (panel, + table, + visible); +} + +static void +_context_register_properties_cb (BusInputContext *context, + IBusPropList *prop_list, + BusPanelProxy *panel) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (BUS_IS_PANEL_PROXY (panel)); + + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + g_return_if_fail (priv->focused_context == context); + + bus_panel_proxy_register_properties (panel, + prop_list); +} + +static void +_context_update_property_cb (BusInputContext *context, + IBusProperty *prop, + BusPanelProxy *panel) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (BUS_IS_PANEL_PROXY (panel)); + + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + g_return_if_fail (priv->focused_context == context); + + bus_panel_proxy_update_property (panel, + prop); +} + +#if 0 +static void +_context_destroy_cb (BusInputContext *context, + BusPanelProxy *panel) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (BUS_IS_PANEL_PROXY (panel)); + + BusPanelProxyPrivate *priv; + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); + + g_assert (context == priv->focused_context); + + bus_panel_proxy_focus_out (panel, context); +} +#endif + +#define DEFINE_FUNCTION(name) \ + static void _context_##name##_cb (BusInputContext *context, \ + BusPanelProxy *panel) \ + { \ + g_assert (BUS_IS_INPUT_CONTEXT (context)); \ + g_assert (BUS_IS_PANEL_PROXY (panel)); \ + \ + BusPanelProxyPrivate *priv; \ + priv = BUS_PANEL_PROXY_GET_PRIVATE (panel); \ + \ + g_return_if_fail (priv->focused_context == context); \ + \ + bus_panel_proxy_##name (panel); \ + } + +DEFINE_FUNCTION (show_preedit_text) +DEFINE_FUNCTION (hide_preedit_text) +DEFINE_FUNCTION (show_auxiliary_text) +DEFINE_FUNCTION (hide_auxiliary_text) +DEFINE_FUNCTION (show_lookup_table) +DEFINE_FUNCTION (hide_lookup_table) +DEFINE_FUNCTION (page_up_lookup_table) +DEFINE_FUNCTION (page_down_lookup_table) +DEFINE_FUNCTION (cursor_up_lookup_table) +DEFINE_FUNCTION (cursor_down_lookup_table) +DEFINE_FUNCTION (state_changed) + +#undef DEFINE_FUNCTION + +static const struct _SignalCallbackTable +__signals[] = { + { "set-cursor-location", G_CALLBACK (_context_set_cursor_location_cb) }, + + { "update-preedit-text", G_CALLBACK (_context_update_preedit_text_cb) }, + { "show-preedit-text", G_CALLBACK (_context_show_preedit_text_cb) }, + { "hide-preedit-text", G_CALLBACK (_context_hide_preedit_text_cb) }, + + { "update-auxiliary-text", G_CALLBACK (_context_update_auxiliary_text_cb) }, + { "show-auxiliary-text", G_CALLBACK (_context_show_auxiliary_text_cb) }, + { "hide-auxiliary-text", G_CALLBACK (_context_hide_auxiliary_text_cb) }, + + { "update-lookup-table", G_CALLBACK (_context_update_lookup_table_cb) }, + { "show-lookup-table", G_CALLBACK (_context_show_lookup_table_cb) }, + { "hide-lookup-table", G_CALLBACK (_context_hide_lookup_table_cb) }, + { "page-up-lookup-table", G_CALLBACK (_context_page_up_lookup_table_cb) }, + { "page-down-lookup-table", G_CALLBACK (_context_page_down_lookup_table_cb) }, + { "cursor-up-lookup-table", G_CALLBACK (_context_cursor_up_lookup_table_cb) }, + { "cursor-down-lookup-table", G_CALLBACK (_context_cursor_down_lookup_table_cb) }, + + { "register-properties", G_CALLBACK (_context_register_properties_cb) }, + { "update-property", G_CALLBACK (_context_update_property_cb) }, + + { "enabled", G_CALLBACK (_context_state_changed_cb) }, + { "disabled", G_CALLBACK (_context_state_changed_cb) }, + { "factory-changed", G_CALLBACK (_context_state_changed_cb) }, + + // { "destroy", G_CALLBACK (_context_destroy_cb) }, + { NULL, NULL} +}; + diff --git a/bus/panelproxy.h b/bus/panelproxy.h new file mode 100644 index 0000000..0ecc4a7 --- /dev/null +++ b/bus/panelproxy.h @@ -0,0 +1,112 @@ +/* vim:set et sts=4: */ +/* ibus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ +#ifndef __PANEL_PROXY_H_ +#define __PANEL_PROXY_H_ + +#include <ibus.h> +#include "connection.h" +#include "inputcontext.h" + +/* + * Type macros. + */ + +/* define GOBJECT macros */ +#define BUS_TYPE_PANEL_PROXY \ + (bus_panel_proxy_get_type ()) +#define BUS_PANEL_PROXY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUS_TYPE_PANEL_PROXY, BusPanelProxy)) +#define BUS_PANEL_PROXY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), BUS_TYPE_PANEL_PROXY, BusPanelProxyClass)) +#define BUS_IS_PANEL_PROXY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUS_TYPE_PANEL_PROXY)) +#define BUS_IS_PANEL_PROXY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), BUS_TYPE_PANEL_PROXY)) +#define BUS_PANEL_PROXY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), BUS_TYPE_PANEL_PROXY, BusPanelProxyClass)) + +G_BEGIN_DECLS + +typedef struct _BusPanelProxy BusPanelProxy; +typedef struct _BusPanelProxyClass BusPanelProxyClass; + +struct _BusPanelProxy { + IBusProxy parent; + /* instance members */ +}; + +struct _BusPanelProxyClass { + IBusProxyClass parent; + /* class members */ + + void (* page_up) (BusPanelProxy *panel); + void (* page_down) (BusPanelProxy *panel); + void (* cursor_up) (BusPanelProxy *panel); + void (* cursor_down) (BusPanelProxy *panel); + + void (* property_activate) (BusPanelProxy *panel, + const gchar *prop_name, + gint prop_state); +}; + +GType bus_panel_proxy_get_type (void); +BusPanelProxy *bus_panel_proxy_new (BusConnection *connection); +void bus_panel_proxy_focus_in (BusPanelProxy *panel, + BusInputContext *context); +void bus_panel_proxy_focus_out (BusPanelProxy *panel, + BusInputContext *context); +void bus_panel_proxy_set_cursor_location + (BusPanelProxy *panel, + gint32 x, + gint32 y, + gint32 w, + gint32 h); +void bus_panel_proxy_update_preedit_text(BusPanelProxy *panel, + IBusText *text, + guint cursor_pos, + gboolean visible); +void bus_panel_proxy_show_preedit_text (BusPanelProxy *panel); +void bus_panel_proxy_hide_preedit_text (BusPanelProxy *panel); +void bus_panel_proxy_update_auxiliary_text + (BusPanelProxy *panel, + IBusText *text, + gboolean visible); +void bus_panel_proxy_show_auxiliary_text(BusPanelProxy *panel); +void bus_panel_proxy_hide_auxiliary_text(BusPanelProxy *panel); +void bus_panel_proxy_update_lookup_table(BusPanelProxy *panel, + IBusLookupTable *table, + gboolean visible); +void bus_panel_proxy_show_lookup_table (BusPanelProxy *panel); +void bus_panel_proxy_hide_lookup_table (BusPanelProxy *panel); +void bus_panel_proxy_page_up_lookup_table + (BusPanelProxy *panel); +void bus_panel_proxy_page_down_lookup_table + (BusPanelProxy *panel); +void bus_panel_proxy_cursor_up_lookup_table + (BusPanelProxy *panel); +void bus_panel_proxy_cursor_down_lookup_table + (BusPanelProxy *panel); +void bus_panel_proxy_register_properties(BusPanelProxy *panel, + IBusPropList *prop_list); +void bus_panel_proxy_update_property (BusPanelProxy *panel, + IBusProperty *prop); +G_END_DECLS +#endif + diff --git a/bus/registry.c b/bus/registry.c new file mode 100644 index 0000000..cdc1aa7 --- /dev/null +++ b/bus/registry.c @@ -0,0 +1,447 @@ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ +#include <glib/gstdio.h> +#include <gio/gio.h> +#include <stdlib.h> +#include "registry.h" + +enum { + LAST_SIGNAL, +}; + +// static guint _signals[LAST_SIGNAL] = { 0 }; + +/* functions prototype */ +static void bus_registry_class_init (BusRegistryClass *klass); +static void bus_registry_init (BusRegistry *registry); +static void bus_registry_destroy (BusRegistry *registry); +static void bus_registry_load (BusRegistry *registry); +static void bus_registry_load_in_dir (BusRegistry *registry, + const gchar *dirname); +static gboolean bus_registry_save_cache (BusRegistry *registry); +static gboolean bus_registry_load_cache (BusRegistry *registry); +static gboolean bus_registry_check_modification(BusRegistry *registry); +static void bus_registry_remove_all (BusRegistry *registry); + +static IBusObjectClass *parent_class = NULL; + +GType +bus_registry_get_type (void) +{ + static GType type = 0; + + static const GTypeInfo type_info = { + sizeof (BusRegistryClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) bus_registry_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (BusRegistry), + 0, + (GInstanceInitFunc) bus_registry_init, + }; + + if (type == 0) { + type = g_type_register_static (IBUS_TYPE_OBJECT, + "BusRegistry", + &type_info, + (GTypeFlags)0); + } + + return type; +} + + +static void +bus_registry_class_init (BusRegistryClass *klass) +{ + IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass); + + parent_class = (IBusObjectClass *) g_type_class_peek_parent (klass); + + ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_registry_destroy; +} + +static void +bus_registry_init (BusRegistry *registry) +{ + GList *p; + registry->observed_paths = NULL; + registry->components = NULL; + registry->engine_table = g_hash_table_new (g_str_hash, g_str_equal); + + if (bus_registry_load_cache (registry) == FALSE || bus_registry_check_modification (registry)) { + bus_registry_remove_all (registry); + bus_registry_load (registry); + bus_registry_save_cache (registry); + } + + for (p = registry->components; p != NULL; p = p->next) { + IBusComponent *comp = (IBusComponent *)p->data; + GList *p1; + + for (p1 = comp->engines; p1 != NULL; p1 = p1->next) { + IBusEngineDesc *desc = (IBusEngineDesc *)p1->data; + g_hash_table_insert (registry->engine_table, desc->name, desc); + g_object_set_data ((GObject *)desc, "component", comp); + } + } +} + +static void +bus_registry_remove_all (BusRegistry *registry) +{ + g_list_foreach (registry->observed_paths, (GFunc) g_object_unref, NULL); + g_list_free (registry->observed_paths); + registry->observed_paths = NULL; + + g_list_foreach (registry->components, (GFunc) g_object_unref, NULL); + g_list_free (registry->components); + registry->components = NULL; + + g_hash_table_remove_all (registry->engine_table); +} + +static void +bus_registry_destroy (BusRegistry *registry) +{ + bus_registry_remove_all (registry); + + g_hash_table_destroy (registry->engine_table); + registry->engine_table = NULL; + + IBUS_OBJECT_CLASS (parent_class)->destroy (IBUS_OBJECT (registry)); +} + + +static void +bus_registry_load (BusRegistry *registry) +{ + g_assert (BUS_IS_REGISTRY (registry)); + + gchar *dirname; + gchar *homedir; + IBusObservedPath *path; + + dirname = g_build_filename (PKGDATADIR, "component", NULL); + + path = ibus_observed_path_new (dirname, TRUE); + registry->observed_paths = g_list_append (registry->observed_paths, path); + + bus_registry_load_in_dir (registry, dirname); + + g_free (dirname); + + homedir = (gchar *) g_getenv ("HOME"); + if (!homedir) + homedir = (gchar *) g_get_home_dir (); + dirname = g_build_filename (homedir, ".ibus", "component", NULL); + + path = ibus_observed_path_new (dirname, TRUE); + registry->observed_paths = g_list_append (registry->observed_paths, path); + + if (g_file_test(dirname, G_FILE_TEST_EXISTS)) { + bus_registry_load_in_dir (registry, dirname); + } + + g_free (dirname); +} + + +#define g_string_append_indent(string, indent) \ + { \ + gint i; \ + for (i = 0; i < (indent); i++) { \ + g_string_append (string, " "); \ + } \ + } + +static gboolean +bus_registry_load_cache (BusRegistry *registry) +{ + g_assert (BUS_IS_REGISTRY (registry)); + + gchar *filename; + XMLNode *node; + GList *p; + + filename = g_build_filename (g_get_user_cache_dir (), "ibus", "registry.xml", NULL); + node = ibus_xml_parse_file (filename); + g_free (filename); + + if (node == NULL) { + return FALSE; + } + + if (g_strcmp0 (node->name, "ibus-registry") != 0) { + ibus_xml_free (node); + return FALSE; + } + + for (p = node->sub_nodes; p != NULL; p = p->next) { + XMLNode *sub_node = (XMLNode *) p->data; + + if (g_strcmp0 (sub_node->name, "observed-paths") == 0) { + GList *pp; + for (pp = sub_node->sub_nodes; pp != NULL; pp = pp->next) { + IBusObservedPath *path; + path = ibus_observed_path_new_from_xml_node (pp->data, FALSE); + if (path) { + registry->observed_paths = g_list_append (registry->observed_paths, path); + } + } + continue; + } + if (g_strcmp0 (sub_node->name, "components") == 0) { + GList *pp; + for (pp = sub_node->sub_nodes; pp != NULL; pp = pp->next) { + IBusComponent *component; + component = ibus_component_new_from_xml_node (pp->data); + if (component) { + registry->components = g_list_append (registry->components, component); + } + } + + continue; + } + g_warning ("Unknown element <%s>", sub_node->name); + } + + ibus_xml_free (node); + return TRUE; +} + +static gboolean +bus_registry_check_modification (BusRegistry *registry) +{ + GList *p; + + for (p = registry->observed_paths; p != NULL; p = p->next) { + if (ibus_observed_path_check_modification ((IBusObservedPath *)p->data)) + return TRUE; + } + + for (p = registry->components; p != NULL; p = p->next) { + if (ibus_component_check_modification ((IBusComponent *)p->data)) + return TRUE; + } + + return FALSE; +} + +static gboolean +bus_registry_save_cache (BusRegistry *registry) +{ + g_assert (BUS_IS_REGISTRY (registry)); + + gchar *cachedir; + gchar *filename; + GString *output; + GList *p; + FILE *pf; + + cachedir = g_build_filename (g_get_user_cache_dir (), "ibus", NULL); + filename = g_build_filename (cachedir, "registry.xml", NULL); + g_mkdir_with_parents (cachedir, 0775); + pf = g_fopen (filename, "w"); + g_free (filename); + g_free (cachedir); + + if (pf == NULL) { + g_warning ("create registry.xml failed"); + return FALSE; + } + + output = g_string_new (""); + g_string_append (output, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); + g_string_append (output, "<!-- \n" + " This file was generated by ibus-daemon. Please don't modify it.\n" + " -->\n"); + g_string_append (output, "<ibus-registry>\n"); + + if (registry->observed_paths) { + g_string_append_indent (output, 1); + g_string_append (output, "<observed-paths>\n"); + for (p = registry->observed_paths; p != NULL; p = p->next) { + ibus_observed_path_output ((IBusObservedPath *)p->data, + output, 2); + } + g_string_append_indent (output, 1); + g_string_append (output, "</observed-paths>\n"); + } + + if (registry->components) { + g_string_append_indent (output, 1); + g_string_append (output, "<components>\n"); + for (p = registry->components; p != NULL; p = p->next) { + ibus_component_output ((IBusComponent *)p->data, + output, 2); + } + g_string_append_indent (output, 1); + g_string_append (output, "</components>\n"); + } + + g_string_append (output, "</ibus-registry>\n"); + fwrite (output->str, output->len, 1, pf); + g_string_free (output, TRUE); + fclose (pf); + return TRUE; +} + +static void +bus_registry_load_in_dir (BusRegistry *registry, + const gchar *dirname) +{ + g_assert (BUS_IS_REGISTRY (registry)); + g_assert (dirname); + + GError *error = NULL; + GDir *dir; + const gchar *filename; + + dir = g_dir_open (dirname, 0, &error); + + if (dir == NULL) { + g_warning ("Unable open directory %s : %s", dirname, error->message); + g_error_free (error); + return; + } + + while ((filename = g_dir_read_name (dir)) != NULL) { + glong size; + gchar *path; + IBusComponent *component; + + size = g_utf8_strlen (filename, -1); + if (g_strcmp0 (MAX (filename, filename + size -4), ".xml" ) != 0) + continue; + + path = g_build_filename (dirname, filename, NULL); + component = ibus_component_new_from_file (path); + registry->components = g_list_append (registry->components, component); + + g_free (path); + } + + g_dir_close (dir); +} + + +BusRegistry * +bus_registry_new (void) +{ + BusRegistry *registry; + registry = (BusRegistry *)g_object_new (BUS_TYPE_REGISTRY, NULL); + return registry; +} + +static gint +_component_is_name (IBusComponent *component, + const gchar *name) +{ + g_assert (IBUS_IS_COMPONENT (component)); + g_assert (name); + + return g_strcmp0 (component->name, name); +} + +IBusComponent * +bus_registry_lookup_component_by_name (BusRegistry *registry, + const gchar *name) +{ + g_assert (BUS_IS_REGISTRY (registry)); + g_assert (name); + + GList *p; + p = g_list_find_custom (registry->components, + name, + (GCompareFunc)_component_is_name); + if (p) { + return (IBusComponent *)p->data; + } + else { + return NULL; + } +} + +GList * +bus_registry_get_components (BusRegistry *registry) +{ + g_assert (BUS_IS_REGISTRY (registry)); + + return g_list_copy (registry->components); +} + +GList * +bus_registry_get_engines (BusRegistry *registry) +{ + g_assert (BUS_IS_REGISTRY (registry)); + + return g_hash_table_get_values (registry->engine_table); +} + + +IBusEngineDesc * +bus_registry_find_engine_by_name (BusRegistry *registry, + const gchar *name) +{ + g_assert (BUS_IS_REGISTRY (registry)); + g_assert (name); + + return (IBusEngineDesc *) g_hash_table_lookup (registry->engine_table, name); +} + + +BusFactoryProxy * +bus_registry_name_owner_changed (BusRegistry *registry, + const gchar *name, + const gchar *old_name, + const gchar *new_name) +{ + g_assert (BUS_IS_REGISTRY (registry)); + g_assert (name); + g_assert (old_name); + g_assert (new_name); + + IBusComponent *component; + BusFactoryProxy *factory; + + component = bus_registry_lookup_component_by_name (registry, name); + + if (component == NULL) { + return NULL; + } + + if (g_strcmp0 (old_name, "") != 0) { + factory = bus_factory_proxy_get_from_component (component); + + if (factory != NULL) { + ibus_object_destroy ((IBusObject *)factory); + } + } + + if (g_strcmp0 (new_name, "") != 0) { + factory = bus_factory_proxy_new (component, NULL); + return factory; + } + + return NULL; +} diff --git a/bus/registry.h b/bus/registry.h new file mode 100644 index 0000000..9dadda6 --- /dev/null +++ b/bus/registry.h @@ -0,0 +1,88 @@ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ +#ifndef __REGISTRY_H_ +#define __REGISTRY_H_ + +#include <ibus.h> +#include "factoryproxy.h" + +/* + * Type macros. + */ + +/* define GOBJECT macros */ +#define BUS_TYPE_REGISTRY \ + (bus_registry_get_type ()) +#define BUS_REGISTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUS_TYPE_REGISTRY, BusRegistry)) +#define BUS_REGISTRY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), BUS_TYPE_REGISTRY, BusRegistryClass)) +#define BUS_IS_REGISTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUS_TYPE_REGISTRY)) +#define BUS_IS_REGISTRY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), BUS_TYPE_REGISTRY)) +#define BUS_REGISTRY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), BUS_TYPE_REGISTRY, BusRegistryClass)) + +G_BEGIN_DECLS + +typedef struct _BusRegistry BusRegistry; +typedef struct _BusRegistryClass BusRegistryClass; + +struct _BusRegistry { + IBusObject parent; + + /* instance members */ + GList *observed_paths; + GList *components; + + GHashTable *engine_table; + GList *active_engines; +}; + +struct _BusRegistryClass { + IBusObjectClass parent; + + /* class members */ +}; + +GType bus_registry_get_type (void); +BusRegistry *bus_registry_new (void); +GList *bus_registry_get_components (BusRegistry *registry); +GList *bus_registry_get_engines (BusRegistry *registry); +gboolean bus_registry_exec_component (BusRegistry *registry, + const gchar *name); +gboolean bus_registry_kill_component (BusRegistry *registry, + const gchar *name); + +IBusComponent *bus_registry_lookup_component_by_name + (BusRegistry *registry, + const gchar *name); +IBusEngineDesc *bus_registry_find_engine_by_name + (BusRegistry *registry, + const gchar *name); +BusFactoryProxy *bus_registry_name_owner_changed(BusRegistry *registry, + const gchar *name, + const gchar *old_name, + const gchar *new_name); + +G_END_DECLS +#endif + diff --git a/bus/server.c b/bus/server.c new file mode 100644 index 0000000..2ae36bf --- /dev/null +++ b/bus/server.c @@ -0,0 +1,152 @@ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ + +#include <sys/stat.h> +#include <sys/types.h> +#include <libgen.h> + +#include "server.h" +#include "connection.h" +#include "dbusimpl.h" +#include "ibusimpl.h" + +/* functions prototype */ +static void bus_server_class_init (BusServerClass *klass); +static void bus_server_init (BusServer *server); +static void bus_server_destroy (BusServer *server); +static void bus_server_new_connection + (BusServer *server, + BusConnection *connection); + +static IBusObjectClass *parent_class = NULL; + +GType +bus_server_get_type (void) +{ + static GType type = 0; + + static const GTypeInfo type_info = { + sizeof (BusServerClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) bus_server_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (BusServer), + 0, + (GInstanceInitFunc) bus_server_init, + }; + + if (type == 0) { + type = g_type_register_static (IBUS_TYPE_SERVER, + "BusServer", + &type_info, + (GTypeFlags)0); + } + + return type; +} + +BusServer * +bus_server_get_default (void) +{ + static BusServer *server = NULL; + + if (server == NULL) { + server = (BusServer *) g_object_new (BUS_TYPE_SERVER, + "connection-type", BUS_TYPE_CONNECTION, + NULL); + bus_dbus_impl_get_default (); + bus_ibus_impl_get_default (); + } + return server; +} + +gboolean +bus_server_listen (BusServer *server) +{ + g_assert (BUS_IS_SERVER (server)); + + // const gchar *address = "unix:abstract=/tmp/ibus-c" + const gchar *address; + gchar *path; + + path = g_strdup_printf("/tmp/ibus-%s", ibus_get_user_name ()); + mkdir (path, 0775); + g_free(path); + address = ibus_get_address (); + + return ibus_server_listen (IBUS_SERVER (server), address); +} + +void +bus_server_run (BusServer *server) +{ + g_assert (BUS_IS_SERVER (server)); + + g_main_loop_run (server->loop); +} + +void +bus_server_quit (BusServer *server) +{ + g_assert (BUS_IS_SERVER (server)); + + g_main_loop_quit (server->loop); +} + +static void +bus_server_class_init (BusServerClass *klass) +{ + IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass); + + parent_class = (IBusObjectClass *) g_type_class_peek_parent (klass); + + ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_server_destroy; + + IBUS_SERVER_CLASS (klass)->new_connection = (IBusNewConnectionFunc) bus_server_new_connection; +} + +static void +bus_server_init (BusServer *server) +{ + server->loop = g_main_loop_new (NULL, FALSE); +} + +static void +bus_server_new_connection (BusServer *server, + BusConnection *connection) +{ + g_assert (BUS_IS_SERVER (server)); + bus_dbus_impl_new_connection (BUS_DEFAULT_DBUS, connection); +} + +static void +bus_server_destroy (BusServer *server) +{ + g_assert (BUS_IS_SERVER (server)); + + while (g_main_loop_is_running (server->loop)) { + g_main_loop_quit (server->loop); + } + g_main_loop_unref (server->loop); + + IBUS_OBJECT_CLASS (parent_class)->destroy (IBUS_OBJECT (server)); +} diff --git a/bus/server.h b/bus/server.h new file mode 100644 index 0000000..9ea5794 --- /dev/null +++ b/bus/server.h @@ -0,0 +1,72 @@ +/* vim:set et sts=4: */ +/* bus - The Input Bus + * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com> + * + * 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. + */ +#ifndef __SERVER_H_ +#define __SERVER_H_ + +#include <ibus.h> + +/* + * Type macros. + */ + +/* define GOBJECT macros */ +#define BUS_TYPE_SERVER \ + (bus_server_get_type ()) +#define BUS_SERVER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUS_TYPE_SERVER, BusServer)) +#define BUS_SERVER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), BUS_TYPE_SERVER, BusServerClass)) +#define BUS_IS_SERVER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUS_TYPE_SERVER)) +#define BUS_IS_SERVER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), BUS_TYPE_SERVER)) +#define BUS_SERVER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), BUS_TYPE_SERVER, BusServerClass)) +#define BUS_DEFAULT_SERVER \ + (bus_server_get_default ()) + +G_BEGIN_DECLS + +typedef struct _BusServer BusServer; +typedef struct _BusServerClass BusServerClass; + +struct _BusServer { + IBusServer parent; + + /* instance members */ + GMainLoop *loop; + +}; + +struct _BusServerClass { + IBusServerClass parent; + + /* class members */ +}; + +GType bus_server_get_type (void); +BusServer *bus_server_get_default (void); +gboolean bus_server_listen (BusServer *server); +void bus_server_run (BusServer *server); +void bus_server_quit (BusServer *server); + +G_END_DECLS +#endif + diff --git a/bus/test-matchrule.c b/bus/test-matchrule.c new file mode 100644 index 0000000..63110fd --- /dev/null +++ b/bus/test-matchrule.c @@ -0,0 +1,40 @@ +#include "matchrule.h" + +int +main(gint argc, gchar **argv) +{ + BusMatchRule *rule, *rule1; + g_type_init (); + + rule = bus_match_rule_new (" type='signal' , interface = 'org.freedesktop.IBus' "); + g_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL); + g_assert (g_strcmp0 (rule->interface, "org.freedesktop.IBus") == 0 ); + g_object_unref (rule); + + rule = bus_match_rule_new ("type='method_call', interface='org.freedesktop.IBus' "); + g_assert (rule->message_type == DBUS_MESSAGE_TYPE_METHOD_CALL); + g_assert (g_strcmp0 (rule->interface, "org.freedesktop.IBus") == 0 ); + g_object_unref (rule); + + rule = bus_match_rule_new ("type='signal'," + "interface='org.freedesktop.DBus'," + "member='NameOwnerChanged'," + "arg0='ibus.freedesktop.IBus.config'," + "arg0='ibus.freedesktop.IBus.config'," + "arg2='ibus.freedesktop.IBus.config'"); + g_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL); + g_assert (g_strcmp0 (rule->interface, "org.freedesktop.DBus") == 0 ); + rule1 = bus_match_rule_new ("type='signal'," + "interface='org.freedesktop.DBus'," + "member='NameOwnerChanged'," + "arg0='ibus.freedesktop.IBus.config'," + "arg0='ibus.freedesktop.IBus.config'," + "arg2='ibus.freedesktop.IBus.config'"); + + g_assert (bus_match_rule_is_equal (rule, rule1)); + + g_object_unref (rule); + g_object_unref (rule1); + + return 0; +} diff --git a/bus/test-registry.c b/bus/test-registry.c new file mode 100644 index 0000000..fdcbba8 --- /dev/null +++ b/bus/test-registry.c @@ -0,0 +1,9 @@ +#include "registry.h" + +int main() +{ + g_type_init (); + BusRegistry *registry = bus_registry_new (); + g_object_unref (registry); + return 0; +} |
