summaryrefslogtreecommitdiffstats
path: root/bus
diff options
context:
space:
mode:
authorHuang Peng <shawn.p.huang@gmail.com>2009-02-05 10:39:56 +0800
committerHuang Peng <shawn.p.huang@gmail.com>2009-02-05 10:39:56 +0800
commitaedad1ea0a7fef604aa27f4b58433fd8f2ece29e (patch)
treeffcb531d8474bde18b90341bcd4eb639edd74525 /bus
parent41ad46305a88637dd99f00a2d2a3f455505d357b (diff)
re-implement ibus in c language.
Diffstat (limited to 'bus')
-rw-r--r--bus/.gitignore2
-rw-r--r--bus/Makefile.am99
-rw-r--r--bus/connection.c261
-rw-r--r--bus/connection.h71
-rw-r--r--bus/dbusimpl.c1174
-rw-r--r--bus/dbusimpl.h87
-rw-r--r--bus/engineproxy.c716
-rw-r--r--bus/engineproxy.h95
-rw-r--r--bus/factoryproxy.c220
-rw-r--r--bus/factoryproxy.h84
-rw-r--r--bus/ibusimpl.c935
-rw-r--r--bus/ibusimpl.h102
-rw-r--r--bus/inputcontext.c1559
-rw-r--r--bus/inputcontext.h82
-rw-r--r--bus/main.c148
-rw-r--r--bus/matchrule.c672
-rw-r--r--bus/matchrule.h124
-rw-r--r--bus/panelproxy.c757
-rw-r--r--bus/panelproxy.h112
-rw-r--r--bus/registry.c447
-rw-r--r--bus/registry.h88
-rw-r--r--bus/server.c152
-rw-r--r--bus/server.h72
-rw-r--r--bus/test-matchrule.c40
-rw-r--r--bus/test-registry.c9
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;
+}