/* -*- 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 * Copyright (C) 2015-2018 Takao Fujiwara * Copyright (C) 2008-2018 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 "ibusservice.h" #include "ibusinternal.h" #define IBUS_SERVICE_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_SERVICE, IBusServicePrivate)) enum { LAST_SIGNAL }; enum { PROP_0, PROP_OBJECT_PATH, PROP_CONNECTION, }; /* IBusServicePrivate */ struct _IBusServicePrivate { gchar *object_path; GDBusConnection *connection; GHashTable *table; gboolean constructed; }; /* static guint service_signals[LAST_SIGNAL] = { 0 }; */ /* functions prototype */ static void ibus_service_base_init (IBusServiceClass *class); static void ibus_service_base_fini (IBusServiceClass *class); static void ibus_service_class_init (IBusServiceClass *class); static void ibus_service_init (IBusService *service); static void ibus_service_constructed (GObject *object); static void ibus_service_set_property (IBusService *service, guint prop_id, const GValue *value, GParamSpec *pspec); static void ibus_service_get_property (IBusService *service, guint prop_id, GValue *value, GParamSpec *pspec); static void ibus_service_destroy (IBusService *service); static void ibus_service_service_method_call (IBusService *service, GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation); static GVariant *ibus_service_service_get_property (IBusService *service, GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error); static gboolean ibus_service_service_set_property (IBusService *service, GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GVariant *value, GError **error); static void ibus_service_service_method_call_cb (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, IBusService *service); static GVariant *ibus_service_service_get_property_cb (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error, IBusService *service); static gboolean ibus_service_service_set_property_cb (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GVariant *value, GError **error, IBusService *service); static void ibus_service_connection_closed_cb (GDBusConnection *connection, gboolean remote_peer_vanished, GError *error, IBusService *service); static void ibus_service_unregister_cb (GDBusConnection *connection, guint *ids, IBusService *service); static const GDBusInterfaceVTable ibus_service_interface_vtable = { (GDBusInterfaceMethodCallFunc) ibus_service_service_method_call_cb, (GDBusInterfaceGetPropertyFunc) ibus_service_service_get_property_cb, (GDBusInterfaceSetPropertyFunc) ibus_service_service_set_property_cb }; static IBusObjectClass *ibus_service_parent_class = NULL; GType ibus_service_get_type (void) { static GType type = 0; static const GTypeInfo type_info = { sizeof (IBusServiceClass), (GBaseInitFunc) ibus_service_base_init, (GBaseFinalizeFunc) ibus_service_base_fini, (GClassInitFunc) ibus_service_class_init, NULL, /* class finialize */ NULL, /* class data */ sizeof (IBusService), 0, (GInstanceInitFunc) ibus_service_init, }; if (type == 0) { type = g_type_register_static (IBUS_TYPE_OBJECT, "IBusService", &type_info, 0); } return type; } static void ibus_service_base_init (IBusServiceClass *class) { GArray *old = class->interfaces; class->interfaces = g_array_new (TRUE, TRUE, sizeof (GDBusInterfaceInfo *)); if (old != NULL) { GDBusInterfaceInfo **p = (GDBusInterfaceInfo **)old->data; while (*p != NULL) { g_array_append_val (class->interfaces, *p++); } } } static void ibus_service_base_fini (IBusServiceClass *class) { GDBusInterfaceInfo **interfaces = (GDBusInterfaceInfo **) g_array_free (class->interfaces, FALSE); GDBusInterfaceInfo **p = interfaces; while (*p != NULL) { g_dbus_interface_info_unref (*p++); } g_free (interfaces); } static const gchar introspection_xml[] = "" " " " " " " ""; static void ibus_service_class_init (IBusServiceClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS (class); IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class); ibus_service_parent_class = IBUS_OBJECT_CLASS (g_type_class_peek_parent (class)); gobject_class->constructed = ibus_service_constructed; gobject_class->set_property = (GObjectSetPropertyFunc) ibus_service_set_property; gobject_class->get_property = (GObjectGetPropertyFunc) ibus_service_get_property; ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_service_destroy; /* virtual functions */ class->service_method_call = ibus_service_service_method_call; class->service_get_property = ibus_service_service_get_property; class->service_set_property = ibus_service_service_set_property; /* class members */ ibus_service_class_add_interfaces (class, introspection_xml); /* install properties */ /** * IBusService:object-path: * * The path of service object. */ g_object_class_install_property ( gobject_class, PROP_OBJECT_PATH, g_param_spec_string ( "object-path", "object path", "The path of service object", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB) ); /** * IBusService:connection: * * The connection of service object. */ g_object_class_install_property ( gobject_class, PROP_CONNECTION, g_param_spec_object ( "connection", "connection", "The connection of service object", G_TYPE_DBUS_CONNECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB) ); g_type_class_add_private (class, sizeof (IBusServicePrivate)); } static void ibus_service_init (IBusService *service) { service->priv = IBUS_SERVICE_GET_PRIVATE (service); service->priv->table = g_hash_table_new (NULL, NULL); } static void ibus_service_constructed (GObject *object) { IBusService *service = (IBusService *)object; if (service->priv->connection) { GError *error = NULL; if (!ibus_service_register (service, service->priv->connection, &error)) { g_warning ("%s", error->message); g_error_free (error); } } service->priv->constructed = TRUE; } static void ibus_service_set_property (IBusService *service, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { case PROP_OBJECT_PATH: service->priv->object_path = g_value_dup_string (value); break; case PROP_CONNECTION: service->priv->connection = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (service, prop_id, pspec); } } static void ibus_service_get_property (IBusService *service, guint prop_id, GValue *value, GParamSpec *pspec) { switch (prop_id) { case PROP_OBJECT_PATH: g_value_set_string (value, service->priv->object_path); break; case PROP_CONNECTION: g_value_set_object (value, service->priv->connection); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (service, prop_id, pspec); } } static void ibus_service_destroy (IBusService *service) { g_free (service->priv->object_path); service->priv->object_path = NULL; if (service->priv->connection) { g_object_unref (service->priv->connection); service->priv->connection = NULL; } if (service->priv->table) { g_hash_table_foreach_remove (service->priv->table, (GHRFunc)ibus_service_unregister_cb, service); g_hash_table_destroy (service->priv->table); service->priv->table = NULL; } IBUS_OBJECT_CLASS(ibus_service_parent_class)->destroy (IBUS_OBJECT (service)); } static void ibus_service_service_method_call (IBusService *service, GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation) { if (g_strcmp0 (method_name, "Destroy") == 0) { g_dbus_method_invocation_return_value (invocation, NULL); ibus_object_destroy ((IBusObject *)service); return; } g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "%s::%s", interface_name, method_name); return; } static GVariant * ibus_service_service_get_property (IBusService *service, GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error) { return NULL; } static gboolean ibus_service_service_set_property (IBusService *service, GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GVariant *value, GError **error) { return FALSE; } static void ibus_service_service_method_call_cb (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, IBusService *service) { IBUS_SERVICE_GET_CLASS (service)->service_method_call (service, connection, sender, object_path, interface_name, method_name, parameters, invocation); } static GVariant * ibus_service_service_get_property_cb (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error, IBusService *service) { return IBUS_SERVICE_GET_CLASS (service)->service_get_property (service, connection, sender, object_path, interface_name, property_name, error); } static gboolean ibus_service_service_set_property_cb (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GVariant *value, GError **error, IBusService *service) { return IBUS_SERVICE_GET_CLASS (service)->service_set_property (service, connection, sender, object_path, interface_name, property_name, value, error); } static void ibus_service_connection_closed_cb (GDBusConnection *connection, gboolean remote_peer_vanished, GError *error, IBusService *service) { ibus_service_unregister (service, connection); if (connection == service->priv->connection) { ibus_object_destroy ((IBusObject *) service); } } static void ibus_service_unregister_cb (GDBusConnection *connection, guint *ids, IBusService *service) { guint *p = ids; while (*p != 0) { g_dbus_connection_unregister_object (connection, *p++); } g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (ibus_service_connection_closed_cb), service); g_object_unref (connection); g_free (ids); } IBusService * ibus_service_new (GDBusConnection *connection, const gchar *object_path) { g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); g_return_val_if_fail (object_path != NULL, NULL); GObject *obj = g_object_new (IBUS_TYPE_SERVICE, "object-path", object_path, "connection", connection, NULL); return IBUS_SERVICE (obj); } const gchar * ibus_service_get_object_path (IBusService *service) { g_return_val_if_fail (IBUS_IS_SERVICE (service), NULL); return service->priv->object_path; } GDBusConnection * ibus_service_get_connection (IBusService *service) { g_return_val_if_fail (IBUS_IS_SERVICE (service), NULL); return service->priv->connection; } gboolean ibus_service_register (IBusService *service, GDBusConnection *connection, GError **error) { GArray *array = NULL; GArray *interfaces; GDBusInterfaceInfo **p; g_return_val_if_fail (IBUS_IS_SERVICE (service), FALSE); g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (g_hash_table_lookup (service->priv->table, connection)) { if (error) { *error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_OBJECT_PATH_IN_USE, "Service %p has been registered with connection %p.", service, connection); } goto error_out; } interfaces = IBUS_SERVICE_GET_CLASS (service)->interfaces; g_assert (interfaces); p = (GDBusInterfaceInfo **)interfaces->data; if (*p == NULL) { if (error) { *error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Service %p does not have any interface.", service); } goto error_out; } array = g_array_new (TRUE, TRUE, sizeof (guint)); while (*p != NULL) { guint id = g_dbus_connection_register_object (connection, service->priv->object_path, *p, &ibus_service_interface_vtable, g_object_ref (service), (GDestroyNotify)g_object_unref, error); if (id != 0) { g_array_append_val (array, id); } else { g_object_unref (service); goto error_out; } p++; } g_signal_connect (connection, "closed", G_CALLBACK (ibus_service_connection_closed_cb), service); g_hash_table_insert (service->priv->table, g_object_ref (connection), g_array_free (array, FALSE)); return TRUE; error_out: if (array != NULL) { guint *ids = (guint*) array->data; while (*ids != 0) { g_dbus_connection_unregister_object (connection, *ids++); } g_array_free (array, TRUE); } return FALSE; } void ibus_service_unregister (IBusService *service, GDBusConnection *connection) { g_return_if_fail (IBUS_IS_SERVICE (service)); g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); guint *ids = (guint *) g_hash_table_lookup (service->priv->table, connection); g_return_if_fail (ids != NULL); g_hash_table_remove (service->priv->table, connection); ibus_service_unregister_cb (connection, ids, service); } gboolean ibus_service_emit_signal (IBusService *service, const gchar *dest_bus_name, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, GError **error) { g_return_val_if_fail (IBUS_IS_SERVICE (service), FALSE); g_return_val_if_fail (interface_name != NULL, FALSE); g_return_val_if_fail (signal_name != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); g_return_val_if_fail (service->priv->connection != NULL, FALSE); return g_dbus_connection_emit_signal (service->priv->connection, dest_bus_name, service->priv->object_path, interface_name, signal_name, parameters, error); } gboolean ibus_service_class_add_interfaces (IBusServiceClass *class, const gchar *xml_data) { g_return_val_if_fail (IBUS_IS_SERVICE_CLASS (class), FALSE); g_return_val_if_fail (xml_data != NULL, FALSE); GError *error = NULL; GDBusNodeInfo *introspection_data = g_dbus_node_info_new_for_xml (xml_data, &error); if (introspection_data == NULL) { g_warning ("%s", error->message); g_error_free (error); return FALSE; } else { GDBusInterfaceInfo **p = introspection_data->interfaces; while (*p != NULL) { g_dbus_interface_info_ref (*p); g_array_append_val (class->interfaces, *p); p++; } g_dbus_node_info_unref (introspection_data); return TRUE; } }