2
* Copyright © 2012 Canonical Ltd.
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.
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.
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/>.
16
* Author: Ted Gould <ted@canonical.com>
19
#define G_LOG_DOMAIN "hudapplicationlist"
25
#include "window-info.h"
27
#include "application-list.h"
28
#include "application-source.h"
30
#include "window-stack-iface.h"
32
typedef struct _HudApplicationListPrivate HudApplicationListPrivate;
34
struct _HudApplicationListPrivate {
35
HudApplicationSource * last_focused_main_stage_source;
37
DBusWindowStack * window_stack;
39
gulong matcher_app_sig;
40
gulong matcher_view_open_sig;
41
gulong matcher_view_close_sig;
42
GHashTable * applications;
43
HudSource * used_source;
46
#define HUD_APPLICATION_LIST_GET_PRIVATE(o) \
47
(G_TYPE_INSTANCE_GET_PRIVATE ((o), HUD_TYPE_APPLICATION_LIST, HudApplicationListPrivate))
49
static void hud_application_list_class_init (HudApplicationListClass * klass);
50
static void hud_application_list_init (HudApplicationList * self);
51
static void hud_application_list_constructed (GObject * object);
52
static void matching_setup (HudApplicationList * self);
53
static void hud_application_list_dispose (GObject * object);
54
static void hud_application_list_finalize (GObject * object);
55
static void source_iface_init (HudSourceInterface * iface);
56
static void window_changed (DBusWindowStack * matcher,
61
static void view_opened (DBusWindowStack * matcher,
65
static void view_closed (DBusWindowStack * matcher,
69
static void source_use (HudSource * hud_source);
70
static void source_unuse (HudSource * hud_source);
71
static void source_search (HudSource * hud_source,
72
HudTokenList * search_string,
73
void (*append_func) (HudResult * result, gpointer user_data),
75
static void source_list_applications (HudSource * hud_source,
76
HudTokenList * search_string,
77
void (*append_func) (const gchar *application_id, const gchar *application_icon, HudSourceItemType type, gpointer user_data),
79
static HudSource * source_get (HudSource * hud_source,
80
const gchar * application_id);
81
static GList * source_get_items (HudSource * list);
82
static void application_source_changed (HudSource * source,
84
static gboolean hud_application_list_name_in_ignore_list (HudWindowInfo *window);
86
G_DEFINE_TYPE_WITH_CODE (HudApplicationList, hud_application_list, G_TYPE_OBJECT,
87
G_IMPLEMENT_INTERFACE (HUD_TYPE_SOURCE, source_iface_init))
91
hud_application_list_class_init (HudApplicationListClass *klass)
93
GObjectClass *object_class = G_OBJECT_CLASS (klass);
95
g_type_class_add_private (klass, sizeof (HudApplicationListPrivate));
97
object_class->constructed = hud_application_list_constructed;
98
object_class->dispose = hud_application_list_dispose;
99
object_class->finalize = hud_application_list_finalize;
101
klass->matching_setup = matching_setup;
106
/* Intialized the source interface */
108
source_iface_init (HudSourceInterface *iface)
110
iface->use = source_use;
111
iface->unuse = source_unuse;
112
iface->search = source_search;
113
iface->list_applications = source_list_applications;
114
iface->get = source_get;
115
iface->get_items = source_get_items;
122
hud_application_list_init (HudApplicationList *self)
124
self->priv = HUD_APPLICATION_LIST_GET_PRIVATE(self);
125
self->priv->applications = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref);
130
/* Final build steps */
132
hud_application_list_constructed (GObject * object)
134
HudApplicationList * self = HUD_APPLICATION_LIST(object);
136
HudApplicationListClass * aclass = HUD_APPLICATION_LIST_GET_CLASS(self);
137
if (aclass->matching_setup != NULL) {
138
aclass->matching_setup(self);
145
window_stack_vanished(G_GNUC_UNUSED GDBusConnection *connection,
146
const gchar *name, gpointer user_data) {
147
HudApplicationList *self = HUD_APPLICATION_LIST(user_data);
149
g_debug("window stack vanished %s", name);
151
g_hash_table_remove_all(self->priv->applications);
153
self->priv->used_source = NULL;
154
self->priv->last_focused_main_stage_source = NULL;
156
hud_source_changed(HUD_SOURCE(self));
160
matching_setup (HudApplicationList * self)
162
self->priv->bus_watch_id = g_bus_watch_name(G_BUS_TYPE_SESSION,
163
"com.canonical.Unity.WindowStack", G_BUS_NAME_WATCHER_FLAGS_NONE,
164
NULL, window_stack_vanished, self, NULL);
166
GError *error = NULL;
167
self->priv->window_stack = dbus_window_stack_proxy_new_for_bus_sync(
168
G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE,
169
"com.canonical.Unity.WindowStack",
170
"/com/canonical/Unity/WindowStack",
172
if(self->priv->window_stack == NULL) {
173
g_warning("Could not construct window stack proxy: %s", error->message);
178
g_debug("connecting to window stack signals");
179
self->priv->matcher_app_sig = g_signal_connect(self->priv->window_stack,
180
"focused-window-changed",
181
G_CALLBACK(window_changed), self);
182
self->priv->matcher_view_open_sig = g_signal_connect(self->priv->window_stack,
184
G_CALLBACK(view_opened), self);
185
self->priv->matcher_view_close_sig = g_signal_connect(self->priv->window_stack,
187
G_CALLBACK(view_closed), self);
188
g_debug("connected to window stack signals");
190
GVariant *stack_variant = NULL;
192
if (!dbus_window_stack_call_get_window_stack_sync(self->priv->window_stack,
193
&stack_variant, NULL, &error)) {
194
g_warning("Could not get window stack: %s", error->message);
200
g_variant_iter_init(&iter, stack_variant);
202
GVariant *window_info = NULL;
203
while ((window_info = g_variant_iter_next_value(&iter))) {
204
GVariantIter window_info_iter;
205
g_variant_iter_init(&window_info_iter, window_info);
207
GVariant *window_id_variant = g_variant_iter_next_value(&window_info_iter);
208
GVariant *app_id_variant = g_variant_iter_next_value(&window_info_iter);
209
GVariant *focused_variant = g_variant_iter_next_value(&window_info_iter);
210
GVariant *stage_variant = g_variant_iter_next_value(&window_info_iter);
212
view_opened(self->priv->window_stack,
213
g_variant_get_uint32(window_id_variant),
214
g_variant_get_string(app_id_variant, NULL),
215
/*g_variant_get_uint32(stage_variant),*/self);
217
if (g_variant_get_boolean(focused_variant)) {
218
HudApplicationSource *source = g_hash_table_lookup(
219
self->priv->applications,
220
g_variant_get_string(app_id_variant, NULL));
223
&& !hud_application_list_name_in_ignore_list(
224
hud_application_source_get_application_info(
226
window_changed(self->priv->window_stack,
227
g_variant_get_uint32(window_id_variant),
228
g_variant_get_string(app_id_variant, NULL),
229
g_variant_get_uint32(stage_variant), self);
233
g_variant_unref (window_id_variant);
234
g_variant_unref (app_id_variant);
235
g_variant_unref (focused_variant);
236
g_variant_unref (stage_variant);
237
g_variant_unref (window_info);
240
g_variant_unref(stack_variant);
243
/* Clean up references */
245
hud_application_list_dispose (GObject *object)
247
HudApplicationList * self = HUD_APPLICATION_LIST(object);
248
g_debug("Application List Dispose Start");
250
if(self->priv->bus_watch_id > 0) {
251
g_bus_unwatch_name(self->priv->bus_watch_id);
254
if (self->priv->used_source != NULL) {
255
hud_source_unuse(self->priv->used_source);
256
self->priv->used_source = NULL;
259
if (self->priv->matcher_app_sig != 0 && self->priv->window_stack != NULL) {
260
g_signal_handler_disconnect(self->priv->window_stack, self->priv->matcher_app_sig);
262
self->priv->matcher_app_sig = 0;
264
if (self->priv->matcher_view_open_sig != 0 && self->priv->window_stack != NULL) {
265
g_signal_handler_disconnect(self->priv->window_stack, self->priv->matcher_view_open_sig);
267
self->priv->matcher_view_open_sig = 0;
269
if (self->priv->matcher_view_close_sig != 0 && self->priv->window_stack != NULL) {
270
g_signal_handler_disconnect(self->priv->window_stack, self->priv->matcher_view_close_sig);
272
self->priv->matcher_view_close_sig = 0;
274
g_debug("Unrefing window stack");
275
g_clear_object(&self->priv->window_stack);
276
g_debug("Unref'd window stack");
278
g_hash_table_remove_all(self->priv->applications);
280
g_debug("Application List Dispose Recurse");
281
G_OBJECT_CLASS (hud_application_list_parent_class)->dispose (object);
282
g_debug("Application List Dispose Stop");
288
hud_application_list_finalize (GObject *object)
290
HudApplicationList * self = HUD_APPLICATION_LIST(object);
291
g_debug("Application List Finalize Start");
293
g_clear_pointer(&self->priv->applications, g_hash_table_unref);
295
g_debug("Application List Finalize Recurse");
296
G_OBJECT_CLASS (hud_application_list_parent_class)->finalize (object);
297
g_debug("Application List Finalize Stop");
301
static HudApplicationSource *
302
application_info_to_source (HudApplicationList * list, HudApplicationInfo * bapp)
304
const gchar * id = hud_window_info_get_app_id(bapp);
306
HudApplicationSource * source = g_hash_table_lookup(list->priv->applications, id);
307
if (source == NULL) {
308
source = hud_application_source_new_for_app(bapp);
309
g_signal_connect(source, "changed", G_CALLBACK(application_source_changed), list);
311
g_hash_table_insert(list->priv->applications, g_strdup(id), source);
312
id = NULL; /* We used the malloc in the table */
314
hud_source_changed(HUD_SOURCE(list));
321
hud_application_list_name_in_ignore_list (HudWindowInfo *window)
323
static const gchar * const ignored_names[] = {
324
"Hud Prototype Test",
326
"DNDCollectionWindow",
336
"XdndCollectionWindowImp",
338
gboolean ignored = FALSE;
341
gchar *window_name = hud_window_info_get_utf8_prop(window, "WM_NAME");
342
g_debug ("checking window name '%s'", window_name);
344
/* it's possible to get NULL here */
345
if (window_name == NULL)
349
for (i = 0; i < G_N_ELEMENTS (ignored_names); i++)
350
if (g_str_equal (ignored_names[i], window_name))
352
g_debug ("window name '%s' blocked", window_name);
362
/* Called each time the focused application changes */
364
window_changed (DBusWindowStack *window_stack, guint window_id, const gchar *app_id, guint stack, gpointer user_data)
366
g_debug("window_changed(%d, %s, %d)", window_id, app_id, stack);
368
HudApplicationList * list = HUD_APPLICATION_LIST(user_data);
370
HudWindowInfo *window = hud_window_info_new(list->priv->window_stack, window_id, app_id, stack);
372
if (hud_application_list_name_in_ignore_list (window)) {
373
g_object_unref(window);
377
/* Clear the last source, as we've obviously changed */
378
list->priv->last_focused_main_stage_source = NULL;
380
HudApplicationSource *source = application_info_to_source(list, window);
382
g_debug("looking up application source for: %s", app_id);
384
/* If we weren't able to lookup the app, let's try to find a source
386
if (source == NULL) {
387
g_debug("no applicationn source was found");
388
guint xid = hud_window_info_get_window_id(window);
389
GList * sources = g_hash_table_get_values(list->priv->applications);
390
GList * lsource = NULL;
392
for (lsource = sources; lsource != NULL; lsource = g_list_next(lsource)) {
393
HudApplicationSource * appsource = HUD_APPLICATION_SOURCE(lsource->data);
394
if (appsource == NULL) continue;
396
if (hud_application_source_has_xid(appsource, xid)) {
403
if (source == NULL) {
404
g_warning("Unable to find source for window");
408
list->priv->last_focused_main_stage_source = source;
410
hud_application_source_focus(source, window, window);
412
hud_source_changed(HUD_SOURCE(list));
414
g_object_unref(window);
420
/* A new view has been opened */
422
view_opened (DBusWindowStack * window_stack, guint window_id, const gchar *app_id, gpointer user_data)
424
g_debug("view_opened(%d, %s)", window_id, app_id);
425
HudApplicationList * list = HUD_APPLICATION_LIST(user_data);
427
HudWindowInfo *window = hud_window_info_new(list->priv->window_stack,
428
window_id, app_id, HUD_WINDOW_INFO_STAGE_MAIN);
430
HudApplicationSource * source = application_info_to_source(list, window);
431
if (source == NULL) {
432
g_object_unref(window);
436
hud_application_source_add_window(source, window);
438
g_object_unref(window);
443
/* A view has been closed */
445
view_closed (DBusWindowStack * window_stack, guint window_id, const gchar *app_id, gpointer user_data)
447
g_debug("view_closed(%d, %s)", window_id, app_id);
449
HudApplicationList * list = HUD_APPLICATION_LIST(user_data);
450
HudSource *source = source_get(HUD_SOURCE(user_data), app_id);
452
if (source == NULL) {
453
g_warning("Window closed for unknown app: %s", app_id);
457
HudApplicationSource *appsource = HUD_APPLICATION_SOURCE(source);
459
if (hud_application_source_has_xid(appsource, window_id)) {
460
hud_application_source_window_closed(appsource, window_id);
463
/* If the application source has become empty it means that
464
* the corresponding app has terminated and it's time to do the
467
if (hud_application_source_is_empty (appsource)) {
468
if ((gpointer)appsource == (gpointer)list->priv->used_source) {
469
hud_source_unuse(HUD_SOURCE(appsource));
470
list->priv->used_source = NULL;
473
if (appsource == list->priv->last_focused_main_stage_source) {
474
list->priv->last_focused_main_stage_source = NULL;
477
g_debug("Removing application %s", app_id);
478
g_hash_table_remove(list->priv->applications, app_id);
481
hud_source_changed(HUD_SOURCE(list));
484
/* Source interface using this source */
486
source_use (HudSource *hud_source)
488
g_return_if_fail(HUD_IS_APPLICATION_LIST(hud_source));
489
HudApplicationList * list = HUD_APPLICATION_LIST(hud_source);
491
HudApplicationSource * source = NULL;
493
/* First see if we've already got it */
494
source = list->priv->last_focused_main_stage_source;
496
if (source == NULL) {
497
g_warning("Unable to find source for window");
501
if (list->priv->used_source != NULL) {
502
hud_source_unuse(list->priv->used_source);
505
list->priv->used_source = HUD_SOURCE(source);
507
hud_source_use(HUD_SOURCE(source));
512
/* Source interface unusing this source */
514
source_unuse (HudSource *hud_source)
516
g_return_if_fail(HUD_IS_APPLICATION_LIST(hud_source));
517
HudApplicationList * list = HUD_APPLICATION_LIST(hud_source);
519
g_return_if_fail(list->priv->used_source != NULL);
521
hud_source_unuse(list->priv->used_source);
522
list->priv->used_source = NULL;
527
/* Search this source */
529
source_search (HudSource * hud_source,
530
HudTokenList * search_string,
531
void (*append_func) (HudResult * result, gpointer user_data),
534
g_return_if_fail(HUD_IS_APPLICATION_LIST(hud_source));
535
HudApplicationList * list = HUD_APPLICATION_LIST(hud_source);
537
g_return_if_fail(list->priv->used_source != NULL);
539
hud_source_search(list->priv->used_source, search_string, append_func, user_data);
545
source_list_applications (HudSource * hud_source,
546
HudTokenList * search_string,
547
void (*append_func) (const gchar *application_id, const gchar *application_icon, HudSourceItemType type, gpointer user_data),
550
g_return_if_fail(HUD_IS_APPLICATION_LIST(hud_source));
551
HudApplicationList * list = HUD_APPLICATION_LIST(hud_source);
552
GList * sources = g_hash_table_get_values(list->priv->applications);
553
GList * lsource = NULL;
555
for (lsource = sources; lsource != NULL; lsource = g_list_next(lsource)) {
556
HudApplicationSource * appsource = HUD_APPLICATION_SOURCE(lsource->data);
557
if (appsource == NULL || HUD_SOURCE(appsource) == list->priv->used_source) continue;
559
hud_source_list_applications(HUD_SOURCE(appsource), search_string, append_func, user_data);
562
if (list->priv->used_source != NULL) {
563
hud_source_list_applications(list->priv->used_source, search_string, append_func, user_data);
568
source_get (HudSource * hud_source,
569
const gchar * application_id)
571
g_return_val_if_fail(HUD_IS_APPLICATION_LIST(hud_source), NULL);
572
g_return_val_if_fail(application_id != NULL, NULL);
573
HudApplicationList * list = HUD_APPLICATION_LIST(hud_source);
575
return g_hash_table_lookup(list->priv->applications, application_id);
578
/* An application has signaled that it's items have changed */
580
application_source_changed (HudSource * source, gpointer user_data)
582
HudApplicationList * list = HUD_APPLICATION_LIST(user_data);
584
hud_source_changed(HUD_SOURCE(list));
590
* hud_application_list_new:
592
* Create a new application list.
594
* Return Value: (transfer full): New #HudApplicationList
597
hud_application_list_new (void)
599
return g_object_new(HUD_TYPE_APPLICATION_LIST,
604
* hud_application_list_get_source:
605
* @list: A #HudApplicationList object
606
* @id: Application ID to find
608
* Looks for a source in the application list database or if it
609
* doesn't exist, it creates it.
611
* Return value: (transfer none): An #HudApplicationSource matching @id
613
HudApplicationSource *
614
hud_application_list_get_source (HudApplicationList * list, const gchar * id)
616
g_return_val_if_fail(HUD_IS_APPLICATION_LIST(list), NULL);
617
g_return_val_if_fail(id != NULL, NULL);
620
HudApplicationSource * source = HUD_APPLICATION_SOURCE(source_get(HUD_SOURCE(list), id));
621
if (source == NULL) {
622
source = hud_application_source_new_for_id(id);
623
g_signal_connect(source, "changed", G_CALLBACK(application_source_changed), list);
624
g_hash_table_insert(list->priv->applications, g_strdup(id), source);
631
* hud_application_list_get_focused_app:
632
* @list: A #HudApplicationList object
634
* Gets the focused app source
636
* Return value: (transfer none): The current #HudApplicationSource
639
hud_application_list_get_focused_app (HudApplicationList * list)
641
g_return_val_if_fail(HUD_IS_APPLICATION_LIST(list), NULL);
643
HudApplicationListClass * aclass = HUD_APPLICATION_LIST_GET_CLASS(list);
644
if (G_UNLIKELY(aclass->get_focused_app != NULL)) {
645
return aclass->get_focused_app(list);
648
return HUD_SOURCE(list->priv->last_focused_main_stage_source);
652
* hud_application_list_get_side_stage_focused_app:
653
* @list: A #HudApplicationList object
655
* Gets the side stage focused app source
657
* Return value: (transfer none): The current #HudApplicationSource
660
hud_application_list_get_side_stage_focused_app (HudApplicationList * list)
662
g_return_val_if_fail(HUD_IS_APPLICATION_LIST(list), NULL);
668
* hud_application_list_get_apps:
669
* @list: A #HudApplicationList object
671
* Gets a list of applications
673
* Return value: A list of #HudApplicationSource objects
676
hud_application_list_get_apps (HudApplicationList * list)
678
g_return_val_if_fail(HUD_IS_APPLICATION_LIST(list), NULL);
680
return g_hash_table_get_values(list->priv->applications);
684
* hud_application_list_get_active_collector:
686
* Returns the active collector if there is one
688
* Returns: (transfer none): A #HudCollector or NULL if none
691
source_get_items (HudSource * source)
693
g_return_val_if_fail(HUD_IS_APPLICATION_LIST(source), NULL);
694
HudApplicationList *list = HUD_APPLICATION_LIST(source);
695
g_return_val_if_fail(list->priv->used_source != NULL, NULL);
696
return hud_source_get_items(list->priv->used_source);