/* diversity-control.c - Everthing is under control * * Copyright 2007-2008 OpenMoko, Inc. * Authored by Chia-I Wu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include /* for strcmp */ #include "diversity-control.h" #include #include #include #include #include #include #include #include #include "control-marshal.h" #include "control-ap.h" #include "control-atlas.h" #include "control-bard.h" #include "control-equipment.h" #include "control-object.h" #include "control-sms.h" #include "control-tag.h" #include "control-viewport.h" #include "control-world.h" #define DIVERSITY_CONTROL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), DIVERSITY_TYPE_CONTROL, DiversityControlPrivate)) typedef struct _DiversityControlPrivate DiversityControlPrivate; struct _DiversityControlPrivate { DiversityWorld *world; DBusGConnection *connection; gchar *path; DBusGProxy *dbus_proxy; GHashTable *clients; }; static GQuark diversity_control_export_quark = 0; static gboolean diversity_control_version(DiversityControl *ctrl, gint *OUT_major, gint *OUT_minor, gint *OUT_micro, GError **error); #include "diversity-control-glue.h" G_DEFINE_TYPE(DiversityControl, diversity_control, G_TYPE_OBJECT); static gboolean remove_client(gpointer val, gpointer data) { DiversityControl *ctrl = data; DiversityControlPrivate *priv = DIVERSITY_CONTROL_GET_PRIVATE(ctrl); GSList *list, *tmp_list; DiversityObject *obj; /* make a copy for traversal */ list = g_slist_copy(val); tmp_list = list; while (tmp_list) { obj = tmp_list->data; switch (obj->type) { case DIVERSITY_OBJECT_TYPE_VIEWPORT: diversity_control_object_unexport(ctrl, obj); diversity_world_remove_viewport(priv->world, DIVERSITY_VIEWPORT(obj)); break; default: g_warning("object of type %d is owned", obj->type); break; } tmp_list = tmp_list->next; } g_slist_free(list); return TRUE; } static void diversity_control_finalize(GObject *obj) { DiversityControlPrivate *priv = DIVERSITY_CONTROL_GET_PRIVATE(obj); GList *clients; dbus_g_connection_unref(priv->connection); clients = g_hash_table_get_values(priv->clients); g_list_foreach(clients, (GFunc) remove_client, obj); g_list_free(clients); g_hash_table_destroy(priv->clients); g_free(priv->path); ((GObjectClass *) diversity_control_parent_class)->finalize(obj); } static void diversity_control_dispose(GObject *obj) { DiversityControlPrivate *priv = DIVERSITY_CONTROL_GET_PRIVATE(obj); g_object_unref(priv->dbus_proxy); g_object_unref(priv->world); ((GObjectClass *) diversity_control_parent_class)->dispose(obj); } static void diversity_control_class_init(DiversityControlClass *klass) { GObjectClass *o_class = (GObjectClass *) klass; o_class->finalize = diversity_control_finalize; o_class->dispose = diversity_control_dispose; g_type_class_add_private(klass, sizeof(DiversityControlPrivate)); dbus_g_object_type_install_info(G_TYPE_FROM_CLASS(klass), &dbus_glib_diversity_control_object_info); } static void diversity_control_init(DiversityControl *ctrl) { DiversityControlPrivate *priv = DIVERSITY_CONTROL_GET_PRIVATE(ctrl); priv->world = NULL; priv->connection = NULL; priv->path = NULL; priv->dbus_proxy = NULL; priv->clients = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); } static void diversity_control_client_own(DiversityControl *ctrl, gchar *owner, DiversityObject *obj) { DiversityControlPrivate *priv = DIVERSITY_CONTROL_GET_PRIVATE(ctrl); GSList *list; list = g_hash_table_lookup(priv->clients, owner); switch (obj->type) { case DIVERSITY_OBJECT_TYPE_VIEWPORT: list = g_slist_prepend(list, obj); g_object_ref(obj); break; default: g_warning("only viewports can be owned"); break; } g_hash_table_insert(priv->clients, owner, list); } static void diversity_control_client_unown(DiversityControl *ctrl, const gchar *owner, DiversityObject *obj) { DiversityControlPrivate *priv = DIVERSITY_CONTROL_GET_PRIVATE(ctrl); GSList *list; list = g_hash_table_lookup(priv->clients, owner); switch (obj->type) { case DIVERSITY_OBJECT_TYPE_VIEWPORT: if (g_slist_find(list, obj)) { list = g_slist_remove(list, obj); g_object_unref(obj); } break; default: g_warning("only viewports can be unowned"); break; } if (list) g_hash_table_insert(priv->clients, g_strdup(owner), list); else g_hash_table_remove(priv->clients, owner); } static void on_name_owner_changed(DBusGProxy *proxy, const gchar *name, const gchar *old_owner, const gchar *new_owner, gpointer data) { DiversityControl *ctrl = data; /* only interested in client disconnection */ if (new_owner[0] == '\0' && strcmp(name, old_owner) == 0) { DiversityControlPrivate *priv = DIVERSITY_CONTROL_GET_PRIVATE(ctrl); gpointer val; val = g_hash_table_lookup(priv->clients, name); if (val) { remove_client(val, ctrl); g_hash_table_remove(priv->clients, name); } } } DiversityControl *diversity_control_new(DiversityWorld *world, DBusGConnection *connection, const gchar *path) { DiversityControl *ctrl; DiversityControlPrivate *priv; gchar *world_path; if (!world || !connection || !path) return NULL; if (!diversity_control_export_quark) diversity_control_export_quark = g_quark_from_static_string( "diversity_control_export_quark"); ctrl = g_object_new(DIVERSITY_TYPE_CONTROL, NULL); priv = DIVERSITY_CONTROL_GET_PRIVATE(ctrl); priv->world = world; g_object_ref(world); priv->connection = connection; dbus_g_connection_ref(connection); priv->path = g_strdup(path); priv->dbus_proxy = dbus_g_proxy_new_for_name(priv->connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); dbus_g_proxy_add_signal(priv->dbus_proxy, "NameOwnerChanged", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); dbus_g_proxy_connect_signal(priv->dbus_proxy, "NameOwnerChanged", G_CALLBACK(on_name_owner_changed), ctrl, NULL); dbus_g_connection_register_g_object(priv->connection, priv->path, G_OBJECT(ctrl)); dbus_g_object_type_install_info(DIVERSITY_TYPE_WORLD, &dbus_glib_control_world_object_info); dbus_g_object_type_install_info(DIVERSITY_TYPE_VIEWPORT, &dbus_glib_control_viewport_object_info); dbus_g_object_type_install_info(DIVERSITY_TYPE_TAG, &dbus_glib_control_tag_object_info); dbus_g_object_type_install_info(DIVERSITY_TYPE_OBJECT, &dbus_glib_control_object_object_info); dbus_g_object_type_install_info(DIVERSITY_TYPE_BARD, &dbus_glib_control_bard_object_info); dbus_g_object_type_install_info(DIVERSITY_TYPE_AP, &dbus_glib_control_ap_object_info); dbus_g_object_type_install_info(DIVERSITY_TYPE_EQUIPMENT, &dbus_glib_control_equipment_object_info); dbus_g_object_type_install_info(DIVERSITY_TYPE_ATLAS, &dbus_glib_control_atlas_object_info); dbus_g_object_type_install_info(DIVERSITY_TYPE_SMS, &dbus_glib_control_sms_object_info); world_path = g_strdup_printf("%s/world", path); dbus_g_connection_register_g_object(priv->connection, world_path, G_OBJECT(priv->world)); g_free(world_path); /* abusing diversity_control_export_quark */ g_object_set_qdata(G_OBJECT(world), diversity_control_export_quark, ctrl); return ctrl; } static gboolean diversity_control_version(DiversityControl *ctrl, gint *OUT_major, gint *OUT_minor, gint *OUT_micro, GError **error) { *OUT_major = 0; *OUT_minor = 0; *OUT_micro = 0; return TRUE; } GQuark diversity_control_error_quark() { static GQuark q = 0; if (!q) q = g_quark_from_static_string( "diversity-control-error-quark"); return q; } static void _diversity_control_export_free(DiversityControlExport *export) { g_free(export->path); g_slice_free(DiversityControlExport, export); } void on_object_added(DiversityViewport *view, DiversityObject *obj, gpointer data) { DiversityControl *ctrl = data; diversity_control_object_export(ctrl, DIVERSITY_OBJECT(obj), NULL); } static void export_equipments(DiversityControl *ctrl, DiversityBard *bard) { DiversityControlPrivate *priv = DIVERSITY_CONTROL_GET_PRIVATE(ctrl); DiversityControlExport *export; GSList *eqp_list; export = g_object_get_qdata(G_OBJECT(bard), diversity_control_export_quark); eqp_list = (GSList *) diversity_bard_get_equipments(bard); while (eqp_list) { DiversityEquipment *eqp = eqp_list->data; gchar *path; path = g_strdup_printf("%s/equipments/%s", export->path, diversity_equipment_get_name(eqp)); dbus_g_connection_register_g_object(priv->connection, path, G_OBJECT(eqp)); g_free(path); eqp_list = eqp_list->next; } } DiversityControlExport *diversity_control_object_export(DiversityControl *ctrl, DiversityObject *obj, gchar *owner) { DiversityControlPrivate *priv = DIVERSITY_CONTROL_GET_PRIVATE(ctrl); DiversityControlExport *export; gchar *category; export = g_object_get_qdata(G_OBJECT(obj), diversity_control_export_quark); if (export) return export; switch (obj->type) { case DIVERSITY_OBJECT_TYPE_VIEWPORT: category = "viewports"; /* * This should come before dbus registration. * The handlers are called in the order of registration. */ g_signal_connect(obj, "object-added", G_CALLBACK(on_object_added), ctrl); break; default: category = "objects"; break; } export = g_slice_new(DiversityControlExport); export->ctrl = ctrl; export->connection = priv->connection; export->path = g_strdup_printf("%s/%s/%d", priv->path, category, obj->serial_number); dbus_g_connection_register_g_object(export->connection, export->path, G_OBJECT(obj)); g_object_set_qdata_full(G_OBJECT(obj), diversity_control_export_quark, export, (GDestroyNotify) _diversity_control_export_free); switch (obj->type) { case DIVERSITY_OBJECT_TYPE_BARD: export_equipments(ctrl, DIVERSITY_BARD(obj)); break; default: break; } if (owner) { diversity_control_client_own(ctrl, owner, obj); /* owner points to a hash key inside clients hash table */ export->owner = owner; } return export; } void diversity_control_object_unexport(DiversityControl *ctrl, DiversityObject *obj) { DiversityControlExport *export; export = g_object_steal_qdata(G_OBJECT(obj), diversity_control_export_quark); if (!export) return; if (export->owner) { diversity_control_client_unown(ctrl, export->owner, obj); export->owner = NULL; } _diversity_control_export_free(export); /* XXX really unexport*/ } DiversityObject *diversity_control_object_lookup(DiversityControl *ctrl, const gchar *path) { DiversityControlPrivate *priv = DIVERSITY_CONTROL_GET_PRIVATE(ctrl); GObject *gobj; gobj = dbus_g_connection_lookup_g_object(priv->connection, path); if (!gobj) return NULL; return DIVERSITY_OBJECT(gobj); } DiversityControl *diversity_control_from_world(DiversityWorld *world) { return (DiversityControl *) g_object_get_qdata(G_OBJECT(world), diversity_control_export_quark); } DiversityControl *diversity_control_from_object(DiversityObject *obj) { DiversityControlExport *export; export = g_object_get_qdata(G_OBJECT(obj), diversity_control_export_quark); g_assert(export); return export->ctrl; } DiversityControl *diversity_control_from_equipment(DiversityEquipment *eqp) { DiversityBard *owner; owner = diversity_equipment_get_equipper(eqp); if (!owner) { const gchar *name; name = diversity_equipment_get_name(eqp); g_warning("equipment %s is not equipped\n", name); return NULL; } return diversity_control_from_object(DIVERSITY_OBJECT(owner)); }