~sil2100/indicator-appmenu/indicator-appmenu-qt5

« back to all changes in this revision

Viewing changes to src/hudappmenuregistrar.c

  • Committer: Tarmac
  • Author(s): Ted Gould
  • Date: 2013-02-19 17:55:05 UTC
  • mfrom: (216.1.15 hudectomy)
  • Revision ID: tarmac-20130219175505-jjynbyonwdf8ui8p
Remove the HUD files from indicator-appmenu.

Approved by PS Jenkins bot, Mathieu Trudel-Lapierre, Charles Kerr.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright © 2012 Canonical Ltd.
3
 
 *
4
 
 * This program is free software: you can redistribute it and/or modify it
5
 
 * under the terms of the GNU General Public License version 3, as
6
 
 * published by the Free Software Foundation.
7
 
 *
8
 
 * This program is distributed in the hope that it will be useful, but
9
 
 * WITHOUT ANY WARRANTY; without even the implied warranties of
10
 
 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11
 
 * PURPOSE.  See the GNU General Public License for more details.
12
 
 *
13
 
 * You should have received a copy of the GNU General Public License along
14
 
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
15
 
 *
16
 
 * Author: Ryan Lortie <desrt@desrt.ca>
17
 
 */
18
 
 
19
 
#define G_LOG_DOMAIN "hudappmenuregistrar"
20
 
 
21
 
#include "hudappmenuregistrar.h"
22
 
 
23
 
#include <gio/gio.h>
24
 
 
25
 
/**
26
 
 * SECTION:hudappmenuregistrar
27
 
 * @title: HudAppMenuRegistrar
28
 
 * @short_description: client for the com.canonical.AppMenu.Registrar
29
 
 *   D-Bus service
30
 
 *
31
 
 * The #HudAppMenuRegistrar is a singleton object that monitors the
32
 
 * com.canonical.AppMenu.Registrar D-Bus service.
33
 
 *
34
 
 * On instantiation, a D-Bus name watch is setup for the registrar.
35
 
 * When the registrar is found to exist, a local copy is made of the
36
 
 * windows and menus that the registrar knows about.  Change
37
 
 * notifications are also monitored to keep the local cache in sync.
38
 
 *
39
 
 * After that point, all queries for information from the registrar are
40
 
 * satisfied from the local cache, without blocking.
41
 
 *
42
 
 * Information is acquired from #HudAppMenuRegistrar by using
43
 
 * hud_app_menu_registrar_add_observer().  This immediately calls a
44
 
 * callback with the initial information and makes future calls to the
45
 
 * same callback if the information is found to have changed.
46
 
 *
47
 
 * If the registrar is offline or the information is not yet available
48
 
 * at the time of the original query, the window will initially be
49
 
 * reported as having no menu but a change notification will arrive when
50
 
 * the proper information becomes available.
51
 
 **/
52
 
 
53
 
/**
54
 
 * HudAppMenuRegistrar:
55
 
 *
56
 
 * This is an opaque structure type.
57
 
 **/
58
 
 
59
 
#define APPMENU_REGISTRAR_BUS_NAME    "com.canonical.AppMenu.Registrar"
60
 
#define APPMENU_REGISTRAR_OBJECT_PATH "/com/canonical/AppMenu/Registrar"
61
 
#define APPMENU_REGISTRAR_IFACE       "com.canonical.AppMenu.Registrar"
62
 
 
63
 
typedef struct
64
 
{
65
 
  HudAppMenuRegistrarObserverFunc callback;
66
 
  gpointer                        user_data;
67
 
} HudAppMenuRegistrarObserver;
68
 
 
69
 
typedef struct
70
 
{
71
 
  guint xid;
72
 
  gchar *bus_name;
73
 
  gchar *object_path;
74
 
  GSList *observers;
75
 
} HudAppMenuRegistrarWindow;
76
 
 
77
 
struct _HudAppMenuRegistrar
78
 
{
79
 
  GObject parent_instance;
80
 
 
81
 
  guint subscription;
82
 
  GCancellable *cancellable;
83
 
  GHashTable *windows;
84
 
  gboolean notifying;
85
 
  gboolean ready;
86
 
};
87
 
 
88
 
typedef GObjectClass HudAppMenuRegistrarClass;
89
 
 
90
 
G_DEFINE_TYPE (HudAppMenuRegistrar, hud_app_menu_registrar, G_TYPE_OBJECT)
91
 
 
92
 
