Back to index

libindicate  12.10.0
listener.c
Go to the documentation of this file.
00001 /*
00002 A library to allow applictions to provide simple indications of
00003 information to be displayed to users of the application through the
00004 interface shell.
00005 
00006 Copyright 2009 Canonical Ltd.
00007 
00008 Authors:
00009     Ted Gould <ted@canonical.com>
00010 
00011 This program is free software: you can redistribute it and/or modify it 
00012 under the terms of either or both of the following licenses:
00013 
00014 1) the GNU Lesser General Public License version 3, as published by the 
00015 Free Software Foundation; and/or
00016 2) the GNU Lesser General Public License version 2.1, as published by 
00017 the Free Software Foundation.
00018 
00019 This program is distributed in the hope that it will be useful, but 
00020 WITHOUT ANY WARRANTY; without even the implied warranties of 
00021 MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR 
00022 PURPOSE.  See the applicable version of the GNU Lesser General Public 
00023 License for more details.
00024 
00025 You should have received a copy of both the GNU Lesser General Public 
00026 License version 3 and version 2.1 along with this program.  If not, see 
00027 <http://www.gnu.org/licenses/>
00028 */
00029 
00030 #include <stdlib.h>
00031 #include <gio/gio.h>
00032 #include "listener.h"
00033 #include "interests-priv.h"
00034 #include "dbus-shared.h"
00035 #include "gen-indicate-interface.xml.h"
00036 #include "gen-indicate-listener.xml.h"
00037 #include "indicate-marshal.h"
00038 
00039 /* Errors */
00040 enum {
00041        LAST_ERROR
00042 };
00043 
00044 /* Signals */
00045 enum {
00046        INDICATOR_ADDED,
00047        INDICATOR_REMOVED,
00048        INDICATOR_MODIFIED,
00049        SERVER_ADDED,
00050        SERVER_REMOVED,
00051        SERVER_COUNT_CHANGED,
00052        INDICATOR_SERVERS_REPORT,
00053        LAST_SIGNAL
00054 };
00055 
00056 static guint signals[LAST_SIGNAL] = { 0 };
00057 
00058 #include "listener-private.h"
00059 
00060 typedef struct {
00061        GDBusProxy * proxy;
00062        GDBusProxy * property_proxy;
00063        GDBusConnection * connection;
00064        gchar * name;
00065        gchar * path;
00066        gchar * type;
00067        IndicateListener * listener;
00068        GHashTable * indicators;
00069        guint introspect_level;
00070        gboolean hidden;
00071        guint dbus_listener_sub;
00072 
00073        IndicateListenerServer server;
00074 } proxy_t;
00075 
00076 static gint
00077 proxy_t_equal (gconstpointer pa, gconstpointer pb)
00078 {
00079        proxy_t * a = (proxy_t *)pa; proxy_t * b = (proxy_t *)pb;
00080 
00081        if  (a->connection == b->connection) {
00082                if (g_strcmp0(a->name, b->name) == 0) {
00083                       return g_strcmp0(a->path, b->path);
00084               }
00085         }
00086 
00087        /* we're only using this for equal, not sorting */
00088        return 1;
00089 
00090 }
00091 
00092 typedef struct {
00093        GDBusConnection * bus;
00094        gchar * name;
00095        gchar * path;
00096 } proxy_todo_t;
00097 
00098 G_DEFINE_TYPE (IndicateListener, indicate_listener, G_TYPE_OBJECT);
00099 
00100 /* Prototypes */
00101 static void indicate_listener_finalize (GObject * obj);
00102 static void indicate_listener_dispose (GObject * obj);
00103 static void proxy_struct_destroy (gpointer data);
00104 static void todo_list_add (const gchar * name, const gchar * path, IndicateListener * listener);
00105 static gboolean todo_idle (gpointer data);
00106 static void set_max_indicators_cb (GObject * object, GAsyncResult * res, gpointer user_data);
00107 static void get_type_initial_cb (IndicateListener * listener, IndicateListenerServer * server, const gchar * type, gpointer data);
00108 static void get_type_cb (IndicateListener * listener, IndicateListenerServer * server, const gchar * type, gpointer data);
00109 static void proxy_indicator_added_legacy (guint id, gchar * type, proxy_t * proxyt);
00110 static void proxy_indicator_added (guint id, proxy_t * proxyt);
00111 static void proxy_indicator_removed_legacy (guint id, gchar * type, proxy_t * proxyt);
00112 static void proxy_indicator_removed (guint id, proxy_t * proxyt);
00113 static void proxy_indicator_modified (guint id, const gchar * property, proxy_t * proxyt);
00114 static void proxy_server_count_changed (guint count, proxy_t * proxyt);
00115 static void proxy_get_indicator_list (GObject * object, GAsyncResult * res, gpointer data);
00116 static void proxy_destroyed (GObject * proxy, gpointer user_data);
00117 static void bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * method, GVariant * params, GDBusMethodInvocation * invocation, gpointer user_data);
00118 static void bus_get_cb (GObject * object, GAsyncResult * res, gpointer user_data);
00119 static void bus_filter_show_server (GDBusConnection * connection, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data);
00120 static void prox_ready_cb (GObject * object, GAsyncResult * res, gpointer user_data);
00121 static void proxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVariant * params, gpointer user_data);
00122 
00123 /* Bus Stuff */
00124 static GDBusNodeInfo *            bus_indicate_node_info = NULL;
00125 static GDBusInterfaceInfo *       bus_indicate_interface_info = NULL;
00126 static GDBusNodeInfo *            bus_listener_node_info = NULL;
00127 static GDBusInterfaceInfo *       bus_listener_interface_info = NULL;
00128 static const GDBusInterfaceVTable bus_interface_table = {
00129        method_call:    bus_method_call,
00130        get_property:   NULL, /* No properties that can be get */
00131        set_property:   NULL  /* No properties that can be set */
00132 };
00133 
00134 
00135 /* Code */
00136 static void
00137 indicate_listener_class_init (IndicateListenerClass * class)
00138 {
00139        /* g_debug("Listener Class Initialized"); */
00140        GObjectClass * gobj;
00141        gobj = G_OBJECT_CLASS(class);
00142 
00143        g_type_class_add_private (class, sizeof (IndicateListenerPrivate));
00144 
00145        gobj->dispose = indicate_listener_dispose;
00146        gobj->finalize = indicate_listener_finalize;
00147 
00148        signals[INDICATOR_ADDED] = g_signal_new(INDICATE_LISTENER_SIGNAL_INDICATOR_ADDED,
00149                                                G_TYPE_FROM_CLASS (class),
00150                                                G_SIGNAL_RUN_LAST,
00151                                                G_STRUCT_OFFSET (IndicateListenerClass, indicator_added),
00152                                                NULL, NULL,
00153                                                _indicate_marshal_VOID__POINTER_POINTER,
00154                                                G_TYPE_NONE, 2, INDICATE_TYPE_LISTENER_SERVER, INDICATE_TYPE_LISTENER_INDICATOR);
00155        signals[INDICATOR_REMOVED] = g_signal_new(INDICATE_LISTENER_SIGNAL_INDICATOR_REMOVED,
00156                                                G_TYPE_FROM_CLASS (class),
00157                                                G_SIGNAL_RUN_LAST,
00158                                                G_STRUCT_OFFSET (IndicateListenerClass, indicator_removed),
00159                                                NULL, NULL,
00160                                                _indicate_marshal_VOID__POINTER_POINTER,
00161                                                G_TYPE_NONE, 2, INDICATE_TYPE_LISTENER_SERVER, INDICATE_TYPE_LISTENER_INDICATOR);
00162        signals[INDICATOR_MODIFIED] = g_signal_new(INDICATE_LISTENER_SIGNAL_INDICATOR_MODIFIED,
00163                                                G_TYPE_FROM_CLASS (class),
00164                                                G_SIGNAL_RUN_LAST,
00165                                                G_STRUCT_OFFSET (IndicateListenerClass, indicator_modified),
00166                                                NULL, NULL,
00167                                                _indicate_marshal_VOID__POINTER_POINTER_STRING,
00168                                                G_TYPE_NONE, 3, INDICATE_TYPE_LISTENER_SERVER, INDICATE_TYPE_LISTENER_INDICATOR, G_TYPE_STRING);
00169        signals[SERVER_ADDED] = g_signal_new(INDICATE_LISTENER_SIGNAL_SERVER_ADDED,
00170                                                G_TYPE_FROM_CLASS (class),
00171                                                G_SIGNAL_RUN_LAST,
00172                                                G_STRUCT_OFFSET (IndicateListenerClass, server_added),
00173                                                NULL, NULL,
00174                                                _indicate_marshal_VOID__POINTER_STRING,
00175                                                G_TYPE_NONE, 2, INDICATE_TYPE_LISTENER_SERVER, G_TYPE_STRING);
00176        signals[SERVER_REMOVED] = g_signal_new(INDICATE_LISTENER_SIGNAL_SERVER_REMOVED,
00177                                                G_TYPE_FROM_CLASS (class),
00178                                                G_SIGNAL_RUN_LAST,
00179                                                G_STRUCT_OFFSET (IndicateListenerClass, server_removed),
00180                                                NULL, NULL,
00181                                                _indicate_marshal_VOID__POINTER_STRING,
00182                                                G_TYPE_NONE, 2, INDICATE_TYPE_LISTENER_SERVER, G_TYPE_STRING);
00183        signals[SERVER_COUNT_CHANGED] = g_signal_new(INDICATE_LISTENER_SIGNAL_SERVER_COUNT_CHANGED,
00184                                                G_TYPE_FROM_CLASS (class),
00185                                                G_SIGNAL_RUN_LAST,
00186                                                G_STRUCT_OFFSET (IndicateListenerClass, server_count_changed),
00187                                                NULL, NULL,
00188                                                _indicate_marshal_VOID__POINTER_UINT,
00189                                                G_TYPE_NONE, 2, INDICATE_TYPE_LISTENER_SERVER, G_TYPE_UINT);
00190        signals[INDICATOR_SERVERS_REPORT] = g_signal_new("indicator-servers-report",
00191                                                G_TYPE_FROM_CLASS (class),
00192                                                G_SIGNAL_RUN_LAST,
00193                                                G_STRUCT_OFFSET (IndicateListenerClass, indicator_servers_report),
00194                                                NULL, NULL,
00195                                                g_cclosure_marshal_VOID__VOID,
00196                                                G_TYPE_NONE, 0, G_TYPE_NONE);
00197 
00198        /* DBus interfaces */
00199        if (bus_indicate_node_info == NULL) {
00200               GError * error = NULL;
00201 
00202               bus_indicate_node_info = g_dbus_node_info_new_for_xml(_indicate_interface, &error);
00203               if (error != NULL) {
00204                      g_error("Unable to parse Indicate Interface description: %s", error->message);
00205                      g_error_free(error);
00206               }
00207        }
00208 
00209        if (bus_indicate_interface_info == NULL) {
00210               bus_indicate_interface_info = g_dbus_node_info_lookup_interface(bus_indicate_node_info, INDICATE_DBUS_IFACE);
00211 
00212               if (bus_indicate_interface_info == NULL) {
00213                      g_error("Unable to find interface '" INDICATE_DBUS_IFACE "'");
00214               }
00215        }
00216 
00217        if (bus_listener_node_info == NULL) {
00218               GError * error = NULL;
00219 
00220               bus_listener_node_info = g_dbus_node_info_new_for_xml(_indicate_listener, &error);
00221               if (error != NULL) {
00222                      g_error("Unable to parse Listener Interface description: %s", error->message);
00223                      g_error_free(error);
00224               }
00225        }
00226 
00227        if (bus_listener_interface_info == NULL) {
00228               bus_listener_interface_info = g_dbus_node_info_lookup_interface(bus_listener_node_info, INDICATE_LISTENER_DBUS_IFACE);
00229 
00230               if (bus_listener_interface_info == NULL) {
00231                      g_error("Unable to find interface '" INDICATE_LISTENER_DBUS_IFACE "'");
00232               }
00233        }
00234 
00235        return;
00236 }
00237 
00238 static void
00239 indicate_listener_init (IndicateListener * listener)
00240 {
00241        /* g_debug("Listener Object Initialized"); */
00242        IndicateListenerPrivate * priv = INDICATE_LISTENER_GET_PRIVATE(listener);
00243 
00244        /* Get the buses */
00245        g_bus_get(G_BUS_TYPE_SESSION,
00246                  NULL, /* TODO: Cancelable */
00247                  bus_get_cb,
00248                  listener);
00249 
00250        /* Initialize Data structures */
00251        priv->proxies = NULL;
00252 
00253        /* TODO: Look at some common scenarios and find out how to make this sized */
00254        priv->proxy_todo = g_array_new(FALSE, TRUE, sizeof(proxy_todo_t));
00255        priv->todo_idle = 0;
00256 
00257        priv->max_indicators = -1;
00258 
00259        return;
00260 }
00261 
00262 /* Remove references to objects */
00263 static void
00264 indicate_listener_dispose (GObject * obj)
00265 {
00266        IndicateListener * listener = INDICATE_LISTENER(obj);
00267         IndicateListenerPrivate * priv = INDICATE_LISTENER_GET_PRIVATE(listener);
00268 
00269        if (priv->signal_subscription != 0) {
00270               g_dbus_connection_signal_unsubscribe(priv->session_bus, priv->signal_subscription);
00271               priv->signal_subscription = 0;
00272        }
00273 
00274        if (priv->object_registration != 0) {
00275               g_dbus_connection_signal_unsubscribe(priv->session_bus, priv->object_registration);
00276               priv->object_registration = 0;
00277        }
00278 
00279        G_OBJECT_CLASS (indicate_listener_parent_class)->dispose (obj);
00280        return;
00281 }
00282 
00283 /* Free memory */
00284 static void
00285 indicate_listener_finalize (GObject * obj)
00286 {
00287        IndicateListener * listener = INDICATE_LISTENER(obj);
00288        IndicateListenerPrivate * priv = INDICATE_LISTENER_GET_PRIVATE(listener);
00289 
00290        if (priv->todo_idle != 0) {
00291               g_idle_remove_by_data(obj);
00292        }
00293 
00294        /* Hack: proxy_struct_destroy() lacks a user_data parameter, but since the
00295         * caller is responsible for handling params on the stack, it works
00296         */
00297        g_list_foreach(priv->proxies, (GFunc)proxy_struct_destroy, NULL);
00298        g_list_free(priv->proxies);
00299 
00300        G_OBJECT_CLASS (indicate_listener_parent_class)->finalize (obj);
00301        return;
00302 }
00303 
00304 /* Respond to a name change related to the proxy, usually mean that
00305    it's falling off of the bus. */
00306 static void
00307 proxy_name_owner_changed (GDBusConnection * connection, const gchar * sender_name, const gchar * object_path, const gchar * interface_name, const gchar * signal_name, GVariant * parameters, gpointer user_data)
00308 {
00309        const gchar * new_name;
00310        g_variant_get(parameters, "(&s&s&s)", NULL, NULL, &new_name);
00311 
00312        if (new_name == NULL || new_name[0] == 0) {
00313               proxy_t * proxyt = (proxy_t *)user_data;
00314 
00315               g_object_unref(proxyt->proxy);
00316               proxyt->proxy = NULL;
00317 
00318               proxy_destroyed(NULL, user_data);
00319        }
00320 
00321        return;
00322 }
00323 
00324 /* Response to connecting to the session bus */
00325 static void
00326 bus_get_cb (GObject * object, GAsyncResult * res, gpointer user_data)
00327 {
00328        GError * error = NULL;
00329        GDBusConnection * connection = g_bus_get_finish(res, &error);
00330 
00331        if (error != NULL) {
00332               g_error("Unable to get session bus: %s", error->message);
00333               g_error_free(error);
00334               return;
00335        }
00336 
00337        IndicateListenerPrivate * priv = INDICATE_LISTENER_GET_PRIVATE(user_data);
00338        priv->session_bus = connection;
00339 
00340        /* Export this object onto the bus */
00341        priv->object_registration = g_dbus_connection_register_object(priv->session_bus,
00342                                                                      INDICATE_LISTENER_OBJ_PATH,
00343                                                                      bus_listener_interface_info,
00344                                                                      &bus_interface_table,
00345                                                                      user_data,
00346                                                                      NULL,
00347                                                                      &error);
00348 
00349        if (error != NULL) {
00350               g_error("Unable to register listener on the bus: %s", error->message);
00351               g_error_free(error);
00352               return;
00353        }
00354 
00355        /* Watch for servers starting up */
00356        priv->signal_subscription = g_dbus_connection_signal_subscribe(priv->session_bus,
00357                                                                       NULL, /* sender */
00358                                                                       INDICATE_DBUS_IFACE,
00359                                                                       "ServerShow",
00360                                                                       NULL, /* path */
00361                                                                       NULL, /* arg0 */
00362                                                                       G_DBUS_SIGNAL_FLAGS_NONE,
00363                                                                       bus_filter_show_server,
00364                                                                       user_data,
00365                                                                       NULL); /* destroy notify */
00366 
00367        /* Tell that we're here! */
00368        g_dbus_connection_emit_signal(priv->session_bus,
00369                                      NULL, /* dest */
00370                                      INDICATE_LISTENER_OBJ_PATH,
00371                                      INDICATE_LISTENER_DBUS_IFACE,
00372                                      "IndicatorServersReport",
00373                                      NULL, /* params */
00374                                      &error);
00375 
00376        if (error != NULL) {
00377               g_warning("Unable to send the 'IndicatorServersReport' signal: %s", error->message);
00378               g_error_free(error);
00379               return;
00380        }
00381 
00382        return;
00383 }
00384 
00385 /* Handle a metho call from dbus */
00386 static void
00387 bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * method, GVariant * params, GDBusMethodInvocation * invocation, gpointer user_data)
00388 {
00389 
00390        /* TODO: We should do this, in the future though */
00391 }
00392 
00401 IndicateListener *
00402 indicate_listener_new (void)
00403 {
00404        IndicateListener * listener;
00405        listener = g_object_new(INDICATE_TYPE_LISTENER, NULL);
00406        return listener;
00407 }
00408 
00409 /* The pointer to the default listener object */
00410 static IndicateListener * default_indicate_listener = NULL;
00411 
00425 IndicateListener *
00426 indicate_listener_ref_default (void)
00427 {
00428        if (default_indicate_listener != NULL) {
00429               g_object_ref(default_indicate_listener);
00430        } else {
00431               default_indicate_listener = g_object_new(INDICATE_TYPE_LISTENER, NULL);
00432               g_object_add_weak_pointer(G_OBJECT(default_indicate_listener),
00433                                         (gpointer *)&default_indicate_listener);
00434        }
00435 
00436        return default_indicate_listener;
00437 }
00438 
00439 /* A small filter function that notices when someone sends
00440    a ServerShow signal and creates an entry for us to investigate
00441    them more in an idle loop lookup. */
00442 static void
00443 bus_filter_show_server (GDBusConnection * connection, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
00444 {
00445        if (g_strcmp0(signal, "ServerShow") != 0) {
00446               return;
00447        }
00448 
00449        todo_list_add(sender, object, INDICATE_LISTENER(user_data));
00450 
00451        return;
00452 }
00453 
00454 /* A hashtable for each function to look at all of the indicators
00455    on a proxy_t object and signal their destruction */
00456 static void
00457 proxy_struct_destroy_indicators (gpointer key, gpointer value, gpointer data)
00458 {
00459        proxy_t * proxy_data = (proxy_t *)data;
00460 
00461        if (value) {
00462               g_signal_emit(proxy_data->listener, signals[INDICATOR_REMOVED], 0, &proxy_data->server, GUINT_TO_POINTER(key), TRUE);
00463        }
00464        return;
00465 }
00466 
00467 /* Cleans up a proxy_t struct after allocation.  It signals that
00468    all of the indicators are going away and the server itself. */
00469 static void
00470 proxy_struct_destroy (gpointer data)
00471 {
00472        proxy_t * proxy_data = data;
00473 
00474        if (proxy_data->indicators != NULL) {
00475               g_hash_table_foreach(proxy_data->indicators,
00476                                                   proxy_struct_destroy_indicators,
00477                                                   proxy_data);
00478               g_hash_table_destroy(proxy_data->indicators);
00479 
00480               g_signal_emit(proxy_data->listener, signals[SERVER_REMOVED], 0, &proxy_data->server, proxy_data->type, TRUE);
00481               proxy_data->indicators = NULL;
00482        }
00483 
00484        if (proxy_data->dbus_listener_sub != 0) {
00485               g_dbus_connection_signal_unsubscribe(proxy_data->connection, proxy_data->dbus_listener_sub);
00486               proxy_data->dbus_listener_sub = 0;
00487        }
00488 
00489        if (proxy_data->proxy != NULL) {
00490               g_object_unref(G_OBJECT(proxy_data->proxy));
00491        }
00492 
00493        if (proxy_data->name != NULL) {
00494               g_free(proxy_data->name);
00495        }
00496 
00497        if (proxy_data->path != NULL) {
00498               g_free(proxy_data->path);
00499        }
00500 
00501        if (proxy_data->type != NULL) {
00502               g_free(proxy_data->type);
00503        }
00504        g_free(proxy_data);
00505 
00506        return;
00507 }
00508 
00509 /* Creates a todo list item for the particular server and
00510    path as they've signaled that they're on DBus and like
00511    talking about indicators. */
00512 static void
00513 todo_list_add (const gchar * name, const gchar * path, IndicateListener * listener)
00514 {
00515        if (name == NULL || name[0] != ':') {
00516               return;
00517        }
00518 
00519        IndicateListenerPrivate * priv = INDICATE_LISTENER_GET_PRIVATE(listener);
00520 
00521        proxy_todo_t todo;
00522        todo.name = g_strdup(name);
00523        todo.path = g_strdup(path);
00524        todo.bus  = priv->session_bus;
00525 
00526        g_array_append_val(priv->proxy_todo, todo);
00527 
00528        if (priv->todo_idle == 0) {
00529               priv->todo_idle = g_idle_add(todo_idle, listener);
00530        }
00531 
00532        return;
00533 }
00534 
00535 /* This is the callback for when the proxy, which is attached
00536    to the name owner, is destroy (they fell off the bus) we handle
00537    that and free up our memory too. */
00538 void
00539 proxy_destroyed (GObject * proxy, gpointer user_data)
00540 {
00541        proxy_t * proxyt = (proxy_t *)user_data;
00542        proxyt->proxy = NULL; /* Clear this so we don't get a double free on this guy */
00543        IndicateListener * listener = proxyt->listener;
00544        IndicateListenerPrivate * priv = INDICATE_LISTENER_GET_PRIVATE(listener);
00545        priv->proxies = g_list_remove_all(priv->proxies, proxyt);
00546        proxy_struct_destroy(proxyt);
00547        return;
00548 }
00549 
00550 gboolean
00551 todo_idle (gpointer data)
00552 {
00553        IndicateListener * listener = INDICATE_LISTENER(data);
00554        if (listener == NULL) {
00555               g_error("Listener got lost in todo_idle");
00556               return FALSE;
00557        }
00558 
00559        IndicateListenerPrivate * priv = INDICATE_LISTENER_GET_PRIVATE(listener);
00560 
00561        if (priv->proxy_todo->len == 0) {
00562               /* Basically if we have no todo, we need to stop running.  This
00563                * is done this way to make the function error handling simpler
00564                * and results in an extra run */
00565               priv->todo_idle = 0;
00566               return FALSE;
00567        }
00568 
00569        proxy_todo_t * todo = &g_array_index(priv->proxy_todo, proxy_todo_t, priv->proxy_todo->len - 1);
00570        /* Remove the todo list */
00571        priv->proxy_todo = g_array_remove_index(priv->proxy_todo, priv->proxy_todo->len - 1);
00572 
00573        /* Check to see if we already have this item, if so,
00574           we assume that it's signal handler will handle the
00575           ServerShow signal.  We're just going to exit this 
00576           function. */
00577        if (TRUE) {
00578               proxy_t searchitem;
00579               searchitem.name = todo->name;
00580               searchitem.connection = todo->bus;
00581               searchitem.path = todo->path;
00582 
00583               GList * proxyitem = g_list_find_custom(priv->proxies, &searchitem, proxy_t_equal);
00584               if (proxyitem != NULL) {
00585                      g_free(todo->name);
00586                      g_free(todo->path);
00587                      return TRUE;
00588               }
00589        }
00590 
00591        proxy_t * proxyt = g_new0(proxy_t, 1);
00592        proxyt->name = todo->name;
00593        proxyt->path = todo->path;
00594        proxyt->type = NULL;
00595        proxyt->property_proxy = NULL;
00596        proxyt->proxy = NULL;
00597        proxyt->listener = listener;
00598        proxyt->indicators = NULL;
00599        proxyt->hidden = FALSE;
00600        proxyt->connection = todo->bus;
00601        proxyt->server.name = todo->name;
00602        proxyt->server.proxy = NULL;
00603        proxyt->server.connection = proxyt->connection;
00604        proxyt->server.max_indicators = priv->max_indicators;
00605 
00606        /* Build the indicators hash */
00607        proxyt->indicators = g_hash_table_new(g_direct_hash, g_direct_equal);
00608 
00609        /* Build the proxy and ensure that it gets created.  If
00610           it gets created we're all happy. */
00611        g_dbus_proxy_new(priv->session_bus,
00612                         G_DBUS_PROXY_FLAGS_NONE,
00613                         bus_indicate_interface_info,
00614                         proxyt->name,
00615                         todo->path,
00616                         INDICATE_DBUS_IFACE,
00617                         NULL, /* cancel */
00618                         prox_ready_cb,
00619                         proxyt);
00620 
00621        return TRUE; /* do the next entry */
00622 }
00623 
00624 /* Async result of building a proxy */
00625 static void
00626 prox_ready_cb (GObject * object, GAsyncResult * res, gpointer user_data)
00627 {
00628        proxy_t * proxyt = (proxy_t *)user_data;
00629        GError * error = NULL;
00630        GDBusProxy * proxy = g_dbus_proxy_new_finish(res, &error);
00631 
00632        if (error != NULL) {
00633               g_warning("Unable to create proxy for %s", proxyt->name);
00634               g_error_free(error);
00635               return;
00636        }
00637 
00638        proxyt->proxy = proxy;
00639 
00640        g_signal_connect(G_OBJECT(proxyt->proxy), "notify::g-name-owner", G_CALLBACK(proxy_destroyed), proxyt);
00641        g_signal_connect(G_OBJECT(proxyt->proxy), "g-signal", G_CALLBACK(proxy_signal_cb), proxyt);
00642 
00643        proxyt->dbus_listener_sub = g_dbus_connection_signal_subscribe(g_dbus_proxy_get_connection(proxy),
00644                                                                       "org.freedesktop.DBus", /* sender */
00645                                                                       "org.freedesktop.DBus",
00646                                                                       "NameOwnerChanged",
00647                                                                       "/org/freedesktop/DBus", /* path */
00648                                                                       g_dbus_proxy_get_name(proxy), /* arg0 */
00649                                                                       G_DBUS_SIGNAL_FLAGS_NONE,
00650                                                                       proxy_name_owner_changed,
00651                                                                       proxyt,
00652                                                                       NULL); /* destroy notify */
00653 
00654        /* Making sure the server has the proxy as well */
00655        proxyt->server.proxy = proxyt->proxy;
00656 
00657        IndicateListenerPrivate * priv = INDICATE_LISTENER_GET_PRIVATE(proxyt->listener);
00658        /* Adding into the list of proxies */
00659        priv->proxies = g_list_prepend(priv->proxies, proxyt);
00660 
00661        /* We're setting the max number of indicators from the default
00662           when we detect it.  This should give a reasonable amount of
00663           time for listeners to set the default if they want something
00664           different from infinite.  Otherwise it'd be easy to miss the
00665           first couple. */
00666        proxyt->server.max_indicators = priv->max_indicators;
00667        if (proxyt->server.max_indicators != -1) {
00668               g_dbus_proxy_call(proxyt->proxy,
00669                                 "SetMaxIndicators",
00670                                 g_variant_new("(i)", proxyt->server.max_indicators),
00671                                 G_DBUS_CALL_FLAGS_NONE,
00672                                 -1, /* timeout */
00673                                 NULL,
00674                                 set_max_indicators_cb,
00675                                 proxyt->server.name);
00676        }
00677 
00678        indicate_listener_server_get_type(proxyt->listener, &proxyt->server, get_type_initial_cb, proxyt);
00679 
00680 
00681        g_dbus_proxy_call(proxyt->proxy,
00682                          "GetIndicatorList",
00683                          NULL, /* params */
00684                          G_DBUS_CALL_FLAGS_NONE,
00685                          -1, /* timeout */
00686                          NULL,
00687                          proxy_get_indicator_list,
00688                          proxyt);
00689 
00690        return;
00691 }
00692 
00693 /* We need to do the right signal now */
00694 static void
00695 proxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVariant * params, gpointer user_data)
00696 {
00697        proxy_t * proxyt = (proxy_t *)user_data;
00698 
00699        if (g_strcmp0(signal, "IndicatorAdded") == 0) {
00700               guint id; gchar * type;
00701               g_variant_get(params, "(us)", &id, &type);
00702               proxy_indicator_added_legacy(id, type, proxyt);
00703        } else if (g_strcmp0(signal, "IndicatorNew") == 0) {
00704               guint id;
00705               g_variant_get(params, "(u)", &id);
00706               proxy_indicator_added(id, proxyt);
00707        } else if (g_strcmp0(signal, "IndicatorRemoved") == 0) {
00708               guint id; gchar * type;
00709               g_variant_get(params, "(us)", &id, &type);
00710               proxy_indicator_removed_legacy(id, type, proxyt);
00711        } else if (g_strcmp0(signal, "IndicatorDelete") == 0) {
00712               guint id;
00713               g_variant_get(params, "(u)", &id);
00714               proxy_indicator_removed(id, proxyt);
00715        } else if (g_strcmp0(signal, "IndicatorModified") == 0) {
00716               guint id; gchar * property;
00717               g_variant_get(params, "(us)", &id, &property);
00718               proxy_indicator_modified(id, property, proxyt);
00719        } else if (g_strcmp0(signal, "ServerCountChanged") == 0) {
00720               guint count;
00721               g_variant_get(params, "(u)", &count);
00722               proxy_server_count_changed(count, proxyt);
00723        } else if (g_strcmp0(signal, "ServerShow") == 0) {
00724               /* Unused here */
00725        } else if (g_strcmp0(signal, "ServerHide") == 0) {
00726               /* Unused here */
00727        } else {
00728               g_warning("Unknown signal from server '%s'", signal);
00729        }
00730 
00731        return;
00732 }
00733 
00734 /* A callback function for the getting the type when it is looked
00735    at initially.  So that we'll send a signal that the server has
00736    been added so that everyone knows. */
00737 static void
00738 get_type_initial_cb (IndicateListener * listener, IndicateListenerServer * server, const gchar * type, gpointer data)
00739 {
00740        get_type_cb(listener, server, type, data);
00741 
00742        proxy_t * proxyt = (proxy_t *)data;
00743 
00744        if (!proxyt->hidden && proxyt->type != NULL) {
00745               g_signal_emit(proxyt->listener, signals[SERVER_ADDED], 0, &proxyt->server, proxyt->type, TRUE);
00746        }
00747 
00748        return;
00749 }
00750 
00751 /* Callback from getting the type of the server.  We're not using
00752    this directly now, only through get_type_initial_cb right now
00753    though we might use it more directly later. */
00754 static void
00755 get_type_cb (IndicateListener * listener, IndicateListenerServer * server, const gchar * type, gpointer data)
00756 {
00757        if (type == NULL) {
00758               /* This is usually caused by an error getting the type,
00759                * which would mean that this isn't an indicator server */
00760               return;
00761        }
00762 
00763        proxy_t * proxyt = (proxy_t *)data;
00764 
00765        if (proxyt->type != NULL) {
00766               g_free(proxyt->type);
00767               proxyt->type = NULL;
00768        }
00769        proxyt->type = g_strdup(type);
00770 
00771        return;
00772 }
00773 
00774 /* A call back from setting the max indicators.  We really can't
00775    do anything about it, so this function is kinda useless. */
00776 static void
00777 set_max_indicators_cb (GObject * object, GAsyncResult * res, gpointer user_data)
00778 {
00779        GError * error = NULL;
00780        g_dbus_proxy_call_finish(G_DBUS_PROXY(object), res, &error);
00781 
00782        if (error != NULL) {
00783               g_warning("Unable to set the max indicators on '%s': %s", (gchar *)user_data, error->message);
00784               g_error_free(error);
00785        }
00786 
00787        return;
00788 }
00789 
00790 /* Callback from the call to get the indicator list on new
00791    servers that we've found through introspection.  It takes
00792    the list and then calls indicator_added on each one. */
00793 static void
00794 proxy_get_indicator_list (GObject * object, GAsyncResult * res, gpointer data)
00795 {
00796        GError * error = NULL;
00797        GVariant * retval = g_dbus_proxy_call_finish(G_DBUS_PROXY(object), res, &error);
00798 
00799        if (error != NULL) {
00800               g_warning("Unable to get indicator list");
00801               g_error_free(error);
00802               return;
00803        }
00804 
00805        proxy_t * proxyt = (proxy_t *)data;
00806 
00807        GVariant * list = g_variant_get_child_value(retval, 0);
00808        GVariantIter iter;
00809        g_variant_iter_init(&iter, list);
00810        gint id;
00811 
00812        while (g_variant_iter_next(&iter, "i", &id)) {
00813               proxy_indicator_added(id, proxyt);
00814        }
00815 
00816        if (retval != NULL) {
00817               g_variant_unref(retval);
00818        }
00819 
00820        return;
00821 }
00822 
00823 
00824 /* A fun little wrapper so that we can support the
00825    signals on the indicator v1 interface.  It just drops
00826    the type and calls the new function. */
00827 static void
00828 proxy_indicator_added_legacy (guint id, gchar * type, proxy_t * proxyt)
00829 {
00830        return proxy_indicator_added(id, proxyt);
00831 }
00832 
00833 /* Gets called when we get a signal from the server that
00834    there is a new indicator.  We put it into our list of
00835    indicators and pass the signal up.  If the server hasn't
00836    been known about before, we create the appropriate
00837    structures for it. */
00838 static void
00839 proxy_indicator_added (guint id, proxy_t * proxyt)
00840 {
00841        g_debug("Proxy Indicator Added");
00842        g_return_if_fail(proxyt != NULL);
00843 
00844        if (!g_hash_table_lookup(proxyt->indicators, GUINT_TO_POINTER(id))) {
00845               g_hash_table_insert(proxyt->indicators, GUINT_TO_POINTER(id), GUINT_TO_POINTER(TRUE));
00846               g_signal_emit(proxyt->listener, signals[INDICATOR_ADDED], 0, &proxyt->server, GUINT_TO_POINTER(id), TRUE);
00847        }
00848 
00849        return;
00850 }
00851 
00852 /* A fun little wrapper so that we can support the
00853    signals on the indicator v1 interface.  It just drops
00854    the type and calls the new function. */
00855 static void
00856 proxy_indicator_removed_legacy (guint id, gchar * type, proxy_t * proxyt)
00857 {
00858        return proxy_indicator_removed(id, proxyt);
00859 }
00860 
00861 /* A server removed an indicator.  This function removes all the
00862    local data structures and then passes the signal up the stack.
00863    */
00864 static void
00865 proxy_indicator_removed (guint id, proxy_t * proxyt)
00866 {
00867        g_debug("Proxy Indicator Removed");
00868        g_return_if_fail(proxyt != NULL);
00869 
00870        if (proxyt->indicators == NULL) {
00871               g_warning("Oddly we had an indicator removed from an interface that we didn't think had indicators.");
00872               return;
00873        }
00874 
00875        if (!g_hash_table_lookup(proxyt->indicators, GUINT_TO_POINTER(id))) {
00876               g_warning("No indicator %d on '%s'.", id, proxyt->name);
00877               return;
00878        }
00879 
00880        g_hash_table_remove(proxyt->indicators, GUINT_TO_POINTER(id));
00881        g_signal_emit(proxyt->listener, signals[INDICATOR_REMOVED], 0, &proxyt->server, GUINT_TO_POINTER(id), TRUE);
00882 
00883        return;
00884 }
00885 
00886 /* This is a signal from the server that a property on an indicator
00887    has been modified.  We try and find the indicator, convert all
00888    the parameters to the local ones, and then pass up the signal. */
00889 static void
00890 proxy_indicator_modified (guint id, const gchar * property, proxy_t * proxyt)
00891 {
00892        g_debug("Proxy Indicator Modified");
00893        g_return_if_fail(proxyt != NULL);
00894 
00895        if (proxyt->indicators == NULL) {
00896               g_warning("Oddly we had an indicator modified from an interface that we didn't think had indicators.");
00897               return;
00898        }
00899 
00900        if (!g_hash_table_lookup(proxyt->indicators, GUINT_TO_POINTER(id))) {
00901               g_warning("Can not modify indicator %d with property '%s' as there are no indicators with that id on %s.", id, property, proxyt->name);
00902               return;
00903        }
00904 
00905        g_signal_emit(proxyt->listener, signals[INDICATOR_MODIFIED], 0, &proxyt->server, GUINT_TO_POINTER(id), property, TRUE);
00906 
00907        return;
00908 }
00909 
00910 /* This function gets called when the dbus count
00911    signal comes it.  Basically we're just translating
00912    it into a local signal with the appropraite parameters
00913    and structures. */
00914 static void
00915 proxy_server_count_changed (guint count, proxy_t * proxyt)
00916 {
00917        g_debug("Proxy Server Count Changed");
00918        g_return_if_fail(proxyt != NULL);
00919        g_signal_emit(proxyt->listener, signals[SERVER_COUNT_CHANGED], 0, &proxyt->server, count, TRUE);
00920        return;
00921 }
00922 
00923 typedef enum _get_property_type get_property_type;
00924 enum _get_property_type {
00925        PROPERTY_TYPE_VARIANT,
00926        PROPERTY_TYPE_STRING,
00927        PROPERTY_TYPE_TIME,
00928        PROPERTY_TYPE_INT,
00929        PROPERTY_TYPE_BOOL
00930 };
00931 
00932 typedef struct _get_property_t get_property_t;
00933 struct _get_property_t {
00934        GCallback cb;
00935        gpointer data;
00936        IndicateListener * listener;
00937        IndicateListenerServer * server;
00938        IndicateListenerIndicator * indicator;
00939        gchar * property;
00940        get_property_type type;
00941 };
00942 
00943 /* Look at the right align on this comment.  Sweeeeeeeet */
00944 /* A callback from getting a property that takes the string 
00945    passed across the bus and turning it into something more
00946    related to what we want on this side.  If it's a time it
00947    gets converted to a #GTimeVal, if it's an int it goes to
00948    a gint and if it's a bool we check that too.  This makes
00949    it nice to work with properties and the listener.
00950 */
00951 static void
00952 get_property_cb (GObject * object, GAsyncResult * res, gpointer user_data)
00953 {
00954        get_property_t * get_property_data = (get_property_t *)user_data;
00955        GError * error = NULL;
00956        GVariant * retvalue = g_dbus_proxy_call_finish(G_DBUS_PROXY(object), res, &error);
00957 
00958        if (error != NULL) {
00959               g_warning("Unable to get property data: %s", error->message);
00960               g_error_free(error);
00961        } else {
00962 
00963               GVariant * value = g_variant_get_child_value(retvalue, 0);
00964               if (g_variant_is_of_type(value, G_VARIANT_TYPE_VARIANT)) {
00965                      GVariant * tmp = g_variant_get_variant(value);
00966                      g_variant_unref (value);
00967                      value = tmp;
00968               }
00969 
00970               switch (get_property_data->type) {
00971               case PROPERTY_TYPE_VARIANT: {
00972                      /* Just pass the GVariant along. */
00973                      indicate_listener_get_property_variant_cb cb =(indicate_listener_get_property_variant_cb)get_property_data->cb;
00974                      cb(get_property_data->listener,
00975                         get_property_data->server,
00976                         get_property_data->indicator,
00977                         get_property_data->property,
00978                         value,
00979                         get_property_data->data);
00980                      break;
00981               }
00982               case PROPERTY_TYPE_STRING: {
00983                      /* Just pass the string along. */
00984                      indicate_listener_get_property_cb cb = (indicate_listener_get_property_cb)get_property_data->cb;
00985                      cb(get_property_data->listener,
00986                         get_property_data->server,
00987                         get_property_data->indicator,
00988                         get_property_data->property,
00989                         g_variant_get_string(value, NULL),
00990                         get_property_data->data);
00991                      break;
00992               }
00993               case PROPERTY_TYPE_TIME: {
00994                      /* Convert it to a time val */
00995                      indicate_listener_get_property_time_cb cb = (indicate_listener_get_property_time_cb)get_property_data->cb;
00996                      GTimeVal time;
00997                      if (g_time_val_from_iso8601(g_variant_get_string(value, NULL), &time)) {
00998                             cb(get_property_data->listener,
00999                                get_property_data->server,
01000                                get_property_data->indicator,
01001                                get_property_data->property,
01002                                &time,
01003                                get_property_data->data);
01004                      }
01005                      break;
01006               }
01007               case PROPERTY_TYPE_INT: {
01008                      /* Take the string and convert it to an integer */
01009                      indicate_listener_get_property_int_cb cb = (indicate_listener_get_property_int_cb)get_property_data->cb;
01010                      cb(get_property_data->listener,
01011                         get_property_data->server,
01012                         get_property_data->indicator,
01013                         get_property_data->property,
01014                         g_variant_get_int32(value),
01015                         get_property_data->data);
01016                      break;
01017               }
01018               case PROPERTY_TYPE_BOOL: {
01019                      /* Check to see if it's 'true', if not assume that
01020                         it's false */
01021                      indicate_listener_get_property_bool_cb cb = (indicate_listener_get_property_bool_cb)get_property_data->cb;
01022                      cb(get_property_data->listener,
01023                         get_property_data->server,
01024                         get_property_data->indicator,
01025                         get_property_data->property,
01026                         g_variant_get_boolean(value),
01027                         get_property_data->data);
01028                      break;
01029               }
01030               }
01031 
01032               g_variant_unref (value);
01033        }
01034 
01035        if (retvalue != NULL) {
01036               g_variant_unref(retvalue);
01037        }
01038        g_free(get_property_data->property);
01039        g_free(get_property_data);
01040 
01041        return;
01042 };
01043 
01044 /* A small function to take the common list of parameters and
01045    build a callback structure to hold them all.  Eventually we
01046    get the data and unwind this structure. */
01047 static void
01048 get_property_helper (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, GCallback callback, gpointer data, get_property_type prop_type)
01049 {
01050        /* g_debug("get_property_helper: %s %d", property, prop_type); */
01051        /* TODO: Do we need to somehow refcount the server/indicator while we're waiting on this? */
01052        get_property_t * get_property_data = g_new0(get_property_t, 1);
01053        get_property_data->cb = callback;
01054        get_property_data->data = data;
01055        get_property_data->listener = listener;
01056        get_property_data->server = server;
01057        get_property_data->indicator = indicator;
01058        get_property_data->property = g_strdup(property);
01059        get_property_data->type = prop_type;
01060        
01061        g_dbus_proxy_call(server->proxy,
01062                          "GetIndicatorProperty",
01063                          g_variant_new("(us)",
01064                                        INDICATE_LISTENER_INDICATOR_ID(indicator), property),
01065                          G_DBUS_CALL_FLAGS_NONE,
01066                          -1, /* timeout */
01067                          NULL, /* cancel */
01068                          get_property_cb,
01069                          get_property_data);
01070 
01071        return;
01072 }
01073 
01089 void
01090 indicate_listener_get_property_variant (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, indicate_listener_get_property_variant_cb callback, gpointer data)
01091 {
01092        return get_property_helper(listener, server, indicator, property, G_CALLBACK(callback), data, PROPERTY_TYPE_VARIANT);
01093 }
01094 
01108 void
01109 indicate_listener_get_property (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, indicate_listener_get_property_cb callback, gpointer data)
01110 {
01111        return get_property_helper(listener, server, indicator, property, G_CALLBACK(callback), data, PROPERTY_TYPE_STRING);
01112 }
01113 
01131 void
01132 indicate_listener_get_property_time (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, indicate_listener_get_property_time_cb callback, gpointer data)
01133 {
01134        return get_property_helper(listener, server, indicator, property, G_CALLBACK(callback), data, PROPERTY_TYPE_TIME);
01135 }
01136 
01154 void
01155 indicate_listener_get_property_int (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, indicate_listener_get_property_int_cb callback, gpointer data)
01156 {
01157        return get_property_helper(listener, server, indicator, property, G_CALLBACK(callback), data, PROPERTY_TYPE_INT);
01158 }
01159 
01177 void
01178 indicate_listener_get_property_bool (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, indicate_listener_get_property_bool_cb callback, gpointer data)
01179 {
01180        return get_property_helper(listener, server, indicator, property, G_CALLBACK(callback), data, PROPERTY_TYPE_BOOL);
01181 }
01182 
01183 /* A callback for asking an indicator to be displayed,
01184    which is unlikely to fail.  So we're throwing a warning. */
01185 static void 
01186 listener_display_cb (GObject * object, GAsyncResult * res, gpointer user_data)
01187 {
01188        GError * error = NULL;
01189        g_dbus_proxy_call_finish(G_DBUS_PROXY(object), res, &error);
01190 
01191        if (error != NULL) {
01192               g_warning("Listener display caused an error: %s", error->message);
01193               g_error_free(error);
01194        }
01195        return;
01196 }
01197 
01208 GList *
01209 indicate_listener_get_servers (IndicateListener * listener)
01210 {
01211        g_return_val_if_fail(INDICATE_IS_LISTENER(listener), NULL);
01212        IndicateListenerPrivate * priv = INDICATE_LISTENER_GET_PRIVATE(listener);
01213 
01214        GList * output = NULL;
01215        GList * pntr;
01216 
01217        for (pntr = priv->proxies; pntr != NULL; pntr = g_list_next(pntr)) {
01218               proxy_t * proxy = (proxy_t *)pntr->data;
01219               output = g_list_prepend(output, &proxy->server);
01220        }
01221 
01222        return output;
01223 }
01224 
01225 void
01226 indicate_listener_display (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, guint timestamp)
01227 {
01228        g_dbus_proxy_call(server->proxy,
01229                          "ShowIndicatorToUser",
01230                          g_variant_new("(uu)", INDICATE_LISTENER_INDICATOR_ID(indicator), timestamp),
01231                          G_DBUS_CALL_FLAGS_NONE,
01232                          -1, /* timeout */
01233                          NULL, /* cancel */
01234                          listener_display_cb,
01235                          NULL);
01236 
01237        return;
01238 }
01239 
01240 /* A callback for saying an indicator is displayed,
01241    which is unlikely to fail.  So we're throwing a warning. */
01242 static void 
01243 listener_displayed_cb (GObject * object, GAsyncResult * res, gpointer user_data)
01244 {
01245        GError * error = NULL;
01246        g_dbus_proxy_call_finish(G_DBUS_PROXY(object), res, &error);
01247 
01248        if (error != NULL) {
01249               g_warning("Listener displayed caused an error: %s", error->message);
01250               g_error_free(error);
01251        }
01252        return;
01253 }
01254 
01267 void
01268 indicate_listener_displayed (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gboolean displayed)
01269 {
01270        g_dbus_proxy_call(server->proxy,
01271                          "IndicatorDisplayed",
01272                          g_variant_new("(ub)", INDICATE_LISTENER_INDICATOR_ID(indicator), displayed),
01273                          G_DBUS_CALL_FLAGS_NONE,
01274                          -1, /* timeout */
01275                          NULL, /* cancel */
01276                          listener_displayed_cb,
01277                          NULL);
01278 
01279        if (!server->interests[INDICATE_INTEREST_INDICATOR_DISPLAY] && displayed) {
01280               g_warning("It's awful odd that you said in the interest survey you weren't displaying indicators and then you displayed one.  I'm just saying, you've probably confused someone besides me.");
01281        }
01282 
01283        return;
01284 }
01285 
01286 /* Callback data structure */
01287 typedef struct _get_server_prop_data_t get_server_prop_data_t;
01288 struct _get_server_prop_data_t {
01289        IndicateListener * listener;
01290        IndicateListenerServer * server;
01291        indicate_listener_get_server_property_cb callback;
01292        indicate_listener_get_server_uint_property_cb callback_uint;
01293        gpointer data;
01294 };
01295 
01296 /* This does the actual work of calling the callbacks.  It's split out
01297    so that we can use it in the DBus function callback but also in the
01298    case where we already have the cached value. */
01299 static void
01300 get_server_property_work (get_server_prop_data_t * prop_t, GVariant * prop)
01301 {
01302        if (prop == NULL) {
01303               if (prop_t->callback == NULL) {
01304                      prop_t->callback_uint(prop_t->listener, prop_t->server, 0, prop_t->data);
01305               } else {
01306                      prop_t->callback(prop_t->listener, prop_t->server, NULL, prop_t->data);
01307               }
01308               return;
01309        }
01310 
01311        if (g_variant_is_of_type(prop, G_VARIANT_TYPE_STRING) && prop_t->callback != NULL) {
01312               prop_t->callback(prop_t->listener, prop_t->server, g_variant_get_string(prop, NULL), prop_t->data);
01313        } else if (g_variant_is_of_type(prop, G_VARIANT_TYPE_OBJECT_PATH) && prop_t->callback != NULL) {
01314               prop_t->callback(prop_t->listener, prop_t->server, g_variant_get_string(prop, NULL), prop_t->data);
01315        } else if (g_variant_is_of_type(prop, G_VARIANT_TYPE_UINT32) && prop_t->callback_uint != NULL) {
01316               prop_t->callback_uint(prop_t->listener, prop_t->server, g_variant_get_uint32(prop), prop_t->data);
01317        } else {
01318               g_warning("Really?  This can't happen.  WTF!  %s", g_variant_get_type_string(prop));
01319        }
01320 
01321        return;
01322 }
01323 
01324 /* Callback from getting the property off of DBus.  Try to complete
01325    the call and then pass it up to the worker, clearing out the
01326    memory allocations caused here. */
01327 static void
01328 get_server_property_cb (GObject * obj, GAsyncResult * res, gpointer user_data)
01329 {
01330        GError * error = NULL;
01331        get_server_prop_data_t * prop_t = (get_server_prop_data_t *)user_data;
01332 
01333        GVariant * prop = g_dbus_connection_call_finish(G_DBUS_CONNECTION(obj), res, &error);
01334        if (error != NULL) {
01335               g_warning("Error getting property! %s", error->message);
01336               g_error_free(error);
01337        }
01338 
01339        GVariant * unwrap = NULL;
01340        if (prop != NULL) {
01341               unwrap = g_variant_get_child_value(prop, 0);
01342        }
01343 
01344        if (unwrap != NULL && g_variant_is_of_type(unwrap, G_VARIANT_TYPE_VARIANT)) {
01345               get_server_property_work(prop_t, g_variant_get_variant(unwrap));
01346        } else {
01347               get_server_property_work(prop_t, NULL);
01348        }
01349 
01350        if (prop != NULL) {
01351               g_variant_unref(prop);
01352        }
01353        
01354        g_object_unref(G_OBJECT(prop_t->listener));
01355        g_free(prop_t);
01356        return;
01357 }
01358 
01359 /* This is a helper function for all the functions that
01360    get properties from the server.  They all need to have
01361    a callback setup with an intermediary data structure
01362    and this function builds and populates that, then uses
01363    a custom callback to call their callback */
01364 static void
01365 get_server_property (IndicateListener * listener, IndicateListenerServer * server, indicate_listener_get_server_property_cb callback, indicate_listener_get_server_uint_property_cb callback_uint, const gchar * property_name, gpointer data)
01366 {
01367        IndicateListenerPrivate * priv = INDICATE_LISTENER_GET_PRIVATE(listener);
01368        get_server_prop_data_t * prop_t = g_new0(get_server_prop_data_t, 1);
01369 
01370        prop_t->listener = listener;
01371        prop_t->server = server;
01372        prop_t->callback = callback;
01373        prop_t->callback_uint = callback_uint;
01374        prop_t->data = data;
01375 
01376        g_object_ref(G_OBJECT(listener));
01377 
01378        /* If we don't have the property cached, let's go and find
01379           it on the bus. */
01380        g_dbus_connection_call(priv->session_bus,
01381                               g_dbus_proxy_get_name(server->proxy),
01382                               g_dbus_proxy_get_object_path(server->proxy),
01383                               "org.freedesktop.DBus.Properties",
01384                               "Get",
01385                               g_variant_new("(ss)", INDICATE_DBUS_IFACE, property_name),
01386                               G_VARIANT_TYPE("(v)"),
01387                               G_DBUS_CALL_FLAGS_NONE,
01388                               -1,
01389                               NULL,
01390                               get_server_property_cb,
01391                               prop_t);
01392 
01393        return;
01394 }
01395 
01396 void
01397 indicate_listener_server_get_type (IndicateListener * listener, IndicateListenerServer * server, indicate_listener_get_server_property_cb callback, gpointer data)
01398 {
01399        return get_server_property(listener, server, callback, NULL, "type", data);
01400 }
01401 
01402 void
01403 indicate_listener_server_get_desktop (IndicateListener * listener, IndicateListenerServer * server, indicate_listener_get_server_property_cb callback, gpointer data)
01404 {
01405        return get_server_property(listener, server, callback, NULL, "desktop", data);
01406 }
01407 
01408 void
01409 indicate_listener_server_get_count (IndicateListener * listener, IndicateListenerServer * server, indicate_listener_get_server_uint_property_cb callback, gpointer data)
01410 {
01411        return get_server_property(listener, server, NULL, callback, "count", data);
01412 }
01413 
01414 void
01415 indicate_listener_server_get_menu (IndicateListener * listener, IndicateListenerServer * server, indicate_listener_get_server_property_cb callback, gpointer data)
01416 {
01417        return get_server_property(listener, server, callback, NULL, "menu", data);
01418 }
01419 
01420 void
01421 indicate_listener_server_get_icon_theme (IndicateListener * listener, IndicateListenerServer * server, indicate_listener_get_server_property_cb callback, gpointer data)
01422 {
01423        return get_server_property(listener, server, callback, NULL, "icontheme", data);
01424 }
01425 
01437 GList *
01438 indicate_listener_server_get_indicators (IndicateListener * listener, IndicateListenerServer * server)
01439 {
01440        g_return_val_if_fail(INDICATE_IS_LISTENER(listener), NULL);
01441        g_return_val_if_fail(server != NULL, NULL);
01442 
01443        IndicateListenerPrivate * priv = INDICATE_LISTENER_GET_PRIVATE(listener);
01444 
01445        proxy_t searchitem;
01446        searchitem.name = server->name;
01447        searchitem.connection = server->connection;
01448        searchitem.path = (gchar *)indicate_listener_server_get_dbuspath (server);
01449 
01450        GList * proxyitem = g_list_find_custom(priv->proxies, &searchitem, proxy_t_equal);
01451        if (proxyitem == NULL) {
01452               g_warning("Unable to find proxy item for server %s", server->name);
01453               return NULL;
01454        }
01455 
01456        proxy_t * item = (proxy_t *)proxyitem->data;
01457 
01458        if (item->indicators == NULL) {
01459               /* We have no indicators for sure, so this is a valid return */
01460               return NULL;
01461        }
01462 
01463        return g_hash_table_get_keys(item->indicators);
01464 }
01465 
01466 const gchar *
01467 indicate_listener_server_get_dbusname (IndicateListenerServer * server)
01468 {
01469        if (server == NULL) return NULL;
01470        return server->name;
01471 }
01472 
01473 const gchar *
01474 indicate_listener_server_get_dbuspath (IndicateListenerServer *server)
01475 {
01476         if (server == NULL) return NULL;
01477   
01478         return g_dbus_proxy_get_object_path (server->proxy);
01479 }
01480 
01481 guint
01482 indicate_listener_indicator_get_id (IndicateListenerIndicator * indicator)
01483 {
01484        return GPOINTER_TO_UINT(indicator);
01485 }
01486 
01487 static const gchar *
01488 interest_to_string (IndicateInterests interest)
01489 {
01490        switch (interest) {
01491        case INDICATE_INTEREST_SERVER_DISPLAY:
01492               return INDICATE_INTEREST_STRING_SERVER_DISPLAY;
01493        case INDICATE_INTEREST_SERVER_SIGNAL:
01494               return INDICATE_INTEREST_STRING_SERVER_SIGNAL;
01495        case INDICATE_INTEREST_INDICATOR_DISPLAY:
01496               return INDICATE_INTEREST_STRING_INDICATOR_DISPLAY;
01497        case INDICATE_INTEREST_INDICATOR_SIGNAL:
01498               return INDICATE_INTEREST_STRING_INDICATOR_SIGNAL;
01499        case INDICATE_INTEREST_INDICATOR_COUNT:
01500               return INDICATE_INTEREST_STRING_INDICATOR_COUNT;
01501        default:
01502               return "";
01503        }
01504 }
01505 
01506 static void
01507 interest_cb (GObject * object, GAsyncResult * res, gpointer user_data)
01508 {
01509        GError * error = NULL;
01510        g_dbus_proxy_call_finish(G_DBUS_PROXY(object), res, &error);
01511 
01512        if (error != NULL) {
01513               g_warning("Unable to configure interest.");
01514               g_error_free(error);
01515        }
01516 
01517        return;
01518 }
01519 
01520 void
01521 indicate_listener_server_show_interest (IndicateListener * listener, IndicateListenerServer * server, IndicateInterests interest)
01522 {
01523        if (!(interest > INDICATE_INTEREST_NONE && interest < INDICATE_INTEREST_LAST)) {
01524               return;
01525        }
01526 
01527        if (!server->interests[interest]) {
01528               g_dbus_proxy_call(server->proxy,
01529                                 "ShowInterest",
01530                                 g_variant_new("(s)", interest_to_string(interest)),
01531                                 G_DBUS_CALL_FLAGS_NONE,
01532                                 -1, /* timeout */
01533                                 NULL, /* cancel */
01534                                 interest_cb,
01535                                 server);
01536               server->interests[interest] = TRUE;
01537        }
01538        return;
01539 }
01540 
01541 void
01542 indicate_listener_server_remove_interest (IndicateListener * listener, IndicateListenerServer * server, IndicateInterests interest)
01543 {
01544        if (server->interests[interest]) {
01545               g_dbus_proxy_call(server->proxy,
01546                                 "RemoveInterest",
01547                                 g_variant_new("(s)", interest_to_string(interest)),
01548                                 G_DBUS_CALL_FLAGS_NONE,
01549                                 -1, /* timeout */
01550                                 NULL, /* cancel */
01551                                 interest_cb,
01552                                 server);
01553               server->interests[interest] = FALSE;
01554        }
01555        return;
01556 }
01557 
01558 gboolean
01559 indicate_listener_server_check_interest (IndicateListener * listener, IndicateListenerServer * server, IndicateInterests interest)
01560 {
01561        return server->interests[interest];
01562 }
01563 
01564 GType
01565 indicate_listener_server_get_gtype (void)
01566 {
01567   static GType our_type = 0;
01568   
01569   if (our_type == 0)
01570     our_type = g_pointer_type_register_static ("IndicateListenerServer");
01571 
01572   return our_type;
01573 }
01574 
01575 GType
01576 indicate_listener_indicator_get_gtype (void)
01577 {
01578   static GType our_type = 0;
01579   
01580   if (our_type == 0)
01581     our_type = g_pointer_type_register_static ("IndicateListenerIndicator");
01582 
01583   return our_type;
01584 }
01585 
01598 void
01599 indicate_listener_set_default_max_indicators (IndicateListener * listener, gint max)
01600 {
01601        g_return_if_fail(INDICATE_IS_LISTENER(listener));
01602        IndicateListenerPrivate * priv = INDICATE_LISTENER_GET_PRIVATE(listener);
01603        priv->max_indicators = max;
01604        return;
01605 }
01606 
01619 void
01620 indicate_listener_set_server_max_indicators (IndicateListener * listener, IndicateListenerServer * server, gint max)
01621 {
01622        g_return_if_fail(INDICATE_IS_LISTENER(listener));
01623        g_return_if_fail(server != NULL); /* All we can really check :-/ */
01624 
01625        if (server->max_indicators != max) {
01626               server->max_indicators = max;
01627               g_dbus_proxy_call(server->proxy,
01628                                 "SetMaxIndicators",
01629                                 g_variant_new("(i)", server->max_indicators),
01630                                 G_DBUS_CALL_FLAGS_NONE,
01631                                 -1, /* timeout */
01632                                 NULL,
01633                                 set_max_indicators_cb,
01634                                 server->name);
01635        }
01636 
01637        return;
01638 }