1241 строка
37 KiB
C
1241 строка
37 KiB
C
/* vim:set et ts=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 <config.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <sys/un.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <glib/gstdio.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <gdk/gdkx.h>
|
|
#include <dbus/dbus.h>
|
|
#include <dbus/dbus-glib.h>
|
|
|
|
#ifdef HAVE_SYS_INOTIFY_H
|
|
#define HAVE_INOTIFY
|
|
# include <sys/inotify.h>
|
|
#endif
|
|
|
|
#include "ibusimclient.h"
|
|
|
|
#define IBUS_NAME "org.freedesktop.IBus"
|
|
#define IBUS_IFACE "org.freedesktop.IBus"
|
|
#define IBUS_PATH "/org/freedesktop/IBus"
|
|
|
|
/* IBusIMClientPriv */
|
|
struct _IBusIMClientPrivate {
|
|
#if USE_DBUS_SESSION_BUS
|
|
DBusConnection *dbus;
|
|
#endif
|
|
|
|
#ifdef HAVE_INOTIFY
|
|
/* inotify */
|
|
gint inotify_wd;
|
|
GIOChannel *inotify_channel;
|
|
guint inotify_source;
|
|
#endif
|
|
|
|
DBusConnection *ibus;
|
|
|
|
GHashTable *ic_table;
|
|
GList *contexts;
|
|
};
|
|
|
|
/* functions prototype */
|
|
static void ibus_im_client_class_init (IBusIMClientClass *klass);
|
|
static void ibus_im_client_init (IBusIMClient *client);
|
|
static void ibus_im_client_finalize (GObject *obj);
|
|
|
|
static const gchar *
|
|
_ibus_im_client_create_input_context
|
|
(IBusIMClient *client);
|
|
|
|
static gboolean _ibus_call_with_reply_and_block
|
|
(DBusConnection *connection,
|
|
const gchar *method,
|
|
int first_arg_type,
|
|
...);
|
|
static gboolean _ibus_call_with_reply (DBusConnection *connection,
|
|
const gchar *method,
|
|
DBusPendingCallNotifyFunction
|
|
function,
|
|
void *data,
|
|
DBusFreeFunction free_function,
|
|
int first_arg_type,
|
|
...);
|
|
static gboolean _dbus_call_with_reply_and_block
|
|
(DBusConnection *connection,
|
|
const gchar *dest,
|
|
const gchar *path,
|
|
const gchar *iface,
|
|
const char *method,
|
|
int first_arg_type,
|
|
...);
|
|
static GtkIMContext *
|
|
_ibus_client_ic_to_context (IBusIMClient *client,
|
|
const gchar *ic);
|
|
|
|
/* callback functions */
|
|
static DBusHandlerResult
|
|
_ibus_im_client_message_filter_cb
|
|
(DBusConnection *connection,
|
|
DBusMessage *message,
|
|
void *user_data);
|
|
|
|
static void _dbus_name_owner_changed_cb
|
|
(DBusGProxy *proxy,
|
|
const gchar *name,
|
|
const gchar *old_name,
|
|
const gchar *new_name,
|
|
IBusIMClient *client);
|
|
|
|
static GType ibus_type_im_client = 0;
|
|
static GtkObjectClass *parent_class = NULL;
|
|
|
|
|
|
GType
|
|
ibus_im_client_get_type (void)
|
|
{
|
|
g_assert (ibus_type_im_client != 0);
|
|
return ibus_type_im_client;
|
|
}
|
|
|
|
void
|
|
ibus_im_client_register_type (GTypeModule *type_module)
|
|
{
|
|
static const GTypeInfo ibus_im_client_info = {
|
|
sizeof (IBusIMClientClass),
|
|
(GBaseInitFunc) NULL,
|
|
(GBaseFinalizeFunc) NULL,
|
|
(GClassInitFunc) ibus_im_client_class_init,
|
|
NULL, /* class finialize */
|
|
NULL, /* class data */
|
|
sizeof (IBusIMClient),
|
|
0,
|
|
(GInstanceInitFunc) ibus_im_client_init,
|
|
};
|
|
|
|
if (! ibus_type_im_client ) {
|
|
if (type_module) {
|
|
ibus_type_im_client =
|
|
g_type_module_register_type (type_module,
|
|
GTK_TYPE_OBJECT,
|
|
"IBusIMClient",
|
|
&ibus_im_client_info,
|
|
(GTypeFlags)0);
|
|
}
|
|
else {
|
|
ibus_type_im_client =
|
|
g_type_register_static (GTK_TYPE_OBJECT,
|
|
"IBusIMClient",
|
|
&ibus_im_client_info,
|
|
(GTypeFlags)0);
|
|
}
|
|
}
|
|
}
|
|
|
|
IBusIMClient *
|
|
ibus_im_client_new (void)
|
|
{
|
|
IBusIMClient *client;
|
|
|
|
client = IBUS_IM_CLIENT(g_object_new (IBUS_TYPE_IM_CLIENT, NULL));
|
|
|
|
return client;
|
|
}
|
|
|
|
static void
|
|
ibus_im_client_class_init (IBusIMClientClass *klass)
|
|
{
|
|
GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
parent_class = (GtkObjectClass *) g_type_class_peek_parent (klass);
|
|
|
|
g_type_class_add_private (klass, sizeof (IBusIMClientPrivate));
|
|
|
|
gobject_class->finalize = ibus_im_client_finalize;
|
|
}
|
|
|
|
/*
|
|
* open ibus connection
|
|
*/
|
|
static void
|
|
_ibus_im_client_ibus_open (IBusIMClient *client)
|
|
{
|
|
gchar *ibus_addr = NULL;
|
|
DBusError error;
|
|
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
|
|
if (priv->ibus != NULL)
|
|
return;
|
|
|
|
#if USE_DBUS_SESSION_BUS
|
|
dbus_connection_setup_with_g_main (priv->dbus, NULL);
|
|
if (!_dbus_call_with_reply_and_block (priv->dbus,
|
|
IBUS_NAME, IBUS_PATH, IBUS_IFACE,
|
|
"GetIBusAddress",
|
|
DBUS_TYPE_INVALID,
|
|
DBUS_TYPE_STRING, &ibus_addr,
|
|
DBUS_TYPE_INVALID
|
|
)) {
|
|
g_warning ("Can not get ibus address");
|
|
return;
|
|
}
|
|
#endif
|
|
if (ibus_addr == NULL) {
|
|
gchar *display, *host, *id;
|
|
display = g_strdup (g_getenv ("DISPLAY"));
|
|
if (display != NULL) {
|
|
id = host = display;
|
|
for (; *id != ':' && *id != '\0'; id++);
|
|
if (*id == '\0')
|
|
id = "";
|
|
else {
|
|
*id = '\0';
|
|
id ++;
|
|
}
|
|
}
|
|
else {
|
|
host = id = "";
|
|
}
|
|
ibus_addr = g_strdup_printf ("unix:path=/tmp/ibus-%s/ibus-%s-%s", g_get_user_name (), host, id);
|
|
g_free (display);
|
|
}
|
|
|
|
/*
|
|
* Init ibus and proxy object
|
|
*/
|
|
dbus_error_init (&error);
|
|
priv->ibus = dbus_connection_open_private (ibus_addr, &error);
|
|
g_free (ibus_addr);
|
|
if (priv->ibus == NULL) {
|
|
g_warning ("Error: %s", error.message);
|
|
dbus_error_free (&error);
|
|
return;
|
|
}
|
|
|
|
if (!dbus_connection_add_filter (priv->ibus,
|
|
_ibus_im_client_message_filter_cb,
|
|
client, NULL)) {
|
|
g_warning ("Out of memory");
|
|
return;
|
|
}
|
|
dbus_connection_setup_with_g_main (priv->ibus, NULL);
|
|
|
|
GList *p;
|
|
for (p = priv->contexts; p != NULL; p = g_list_next (p)) {
|
|
IBusIMContext *context = IBUS_IM_CONTEXT (p->data);
|
|
const gchar *ic = _ibus_im_client_create_input_context (client);
|
|
g_hash_table_insert (priv->ic_table, g_strdup (ic), context);
|
|
ibus_im_context_set_ic (context, ic);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* close ibus connection
|
|
*/
|
|
static void
|
|
_ibus_im_client_ibus_close (IBusIMClient *client)
|
|
{
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
|
|
GList *p;
|
|
for (p = priv->contexts; p != NULL; p = g_list_next (p)) {
|
|
IBusIMContext *ctx = IBUS_IM_CONTEXT (p->data);
|
|
ibus_im_context_set_ic (ctx, NULL);
|
|
}
|
|
|
|
g_hash_table_remove_all (priv->ic_table);
|
|
|
|
if (priv->ibus) {
|
|
dbus_connection_close (priv->ibus);
|
|
dbus_connection_unref (priv->ibus);
|
|
priv->ibus = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* create an im context
|
|
*/
|
|
IBusIMContext *
|
|
ibus_im_client_create_im_context (IBusIMClient *client)
|
|
{
|
|
IBusIMContext *context;
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
|
|
context = IBUS_IM_CONTEXT (ibus_im_context_new ());
|
|
priv->contexts = g_list_append (priv->contexts, context);
|
|
|
|
const gchar *ic = _ibus_im_client_create_input_context (client);
|
|
ibus_im_context_set_ic (context, ic);
|
|
if (ic) {
|
|
g_hash_table_insert (priv->ic_table, (gpointer)g_strdup (ic), context);
|
|
}
|
|
return context;
|
|
}
|
|
|
|
/*
|
|
* create a ibus input context
|
|
*/
|
|
static const gchar *
|
|
_ibus_im_client_create_input_context (IBusIMClient *client)
|
|
{
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
|
|
if (priv->ibus == NULL)
|
|
return NULL;
|
|
|
|
const gchar *app_name = g_get_application_name ();
|
|
gchar *ic = NULL;
|
|
_ibus_call_with_reply_and_block (priv->ibus, "CreateInputContext",
|
|
DBUS_TYPE_STRING, &app_name,
|
|
DBUS_TYPE_INVALID,
|
|
DBUS_TYPE_STRING, &ic,
|
|
DBUS_TYPE_INVALID);
|
|
return ic;
|
|
}
|
|
|
|
#ifdef HAVE_INOTIFY
|
|
static gboolean
|
|
_ibus_im_client_inotify_cb (GIOChannel *source, GIOCondition condition, IBusIMClient *client)
|
|
{
|
|
struct inotify_event *p = NULL;
|
|
gchar *name;
|
|
gsize n;
|
|
|
|
if (condition & G_IO_IN == 0)
|
|
return TRUE;
|
|
|
|
p = g_malloc0 (sizeof (struct inotify_event) + 1024);
|
|
|
|
g_io_channel_read_chars (source, (gchar *) p, sizeof (struct inotify_event), &n, NULL);
|
|
g_io_channel_read_chars (source, ((gchar *)p) + sizeof (struct inotify_event), p->len, &n, NULL);
|
|
|
|
name = g_strdup_printf ("ibus-%s", g_getenv ("DISPLAY"));
|
|
for (n = 0; name[n] != 0; n++) {
|
|
if (name[n] != ':')
|
|
continue;
|
|
name[n] = '-';
|
|
break;
|
|
}
|
|
|
|
if (g_strcmp0 (p->name, name) == 0) {
|
|
if (p->mask & IN_CREATE) {
|
|
g_usleep (1000);
|
|
_ibus_im_client_ibus_open (client);
|
|
}
|
|
}
|
|
g_free (name);
|
|
g_free (p);
|
|
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
ibus_im_client_init (IBusIMClient *obj)
|
|
{
|
|
DEBUG_FUNCTION_IN;
|
|
|
|
DBusError error;
|
|
IBusIMClient *client = IBUS_IM_CLIENT (obj);
|
|
IBusIMClientPrivate *priv;
|
|
|
|
gchar *watch_path;
|
|
struct stat stat_buf;
|
|
|
|
#ifdef HAVE_INOTIFY
|
|
gint inotify_fd = inotify_init ();
|
|
#endif
|
|
|
|
priv = G_TYPE_INSTANCE_GET_PRIVATE (client, IBUS_TYPE_IM_CLIENT, IBusIMClientPrivate);
|
|
client->priv = priv;
|
|
|
|
priv->ic_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
|
priv->contexts = NULL;
|
|
|
|
watch_path = g_strdup_printf ("/tmp/ibus-%s", g_get_user_name ());
|
|
|
|
if (g_stat (watch_path, &stat_buf) != 0) {
|
|
g_mkdir (watch_path, 0750);
|
|
}
|
|
|
|
#ifdef HAVE_INOTIFY
|
|
/* init inotify */
|
|
priv->inotify_wd = inotify_add_watch (inotify_fd, watch_path, IN_CREATE | IN_DELETE);
|
|
priv->inotify_channel = g_io_channel_unix_new (inotify_fd);
|
|
g_io_channel_set_close_on_unref (priv->inotify_channel, TRUE);
|
|
priv->inotify_source = g_io_add_watch (priv->inotify_channel,
|
|
G_IO_IN,
|
|
(GIOFunc)_ibus_im_client_inotify_cb,
|
|
(gpointer)client);
|
|
#endif
|
|
g_free (watch_path);
|
|
|
|
#if USE_DBUS_SESSION_BUS
|
|
/*
|
|
* Init dbus
|
|
*/
|
|
dbus_error_init (&error);
|
|
priv->dbus = dbus_bus_get (DBUS_BUS_SESSION, &error);
|
|
if (priv->dbus == NULL) {
|
|
g_warning ("Error: %s", error.message);
|
|
dbus_error_free (&error);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
_ibus_im_client_ibus_open (client);
|
|
|
|
#if USE_DBUS_SESSION_BUS
|
|
if (!dbus_connection_add_filter (priv->dbus,
|
|
_ibus_im_client_message_filter_cb,
|
|
client, NULL)) {
|
|
g_warning ("Out of memory");
|
|
return;
|
|
}
|
|
|
|
gchar *rule =
|
|
"type='signal',"
|
|
"sender='" DBUS_SERVICE_DBUS "',"
|
|
"interface='" DBUS_INTERFACE_DBUS "',"
|
|
"member='NameOwnerChanged',"
|
|
"path='" DBUS_PATH_DBUS "',"
|
|
"arg0='" IBUS_NAME "'";
|
|
|
|
if (!_dbus_call_with_reply_and_block (priv->dbus,
|
|
DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS,
|
|
"AddMatch",
|
|
DBUS_TYPE_STRING, &rule,
|
|
DBUS_TYPE_INVALID,
|
|
DBUS_TYPE_INVALID
|
|
)) {
|
|
g_warning ("Can not get ibus address");
|
|
return;
|
|
}
|
|
#endif
|
|
#if 0
|
|
/* get dbus proxy */
|
|
priv->dbus = dbus_g_proxy_new_for_name (priv->ibus,
|
|
DBUS_SERVICE_DBUS,
|
|
DBUS_PATH_DBUS,
|
|
DBUS_INTERFACE_DBUS);
|
|
g_assert (priv->dbus != NULL);
|
|
|
|
/* connect NameOwnerChanged signal */
|
|
dbus_g_proxy_add_signal (priv->dbus, "NameOwnerChanged",
|
|
G_TYPE_STRING,
|
|
G_TYPE_STRING,
|
|
G_TYPE_STRING,
|
|
G_TYPE_INVALID);
|
|
|
|
dbus_g_proxy_connect_signal (priv->dbus, "NameOwnerChanged",
|
|
G_CALLBACK (_dbus_name_owner_changed_cb),
|
|
(gpointer)client, NULL);
|
|
dbus_bus_add_match ((DBusConnection *)dbus_g_connection_get_connection (priv->ibus),
|
|
"type='signal',"
|
|
"sender='" DBUS_SERVICE_DBUS
|
|
"',interface='" DBUS_INTERFACE_DBUS
|
|
"',path='" DBUS_PATH_DBUS
|
|
"',member='NameOwnerChanged',"
|
|
"arg0='" IBUS_DBUS_SERVICE "'",
|
|
&dbus_error);
|
|
|
|
_ibus_im_client_reinit_imm (client);
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
static void
|
|
ibus_im_client_finalize (GObject *obj)
|
|
{
|
|
DEBUG_FUNCTION_IN;
|
|
|
|
IBusIMClient *client = IBUS_IM_CLIENT (obj);
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
|
|
g_assert (client == _client);
|
|
|
|
#ifdef HAVE_INOTIFY
|
|
g_source_remove (priv->inotify_source);
|
|
g_io_channel_unref (priv->inotify_channel);
|
|
#endif
|
|
|
|
#if USE_DBUS_SESSION_BUS
|
|
if (priv->dbus) {
|
|
dbus_connection_unref (priv->dbus);
|
|
}
|
|
#endif
|
|
_ibus_im_client_ibus_close (client);
|
|
|
|
G_OBJECT_CLASS(parent_class)->finalize (obj);
|
|
|
|
_client = NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
ibus_im_client_commit_string (IBusIMClient *client, const gchar *ic, const gchar *string)
|
|
{
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
IBusIMContext *context = g_hash_table_lookup (priv->ic_table, (gpointer)ic);
|
|
|
|
if (context == NULL) {
|
|
g_debug ("Can not find context assocate with ic(%s)", ic);
|
|
return;
|
|
}
|
|
g_signal_emit_by_name (G_OBJECT (context), "commit", string);
|
|
}
|
|
|
|
static void
|
|
ibus_im_client_update_preedit (IBusIMClient *client, const gchar *ic, const gchar *string,
|
|
PangoAttrList *attrs, gint cursor_pos, gboolean visible)
|
|
{
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
IBusIMContext *context = g_hash_table_lookup (priv->ic_table, (gpointer)ic);
|
|
|
|
if (context == NULL) {
|
|
g_debug ("Can not find context assocate with ic(%s)", ic);
|
|
return;
|
|
}
|
|
ibus_im_context_update_preedit (context, string, attrs, cursor_pos, visible);
|
|
}
|
|
|
|
static void
|
|
_ibus_signal_commit_string_handler (DBusConnection *connection, DBusMessage *message, IBusIMClient *client)
|
|
{
|
|
/* Handle CommitString signal */
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
DBusError error = {0};
|
|
gchar *ic = NULL;
|
|
gchar *string = NULL;
|
|
|
|
if (!dbus_message_get_args (message, &error,
|
|
DBUS_TYPE_STRING, &ic,
|
|
DBUS_TYPE_STRING, &string,
|
|
DBUS_TYPE_INVALID)) {
|
|
g_warning ("%s", error.message);
|
|
dbus_error_free (&error);
|
|
}
|
|
else {
|
|
IBusIMContext *context = g_hash_table_lookup (priv->ic_table, (gpointer)ic);
|
|
if (context == NULL) {
|
|
g_debug ("Can not find context assocate with ic(%s)", ic);
|
|
return;
|
|
}
|
|
ibus_im_context_commit_string (context, string);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_ibus_signal_update_preedit_handler (DBusConnection *connection, DBusMessage *message, IBusIMClient *client)
|
|
{
|
|
/* Handle UpdatePreedit signal */
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
DBusError error = {0};
|
|
DBusMessageIter iter, sub_iter;
|
|
int type, sub_type;
|
|
|
|
gchar *ic = NULL;
|
|
gchar *string = NULL;
|
|
PangoAttrList *attrs = NULL;
|
|
int cursor = 0;
|
|
gboolean visible = False;
|
|
|
|
if (!dbus_message_iter_init (message, &iter)) {
|
|
g_warning ("The UpdatePreedit signal does have args!");
|
|
return;
|
|
}
|
|
|
|
type = dbus_message_iter_get_arg_type (&iter);
|
|
if (type != DBUS_TYPE_STRING) {
|
|
g_warning ("The 1st argument of UpdatePreedit signal must be a String");
|
|
return;
|
|
}
|
|
dbus_message_iter_get_basic (&iter, &ic);
|
|
dbus_message_iter_next (&iter);
|
|
|
|
type = dbus_message_iter_get_arg_type (&iter);
|
|
if (type != DBUS_TYPE_STRING) {
|
|
g_warning ("The 2nd argument of UpdatePreedit signal must be a String");
|
|
return;
|
|
}
|
|
dbus_message_iter_get_basic (&iter, &string);
|
|
dbus_message_iter_next (&iter);
|
|
|
|
|
|
type = dbus_message_iter_get_arg_type (&iter);
|
|
if (type != DBUS_TYPE_ARRAY) {
|
|
g_warning ("The 3rd argument of UpdatePreedit signal must be a Struct Array");
|
|
return;
|
|
}
|
|
|
|
dbus_message_iter_recurse (&iter, &sub_iter);
|
|
|
|
if (dbus_message_iter_get_arg_type (&sub_iter) != DBUS_TYPE_INVALID) {
|
|
if (dbus_message_iter_get_arg_type (&sub_iter) != DBUS_TYPE_ARRAY ||
|
|
dbus_message_iter_get_element_type (&sub_iter) != DBUS_TYPE_UINT32 ) {
|
|
g_warning ("The 3rd argument of UpdatePreedit signal must be a Struct Array");
|
|
return;
|
|
}
|
|
|
|
attrs = pango_attr_list_new ();
|
|
|
|
while ((sub_type = dbus_message_iter_get_arg_type (&sub_iter) != DBUS_TYPE_INVALID)) {
|
|
PangoAttribute *attr;
|
|
DBusMessageIter sub_sub_iter;
|
|
guint *values = NULL;
|
|
gint length = 0;
|
|
dbus_message_iter_recurse (&sub_iter, &sub_sub_iter);
|
|
dbus_message_iter_get_fixed_array (&sub_sub_iter, &values, &length);
|
|
|
|
if (length <= 0) {
|
|
g_warning ("The element of the 3rd argument of UpdatePreedit should not be a empty array");
|
|
continue;
|
|
}
|
|
|
|
switch (values[0]) {
|
|
case 1: /* Underline */
|
|
attr = pango_attr_underline_new (values[1]);
|
|
attr->start_index = g_utf8_offset_to_pointer (string, values[2]) - string;
|
|
attr->end_index = g_utf8_offset_to_pointer (string, values[3]) - string;
|
|
pango_attr_list_insert (attrs, attr);
|
|
break;
|
|
|
|
case 2: /* Foreground Color */
|
|
attr = pango_attr_foreground_new (
|
|
(values[1] & 0xff0000) >> 8,
|
|
(values[1] & 0x00ff00),
|
|
(values[1] & 0x0000ff) << 8
|
|
);
|
|
attr->start_index = g_utf8_offset_to_pointer (string, values[2]) - string;
|
|
attr->end_index = g_utf8_offset_to_pointer (string, values[3]) - string;
|
|
pango_attr_list_insert (attrs, attr);
|
|
break;
|
|
case 3: /* Background Color */
|
|
attr = pango_attr_background_new (
|
|
(values[1] & 0xff0000) >> 8,
|
|
(values[1] & 0x00ff00),
|
|
(values[1] & 0x0000ff) << 8
|
|
);
|
|
attr->start_index = g_utf8_offset_to_pointer (string, values[2]) - string;
|
|
attr->end_index = g_utf8_offset_to_pointer (string, values[3]) - string;
|
|
pango_attr_list_insert (attrs, attr);
|
|
break;
|
|
default:
|
|
g_warning ("Unkown type attribute type = %d", values[0]);
|
|
|
|
}
|
|
|
|
dbus_message_iter_next (&sub_iter);
|
|
|
|
}
|
|
}
|
|
dbus_message_iter_next (&iter);
|
|
|
|
type = dbus_message_iter_get_arg_type (&iter);
|
|
if (type != DBUS_TYPE_INT32) {
|
|
g_warning ("The 4th argument of UpdatePreedit signal must be an Int32 %c", type);
|
|
pango_attr_list_unref (attrs);
|
|
return;
|
|
}
|
|
dbus_message_iter_get_basic (&iter, &cursor);
|
|
dbus_message_iter_next (&iter);
|
|
|
|
type = dbus_message_iter_get_arg_type (&iter);
|
|
if (type != DBUS_TYPE_BOOLEAN) {
|
|
g_warning ("The 4th argument of UpdatePreedit signal must be an Int32 %c", type);
|
|
pango_attr_list_unref (attrs);
|
|
return;
|
|
}
|
|
dbus_message_iter_get_basic (&iter, &visible);
|
|
dbus_message_iter_next (&iter);
|
|
|
|
{
|
|
IBusIMContext *context = g_hash_table_lookup (priv->ic_table, (gpointer)ic);
|
|
if (context == NULL) {
|
|
g_debug ("Can not find context assocate with ic(%s)", ic);
|
|
return;
|
|
}
|
|
ibus_im_context_update_preedit (context, string, attrs, cursor, visible);
|
|
}
|
|
pango_attr_list_unref (attrs);
|
|
|
|
}
|
|
|
|
static void
|
|
_ibus_signal_show_preedit_handler (DBusConnection *connection, DBusMessage *message, IBusIMClient *client)
|
|
{
|
|
/* Handle CommitString signal */
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
DBusError error = {0};
|
|
gchar *ic = NULL;
|
|
|
|
if (!dbus_message_get_args (message, &error,
|
|
DBUS_TYPE_STRING, &ic,
|
|
DBUS_TYPE_INVALID)) {
|
|
g_warning ("%s", error.message);
|
|
dbus_error_free (&error);
|
|
}
|
|
else {
|
|
IBusIMContext *context = g_hash_table_lookup (priv->ic_table, (gpointer)ic);
|
|
if (context == NULL) {
|
|
g_debug ("Can not find context assocate with ic(%s)", ic);
|
|
return;
|
|
}
|
|
ibus_im_context_show_preedit (context);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_ibus_signal_hide_preedit_handler (DBusConnection *connection, DBusMessage *message, IBusIMClient *client)
|
|
{
|
|
/* Handle CommitString signal */
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
DBusError error = {0};
|
|
gchar *ic = NULL;
|
|
|
|
if (!dbus_message_get_args (message, &error,
|
|
DBUS_TYPE_STRING, &ic,
|
|
DBUS_TYPE_INVALID)) {
|
|
g_warning ("%s", error.message);
|
|
dbus_error_free (&error);
|
|
}
|
|
else {
|
|
IBusIMContext *context = g_hash_table_lookup (priv->ic_table, (gpointer)ic);
|
|
if (context == NULL) {
|
|
g_debug ("Can not find context assocate with ic(%s)", ic);
|
|
return;
|
|
}
|
|
ibus_im_context_hide_preedit (context);
|
|
}
|
|
}
|
|
|
|
#ifdef USE_DBUS_SESSION_BUS
|
|
static void
|
|
_ibus_signal_name_owner_changed_handler (DBusConnection *connection, DBusMessage *message, IBusIMClient *client)
|
|
{
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
|
|
gchar *name = NULL;
|
|
gchar *old_name = NULL;
|
|
gchar *new_name = NULL;
|
|
DBusError error = {0};
|
|
|
|
if (!dbus_message_get_args (message, &error,
|
|
DBUS_TYPE_STRING, &name,
|
|
DBUS_TYPE_STRING, &old_name,
|
|
DBUS_TYPE_STRING, &new_name,
|
|
DBUS_TYPE_INVALID)) {
|
|
g_warning ("%s", error.message);
|
|
dbus_error_free (&error);
|
|
}
|
|
|
|
g_return_if_fail (strcmp (name, IBUS_NAME) == 0);
|
|
|
|
if (g_strcmp0 (new_name, "") == 0) {
|
|
_ibus_im_client_ibus_close (client);
|
|
priv->enable = FALSE;
|
|
}
|
|
else {
|
|
_ibus_im_client_ibus_open (client);
|
|
priv->enable = TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
_ibus_signal_disconnected_handler (DBusConnection *connection, DBusMessage *message, IBusIMClient *client)
|
|
{
|
|
_ibus_im_client_ibus_close (client);
|
|
}
|
|
|
|
static void
|
|
_ibus_signal_enabled_handler (DBusConnection *connection, DBusMessage *message, IBusIMClient *client)
|
|
{
|
|
DEBUG_FUNCTION_IN;
|
|
/* Handle CommitString signal */
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
DBusError error = {0};
|
|
gchar *ic = NULL;
|
|
|
|
if (!dbus_message_get_args (message, &error,
|
|
DBUS_TYPE_STRING, &ic,
|
|
DBUS_TYPE_INVALID)) {
|
|
g_warning ("%s", error.message);
|
|
dbus_error_free (&error);
|
|
}
|
|
else {
|
|
IBusIMContext *context = g_hash_table_lookup (priv->ic_table, (gpointer)ic);
|
|
if (context == NULL) {
|
|
g_debug ("Can not find context assocate with ic(%s)", ic);
|
|
return;
|
|
}
|
|
ibus_im_context_enable (context);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
_ibus_signal_disabled_handler (DBusConnection *connection, DBusMessage *message, IBusIMClient *client)
|
|
{
|
|
DEBUG_FUNCTION_IN;
|
|
/* Handle CommitString signal */
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
DBusError error = {0};
|
|
gchar *ic = NULL;
|
|
|
|
if (!dbus_message_get_args (message, &error,
|
|
DBUS_TYPE_STRING, &ic,
|
|
DBUS_TYPE_INVALID)) {
|
|
g_warning ("%s", error.message);
|
|
dbus_error_free (&error);
|
|
}
|
|
else {
|
|
IBusIMContext *context = g_hash_table_lookup (priv->ic_table, (gpointer)ic);
|
|
if (context == NULL) {
|
|
g_debug ("Can not find context assocate with ic(%s)", ic);
|
|
return;
|
|
}
|
|
ibus_im_context_disable (context);
|
|
}
|
|
}
|
|
|
|
|
|
static DBusHandlerResult
|
|
_ibus_im_client_message_filter_cb (DBusConnection *connection, DBusMessage *message, void *user_data)
|
|
{
|
|
IBusIMClient *client = (IBusIMClient *) user_data;
|
|
|
|
static struct SIGNAL_HANDLER {
|
|
const gchar *iface;
|
|
const gchar *name;
|
|
void (* handler) (DBusConnection *, DBusMessage *, IBusIMClient *);
|
|
} handlers[] = {
|
|
#ifdef USE_DBUS_SESSION_BUS
|
|
{ DBUS_INTERFACE_DBUS, "NameOwnerChanged", _ibus_signal_name_owner_changed_handler },
|
|
#endif
|
|
{ DBUS_INTERFACE_LOCAL, "Disconnected", _ibus_signal_disconnected_handler },
|
|
{ IBUS_IFACE, "CommitString", _ibus_signal_commit_string_handler },
|
|
{ IBUS_IFACE, "UpdatePreedit", _ibus_signal_update_preedit_handler },
|
|
{ IBUS_IFACE, "ShowPreedit", _ibus_signal_show_preedit_handler },
|
|
{ IBUS_IFACE, "HidePreedit", _ibus_signal_hide_preedit_handler },
|
|
{ IBUS_IFACE, "Enabled", _ibus_signal_enabled_handler },
|
|
{ IBUS_IFACE, "Disabled", _ibus_signal_disabled_handler },
|
|
{0},
|
|
};
|
|
|
|
gint i;
|
|
for (i = 0; handlers[i].iface != NULL; i++) {
|
|
if (dbus_message_is_signal (message, handlers[i].iface, handlers[i].name)) {
|
|
handlers[i].handler (connection, message, client);
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
}
|
|
}
|
|
g_debug ("Unknown message %s", dbus_message_get_member (message));
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
}
|
|
|
|
inline static gboolean
|
|
_dbus_call_with_reply_and_block_valist (DBusConnection *connection,
|
|
const gchar *dest, const gchar *path, const gchar* iface, const char *method,
|
|
int first_arg_type, va_list args)
|
|
{
|
|
|
|
DBusMessage *message, *reply;
|
|
DBusError error = {0};
|
|
int type;
|
|
va_list tmp;
|
|
|
|
if (connection == NULL)
|
|
return FALSE;
|
|
|
|
message = dbus_message_new_method_call (dest,
|
|
path, iface, method);
|
|
if (!message) {
|
|
g_warning ("Out of memory!");
|
|
return FALSE;
|
|
}
|
|
|
|
va_copy (tmp, args);
|
|
if (!dbus_message_append_args_valist (message, first_arg_type, tmp)) {
|
|
dbus_message_unref (message);
|
|
g_warning ("Can not create call message");
|
|
return FALSE;
|
|
}
|
|
|
|
reply = dbus_connection_send_with_reply_and_block (connection,
|
|
message, -1, &error);
|
|
|
|
dbus_message_unref (message);
|
|
|
|
if (!reply) {
|
|
g_warning ("%s", error.message);
|
|
dbus_error_free (&error);
|
|
return FALSE;
|
|
}
|
|
|
|
type = first_arg_type;
|
|
while (type != DBUS_TYPE_INVALID) {
|
|
if (type == DBUS_TYPE_ARRAY) {
|
|
va_arg (args, int);
|
|
va_arg (args, void *);
|
|
va_arg (args, int);
|
|
}
|
|
else {
|
|
va_arg (args, void *);
|
|
}
|
|
type = va_arg (args, int);
|
|
}
|
|
|
|
type = va_arg (args, int);
|
|
if (!dbus_message_get_args_valist (reply, &error, type, args)) {
|
|
g_warning ("%s", error.message);
|
|
dbus_error_free (&error);
|
|
dbus_message_unref (reply);
|
|
return FALSE;
|
|
}
|
|
dbus_message_unref (reply);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
inline static gboolean
|
|
_dbus_call_with_reply_and_block (DBusConnection *connection,
|
|
const gchar *dest, const gchar *path, const gchar* iface, const char *method,
|
|
gint first_arg_type, ...)
|
|
{
|
|
va_list args;
|
|
gboolean retval;
|
|
|
|
if (connection == NULL)
|
|
return FALSE;
|
|
|
|
va_start (args, first_arg_type);
|
|
retval = _dbus_call_with_reply_and_block_valist (connection,
|
|
dest, path, iface, method, first_arg_type, args);
|
|
va_end (args);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
static gboolean
|
|
_ibus_call_with_reply_and_block (DBusConnection *connection, const gchar *method, int first_arg_type, ...)
|
|
{
|
|
va_list args;
|
|
gboolean retval;
|
|
|
|
if (connection == NULL)
|
|
return FALSE;
|
|
|
|
va_start (args, first_arg_type);
|
|
retval = _dbus_call_with_reply_and_block_valist (connection,
|
|
IBUS_NAME, IBUS_PATH, IBUS_IFACE, method, first_arg_type, args);
|
|
va_end (args);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
inline static gboolean
|
|
_dbus_call_with_reply_valist (DBusConnection *connection,
|
|
const gchar *dest, const gchar *path, const gchar* iface, const char *method,
|
|
DBusPendingCallNotifyFunction notify_function,
|
|
void *user_data, DBusFreeFunction free_function,
|
|
gint first_arg_type, va_list args)
|
|
{
|
|
DBusMessage *message = NULL;
|
|
DBusPendingCall *pendingcall = NULL;
|
|
DBusError error = {0};
|
|
int type;
|
|
|
|
if (connection == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
message = dbus_message_new_method_call (dest,
|
|
path, iface, method);
|
|
if (!message) {
|
|
g_warning ("Out of memory!");
|
|
goto error;
|
|
}
|
|
|
|
if (!dbus_message_append_args_valist (message, first_arg_type, args)) {
|
|
g_warning ("Can not create call message");
|
|
goto error;
|
|
}
|
|
|
|
if (!dbus_connection_send_with_reply (connection,
|
|
message, &pendingcall, -1)) {
|
|
g_warning ("Out of memory!");
|
|
goto error;
|
|
}
|
|
|
|
if (!dbus_pending_call_set_notify (pendingcall, notify_function,
|
|
user_data, free_function)) {
|
|
g_warning ("Out of memory!");
|
|
goto error;
|
|
}
|
|
|
|
dbus_message_unref (message);
|
|
return TRUE;
|
|
|
|
error:
|
|
if (message)
|
|
dbus_message_unref (message);
|
|
if (pendingcall)
|
|
dbus_pending_call_cancel (pendingcall);
|
|
if (user_data && free_function)
|
|
free_function (user_data);
|
|
return False;
|
|
}
|
|
|
|
inline static gboolean
|
|
_dbus_call_with_reply (DBusConnection *connection,
|
|
const gchar *dest, const gchar *path, const gchar* iface, const char *method,
|
|
DBusPendingCallNotifyFunction notify_function,
|
|
void *user_data, DBusFreeFunction free_function,
|
|
gint first_arg_type, ...)
|
|
{
|
|
va_list args;
|
|
gboolean retval;
|
|
|
|
if (connection == NULL)
|
|
return FALSE;
|
|
|
|
va_start (args, first_arg_type);
|
|
retval = _dbus_call_with_reply_valist (connection,
|
|
dest, path, iface, method,
|
|
notify_function,
|
|
user_data, free_function,
|
|
first_arg_type, args);
|
|
va_end (args);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
_ibus_call_with_reply (DBusConnection *connection, const gchar *method,
|
|
DBusPendingCallNotifyFunction notify_function,
|
|
void *user_data, DBusFreeFunction free_function,
|
|
int first_arg_type, ...)
|
|
{
|
|
va_list args;
|
|
gboolean retval;
|
|
|
|
if (connection == NULL)
|
|
return FALSE;
|
|
|
|
va_start (args, first_arg_type);
|
|
retval = _dbus_call_with_reply_valist (connection,
|
|
IBUS_NAME, IBUS_PATH, IBUS_IFACE,
|
|
method, notify_function,
|
|
user_data, free_function,
|
|
first_arg_type, args);
|
|
va_end (args);
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
static void
|
|
_ibus_filter_keypress_reply_cb (DBusPendingCall *pending, void *user_data)
|
|
{
|
|
DBusMessage *reply;
|
|
DBusError error = {0};
|
|
GdkEvent *event = (GdkEvent *) user_data;
|
|
gboolean retval;
|
|
|
|
|
|
reply = dbus_pending_call_steal_reply (pending);
|
|
dbus_pending_call_unref (pending);
|
|
|
|
if (dbus_set_error_from_message (&error, reply)) {
|
|
g_warning ("%s", error.message);
|
|
dbus_error_free (&error);
|
|
retval = FALSE;
|
|
}
|
|
else {
|
|
if (!dbus_message_get_args (reply, &error,
|
|
DBUS_TYPE_BOOLEAN, &retval, DBUS_TYPE_INVALID)) {
|
|
g_warning ("%s", error.message);
|
|
dbus_error_free (&error);
|
|
retval = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!retval) {
|
|
event->any.send_event = TRUE;
|
|
gdk_event_put (event);
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
ibus_im_client_filter_keypress (IBusIMClient *client, IBusIMContext *context, GdkEventKey *event)
|
|
{
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
gchar *ic = ibus_im_context_get_ic (context);
|
|
|
|
if (ic == NULL)
|
|
return FALSE;
|
|
|
|
guint state = event->state & GDK_MODIFIER_MASK;
|
|
gboolean is_press = event->type == GDK_KEY_PRESS;
|
|
|
|
if (event->send_event) {
|
|
return FALSE;
|
|
}
|
|
|
|
/* Call IBus ProcessKeyEvent method */
|
|
if (!_ibus_call_with_reply (priv->ibus,
|
|
"ProcessKeyEvent",
|
|
_ibus_filter_keypress_reply_cb,
|
|
gdk_event_copy ((GdkEvent *)event),
|
|
(DBusFreeFunction)gdk_event_free,
|
|
DBUS_TYPE_STRING, &ic,
|
|
DBUS_TYPE_UINT32, &event->keyval,
|
|
DBUS_TYPE_BOOLEAN, &is_press,
|
|
DBUS_TYPE_UINT32, &state,
|
|
DBUS_TYPE_INVALID))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
ibus_im_client_focus_in (IBusIMClient *client, IBusIMContext *context)
|
|
{
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
gchar *ic = ibus_im_context_get_ic (context);
|
|
|
|
if (ic == NULL)
|
|
return;
|
|
|
|
/* Call IBus FocusIn method */
|
|
_ibus_call_with_reply_and_block (priv->ibus,
|
|
"FocusIn",
|
|
DBUS_TYPE_STRING, &ic,
|
|
DBUS_TYPE_INVALID,
|
|
DBUS_TYPE_INVALID);
|
|
}
|
|
|
|
void
|
|
ibus_im_client_focus_out (IBusIMClient *client, IBusIMContext *context)
|
|
{
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
gchar *ic = ibus_im_context_get_ic (context);
|
|
|
|
if (ic == NULL)
|
|
return;
|
|
|
|
/* Call IBus FocusOut method */
|
|
_ibus_call_with_reply_and_block (priv->ibus,
|
|
"FocusOut",
|
|
DBUS_TYPE_STRING, &ic,
|
|
DBUS_TYPE_INVALID,
|
|
DBUS_TYPE_INVALID);
|
|
|
|
}
|
|
|
|
void
|
|
ibus_im_client_reset (IBusIMClient *client, IBusIMContext *context)
|
|
{
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
gchar *ic = ibus_im_context_get_ic (context);
|
|
|
|
if (ic == NULL)
|
|
return;
|
|
|
|
/* Call IBus Reset method */
|
|
_ibus_call_with_reply_and_block (priv->ibus,
|
|
"Reset",
|
|
DBUS_TYPE_STRING, &ic,
|
|
DBUS_TYPE_INVALID,
|
|
DBUS_TYPE_INVALID);
|
|
|
|
}
|
|
|
|
void
|
|
ibus_im_client_set_cursor_location (IBusIMClient *client, IBusIMContext *context, GdkRectangle *area)
|
|
{
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
gchar *ic = ibus_im_context_get_ic (context);
|
|
|
|
if (ic == NULL)
|
|
return;
|
|
|
|
_ibus_call_with_reply_and_block (client->priv->ibus,
|
|
"SetCursorLocation",
|
|
DBUS_TYPE_STRING, &ic,
|
|
DBUS_TYPE_INT32, &area->x,
|
|
DBUS_TYPE_INT32, &area->y,
|
|
DBUS_TYPE_INT32, &area->width,
|
|
DBUS_TYPE_INT32, &area->height,
|
|
DBUS_TYPE_INVALID,
|
|
DBUS_TYPE_INVALID);
|
|
}
|
|
|
|
void
|
|
ibus_im_client_set_use_preedit (IBusIMClient *client, IBusIMContext *context, gboolean use_preedit)
|
|
{
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
gchar *ic = ibus_im_context_get_ic (context);
|
|
|
|
if (ic == NULL)
|
|
return;
|
|
|
|
_ibus_call_with_reply_and_block (client->priv->ibus,
|
|
"SetCapabilities",
|
|
DBUS_TYPE_STRING, &ic,
|
|
DBUS_TYPE_INT32, &use_preedit,
|
|
DBUS_TYPE_INVALID,
|
|
DBUS_TYPE_INVALID);
|
|
}
|
|
|
|
void
|
|
ibus_im_client_release_im_context (IBusIMClient *client, IBusIMContext *context)
|
|
{
|
|
IBusIMClientPrivate *priv = client->priv;
|
|
gchar *ic = ibus_im_context_get_ic (context);
|
|
priv->contexts = g_list_remove (priv->contexts, context);
|
|
if (ic) {
|
|
g_hash_table_remove (priv->ic_table, ic);
|
|
_ibus_call_with_reply_and_block (priv->ibus, "ReleaseInputContext",
|
|
DBUS_TYPE_STRING, &ic,
|
|
DBUS_TYPE_INVALID,
|
|
DBUS_TYPE_INVALID);
|
|
|
|
}
|
|
|
|
}
|
|
|