static void
93
 
hud_app_menu_registrar_window_free (gpointer user_data)
94
 
{
95
 
  HudAppMenuRegistrarWindow *window = user_data;
96
 
 
97
 
  g_assert (window->bus_name == NULL);
98
 
  g_assert (window->object_path == NULL);
99
 
  g_assert (window->observers == NULL);
100
 
 
101
 
  g_debug ("free window instance for %u", window->xid);
102
 
 
103
 
  g_slice_free (HudAppMenuRegistrarWindow, window);
104
 
}
105
 
 
106
 
static HudAppMenuRegistrarWindow *
107
 
hud_app_menu_registrar_get_window (HudAppMenuRegistrar *registrar,
108
 
                                   guint                xid)
109
 
{
110
 
  HudAppMenuRegistrarWindow *window;
111
 
 
112
 
  window = g_hash_table_lookup (registrar->windows, GINT_TO_POINTER (xid));
113
 
 
114
 
  if (!window)
115
 
    {
116
 
      window = g_slice_new0 (HudAppMenuRegistrarWindow);
117
 
      window->xid = xid;
118
 
 
119
 
      g_debug ("create window instance for %u", xid);
120
 
      g_hash_table_insert (registrar->windows, GINT_TO_POINTER (xid), window);
121
 
    }
122
 
 
123
 
  return window;
124
 
}
125
 
 
126
 
static void
127
 
hud_app_menu_registrar_possibly_free_window (HudAppMenuRegistrar       *registrar,
128
 
                                             HudAppMenuRegistrarWindow *window)
129
 
{
130
 
  if (window->bus_name == NULL && window->observers == NULL)
131
 
    g_hash_table_remove (registrar->windows, GINT_TO_POINTER (window->xid));
132
 
}
133
 
 
134
 
static void
135
 
hud_app_menu_registrar_notify_window_observers (HudAppMenuRegistrar       *registrar,
136
 
                                                HudAppMenuRegistrarWindow *window)
137
 
{
138
 
  GSList *node;
139
 
 
140
 
  registrar->notifying = TRUE;
141
 
 
142
 
  for (node = window->observers; node; node = node->next)
143
 
    {
144
 
      HudAppMenuRegistrarObserver *observer = node->data;
145
 
 
146
 
      g_debug ("notifying %p about %u", observer->user_data, window->xid);
147
 
      (* observer->callback) (registrar, window->xid, window->bus_name, window->object_path, observer->user_data);
148
 
    }
149
 
 
150
 
  registrar->notifying = FALSE;
151
 
}
152
 
 
153
 
static void
154
 
hud_app_menu_registrar_dbus_signal (GDBusConnection *connection,
155
 
                                    const gchar     *sender_name,
156
 
                                    const gchar     *object_path,
157
 
                                    const gchar     *interface_name,
158
 
                                    const gchar     *signal_name,
159
 
                                    GVariant        *parameters,
160
 
                                    gpointer         user_data)
161
 
{
162
 
  HudAppMenuRegistrar *registrar = user_data;
163
 
 
164
 
  g_debug ("got signal");
165
 
 
166
 
  if (!registrar->ready)
167
 
    {
168
 
      g_debug ("not ready, so ignoring signal");
169
 
      return;
170
 
    }
171
 
 
172
 
  if (g_str_equal (signal_name, "WindowRegistered"))
173
 
    {
174
 
      HudAppMenuRegistrarWindow *window;
175
 
      guint xid;
176
 
 
177
 
      if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(uso)")))
178
 
          return;
179
 
 
180
 
      g_variant_get_child (parameters, 0, "u", &xid);
181
 
      window = hud_app_menu_registrar_get_window (registrar, xid);
182
 
 
183
 
      g_free (window->bus_name);
184
 
      g_variant_get_child (parameters, 1, "s", &window->bus_name);
185
 
      g_free (window->object_path);
186
 
      g_variant_get_child (parameters, 2, "o", &window->object_path);
187
 
 
188
 
      g_debug ("xid %u is now at (%s, %s)", xid, window->bus_name, window->object_path);
189
 
 
190
 
      hud_app_menu_registrar_notify_window_observers (registrar, window);
191
 
    }
192
 
 
193
 
  else if (g_str_equal (signal_name, "WindowUnregistered"))
194
 
    {
195
 
      HudAppMenuRegistrarWindow *window;
196
 
      guint xid;
197
 
 
198
 
      if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(u)")))
199
 
        return;
200
 
 
201
 
      g_variant_get (parameters, 0, "u", &xid);
202
 
 
203
 
      g_debug ("xid %u disappeared", xid);
204
 
 
205
 
      window = hud_app_menu_registrar_get_window (registrar, xid);
206
 
 
207
 
      g_free (window->bus_name);
208
 
      window->bus_name = NULL;
209
 
      g_free (window->object_path);
210
 
      window->object_path = NULL;
211
 
 
212
 
      hud_app_menu_registrar_notify_window_observers (registrar, window);
213
 
 
214
 
      hud_app_menu_registrar_possibly_free_window (registrar, window);
215
 
    }
216
 
}
217
 
 
218
 
