Back to index

indicator-appmenu  12.10.0
hudappmenuregistrar.c
Go to the documentation of this file.
00001 /*
00002  * Copyright © 2012 Canonical Ltd.
00003  *
00004  * This program is free software: you can redistribute it and/or modify it
00005  * under the terms of the GNU General Public License version 3, as
00006  * published by the Free Software Foundation.
00007  *
00008  * This program is distributed in the hope that it will be useful, but
00009  * WITHOUT ANY WARRANTY; without even the implied warranties of
00010  * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
00011  * PURPOSE.  See the GNU General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License along
00014  * with this program.  If not, see <http://www.gnu.org/licenses/>.
00015  *
00016  * Author: Ryan Lortie <desrt@desrt.ca>
00017  */
00018 
00019 #define G_LOG_DOMAIN "hudappmenuregistrar"
00020 
00021 #include "hudappmenuregistrar.h"
00022 
00023 #include <gio/gio.h>
00024 
00059 #define APPMENU_REGISTRAR_BUS_NAME    "com.canonical.AppMenu.Registrar"
00060 #define APPMENU_REGISTRAR_OBJECT_PATH "/com/canonical/AppMenu/Registrar"
00061 #define APPMENU_REGISTRAR_IFACE       "com.canonical.AppMenu.Registrar"
00062 
00063 typedef struct
00064 {
00065   HudAppMenuRegistrarObserverFunc callback;
00066   gpointer                        user_data;
00067 } HudAppMenuRegistrarObserver;
00068 
00069 typedef struct
00070 {
00071   guint xid;
00072   gchar *bus_name;
00073   gchar *object_path;
00074   GSList *observers;
00075 } HudAppMenuRegistrarWindow;
00076 
00077 struct _HudAppMenuRegistrar
00078 {
00079   GObject parent_instance;
00080 
00081   guint subscription;
00082   GCancellable *cancellable;
00083   GHashTable *windows;
00084   gboolean notifying;
00085   gboolean ready;
00086 };
00087 
00088 typedef GObjectClass HudAppMenuRegistrarClass;
00089 
00090 G_DEFINE_TYPE (HudAppMenuRegistrar, hud_app_menu_registrar, G_TYPE_OBJECT)
00091 
00092 static void
00093 hud_app_menu_registrar_window_free (gpointer user_data)
00094 {
00095   HudAppMenuRegistrarWindow *window = user_data;
00096 
00097   g_assert (window->bus_name == NULL);
00098   g_assert (window->object_path == NULL);
00099   g_assert (window->observers == NULL);
00100 
00101   g_debug ("free window instance for %u", window->xid);
00102 
00103   g_slice_free (HudAppMenuRegistrarWindow, window);
00104 }
00105 
00106 static HudAppMenuRegistrarWindow *
00107 hud_app_menu_registrar_get_window (HudAppMenuRegistrar *registrar,
00108                                    guint                xid)
00109 {
00110   HudAppMenuRegistrarWindow *window;
00111 
00112   window = g_hash_table_lookup (registrar->windows, GINT_TO_POINTER (xid));
00113 
00114   if (!window)
00115     {
00116       window = g_slice_new0 (HudAppMenuRegistrarWindow);
00117       window->xid = xid;
00118 
00119       g_debug ("create window instance for %u", xid);
00120       g_hash_table_insert (registrar->windows, GINT_TO_POINTER (xid), window);
00121     }
00122 
00123   return window;
00124 }
00125 
00126 static void
00127 hud_app_menu_registrar_possibly_free_window (HudAppMenuRegistrar       *registrar,
00128                                              HudAppMenuRegistrarWindow *window)
00129 {
00130   if (window->bus_name == NULL && window->observers == NULL)
00131     g_hash_table_remove (registrar->windows, GINT_TO_POINTER (window->xid));
00132 }
00133 
00134 static void
00135 hud_app_menu_registrar_notify_window_observers (HudAppMenuRegistrar       *registrar,
00136                                                 HudAppMenuRegistrarWindow *window)
00137 {
00138   GSList *node;
00139 
00140   registrar->notifying = TRUE;
00141 
00142   for (node = window->observers; node; node = node->next)
00143     {
00144       HudAppMenuRegistrarObserver *observer = node->data;
00145 
00146       g_debug ("notifying %p about %u", observer->user_data, window->xid);
00147       (* observer->callback) (registrar, window->xid, window->bus_name, window->object_path, observer->user_data);
00148     }
00149 
00150   registrar->notifying = FALSE;
00151 }
00152 
00153 static void
00154 hud_app_menu_registrar_dbus_signal (GDBusConnection *connection,
00155                                     const gchar     *sender_name,
00156                                     const gchar     *object_path,
00157                                     const gchar     *interface_name,
00158                                     const gchar     *signal_name,
00159                                     GVariant        *parameters,
00160                                     gpointer         user_data)
00161 {
00162   HudAppMenuRegistrar *registrar = user_data;
00163 
00164   g_debug ("got signal");
00165 
00166   if (!registrar->ready)
00167     {
00168       g_debug ("not ready, so ignoring signal");
00169       return;
00170     }
00171 
00172   if (g_str_equal (signal_name, "WindowRegistered"))
00173     {
00174       HudAppMenuRegistrarWindow *window;
00175       guint xid;
00176 
00177       if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(uso)")))
00178           return;
00179 
00180       g_variant_get_child (parameters, 0, "u", &xid);
00181       window = hud_app_menu_registrar_get_window (registrar, xid);
00182 
00183       g_free (window->bus_name);
00184       g_variant_get_child (parameters, 1, "s", &window->bus_name);
00185       g_free (window->object_path);
00186       g_variant_get_child (parameters, 2, "o", &window->object_path);
00187 
00188       g_debug ("xid %u is now at (%s, %s)", xid, window->bus_name, window->object_path);
00189 
00190       hud_app_menu_registrar_notify_window_observers (registrar, window);
00191     }
00192 
00193   else if (g_str_equal (signal_name, "WindowUnregistered"))
00194     {
00195       HudAppMenuRegistrarWindow *window;
00196       guint xid;
00197 
00198       if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(u)")))
00199         return;
00200 
00201       g_variant_get (parameters, 0, "u", &xid);
00202 
00203       g_debug ("xid %u disappeared", xid);
00204 
00205       window = hud_app_menu_registrar_get_window (registrar, xid);
00206 
00207       g_free (window->bus_name);
00208       window->bus_name = NULL;
00209       g_free (window->object_path);
00210       window->object_path = NULL;
00211 
00212       hud_app_menu_registrar_notify_window_observers (registrar, window);
00213 
00214       hud_app_menu_registrar_possibly_free_window (registrar, window);
00215     }
00216 }
00217 
00218 static void
00219 hud_app_menu_registrar_ready (GObject      *source,
00220                               GAsyncResult *result,
00221                               gpointer      user_data)
00222 {
00223   HudAppMenuRegistrar *registrar = user_data;
00224   GError *error = NULL;
00225   GVariant *reply;
00226 
00227   g_debug ("GetMenus returned");
00228 
00229   g_clear_object (&registrar->cancellable);
00230 
00231   reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
00232 
00233   if (reply)
00234     {
00235       GVariantIter *iter;
00236       const gchar *bus_name;
00237       const gchar *object_path;
00238       guint xid;
00239 
00240       g_assert (!registrar->ready);
00241       registrar->ready = TRUE;
00242 
00243       g_debug ("going ready");
00244 
00245       g_variant_get (reply, "(a(uso))", &iter);
00246 
00247       while (g_variant_iter_next (iter, "(u&s&o)", &xid, &bus_name, &object_path))
00248         {
00249           HudAppMenuRegistrarWindow *window;
00250 
00251           window = hud_app_menu_registrar_get_window (registrar, xid);
00252 
00253           /* we were not ready until now, so we expect this to be unset */
00254           g_assert (window->bus_name == NULL);
00255 
00256           window->bus_name = g_strdup (bus_name);
00257           window->object_path = g_strdup (object_path);
00258 
00259           hud_app_menu_registrar_notify_window_observers (registrar, window);
00260         }
00261 
00262       g_variant_iter_free (iter);
00263       g_variant_unref (reply);
00264     }
00265   else
00266     {
00267       g_warning ("GetMenus returned an error: %s", error->message);
00268       g_error_free (error);
00269     }
00270 
00271   g_object_unref (registrar);
00272 
00273   g_debug ("done handling GetMenus reply");
00274 }
00275 
00276 static void
00277 hud_app_menu_registrar_name_appeared (GDBusConnection *connection,
00278                                       const gchar     *name,
00279                                       const gchar     *name_owner,
00280                                       gpointer         user_data)
00281 {
00282   HudAppMenuRegistrar *registrar = user_data;
00283 
00284   g_debug ("name appeared (owner is %s)", name_owner);
00285 
00286   g_assert (registrar->subscription == 0);
00287   registrar->subscription = g_dbus_connection_signal_subscribe (connection, name_owner,
00288                                                                 APPMENU_REGISTRAR_IFACE, NULL,
00289                                                                 APPMENU_REGISTRAR_OBJECT_PATH, NULL,
00290                                                                 G_DBUS_SIGNAL_FLAGS_NONE,
00291                                                                 hud_app_menu_registrar_dbus_signal,
00292                                                                 registrar, NULL);
00293 
00294   g_assert (registrar->cancellable == NULL);
00295   registrar->cancellable = g_cancellable_new ();
00296   g_dbus_connection_call (connection, name_owner, APPMENU_REGISTRAR_OBJECT_PATH,
00297                           APPMENU_REGISTRAR_IFACE, "GetMenus", NULL, G_VARIANT_TYPE ("(a(uso))"),
00298                           G_DBUS_CALL_FLAGS_NONE, -1, registrar->cancellable,
00299                           hud_app_menu_registrar_ready, g_object_ref (registrar));
00300 }
00301 
00302 static void
00303 hud_app_menu_registrar_name_vanished (GDBusConnection *connection,
00304                                       const gchar     *name,
00305                                       gpointer         user_data)
00306 {
00307   HudAppMenuRegistrar *registrar = user_data;
00308 
00309   g_debug ("name vanished");
00310 
00311   if (registrar->subscription > 0)
00312     {
00313       g_dbus_connection_signal_unsubscribe (connection, registrar->subscription);
00314       registrar->subscription = 0;
00315     }
00316 
00317   if (registrar->cancellable)
00318     {
00319       g_cancellable_cancel (registrar->cancellable);
00320       g_clear_object (&registrar->cancellable);
00321     }
00322 
00323   if (registrar->ready)
00324     {
00325       GHashTableIter iter;
00326       gpointer value;
00327 
00328       registrar->ready = FALSE;
00329 
00330       g_hash_table_iter_init (&iter, registrar->windows);
00331       while (g_hash_table_iter_next (&iter, NULL, &value))
00332         {
00333           HudAppMenuRegistrarWindow *window = value;
00334 
00335           g_free (window->bus_name);
00336           window->bus_name = NULL;
00337           g_free (window->object_path);
00338           window->object_path = NULL;
00339 
00340           hud_app_menu_registrar_notify_window_observers (registrar, window);
00341 
00342           /* Cannot go the normal route here because we are iterating... */
00343           if (window->observers == NULL)
00344             g_hash_table_iter_remove (&iter);
00345         }
00346     }
00347 }
00348 
00349 static void
00350 hud_app_menu_registrar_finalize (GObject *object)
00351 {
00352   /* This is an immortal singleton.  If we're here, we have trouble. */
00353   g_assert_not_reached ();
00354 }
00355 
00356 static void
00357 hud_app_menu_registrar_init (HudAppMenuRegistrar *registrar)
00358 {
00359   g_debug ("online");
00360 
00361   registrar->windows = g_hash_table_new_full (NULL, NULL, NULL, hud_app_menu_registrar_window_free);
00362   g_bus_watch_name (G_BUS_TYPE_SESSION, APPMENU_REGISTRAR_BUS_NAME, G_BUS_NAME_WATCHER_FLAGS_NONE,
00363                     hud_app_menu_registrar_name_appeared, hud_app_menu_registrar_name_vanished,
00364                     g_object_ref (registrar), g_object_unref);
00365 }
00366 
00367 static void
00368 hud_app_menu_registrar_class_init (HudAppMenuRegistrarClass *class)
00369 {
00370   class->finalize = hud_app_menu_registrar_finalize;
00371 }
00372 
00417 void
00418 hud_app_menu_registrar_add_observer (HudAppMenuRegistrar             *registrar,
00419                                      guint                            xid,
00420                                      HudAppMenuRegistrarObserverFunc  callback,
00421                                      gpointer                         user_data)
00422 {
00423   HudAppMenuRegistrarObserver *observer;
00424   HudAppMenuRegistrarWindow *window;
00425 
00426   g_return_if_fail (xid != 0);
00427   g_return_if_fail (callback != NULL);
00428   g_return_if_fail (!registrar->notifying);
00429 
00430   g_debug ("observer added for xid %u (%p)", xid, user_data);
00431 
00432   observer = g_slice_new (HudAppMenuRegistrarObserver);
00433   observer->callback = callback;
00434   observer->user_data = user_data;
00435 
00436   window = hud_app_menu_registrar_get_window (registrar, xid);
00437   window->observers = g_slist_prepend (window->observers, observer);
00438 
00439   /* send the first update */
00440   (* callback) (registrar, xid, window->bus_name, window->object_path, user_data);
00441 }
00442 
00460 void
00461 hud_app_menu_registrar_remove_observer (HudAppMenuRegistrar             *registrar,
00462                                         guint                            xid,
00463                                         HudAppMenuRegistrarObserverFunc  callback,
00464                                         gpointer                         user_data)
00465 {
00466   HudAppMenuRegistrarWindow *window;
00467   GSList **node;
00468 
00469   g_return_if_fail (xid != 0);
00470   g_return_if_fail (callback != NULL);
00471   g_return_if_fail (!registrar->notifying);
00472 
00473   g_debug ("observer removed for xid %u (%p)", xid, user_data);
00474 
00475   window = hud_app_menu_registrar_get_window (registrar, xid);
00476   for (node = &window->observers; *node; node = &(*node)->next)
00477     {
00478       HudAppMenuRegistrarObserver *observer = (*node)->data;
00479 
00480       if (observer->callback == callback && observer->user_data == user_data)
00481         {
00482           g_slice_free (HudAppMenuRegistrarObserver, observer);
00483           *node = g_slist_delete_link (*node, *node);
00484           break;
00485         }
00486     }
00487 
00488   hud_app_menu_registrar_possibly_free_window (registrar, window);
00489 }
00490 
00498 HudAppMenuRegistrar *
00499 hud_app_menu_registrar_get (void)
00500 {
00501   static HudAppMenuRegistrar *singleton;
00502 
00503   if (!singleton)
00504     singleton = g_object_new (HUD_TYPE_APP_MENU_REGISTRAR, NULL);
00505 
00506   return singleton;
00507 }