/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
/* vim:set et sts=4: */
/* ibus - The Input Bus
* Copyright (C) 2008-2015 Peng Huang <shawn.p.huang@gmail.com>
* Copyright (C) 2015-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
* Copyright (C) 2008-2016 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include "ibusbus.h"
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include "ibusmarshalers.h"
#include "ibusinternal.h"
#include "ibusshare.h"
#include "ibusenginedesc.h"
#include "ibusserializable.h"
#include "ibusconfig.h"
#define IBUS_BUS_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_BUS, IBusBusPrivate))
enum {
CONNECTED,
DISCONNECTED,
GLOBAL_ENGINE_CHANGED,
NAME_OWNER_CHANGED,
LAST_SIGNAL,
};
enum {
PROP_0 = 0,
PROP_CONNECT_ASYNC,
PROP_CLIENT_ONLY,
};
/* IBusBusPriv */
struct _IBusBusPrivate {
GFileMonitor *monitor;
GDBusConnection *connection;
gboolean connected;
gboolean watch_dbus_signal;
guint watch_dbus_signal_id;
gboolean watch_ibus_signal;
guint watch_ibus_signal_id;
IBusConfig *config;
gchar *unique_name;
gboolean connect_async;
gchar *bus_address;
gboolean use_portal;
gboolean client_only;
GCancellable *cancellable;
guint portal_name_watch_id;
};
static guint bus_signals[LAST_SIGNAL] = { 0 };
static IBusBus *_bus = NULL;
/* functions prototype */
static GObject *ibus_bus_constructor (GType type,
guint n_params,
GObjectConstructParam *params);
static void ibus_bus_destroy (IBusObject *object);
static void ibus_bus_connect_async (IBusBus *bus);
static void ibus_bus_watch_dbus_signal (IBusBus *bus);
static void ibus_bus_unwatch_dbus_signal (IBusBus *bus);
static void ibus_bus_watch_ibus_signal (IBusBus *bus);
static void ibus_bus_unwatch_ibus_signal (IBusBus *bus);
static GVariant *ibus_bus_call_sync (IBusBus *bus,
const gchar *service,
const gchar *path,
const gchar *interface,
const gchar *member,
GVariant *parameters,
const GVariantType *reply_type);
static void ibus_bus_call_async (IBusBus *bus,
const gchar *service,
const gchar *path,
const gchar *interface,
const gchar *member,
GVariant *parameters,
const GVariantType *reply_type,
gpointer source_tag,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
static void ibus_bus_set_property (IBusBus *bus,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void ibus_bus_get_property (IBusBus *bus,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void ibus_bus_close_connection (IBusBus *bus);
G_DEFINE_TYPE (IBusBus, ibus_bus, IBUS_TYPE_OBJECT)
static void
ibus_bus_class_init (IBusBusClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class);
gobject_class->constructor = ibus_bus_constructor;
gobject_class->set_property =
(GObjectSetPropertyFunc) ibus_bus_set_property;
gobject_class->get_property =
(GObjectGetPropertyFunc) ibus_bus_get_property;
ibus_object_class->destroy = ibus_bus_destroy;
/* install properties */
/**
* IBusBus:connect-async:
*
* Whether the #IBusBus object should connect asynchronously to the bus.
*
*/
g_object_class_install_property (
gobject_class,
PROP_CONNECT_ASYNC,
g_param_spec_boolean ("connect-async",
"Connect Async",
"Connect asynchronously to the bus",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
/**
* IBusBus:client-only:
*
* Whether the #IBusBus object is for client use only.
*
*/
g_object_class_install_property (
gobject_class,
PROP_CLIENT_ONLY,
g_param_spec_boolean ("client-only",
"ClientOnly",
"Client use only",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
/* install signals */
/**
* IBusBus::connected:
* @bus: The #IBusBus object which recevied the signal
*
* Emitted when #IBusBus is connected to ibus-daemon.
*
*/
bus_signals[CONNECTED] =
g_signal_new (I_("connected"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
_ibus_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/**
* IBusBus::disconnected:
* @bus: The #IBusBus object which recevied the signal
*
* Emitted when #IBusBus is disconnected from ibus-daemon.
*
*/
bus_signals[DISCONNECTED] =
g_signal_new (I_("disconnected"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
_ibus_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/**
* IBusBus::global-engine-changed:
* @bus: The #IBusBus object which recevied the signal
* @name: The name of the new global engine.
*
* Emitted when global engine is changed.
*
*/
bus_signals[GLOBAL_ENGINE_CHANGED] =
g_signal_new (I_("global-engine-changed"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
_ibus_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
/**
* IBusBus::name-owner-changed:
* @bus: The #IBusBus object which recevied the signal
* @name: The name which ower is changed.
* @old_owner: The unique bus name of the old owner.
* @new_owner: The unique bus name of the new owner.
*
* Emitted when D-Bus name owner is changed.
*
*/
bus_signals[NAME_OWNER_CHANGED] =
g_signal_new (I_("name-owner-changed"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
_ibus_marshal_VOID__STRING_STRING_STRING,
G_TYPE_NONE,
3,
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
g_type_class_add_private (class, sizeof (IBusBusPrivate));
}
static void
_connection_dbus_signal_cb (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
g_return_if_fail (user_data != NULL);
g_return_if_fail (IBUS_IS_BUS (user_data));
if (g_strcmp0 (signal_name, "NameOwnerChanged") == 0) {
gchar *name = NULL;
gchar *old_owner = NULL;
gchar *new_owner = NULL;
g_variant_get (parameters, "(&s&s&s)", &name, &old_owner, &new_owner);
g_signal_emit (IBUS_BUS (user_data),
bus_signals[NAME_OWNER_CHANGED], 0,
name, old_owner, new_owner);
}
/* FIXME handle other D-Bus signals if needed */
}
static void
_connection_ibus_signal_cb (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
g_return_if_fail (user_data != NULL);
g_return_if_fail (IBUS_IS_BUS (user_data));
if (g_strcmp0 (signal_name, "GlobalEngineChanged") == 0) {
gchar *engine_name = NULL;
g_variant_get (parameters, "(&s)", &engine_name);
g_signal_emit (IBUS_BUS (user_data),
bus_signals[GLOBAL_ENGINE_CHANGED], 0,
engine_name);
}
/* FIXME handle org.freedesktop.IBus.RegistryChanged signal if needed */
}
static void
_connection_closed_cb (GDBusConnection *connection,
gboolean remote_peer_vanished,
GError *error,
IBusBus *bus)
{
if (error) {
/* We replaced g_warning with g_debug here because
* currently when ibus-daemon restarts, GTK client calls this and
* _g_dbus_worker_do_read_cb() sets the error message:
* "Underlying GIOStream returned 0 bytes on an async read"
* http://git.gnome.org/browse/glib/tree/gio/gdbusprivate.c#n693
* However we think the error message is almost harmless. */
g_debug ("_connection_closed_cb: %s", error->message);
}
ibus_bus_close_connection (bus);
}
static void
ibus_bus_close_connection (IBusBus *bus)
{
g_free (bus->priv->unique_name);
bus->priv->unique_name = NULL;
bus->priv->watch_dbus_signal_id = 0;
bus->priv->watch_ibus_signal_id = 0;
g_free (bus->priv->bus_address);
bus->priv->bus_address = NULL;
/* Cancel ongoing connect request. */
g_cancellable_cancel (bus->priv->cancellable);
g_cancellable_reset (bus->priv->cancellable);
bus->priv->connected = FALSE;
/* unref the old connection at first */
if (bus->priv->connection != NULL) {
g_signal_handlers_disconnect_by_func (bus->priv->connection,
G_CALLBACK (_connection_closed_cb),
bus);
if (!g_dbus_connection_is_closed(bus->priv->connection))
g_dbus_connection_close(bus->priv->connection, NULL, NULL, NULL);
g_object_unref (bus->priv->connection);
bus->priv->connection = NULL;
g_signal_emit (bus, bus_signals[DISCONNECTED], 0);
}
}
static void
ibus_bus_connect_completed (IBusBus *bus)
{
g_assert (bus->priv->connection);
bus->priv->connected = TRUE;
/* FIXME */
ibus_bus_hello (bus);
g_signal_connect (bus->priv->connection,
"closed",
(GCallback) _connection_closed_cb,
bus);
if (bus->priv->watch_dbus_signal) {
ibus_bus_watch_dbus_signal (bus);
}
if (bus->priv->watch_ibus_signal) {
ibus_bus_watch_ibus_signal (bus);
}
g_signal_emit (bus, bus_signals[CONNECTED], 0);
}
static void
_bus_connect_start_portal_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
IBusBus *bus = IBUS_BUS (user_data);
GVariant *result;
GError *error = NULL;
result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
res,
&error);
if (result != NULL) {
ibus_bus_connect_completed (bus);
g_variant_unref (result);
} else {
g_error_free (error);
g_dbus_connection_close (bus->priv->connection, NULL, NULL, NULL);
g_object_unref (bus->priv->connection);
bus->priv->connection = NULL;
g_free (bus->priv->bus_address);
bus->priv->bus_address = NULL;
}
g_object_unref (bus);
}
static void
_bus_connect_async_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
g_return_if_fail (user_data != NULL);
g_return_if_fail (IBUS_IS_BUS (user_data));
IBusBus *bus = IBUS_BUS (user_data);
GError *error = NULL;
bus->priv->connection =
g_dbus_connection_new_for_address_finish (res, &error);
if (error != NULL) {
g_warning ("Unable to connect to ibus: %s", error->message);
g_error_free (error);
error = NULL;
}
if (bus->priv->connection != NULL) {
if (bus->priv->use_portal) {
g_object_set_data (G_OBJECT (bus->priv->connection),
"ibus-portal-connection",
GINT_TO_POINTER (TRUE));
g_dbus_connection_call (
bus->priv->connection,
IBUS_SERVICE_PORTAL,
IBUS_PATH_IBUS,
"org.freedesktop.DBus.Peer",
"Ping",
g_variant_new ("()"),
G_VARIANT_TYPE ("()"),
G_DBUS_CALL_FLAGS_NONE,
-1,
bus->priv->cancellable,
(GAsyncReadyCallback) _bus_connect_start_portal_cb,
g_object_ref (bus));
} else {
ibus_bus_connect_completed (bus);
}
}
else {
g_free (bus->priv->bus_address);
bus->priv->bus_address = NULL;
}
/* unref the ref from ibus_bus_connect */
g_object_unref (bus);
}
static gchar *
ibus_bus_get_bus_address (IBusBus *bus)
{
if (_bus->priv->use_portal)
return g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, NULL, NULL);
else
return g_strdup (ibus_get_address ());
}
static void
ibus_bus_connect_async (IBusBus *bus)
{
gchar *bus_address = ibus_bus_get_bus_address (bus);
if (bus_address == NULL)
return;
if (g_strcmp0 (bus->priv->bus_address, bus_address) == 0) {
g_free (bus_address);
return;
}
/* Close current connection and cancel ongoing connect request. */
ibus_bus_close_connection (bus);
bus->priv->bus_address = bus_address;
g_object_ref (bus);
g_dbus_connection_new_for_address (
bus_address,
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
NULL,
bus->priv->cancellable,
_bus_connect_async_cb, bus);
}
static gboolean
is_in_flatpak (void)
{
static gboolean flatpak_info_read;
static gboolean in_flatpak;
if (flatpak_info_read)
return in_flatpak;
flatpak_info_read = TRUE;
if (g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS))
in_flatpak = TRUE;
return in_flatpak;
}
static gboolean
ibus_bus_should_connect_portal (IBusBus *bus)
{
return bus->priv->client_only &&
(is_in_flatpak () || g_getenv ("IBUS_USE_PORTAL") != NULL);
}
static void
ibus_bus_connect (IBusBus *bus)
{
const gchar *bus_address = ibus_get_address ();
if (bus_address == NULL)
return;
if (g_strcmp0 (bus_address, bus->priv->bus_address) == 0 &&
bus->priv->connection != NULL)
return;
/* Close current connection and cancel ongoing connect request. */
ibus_bus_close_connection (bus);
bus->priv->connection = g_dbus_connection_new_for_address_sync (
bus_address,
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
NULL, NULL, NULL);
if (bus->priv->connection) {
bus->priv->bus_address = g_strdup (bus_address);
ibus_bus_connect_completed (bus);
}
}
static void
_changed_cb (GFileMonitor *monitor,
GFile *file,
GFile *other_file,
GFileMonitorEvent event_type,
IBusBus *bus)
{
if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
event_type != G_FILE_MONITOR_EVENT_CREATED &&
event_type != G_FILE_MONITOR_EVENT_DELETED)
return;
ibus_bus_connect_async (bus);
}
static void
ibus_bus_init (IBusBus *bus)
{
struct stat buf;
gchar *path;
bus->priv = IBUS_BUS_GET_PRIVATE (bus);
bus->priv->config = NULL;
bus->priv->connection = NULL;
bus->priv->watch_dbus_signal = FALSE;
bus->priv->watch_dbus_signal_id = 0;
bus->priv->watch_ibus_signal = FALSE;
bus->priv->watch_ibus_signal_id = 0;
bus->priv->unique_name = NULL;
bus->priv->connect_async = FALSE;
bus->priv->client_only = FALSE;
bus->priv->bus_address = NULL;
bus->priv->cancellable = g_cancellable_new ();
path = g_path_get_dirname (ibus_get_socket_path ());
g_mkdir_with_parents (path, 0700);
if (stat (path, &buf) == 0) {
if (buf.st_uid != getuid ()) {
g_warning ("The owner of %s is not %s!",
path, ibus_get_user_name ());
return;
}
if (buf.st_mode != (S_IFDIR | S_IRWXU)) {
errno = 0;
if (g_chmod (path, 0700))
g_warning ("chmod failed: %s", errno ? g_strerror (errno) : "");
}
}
g_free (path);
}
static void
ibus_bus_set_property (IBusBus *bus,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
switch (prop_id) {
case PROP_CONNECT_ASYNC:
bus->priv->connect_async = g_value_get_boolean (value);
break;
case PROP_CLIENT_ONLY:
bus->priv->client_only = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (bus, prop_id, pspec);
}
}
static void
ibus_bus_get_property (IBusBus *bus,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
switch (prop_id) {
case PROP_CONNECT_ASYNC:
g_value_set_boolean (value, bus->priv->connect_async);
break;
case PROP_CLIENT_ONLY:
g_value_set_boolean (value, bus->priv->client_only);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (bus, prop_id, pspec);
}
}
static void
portal_name_appeared (GDBusConnection *connection,
const gchar *name,
const gchar *owner,
gpointer user_data)
{
IBusBus *bus = IBUS_BUS (user_data);
if (bus->priv->connection == NULL)
ibus_bus_connect_async (bus);
}
static void
portal_name_vanished (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
IBusBus *bus = IBUS_BUS (user_data);
if (bus->priv->connection)
g_dbus_connection_close (bus->priv->connection, NULL, NULL, NULL);
}
static GObject*
ibus_bus_constructor (GType type,
guint n_params,
GObjectConstructParam *params)
{
GObject *object;
GFile *file;
/* share one IBusBus instance in whole application */
if (_bus == NULL) {
object = G_OBJECT_CLASS (ibus_bus_parent_class)->constructor (
type, n_params, params);
/* make bus object sink */
g_object_ref_sink (object);
_bus = IBUS_BUS (object);
_bus->priv->use_portal = ibus_bus_should_connect_portal (_bus);
if (!_bus->priv->use_portal) {
file = g_file_new_for_path (ibus_get_socket_path ());
_bus->priv->monitor = g_file_monitor_file (file, 0, NULL, NULL);
g_signal_connect (_bus->priv->monitor, "changed",
(GCallback) _changed_cb, _bus);
g_object_unref (file);
} else {
_bus->priv->portal_name_watch_id =
g_bus_watch_name (G_BUS_TYPE_SESSION,
IBUS_SERVICE_PORTAL,
G_BUS_NAME_WATCHER_FLAGS_NONE,
portal_name_appeared,
portal_name_vanished,
_bus, NULL);
}
if (_bus->priv->connect_async)
ibus_bus_connect_async (_bus);
else
ibus_bus_connect (_bus);
}
else {
object = g_object_ref (G_OBJECT (_bus));
}
return object;
}
static void
ibus_bus_destroy (IBusObject *object)
{
g_assert (_bus == (IBusBus *)object);
IBusBus * bus = _bus;
_bus = NULL;
if (bus->priv->monitor) {
g_object_unref (bus->priv->monitor);
bus->priv->monitor = NULL;
}
if (bus->priv->config) {
ibus_proxy_destroy ((IBusProxy *) bus->priv->config);
bus->priv->config = NULL;
}
if (bus->priv->connection) {
g_signal_handlers_disconnect_by_func (bus->priv->connection,
G_CALLBACK (_connection_closed_cb),
bus);
/* FIXME should use async close function */
g_dbus_connection_close_sync (bus->priv->connection, NULL, NULL);
g_object_unref (bus->priv->connection);
bus->priv->connection = NULL;
}
g_free (bus->priv->unique_name);
bus->priv->unique_name = NULL;
g_free (bus->priv->bus_address);
bus->priv->bus_address = NULL;
g_cancellable_cancel (bus->priv->cancellable);
g_object_unref (bus->priv->cancellable);
bus->priv->cancellable = NULL;
if (bus->priv->portal_name_watch_id) {
g_bus_unwatch_name (bus->priv->portal_name_watch_id);
bus->priv->portal_name_watch_id = 0;
}
IBUS_OBJECT_CLASS (ibus_bus_parent_class)->destroy (object);
}
static gboolean
_async_finish_void (GTask *task,
GError **error)
{
/* g_task_propagate_error() is not a public API and
* g_task_had_error() needs to be called before
* g_task_propagate_pointer() clears task->error.
*/
gboolean had_error = g_task_had_error (task);
g_task_propagate_pointer (task, error);
if (had_error)
return FALSE;
return TRUE;
}
static gchar *
_async_finish_object_path (GTask *task,
GError **error)
{
gboolean had_error = g_task_had_error (task);
GVariant *result = g_task_propagate_pointer (task, error);
GVariant *variant = NULL;
gchar *path = NULL;
if (had_error) {
g_assert (result == NULL);
return NULL;
}
g_return_val_if_fail (result != NULL, NULL);
g_variant_get (result, "(v)", &variant);
path = g_variant_dup_string (variant, NULL);
g_variant_unref (variant);
g_variant_unref (result);
return path;
}
static gchar *
_async_finish_string (GTask *task,
GError **error)
{
gboolean had_error = g_task_had_error (task);
GVariant *variant = g_task_propagate_pointer (task, error);
gchar *s = NULL;
if (had_error) {
g_assert (variant == NULL);
return NULL;
}
g_return_val_if_fail (variant != NULL, NULL);
g_variant_get (variant, "(&s)", &s);
g_variant_unref (variant);
return s;
}
static gboolean
_async_finish_gboolean (GTask *task,
GError **error)
{
gboolean had_error = g_task_had_error (task);
GVariant *variant = g_task_propagate_pointer (task, error);
gboolean retval = FALSE;
if (had_error) {
g_assert (variant == NULL);
return FALSE;
}
g_return_val_if_fail (variant != NULL, FALSE);
g_variant_get (variant, "(b)", &retval);
g_variant_unref (variant);
return retval;
}
static guint
_async_finish_guint (GTask *task,
GError **error)
{
gboolean had_error = g_task_had_error (task);
GVariant *variant = g_task_propagate_pointer (task, error);
static const guint bad_id = 0;
guint id = 0;
if (had_error) {
g_assert (variant == NULL);
return bad_id;
}
g_return_val_if_fail (variant != NULL, bad_id);
g_variant_get (variant, "(u)", &id);
g_variant_unref (variant);
return id;
}
IBusBus *
ibus_bus_new (void)
{
IBusBus *bus = IBUS_BUS (g_object_new (IBUS_TYPE_BUS,
"connect-async", FALSE,
"client-only", FALSE,
NULL));
return bus;
}
IBusBus *
ibus_bus_new_async (void)
{
IBusBus *bus = IBUS_BUS (g_object_new (IBUS_TYPE_BUS,
"connect-async", TRUE,
"client-only", FALSE,
NULL));
return bus;
}
IBusBus *
ibus_bus_new_async_client (void)
{
IBusBus *bus = IBUS_BUS (g_object_new (IBUS_TYPE_BUS,
"connect-async", TRUE,
"client-only", TRUE,
NULL));
return bus;
}
gboolean
ibus_bus_is_connected (IBusBus *bus)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), FALSE);
if (bus->priv->connection == NULL || g_dbus_connection_is_closed (bus->priv->connection))
return FALSE;
return bus->priv->connected;
}
IBusInputContext *
ibus_bus_create_input_context (IBusBus *bus,
const gchar *client_name)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), NULL);
g_return_val_if_fail (client_name != NULL, NULL);
gchar *path;
IBusInputContext *context = NULL;
GVariant *result;
result = ibus_bus_call_sync (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
IBUS_INTERFACE_IBUS,
"CreateInputContext",
g_variant_new ("(s)", client_name),
G_VARIANT_TYPE ("(o)"));
if (result != NULL) {
GError *error = NULL;
g_variant_get (result, "(&o)", &path);
context = ibus_input_context_new (path, bus->priv->connection, NULL, &error);
g_variant_unref (result);
if (context == NULL) {
g_warning ("ibus_bus_create_input_context: %s", error->message);
g_error_free (error);
}
}
return context;
}
static void
_create_input_context_async_step_two_done (GObject *source_object,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
IBusInputContext *context =
ibus_input_context_new_async_finish (res, &error);
if (context == NULL)
g_task_return_error (task, error);
else
g_task_return_pointer (task, context, NULL);
g_object_unref (task);
}
static void
_create_input_context_async_step_one_done (GDBusConnection *connection,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
GVariant *variant = g_dbus_connection_call_finish (connection, res, &error);
const gchar *path = NULL;
IBusBus *bus;
GCancellable *cancellable;
if (variant == NULL) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
if (g_dbus_connection_is_closed (connection)) {
/*
* The connection is closed, can not contine next steps, so complete
* the asynchronous request with error.
*/
g_variant_unref(variant);
g_task_return_new_error (task,
G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Connection is closed.");
return;
}
g_variant_get (variant, "(&o)", &path);
g_variant_unref(variant);
bus = (IBusBus *)g_task_get_source_object (task);
g_assert (IBUS_IS_BUS (bus));
cancellable = g_task_get_cancellable (task);
ibus_input_context_new_async (path,
bus->priv->connection,
cancellable,
(GAsyncReadyCallback)_create_input_context_async_step_two_done,
task);
}
void
ibus_bus_create_input_context_async (IBusBus *bus,
const gchar *client_name,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
g_return_if_fail (IBUS_IS_BUS (bus));
g_return_if_fail (client_name != NULL);
g_return_if_fail (callback != NULL);
task = g_task_new (bus, cancellable, callback, user_data);
g_task_set_source_tag (task, ibus_bus_create_input_context_async);
/* do not use ibus_bus_call_async, instread use g_dbus_connection_call
* directly, because we need two async steps for create an IBusInputContext.
* 1. Call CreateInputContext to request ibus-daemon create a remote IC.
* 2. New local IBusInputContext proxy of the remote IC
*/
g_dbus_connection_call (bus->priv->connection,
ibus_bus_get_service_name (bus),
IBUS_PATH_IBUS,
bus->priv->use_portal ? IBUS_INTERFACE_PORTAL : IBUS_INTERFACE_IBUS,
"CreateInputContext",
g_variant_new ("(s)", client_name),
G_VARIANT_TYPE("(o)"),
G_DBUS_CALL_FLAGS_NO_AUTO_START,
timeout_msec,
cancellable,
(GAsyncReadyCallback)_create_input_context_async_step_one_done,
task);
}
IBusInputContext *
ibus_bus_create_input_context_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
gboolean had_error;
IBusInputContext *context = NULL;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_assert (g_task_get_source_tag (task) ==
ibus_bus_create_input_context_async);
had_error = g_task_had_error (task);
context = g_task_propagate_pointer (task, error);
if (had_error) {
g_assert (context == NULL);
return NULL;
}
g_assert (IBUS_IS_INPUT_CONTEXT (context));
return context;
}
gchar *
ibus_bus_current_input_context (IBusBus *bus)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), NULL);
gchar *path = NULL;
GVariant *result;
result = ibus_bus_call_sync (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
"org.freedesktop.DBus.Properties",
"Get",
g_variant_new ("(ss)",
IBUS_INTERFACE_IBUS,
"CurrentInputContext"),
G_VARIANT_TYPE ("(v)"));
if (result != NULL) {
GVariant *variant = NULL;
g_variant_get (result, "(v)", &variant);
path = g_variant_dup_string (variant, NULL);
g_variant_unref (variant);
g_variant_unref (result);
}
return path;
}
void
ibus_bus_current_input_context_async (IBusBus *bus,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
ibus_bus_call_async (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
"org.freedesktop.DBus.Properties",
"Get",
g_variant_new ("(ss)",
IBUS_INTERFACE_IBUS,
"CurrentInputContext"),
G_VARIANT_TYPE ("(v)"),
ibus_bus_current_input_context_async,
timeout_msec,
cancellable,
callback,
user_data);
}
gchar *
ibus_bus_current_input_context_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_assert(g_task_get_source_tag (task) ==
ibus_bus_current_input_context_async);
return _async_finish_object_path (task, error);
}
static void
ibus_bus_watch_dbus_signal (IBusBus *bus)
{
g_return_if_fail (bus->priv->connection != NULL);
g_return_if_fail (bus->priv->watch_dbus_signal_id == 0);
/* Subscribe to dbus signals such as NameOwnerChanged. */
bus->priv->watch_dbus_signal_id
= g_dbus_connection_signal_subscribe (bus->priv->connection,
DBUS_SERVICE_DBUS,
DBUS_INTERFACE_DBUS,
"NameOwnerChanged",
DBUS_PATH_DBUS,
NULL /* arg0 */,
(GDBusSignalFlags) 0,
_connection_dbus_signal_cb,
bus,
NULL /* user_data_free_func */);
/* FIXME handle other D-Bus signals if needed */
}
static void
ibus_bus_unwatch_dbus_signal (IBusBus *bus)
{
g_return_if_fail (bus->priv->watch_dbus_signal_id != 0);
g_dbus_connection_signal_unsubscribe (bus->priv->connection,
bus->priv->watch_dbus_signal_id);
bus->priv->watch_dbus_signal_id = 0;
}
void
ibus_bus_set_watch_dbus_signal (IBusBus *bus,
gboolean watch)
{
g_return_if_fail (IBUS_IS_BUS (bus));
if (bus->priv->watch_dbus_signal == watch)
return;
bus->priv->watch_dbus_signal = watch;
if (ibus_bus_is_connected (bus)) {
if (watch) {
ibus_bus_watch_dbus_signal (bus);
}
else {
ibus_bus_unwatch_dbus_signal (bus);
}
}
}
static void
ibus_bus_watch_ibus_signal (IBusBus *bus)
{
g_return_if_fail (bus->priv->connection != NULL);
g_return_if_fail (bus->priv->watch_ibus_signal_id == 0);
/* Subscribe to ibus signals such as GlboalEngineChanged. */
bus->priv->watch_ibus_signal_id
= g_dbus_connection_signal_subscribe (bus->priv->connection,
"org.freedesktop.IBus",
IBUS_INTERFACE_IBUS,
"GlobalEngineChanged",
IBUS_PATH_IBUS,
NULL /* arg0 */,
(GDBusSignalFlags) 0,
_connection_ibus_signal_cb,
bus,
NULL /* user_data_free_func */);
/* FIXME handle org.freedesktop.IBus.RegistryChanged signal if needed */
}
static void
ibus_bus_unwatch_ibus_signal (IBusBus *bus)
{
g_return_if_fail (bus->priv->watch_ibus_signal_id != 0);
g_dbus_connection_signal_unsubscribe (bus->priv->connection,
bus->priv->watch_ibus_signal_id);
bus->priv->watch_ibus_signal_id = 0;
}
void
ibus_bus_set_watch_ibus_signal (IBusBus *bus,
gboolean watch)
{
g_return_if_fail (IBUS_IS_BUS (bus));
if (bus->priv->watch_ibus_signal == watch)
return;
bus->priv->watch_ibus_signal = watch;
if (ibus_bus_is_connected (bus)) {
if (watch) {
ibus_bus_watch_ibus_signal (bus);
}
else {
ibus_bus_unwatch_ibus_signal (bus);
}
}
}
const gchar *
ibus_bus_hello (IBusBus *bus)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), NULL);
g_return_val_if_fail (ibus_bus_is_connected (bus), NULL);
/* gdbus connection will say hello by self. */
if (bus->priv->connection)
return g_dbus_connection_get_unique_name (bus->priv->connection);
return NULL;
}
guint32
ibus_bus_request_name (IBusBus *bus,
const gchar *name,
guint32 flags)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), 0);
g_return_val_if_fail (name != NULL, 0);
guint32 retval = 0;
GVariant *result;
result = ibus_bus_call_sync (bus,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"RequestName",
g_variant_new ("(su)", name, flags),
G_VARIANT_TYPE ("(u)"));
if (result) {
g_variant_get (result, "(u)", &retval);
g_variant_unref (result);
}
return retval;
}
void
ibus_bus_request_name_async (IBusBus *bus,
const gchar *name,
guint flags,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
g_return_if_fail (name != NULL);
ibus_bus_call_async (bus,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"RequestName",
g_variant_new ("(su)", name, flags),
G_VARIANT_TYPE ("(u)"),
ibus_bus_request_name_async,
timeout_msec,
cancellable,
callback,
user_data);
}
guint
ibus_bus_request_name_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_assert(g_task_get_source_tag (task) == ibus_bus_request_name_async);
return _async_finish_guint (task, error);
}
guint
ibus_bus_release_name (IBusBus *bus,
const gchar *name)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), 0);
g_return_val_if_fail (name != NULL, 0);
guint retval = 0;
GVariant *result;
result = ibus_bus_call_sync (bus,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"ReleaseName",
g_variant_new ("(s)", name),
G_VARIANT_TYPE ("(u)"));
if (result) {
g_variant_get (result, "(u)", &retval);
g_variant_unref (result);
}
return retval;
}
void
ibus_bus_release_name_async (IBusBus *bus,
const gchar *name,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
g_return_if_fail (name != NULL);
ibus_bus_call_async (bus,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"ReleaseName",
g_variant_new ("(s)", name),
G_VARIANT_TYPE ("(u)"),
ibus_bus_release_name_async,
timeout_msec,
cancellable,
callback,
user_data);
}
guint
ibus_bus_release_name_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_assert(g_task_get_source_tag (task) == ibus_bus_release_name_async);
return _async_finish_guint (task, error);
}
GList *
ibus_bus_list_queued_owners (IBusBus *bus,
const gchar *name)
{
GList *retval = NULL;
GVariant *result;
g_return_val_if_fail (IBUS_IS_BUS (bus), NULL);
g_return_val_if_fail (name != NULL, NULL);
result = ibus_bus_call_sync (bus,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"ListQueuedOwners",
g_variant_new ("(s)", name),
G_VARIANT_TYPE ("(as)"));
if (result) {
GVariantIter *iter = NULL;
const gchar *name = NULL;
g_variant_get (result, "(as)", &iter);
while (g_variant_iter_loop (iter, "&s", &name)) {
if (name == NULL) {
continue;
}
retval = g_list_append (retval, g_strdup (name));
}
g_variant_iter_free (iter);
g_variant_unref (result);
}
return retval;
}
gboolean
ibus_bus_name_has_owner (IBusBus *bus,
const gchar *name)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), FALSE);
g_return_val_if_fail (name != NULL, FALSE);
gboolean retval = FALSE;
GVariant *result;
result = ibus_bus_call_sync (bus,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"NameHasOwner",
g_variant_new ("(s)", name),
G_VARIANT_TYPE ("(b)"));
if (result) {
g_variant_get (result, "(b)", &retval);
g_variant_unref (result);
}
return retval;
}
void
ibus_bus_name_has_owner_async (IBusBus *bus,
const gchar *name,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
g_return_if_fail (name != NULL);
ibus_bus_call_async (bus,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"NameHasOwner",
g_variant_new ("(s)", name),
G_VARIANT_TYPE ("(b)"),
ibus_bus_name_has_owner_async,
timeout_msec,
cancellable,
callback,
user_data);
}
gboolean
ibus_bus_name_has_owner_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_assert(g_task_get_source_tag (task) == ibus_bus_name_has_owner_async);
return _async_finish_gboolean (task, error);
}
GList *
ibus_bus_list_names (IBusBus *bus)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), NULL);
return NULL;
}
gboolean
ibus_bus_add_match (IBusBus *bus,
const gchar *rule)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), FALSE);
g_return_val_if_fail (rule != NULL, FALSE);
GVariant *result;
result = ibus_bus_call_sync (bus,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"AddMatch",
g_variant_new ("(s)", rule),
NULL);
if (result) {
g_variant_unref (result);
return TRUE;
}
return FALSE;
}
void
ibus_bus_add_match_async (IBusBus *bus,
const gchar *rule,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
g_return_if_fail (rule != NULL);
ibus_bus_call_async (bus,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"AddMatch",
g_variant_new ("(s)", rule),
NULL,
ibus_bus_add_match_async,
timeout_msec,
cancellable,
callback,
user_data);
}
gboolean
ibus_bus_add_match_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_assert(g_task_get_source_tag (task) == ibus_bus_add_match_async);
return _async_finish_void (task, error);
}
gboolean
ibus_bus_remove_match (IBusBus *bus,
const gchar *rule)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), FALSE);
g_return_val_if_fail (rule != NULL, FALSE);
GVariant *result;
result = ibus_bus_call_sync (bus,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"RemoveMatch",
g_variant_new ("(s)", rule),
NULL);
if (result) {
g_variant_unref (result);
return TRUE;
}
return FALSE;
}
void
ibus_bus_remove_match_async (IBusBus *bus,
const gchar *rule,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
g_return_if_fail (rule != NULL);
ibus_bus_call_async (bus,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"RemoveMatch",
g_variant_new ("(s)", rule),
NULL,
ibus_bus_remove_match_async,
timeout_msec,
cancellable,
callback,
user_data);
}
gboolean
ibus_bus_remove_match_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_assert(g_task_get_source_tag (task) == ibus_bus_remove_match_async);
return _async_finish_void (task, error);
}
gchar *
ibus_bus_get_name_owner (IBusBus *bus,
const gchar *name)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), NULL);
g_return_val_if_fail (name != NULL, NULL);
gchar *retval = NULL;
GVariant *result;
result = ibus_bus_call_sync (bus,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"GetNameOwner",
g_variant_new ("(s)", name),
G_VARIANT_TYPE ("(s)"));
if (result) {
g_variant_get (result, "(s)", &retval);
g_variant_unref (result);
}
return retval;
}
void
ibus_bus_get_name_owner_async (IBusBus *bus,
const gchar *name,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
g_return_if_fail (name != NULL);
ibus_bus_call_async (bus,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"GetNameOwner",
g_variant_new ("(s)", name),
G_VARIANT_TYPE ("(s)"),
ibus_bus_get_name_owner_async,
timeout_msec,
cancellable,
callback,
user_data);
}
gchar *
ibus_bus_get_name_owner_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_assert(g_task_get_source_tag (task) == ibus_bus_get_name_owner_async);
return g_strdup (_async_finish_string (task, error));
}
GDBusConnection *
ibus_bus_get_connection (IBusBus *bus)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), NULL);
return bus->priv->connection;
}
const gchar *
ibus_bus_get_service_name (IBusBus *bus)
{
if (bus->priv->use_portal)
return IBUS_SERVICE_PORTAL;
return IBUS_SERVICE_IBUS;
}
gboolean
ibus_bus_exit (IBusBus *bus,
gboolean restart)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), FALSE);
GVariant *result;
result = ibus_bus_call_sync (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
IBUS_INTERFACE_IBUS,
"Exit",
g_variant_new ("(b)", restart),
NULL);
ibus_bus_close_connection (bus);
if (result) {
g_variant_unref (result);
return TRUE;
}
return FALSE;
}
void
ibus_bus_exit_async (IBusBus *bus,
gboolean restart,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
ibus_bus_call_async (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
IBUS_INTERFACE_IBUS,
"Exit",
g_variant_new ("(b)", restart),
NULL,
ibus_bus_exit_async,
timeout_msec,
cancellable,
callback,
user_data);
}
gboolean
ibus_bus_exit_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_assert (g_task_get_source_tag (task) == ibus_bus_exit_async);
return _async_finish_void (task, error);
}
gboolean
ibus_bus_register_component (IBusBus *bus,
IBusComponent *component)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), FALSE);
g_return_val_if_fail (IBUS_IS_COMPONENT (component), FALSE);
GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)component);
GVariant *result = ibus_bus_call_sync (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
IBUS_INTERFACE_IBUS,
"RegisterComponent",
g_variant_new ("(v)", variant),
NULL);
if (result) {
g_variant_unref (result);
return TRUE;
}
return FALSE;
}
void
ibus_bus_register_component_async (IBusBus *bus,
IBusComponent *component,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
g_return_if_fail (IBUS_IS_COMPONENT (component));
GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)component);
ibus_bus_call_async (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
IBUS_INTERFACE_IBUS,
"RegisterComponent",
g_variant_new ("(v)", variant),
NULL,
ibus_bus_register_component_async,
timeout_msec,
cancellable,
callback,
user_data);
}
gboolean
ibus_bus_register_component_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_assert (
g_task_get_source_tag (task) == ibus_bus_register_component_async);
return _async_finish_void (task, error);
}
static GList *
ibus_bus_do_list_engines (IBusBus *bus, gboolean active_engines_only)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), NULL);
GList *retval = NULL;
GVariant *result;
const gchar *property =
active_engines_only ? "ActiveEngines" : "Engines";
result = ibus_bus_call_sync (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
"org.freedesktop.DBus.Properties",
"Get",
g_variant_new ("(ss)",
IBUS_INTERFACE_IBUS,
property),
G_VARIANT_TYPE ("(v)"));
if (result) {
GVariant *variant = NULL;
GVariantIter *iter = NULL;
g_variant_get (result, "(v)", &variant);
iter = g_variant_iter_new (variant);
GVariant *var;
while (g_variant_iter_loop (iter, "v", &var)) {
IBusSerializable *serializable = ibus_serializable_deserialize (var);
g_object_ref_sink (serializable);
retval = g_list_append (retval, serializable);
}
g_variant_iter_free (iter);
g_variant_unref (variant);
g_variant_unref (result);
}
return retval;
}
GList *
ibus_bus_list_engines (IBusBus *bus)
{
return ibus_bus_do_list_engines (bus, FALSE);
}
void
ibus_bus_list_engines_async (IBusBus *bus,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
ibus_bus_call_async (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
"org.freedesktop.DBus.Properties",
"Get",
g_variant_new ("(ss)",
IBUS_INTERFACE_IBUS,
"Engines"),
G_VARIANT_TYPE ("(v)"),
ibus_bus_list_engines_async,
timeout_msec,
cancellable,
callback,
user_data);
}
GList *
ibus_bus_list_engines_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
gboolean had_error;
GVariant *result = NULL;
GVariant *variant = NULL;
GList *retval = NULL;
GVariantIter *iter = NULL;
GVariant *var;
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
had_error = g_task_had_error (task);
result = g_task_propagate_pointer (task, error);
if (had_error) {
g_assert (result == NULL);
return NULL;
}
g_return_val_if_fail (result != NULL, NULL);
g_variant_get (result, "(v)", &variant);
iter = g_variant_iter_new (variant);
while (g_variant_iter_loop (iter, "v", &var)) {
IBusSerializable *serializable = ibus_serializable_deserialize (var);
g_object_ref_sink (serializable);
retval = g_list_append (retval, serializable);
}
g_variant_iter_free (iter);
g_variant_unref (variant);
g_variant_unref (result);
return retval;
}
#ifndef IBUS_DISABLE_DEPRECATED
GList *
ibus_bus_list_active_engines (IBusBus *bus)
{
return ibus_bus_do_list_engines (bus, TRUE);
}
void
ibus_bus_list_active_engines_async (IBusBus *bus,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
ibus_bus_call_async (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
"org.freedesktop.DBus.Properties",
"Get",
g_variant_new ("(ss)",
IBUS_INTERFACE_IBUS,
"ActiveEngines"),
G_VARIANT_TYPE ("(v)"),
ibus_bus_list_active_engines_async,
timeout_msec,
cancellable,
callback,
user_data);
}
GList *
ibus_bus_list_active_engines_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
return ibus_bus_list_engines_async_finish (bus, res, error);
}
#endif /* IBUS_DISABLE_DEPRECATED */
IBusEngineDesc **
ibus_bus_get_engines_by_names (IBusBus *bus,
const gchar * const *names)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), NULL);
GVariant *result;
result = ibus_bus_call_sync (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
IBUS_INTERFACE_IBUS,
"GetEnginesByNames",
g_variant_new("(^as)", names),
G_VARIANT_TYPE ("(av)"));
if (result == NULL)
return NULL;
GArray *array = g_array_new (TRUE, TRUE, sizeof (IBusEngineDesc *));
GVariantIter *iter = NULL;
g_variant_get (result, "(av)", &iter);
GVariant *var;
while (g_variant_iter_loop (iter, "v", &var)) {
IBusEngineDesc *desc = (IBusEngineDesc *) ibus_serializable_deserialize (var);
g_object_ref_sink (desc);
g_array_append_val (array, desc);
}
g_variant_iter_free (iter);
g_variant_unref (result);
return (IBusEngineDesc **)g_array_free (array, FALSE);
}
static void
_config_destroy_cb (IBusConfig *config,
IBusBus *bus)
{
IBusBusPrivate *priv;
priv = IBUS_BUS_GET_PRIVATE (bus);
g_assert (priv->config == config);
g_object_unref (config);
priv->config = NULL;
}
IBusConfig *
ibus_bus_get_config (IBusBus *bus)
{
g_assert (IBUS_IS_BUS (bus));
g_return_val_if_fail (ibus_bus_is_connected (bus), NULL);
IBusBusPrivate *priv;
priv = IBUS_BUS_GET_PRIVATE (bus);
if (priv->config == NULL && priv->connection) {
priv->config = ibus_config_new (priv->connection, NULL, NULL);
if (priv->config) {
g_signal_connect (priv->config, "destroy", G_CALLBACK (_config_destroy_cb), bus);
}
}
return priv->config;
}
#ifndef IBUS_DISABLE_DEPRECATED
gboolean
ibus_bus_get_use_sys_layout (IBusBus *bus)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), FALSE);
gboolean retval = FALSE;
GVariant *result;
result = ibus_bus_call_sync (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
IBUS_INTERFACE_IBUS,
"GetUseSysLayout",
NULL,
G_VARIANT_TYPE ("(b)"));
if (result) {
g_variant_get (result, "(b)", &retval);
g_variant_unref (result);
}
return retval;
}
void
ibus_bus_get_use_sys_layout_async (IBusBus *bus,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
ibus_bus_call_async (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
IBUS_INTERFACE_IBUS,
"GetUseSysLayout",
NULL,
G_VARIANT_TYPE ("(b)"),
ibus_bus_get_use_sys_layout_async,
timeout_msec,
cancellable,
callback,
user_data);
}
gboolean
ibus_bus_get_use_sys_layout_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_assert(g_task_get_source_tag (task) == ibus_bus_get_use_sys_layout_async);
return _async_finish_gboolean (task, error);
}
gboolean
ibus_bus_get_use_global_engine (IBusBus *bus)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), FALSE);
gboolean retval = FALSE;
GVariant *result;
result = ibus_bus_call_sync (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
IBUS_INTERFACE_IBUS,
"GetUseGlobalEngine",
NULL,
G_VARIANT_TYPE ("(b)"));
if (result) {
g_variant_get (result, "(b)", &retval);
g_variant_unref (result);
}
return retval;
}
void
ibus_bus_get_use_global_engine_async (IBusBus *bus,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
ibus_bus_call_async (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
IBUS_INTERFACE_IBUS,
"GetUseGlobalEngine",
NULL,
G_VARIANT_TYPE ("(b)"),
ibus_bus_get_use_global_engine_async,
timeout_msec,
cancellable,
callback,
user_data);
}
gboolean
ibus_bus_get_use_global_engine_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_assert(g_task_get_source_tag (task) ==
ibus_bus_get_use_global_engine_async);
return _async_finish_gboolean (task, error);
}
gboolean
ibus_bus_is_global_engine_enabled (IBusBus *bus)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), FALSE);
gboolean retval = FALSE;
GVariant *result;
result = ibus_bus_call_sync (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
IBUS_INTERFACE_IBUS,
"IsGlobalEngineEnabled",
NULL,
G_VARIANT_TYPE ("(b)"));
if (result) {
g_variant_get (result, "(b)", &retval);
g_variant_unref (result);
}
return retval;
}
void ibus_bus_is_global_engine_enabled_async (IBusBus *bus,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
ibus_bus_call_async (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
IBUS_INTERFACE_IBUS,
"IsGlobalEngineEnabled",
NULL,
G_VARIANT_TYPE ("(b)"),
ibus_bus_is_global_engine_enabled_async,
timeout_msec,
cancellable,
callback,
user_data);
}
gboolean ibus_bus_is_global_engine_enabled_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_assert(g_task_get_source_tag (task) ==
ibus_bus_is_global_engine_enabled_async);
return _async_finish_gboolean (task, error);
}
#endif /* IBUS_DISABLE_DEPRECATED */
IBusEngineDesc *
ibus_bus_get_global_engine (IBusBus *bus)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), NULL);
GVariant *result;
IBusEngineDesc *engine = NULL;
result = ibus_bus_call_sync (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
"org.freedesktop.DBus.Properties",
"Get",
g_variant_new ("(ss)",
IBUS_INTERFACE_IBUS,
"GlobalEngine"),
G_VARIANT_TYPE ("(v)"));
if (result) {
GVariant *variant = NULL;
g_variant_get (result, "(v)", &variant);
if (variant) {
GVariant *obj = g_variant_get_variant (variant);
engine = IBUS_ENGINE_DESC (ibus_serializable_deserialize (obj));
g_variant_unref (obj);
g_variant_unref (variant);
}
g_variant_unref (result);
}
return engine;
}
void
ibus_bus_get_global_engine_async (IBusBus *bus,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
ibus_bus_call_async (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
"org.freedesktop.DBus.Properties",
"Get",
g_variant_new ("(ss)",
IBUS_INTERFACE_IBUS,
"GlobalEngine"),
G_VARIANT_TYPE ("(v)"),
ibus_bus_get_global_engine_async,
timeout_msec,
cancellable,
callback,
user_data);
}
IBusEngineDesc *
ibus_bus_get_global_engine_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
gboolean had_error;
GVariant *result = NULL;
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
had_error = g_task_had_error (task);
result = g_task_propagate_pointer (task, error);
if (had_error) {
g_assert (result == NULL);
return NULL;
}
g_return_val_if_fail (result != NULL, NULL);
GVariant *variant = NULL;
g_variant_get (result, "(v)", &variant);
IBusEngineDesc *engine = NULL;
if (variant) {
GVariant *obj = g_variant_get_variant (variant);
engine = IBUS_ENGINE_DESC (ibus_serializable_deserialize (obj));
g_variant_unref (obj);
g_variant_unref (variant);
}
g_variant_unref (result);
return engine;
}
gboolean
ibus_bus_set_global_engine (IBusBus *bus,
const gchar *global_engine)
{
g_return_val_if_fail (IBUS_IS_BUS (bus), FALSE);
g_return_val_if_fail (global_engine != NULL, FALSE);
GVariant *result;
result = ibus_bus_call_sync (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
IBUS_INTERFACE_IBUS,
"SetGlobalEngine",
g_variant_new ("(s)", global_engine),
NULL);
if (result) {
g_variant_unref (result);
return TRUE;
}
return FALSE;
}
void
ibus_bus_set_global_engine_async (IBusBus *bus,
const gchar *global_engine,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
g_return_if_fail (global_engine != NULL);
ibus_bus_call_async (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
IBUS_INTERFACE_IBUS,
"SetGlobalEngine",
g_variant_new ("(s)", global_engine),
NULL, /* no return value */
ibus_bus_set_global_engine_async,
timeout_msec,
cancellable,
callback,
user_data);
}
gboolean
ibus_bus_set_global_engine_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_assert (g_task_get_source_tag (task) == ibus_bus_set_global_engine_async);
return _async_finish_void (task, error);
}
gboolean
ibus_bus_preload_engines (IBusBus *bus,
const gchar * const *names)
{
GVariant *result;
GVariant *variant = NULL;
g_return_val_if_fail (IBUS_IS_BUS (bus), FALSE);
g_return_val_if_fail (names != NULL && names[0] != NULL, FALSE);
variant = g_variant_new_strv(names, -1);
result = ibus_bus_call_sync (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new ("(ssv)",
IBUS_INTERFACE_IBUS,
"PreloadEngines",
variant),
NULL);
if (result) {
g_variant_unref (result);
return TRUE;
}
return FALSE;
}
void
ibus_bus_preload_engines_async (IBusBus *bus,
const gchar * const *names,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GVariant *variant = NULL;
g_return_if_fail (IBUS_IS_BUS (bus));
g_return_if_fail (names != NULL && names[0] != NULL);
variant = g_variant_new_strv(names, -1);
ibus_bus_call_async (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new ("(ssv)",
IBUS_INTERFACE_IBUS,
"PreloadEngines",
variant),
NULL, /* no return value */
ibus_bus_preload_engines_async,
timeout_msec,
cancellable,
callback,
user_data);
}
gboolean
ibus_bus_preload_engines_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_assert (g_task_get_source_tag (task) == ibus_bus_preload_engines_async);
return _async_finish_void (task, error);
}
GVariant *
ibus_bus_get_ibus_property (IBusBus *bus,
const gchar *property_name)
{
GVariant *result;
GVariant *retval = NULL;
g_return_val_if_fail (IBUS_IS_BUS (bus), NULL);
g_return_val_if_fail (property_name != NULL, NULL);
result = ibus_bus_call_sync (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
"org.freedesktop.DBus.Properties",
"Get",
g_variant_new ("(ss)",
IBUS_INTERFACE_IBUS,
property_name),
G_VARIANT_TYPE ("(v)"));
if (result) {
g_variant_get (result, "(v)", &retval);
g_variant_unref (result);
}
return retval;
}
void
ibus_bus_get_ibus_property_async (IBusBus *bus,
const gchar *property_name,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
g_return_if_fail (property_name != NULL);
ibus_bus_call_async (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
"org.freedesktop.DBus.Properties",
"Get",
g_variant_new ("(ss)",
IBUS_INTERFACE_IBUS,
property_name),
G_VARIANT_TYPE ("(v)"),
ibus_bus_get_ibus_property_async,
timeout_msec,
cancellable,
callback,
user_data);
}
GVariant *
ibus_bus_get_ibus_property_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
gboolean had_error;
GVariant *result = NULL;
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
had_error = g_task_had_error (task);
result = g_task_propagate_pointer (task, error);
if (had_error) {
g_assert (result == NULL);
return NULL;
}
g_return_val_if_fail (result != NULL, NULL);
GVariant *retval = NULL;
g_variant_get (result, "(v)", &retval);
g_variant_unref (result);
return retval;
}
void
ibus_bus_set_ibus_property (IBusBus *bus,
const gchar *property_name,
GVariant *value)
{
GVariant *result;
g_return_if_fail (IBUS_IS_BUS (bus));
g_return_if_fail (property_name != NULL);
result = ibus_bus_call_sync (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new ("(ssv)",
IBUS_INTERFACE_IBUS,
property_name,
value),
NULL);
if (result) {
g_variant_unref (result);
}
}
void
ibus_bus_set_ibus_property_async (IBusBus *bus,
const gchar *property_name,
GVariant *value,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IBUS_IS_BUS (bus));
g_return_if_fail (property_name != NULL);
ibus_bus_call_async (bus,
IBUS_SERVICE_IBUS,
IBUS_PATH_IBUS,
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new ("(ssv)",
IBUS_INTERFACE_IBUS,
property_name,
value),
NULL, /* no return value */
ibus_bus_set_ibus_property_async,
timeout_msec,
cancellable,
callback,
user_data);
}
gboolean
ibus_bus_set_ibus_property_async_finish (IBusBus *bus,
GAsyncResult *res,
GError **error)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (g_task_is_valid (res, bus));
task = G_TASK (res);
g_return_val_if_fail (
g_task_get_source_tag (task) == ibus_bus_set_ibus_property_async,
FALSE);
return _async_finish_void (task, error);
}
static GVariant *
ibus_bus_call_sync (IBusBus *bus,
const gchar *bus_name,
const gchar *path,
const gchar *interface,
const gchar *member,
GVariant *parameters,
const GVariantType *reply_type)
{
g_assert (IBUS_IS_BUS (bus));
g_assert (member != NULL);
g_return_val_if_fail (ibus_bus_is_connected (bus), NULL);
if (bus->priv->use_portal &&
g_strcmp0 (bus_name, IBUS_SERVICE_IBUS) == 0) {
bus_name = IBUS_SERVICE_PORTAL;
if (g_strcmp0 (interface, IBUS_INTERFACE_IBUS) == 0)
interface = IBUS_INTERFACE_PORTAL;
}
GError *error = NULL;
GVariant *result;
result = g_dbus_connection_call_sync (bus->priv->connection,
bus_name,
path,
interface,
member,
parameters,
reply_type,
G_DBUS_CALL_FLAGS_NO_AUTO_START,
ibus_get_timeout (),
NULL,
&error);
if (result == NULL) {
g_warning ("ibus_bus_call_sync: %s.%s: %s", interface, member, error->message);
g_error_free (error);
return NULL;
}
return result;
}
static void
ibus_bus_call_async_done (GDBusConnection *connection,
GAsyncResult *res,
gpointer user_data)
{
GTask *task;
GError *error = NULL;
GVariant *variant;
g_assert (G_IS_DBUS_CONNECTION (connection));
task = (GTask* ) user_data;
variant = g_dbus_connection_call_finish (connection, res, &error);
if (variant == NULL)
g_task_return_error (task, error);
else
g_task_return_pointer (task, variant, (GDestroyNotify) g_variant_unref);
g_object_unref (task);
}
static void
ibus_bus_call_async (IBusBus *bus,
const gchar *bus_name,
const gchar *path,
const gchar *interface,
const gchar *member,
GVariant *parameters,
const GVariantType *reply_type,
gpointer source_tag,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
g_assert (IBUS_IS_BUS (bus));
g_assert (member != NULL);
g_return_if_fail (ibus_bus_is_connected (bus));
task = g_task_new (bus, cancellable, callback, user_data);
g_task_set_source_tag (task, source_tag);
if (bus->priv->use_portal &&
g_strcmp0 (bus_name, IBUS_SERVICE_IBUS) == 0) {
bus_name = IBUS_SERVICE_PORTAL;
if (g_strcmp0 (interface, IBUS_INTERFACE_IBUS) == 0)
interface = IBUS_INTERFACE_PORTAL;
}
g_dbus_connection_call (bus->priv->connection,
bus_name,
path,
interface,
member,
parameters,
reply_type,
G_DBUS_CALL_FLAGS_NO_AUTO_START,
timeout_msec,
cancellable,
(GAsyncReadyCallback) ibus_bus_call_async_done,
task);
}