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: Ryan Lortie <desrt@desrt.ca>
19
#define G_LOG_DOMAIN "hudwindowsource"
21
#include "hudwindowsource.h"
23
#include <libbamf/libbamf.h>
26
#include "hudmenumodelcollector.h"
27
#include "huddbusmenucollector.h"
28
#include "hudsource.h"
31
* SECTION:hudwindowsource
32
* @title: HudWindowSource
33
* @short_description: a #HudSource for the menubars of windows
35
* #HudWindowSource is a #HudSource that allows searching for items in
36
* the menubars of application windows.
38
* The source tracks which is the active window of the application,
39
* using BAMF. hud_source_search() calls will be redirected to an
40
* appropriate source corresponding to the active window. When the
41
* active window changes, the HudSource::changed signal will be emitted.
43
* #GMenuModel and Dbusmenu-style menus are both understood. They are
44
* implemented via #HudMenuModelCollector and #HudDbusmenuCollector,
47
* #HudWindowSource takes care to avoid various bits of desktop chrome
48
* from becoming considered as the active window. This is done via a
49
* built-in blacklist. It is also possible, for testing purposes, to
50
* use the <envar>INDICATOR_APPMENU_DEBUG_APPS</envar> environment
51
* variable to specify a list of desktop file names corresponding to
52
* applications to ignore windows from (for example, the terminal).
58
* This is an opaque structure type.
61
struct _HudWindowSource
63
GObject parent_instance;
67
BamfWindow *active_window;
68
BamfApplication *active_application;
69
const gchar *active_desktop_file;
71
HudSource *active_collector;
75
typedef GObjectClass HudWindowSourceClass;
77
static void hud_window_source_iface_init (HudSourceInterface *iface);
78
G_DEFINE_TYPE_WITH_CODE (HudWindowSource, hud_window_source, G_TYPE_OBJECT,
79
G_IMPLEMENT_INTERFACE (HUD_TYPE_SOURCE, hud_window_source_iface_init))
82
hud_window_source_desktop_file_in_debug_list (const gchar *desktop_file)
84
static GStrv debug_list = NULL;
87
/* Looks at the envvar to see if there is a list of items that we shouldn't
88
view as focus changes so that we can use those tools for debugging */
89
if (debug_list == NULL)
91
const gchar * dbgenv = g_getenv ("INDICATOR_APPMENU_DEBUG_APPS");
93
debug_list = g_strsplit (dbgenv, ":", 0);
95
debug_list = g_new0 (gchar *, 1);
98
g_debug ("checking desktop file '%s'", desktop_file);
100
for (i = 0; debug_list[i] != NULL; i++)
101
if (debug_list[i][0] != '\0' && strstr (desktop_file, debug_list[i]))
103
g_debug ("desktop file name '%s' blocked (hit debug list item '%s')", desktop_file, debug_list[i]);
111
hud_window_source_name_in_ignore_list (BamfWindow *window)
113
static const gchar * const ignored_names[] = {
114
"Hud Prototype Test",
116
"DNDCollectionWindow",
124
gboolean ignored = FALSE;
128
window_name = bamf_view_get_name (BAMF_VIEW (window));
129
g_debug ("checking window name '%s'", window_name);
131
/* sometimes bamf returns NULL here... protect ourselves */
132
if (window_name == NULL)
135
for (i = 0; i < G_N_ELEMENTS (ignored_names); i++)
136
if (g_str_equal (ignored_names[i], window_name))
138
g_debug ("window name '%s' blocked", window_name);
143
g_free (window_name);
149
hud_window_source_get_collector (HudWindowSource *source)
151
static GQuark menu_collector_quark;
152
HudSource *collector;
154
if (source->active_window == NULL)
157
if (!menu_collector_quark)
158
menu_collector_quark = g_quark_from_string ("menu collector");
160
collector = g_object_get_qdata (G_OBJECT (source->active_window), menu_collector_quark);
161
if (collector == NULL)
163
HudMenuModelCollector *menumodel_collector;
165
/* GMenuModel menus either exist at the start or will never exist.
166
* dbusmenu menus can appear later.
168
* For that reason, we check first for GMenuModel and assume if it
169
* doesn't exist then it must be dbusmenu.
171
menumodel_collector = hud_menu_model_collector_get (source->active_window,
172
source->active_desktop_file,
173
source->active_icon);
174
if (menumodel_collector)
175
collector = HUD_SOURCE (menumodel_collector);
177
collector = HUD_SOURCE (hud_dbusmenu_collector_new_for_window (source->active_window,
178
source->active_desktop_file,
179
source->active_icon));
181
g_object_set_qdata_full (G_OBJECT (source->active_window), menu_collector_quark, collector, g_object_unref);
188
hud_window_source_collector_changed (HudSource *collector,
191
HudWindowSource *source = user_data;
193
hud_source_changed (HUD_SOURCE (source));
197
hud_window_source_active_window_changed (BamfMatcher *matcher,
202
HudWindowSource *source = user_data;
204
BamfApplication *application;
205
const gchar *desktop_file;
207
g_debug ("Switching windows");
209
if (!BAMF_IS_WINDOW (newview))
211
g_debug ("ignoring switch to non-window");
215
window = BAMF_WINDOW (newview);
217
if (window == source->active_window)
219
g_debug ("this is already the active window");
223
if (hud_window_source_name_in_ignore_list (window))
226
application = bamf_matcher_get_application_for_window (source->matcher, window);
228
if (application == NULL)
230
g_debug ("ignoring window with no application");
234
desktop_file = bamf_application_get_desktop_file (application);
236
if (desktop_file == NULL)
238
g_debug ("ignoring application with no desktop file");
242
if (hud_window_source_desktop_file_in_debug_list (desktop_file))
245
g_debug ("new active window (xid %u)", bamf_window_get_xid (window));
248
if (source->active_collector)
250
g_signal_handlers_disconnect_by_func (source->active_collector, hud_window_source_collector_changed, source);
251
if (source->use_count)
252
hud_source_unuse (source->active_collector);
255
g_clear_object (&source->active_collector);
256
g_clear_object (&source->active_application);
257
g_clear_object (&source->active_window);
258
g_free (source->active_icon);
259
source->active_window = g_object_ref (window);
260
source->active_application = g_object_ref (application);
261
source->active_desktop_file = desktop_file;
262
source->active_icon = bamf_view_get_icon (BAMF_VIEW (application));
263
source->active_collector = g_object_ref (hud_window_source_get_collector (source));
265
if (source->use_count)
266
hud_source_use (source->active_collector);
267
g_signal_connect_object (source->active_collector, "changed",
268
G_CALLBACK (hud_window_source_collector_changed), source, 0);
270
hud_source_changed (HUD_SOURCE (source));
274
hud_window_source_use (HudSource *hud_source)
276
HudWindowSource *source = HUD_WINDOW_SOURCE (hud_source);
278
if (source->use_count == 0)
279
if (source->active_collector)
280
hud_source_use (source->active_collector);
286
hud_window_source_unuse (HudSource *hud_source)
288
HudWindowSource *source = HUD_WINDOW_SOURCE (hud_source);
290
g_return_if_fail (source->use_count > 0);
294
if (source->use_count == 0)
295
if (source->active_collector)
296
hud_source_unuse (source->active_collector);
300
hud_window_source_search (HudSource *hud_source,
301
GPtrArray *results_array,
302
HudTokenList *search_string)
304
HudWindowSource *source = HUD_WINDOW_SOURCE (hud_source);
306
if (source->active_collector)
307
hud_source_search (source->active_collector, results_array, search_string);
311
hud_window_source_finalize (GObject *object)
313
HudWindowSource *source = HUD_WINDOW_SOURCE (object);
315
g_assert_cmpint (source->use_count, ==, 0);
317
/* bamf matcher signals already disconnected in dispose */
318
g_clear_object (&source->active_collector);
319
g_clear_object (&source->active_application);
320
g_clear_object (&source->active_window);
321
g_free (source->active_icon);
323
G_OBJECT_CLASS (hud_window_source_parent_class)
328
hud_window_source_init (HudWindowSource *source)
332
source->matcher = bamf_matcher_get_default ();
334
g_signal_connect_object (source->matcher, "active-window-changed",
335
G_CALLBACK (hud_window_source_active_window_changed), source, 0);
336
window = bamf_matcher_get_active_window (source->matcher);
338
hud_window_source_active_window_changed (source->matcher, NULL, BAMF_VIEW (window), source);
342
hud_window_source_iface_init (HudSourceInterface *iface)
344
iface->use = hud_window_source_use;
345
iface->unuse = hud_window_source_unuse;
346
iface->search = hud_window_source_search;
350
hud_window_source_class_init (HudWindowSourceClass *class)
352
class->finalize = hud_window_source_finalize;
356
* hud_window_source_new:
358
* Creates a #HudWindowSource.
360
* Returns: a new #HudWindowSource
363
hud_window_source_new (void)
365
return g_object_new (HUD_TYPE_WINDOW_SOURCE, NULL);
369
hud_window_source_get_active_xid (HudWindowSource *source)
371
return bamf_window_get_xid (source->active_window);