static void
219
 
hud_app_menu_registrar_ready (GObject      *source,
220
 
                              GAsyncResult *result,
221
 
                              gpointer      user_data)
222
 
{
223
 
  HudAppMenuRegistrar *registrar = user_data;
224
 
  GError *error = NULL;
225
 
  GVariant *reply;
226
 
 
227
 
  g_debug ("GetMenus returned");
228
 
 
229
 
  g_clear_object (&registrar->cancellable);
230
 
 
231
 
  reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
232
 
 
233
 
  if (reply)
234
 
    {
235
 
      GVariantIter *iter;
236
 
      const gchar *bus_name;
237
 
      const gchar *object_path;
238
 
      guint xid;
239
 
 
240
 
      g_assert (!registrar->ready);
241
 
      registrar->ready = TRUE;
242
 
 
243
 
      g_debug ("going ready");
244
 
 
245
 
      g_variant_get (reply, "(a(uso))", &iter);
246
 
 
247
 
      while (g_variant_iter_next (iter, "(u&s&o)", &xid, &bus_name, &object_path))
248
 
        {
249
 
          HudAppMenuRegistrarWindow *window;
250
 
 
251
 
          window = hud_app_menu_registrar_get_window (registrar, xid);
252
 
 
253
 
          /* we were not ready until now, so we expect this to be unset */
254
 
          g_assert (window->bus_name == NULL);
255
 
 
256
 
          window->bus_name = g_strdup (bus_name);
257
 
          window->object_path = g_strdup (object_path);
258
 
 
259
 
          hud_app_menu_registrar_notify_window_observers (registrar, window);
260
 
        }
261
 
 
262
 
      g_variant_iter_free (iter);
263
 
      g_variant_unref (reply);
264
 
    }
265
 
  else
266
 
    {
267
 
      g_warning ("GetMenus returned an error: %s", error->message);
268
 
      g_error_free (error);
269
 
    }
270
 
 
271
 
  g_object_unref (registrar);
272
 
 
273
 
  g_debug ("done handling GetMenus reply");
274
 
}
275
 
 
276
 
static void
277
 
hud_app_menu_registrar_name_appeared (GDBusConnection *connection,
278
 
                                      const gchar     *name,
279
 
                                      const gchar     *name_owner,
280
 
                                      gpointer         user_data)
281
 
{
282
 
  HudAppMenuRegistrar *registrar = user_data;
283
 
 
284
 
  g_debug ("name appeared (owner is %s)", name_owner);
285
 
 
286
 
  g_assert (registrar->subscription == 0);
287
 
  registrar->subscription = g_dbus_connection_signal_subscribe (connection, name_owner,
288
 
                                                                APPMENU_REGISTRAR_IFACE, NULL,
289
 
                                                                APPMENU_REGISTRAR_OBJECT_PATH, NULL,
290
 
                                                                G_DBUS_SIGNAL_FLAGS_NONE,
291
 
                                                                hud_app_menu_registrar_dbus_signal,
292
 
                                                                registrar, NULL);
293
 
 
294
 
  g_assert (registrar->cancellable == NULL);
295
 
  registrar->cancellable = g_cancellable_new ();
296
 
  g_dbus_connection_call (connection, name_owner, APPMENU_REGISTRAR_OBJECT_PATH,
297
 
                          APPMENU_REGISTRAR_IFACE, "GetMenus", NULL, G_VARIANT_TYPE ("(a(uso))"),
298
 
                          G_DBUS_CALL_FLAGS_NONE, -1, registrar->cancellable,
299
 
                          hud_app_menu_registrar_ready, g_object_ref (registrar));
300
 
}
301
 
 
302
 
static void
303
 
hud_app_menu_registrar_name_vanished (GDBusConnection *connection,
304
 
                                      const gchar     *name,
305
 
                                      gpointer         user_data)
306
 
{
307
 
  HudAppMenuRegistrar *registrar = user_data;
308
 
 
309
 
  g_debug ("name vanished");
310
 
 
311
 
  if (registrar->subscription > 0)
312
 
    {
313
 
      g_dbus_connection_signal_unsubscribe (connection, registrar->subscription);
314
 
      registrar->subscription = 0;
315
 
    }
316
 
 
317
 
  if (registrar->cancellable)
318
 
    {
319
 
      g_cancellable_cancel (registrar->cancellable);
320
 
      g_clear_object (&registrar->cancellable);
321
 
    }
322
 
 
323
 
  if (registrar->ready)
324
 
    {
325
 
      GHashTableIter iter;
326
 
      gpointer value;
327
 
 
328
 
      registrar->ready = FALSE;
329
 
 
330
 
      g_hash_table_iter_init (&iter, registrar->windows);
331
 
      while (g_hash_table_iter_next (&iter, NULL, &value))
332
 
        {
333
 
          HudAppMenuRegistrarWindow *window = value;
334
 
 
335
 
          g_free (window->bus_name);
336
 
          window->bus_name = NULL;
337
 
          g_free (window->object_path);
338
 
          window->object_path = NULL;
339
 
 
340
 
          hud_app_menu_registrar_notify_window_observers (registrar, window);
341
 
 
342
 
          /* Cannot go the normal route here because we are iterating... */
343
 
          if (window->observers == NULL)
344
 
            g_hash_table_iter_remove (&iter);
345
 
        }
346
 
    }
347
 
}
348
 
 
349
 
static void
350
 
hud_app_menu_registrar_finalize (GObject *object)
351
 
{
352
 
  /* This is an immortal singleton.  If we're here, we have trouble. */
353
 
  g_assert_not_reached ();
354
 
}
355
 
 
356
 
static void
357
 
hud_app_menu_registrar_init (HudAppMenuRegistrar *registrar)
358
 
{
359
 
  g_debug ("online");
360
 
 
361
 
  registrar->windows = g_hash_table_new_full (NULL, NULL, NULL, hud_app_menu_registrar_window_free);
362
 
  g_bus_watch_name (G_BUS_TYPE_SESSION, APPMENU_REGISTRAR_BUS_NAME, G_BUS_NAME_WATCHER_FLAGS_NONE,
363
 
                    hud_app_menu_registrar_name_appeared, hud_app_menu_registrar_name_vanished,
364
 
                    g_object_ref (registrar), g_object_unref);
365
 
}
366
 
 
367
 
static void
368
 
hud_app_menu_registrar_class_init (HudAppMenuRegistrarClass *class)
369
 
{
370
 
  class->finalize = hud_app_menu_registrar_finalize;
371
 
}
372
 
 
373
 
/**
374
 
 * HudAppMenuRegistrarObserverFunc:
375
 
 * @registrar: the #HudAppMenuRegistrar
376
 
 * @xid: the xid that we are notifying about
377
 
 * @bus_name: the bus name for the dbusmenu, or %NULL
378
 
 * @object_path: the object path for the dbusmenu, or %NULL
379
 
 * @user_data: the data pointer
380
 
 *
381
 
 * Notifies about the initial values for or changes to the bus name and
382
 
 * object path at which to find the dbusmenu for @xid.
383
 
 *
384
 
 * You should pass the values of @bus_name and @object_path to
385
 
 * dbusmenu_client_new() to get started.
386
 
 *
387
 
 * If no menu is available then @bus_name and @object_path will both be
388
 
 * given as %NULL.
389
 
 **/
390
 
 
391
 
/**
392
 
 * hud_app_menu_registrar_add_observer:
393
 
 * @registrar: the #HudAppMenuRegistrar
394
 
 * @xid: the xid to begin observing
395
 
 * @callback: a #HudAppMenuRegistrarObserverFunc
396
 
 * @user_data: user data for @callback
397
 
 *
398
 
 * Begins observing @xid.
399
 
 *
400
 
 * @callback will be called exactly once before the function returns
401
 
 * with a set of initial values (the bus name and object path at which
402
 
 * to find the menu for the window).
403
 
 *
404
 
 * If the location of the menu for @xid changes (including being created
405
 
 * or destroyed) then @callback will be called each time an update is
406
 
 * required.
407
 
 *
408
 
 * It is possible that the values are not initially known because they
409
 
 * have not yet been retreived from the registrar or because the
410
 
 * registrar is not running.  In this case, %NULL values will be
411
 
 * provided initially and @callback will be invoked again when the
412
 
 * real values are known.
413
 
 *
414
 
 * Call hud_app_menu_registrar_remove_observer() to when you are no
415
 
 * longer interested in @xid.
416
 
 **/
417
 
void
418
 
hud_app_menu_registrar_add_observer (HudAppMenuRegistrar             *registrar,
419
 
                                     guint                            xid,
420
 
                                     HudAppMenuRegistrarObserverFunc  callback,
421
 
                                     gpointer                         user_data)
422
 
{
423
 
  HudAppMenuRegistrarObserver *observer;
424
 
  HudAppMenuRegistrarWindow *window;
425
 
 
426
 
  g_return_if_fail (xid != 0);
427
 
  g_return_if_fail (callback != NULL);
428
 
  g_return_if_fail (!registrar->notifying);
429
 
 
430
 
  g_debug ("observer added for xid %u (%p)", xid, user_data);
431
 
 
432
 
  observer = g_slice_new (HudAppMenuRegistrarObserver);
433
 
  observer->callback = callback;
434
 
  observer->user_data = user_data;
435
 
 
436
 
  window = hud_app_menu_registrar_get_window (registrar, xid);
437
 
  window->observers = g_slist_prepend (window->observers, observer);
438
 
 
439
 
  /* send the first update */
440
 
  (* callback) (registrar, xid, window->bus_name, window->object_path, user_data);
441
 
}
442
 
 
443
 
/**
444
 
 * hud_app_menu_registrar_remove_observer:
445
 
 * @registrar: the #HudAppMenuRegistrar
446
 
 * @xid: the xid to begin observing
447
 
 * @callback: a #HudAppMenuRegistrarObserverFunc
448
 
 * @user_data: user data for @callback
449
 
 *
450
 
 * Reverses the effect of a previous call to
451
 
 * hud_app_menu_registrar_add_observer().
452
 
 *
453
 
 * @callback and @user_data must be exactly equal to the values passed
454
 
 * to that function.
455
 
 *
456
 
 * One call does not remove all instances of @callback and @user_data.
457
 
 * You need to call this function the same number of times that you
458
 
 * called hud_app_menu_registrar_add_observer().
459
 
 **/
460
 
void
461
 
hud_app_menu_registrar_remove_observer (HudAppMenuRegistrar             *registrar,
462
 
                                        guint                            xid,
463
 
                                        HudAppMenuRegistrarObserverFunc  callback,
464
 
                                        gpointer                         user_data)
465
 
{
466
 
  HudAppMenuRegistrarWindow *window;
467
 
  GSList **node;
468
 
 
469
 
  g_return_if_fail (xid != 0);
470
 
  g_return_if_fail (callback != NULL);
471
 
  g_return_if_fail (!registrar->notifying);
472
 
 
473
 
  g_debug ("observer removed for xid %u (%p)", xid, user_data);
474
 
 
475
 
  window = hud_app_menu_registrar_get_window (registrar, xid);
476
 
  for (node = &window->observers; *node; node = &(*node)->next)
477
 
    {
478
 
      HudAppMenuRegistrarObserver *observer = (*node)->data;
479
 
 
480
 
      if (observer->callback == callback && observer->user_data == user_data)
481
 
        {
482
 
          g_slice_free (HudAppMenuRegistrarObserver, observer);
483
 
          *node = g_slist_delete_link (*node, *node);
484
 
          break;
485
 
        }
486
 
    }
487
 
 
488
 
  hud_app_menu_registrar_possibly_free_window (registrar, window);
489
 
}
490
 
 
491
 
/**
492
 
 * hud_app_menu_registrar_get:
493
 
 *
494
 
 * Gets the singleton instance of #HudAppMenuRegistrar.
495
 
 *
496
 
 * Returns: (transfer none): the instance
497
 
 **/
498
 
HudAppMenuRegistrar *
499
 
hud_app_menu_registrar_get (void)
500
 
{
501
 
  static HudAppMenuRegistrar *singleton;
502
 
 
503
 
  if (!singleton)
504
 
    singleton = g_object_new (HUD_TYPE_APP_MENU_REGISTRAR, NULL);
505
 
 
506
 
  return singleton;
507
 
}