Merge lp:~3v1n0/bamf/desktop-class-matching-improved into lp:bamf/0.4

Proposed by Marco Trevisan (Treviño)
Status: Merged
Approved by: Marco Trevisan (Treviño)
Approved revision: 469
Merged at revision: 460
Proposed branch: lp:~3v1n0/bamf/desktop-class-matching-improved
Merge into: lp:bamf/0.4
Diff against target: 1185 lines (+459/-304)
6 files modified
src/bamf-legacy-window.c (+51/-16)
src/bamf-legacy-window.h (+14/-15)
src/bamf-matcher.c (+319/-250)
src/bamf-view.c (+3/-1)
src/bamf-xutils.c (+68/-20)
src/bamf-xutils.h (+4/-2)
To merge this branch: bzr merge lp:~3v1n0/bamf/desktop-class-matching-improved
Reviewer Review Type Date Requested Status
Jason Smith (community) Approve
Review via email: mp+102741@code.launchpad.net

Commit message

BamfLegacyWindow and BamfMatcher changes needed to get the proper WM_CLASS / desktop-class matching in BAMF.

Description of the change

BamfLegacyWindow and BamfMatcher changes needed to get the proper WM_CLASS matching in BAMF.

This mostly allow to get the wanted experience with the Chrome/Chromium web applications
so now:
 - When a Web application is launched and has a matching .desktop file (this means that
   the file should include the StartupWMClass parameter that matches the window's class
   instance name) then it's matched as a new application using that file.
 - When a Web application is launched and has not a matching .desktop file (no file
   matches its class), then we fallback to the know .desktop files (so very likely to the
   parent application desktop).

Changes include:
 - BamfLegacyWindow is now able to fetch both the window class name and instance name also
   for non-gtk3 builds (instead of using libwnck, we fallback to Xlib)
 - BamfXutils now avoid to open a display connection per request.
 - BamfMatcher includes now the utility function
   bamf_matcher_get_application_by_desktop_file that allows to remove a lot of code
   duplication. It correctly matches the chromium web applications and avoids the re-usage
   of non-optimum running .desktop files.

To post a comment you must log in.
467. By Marco Trevisan (Treviño)

BamfMatcher: free the temporary list we use to order the windows by stack

468. By Marco Trevisan (Treviño)

BamfMatcher: be more explicit about the desktop file to use for a new application.

469. By Marco Trevisan (Treviño)

BamfMatcher: oops... Forgot an else when checking the type of new application we should create

Revision history for this message
Jason Smith (jassmith) wrote :

+1 looks okay

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/bamf-legacy-window.c'
2--- src/bamf-legacy-window.c 2012-02-10 20:05:54 +0000
3+++ src/bamf-legacy-window.c 2012-04-20 17:39:18 +0000
4@@ -1,5 +1,5 @@
5 /*
6- * Copyright (C) 2010 Canonical Ltd
7+ * Copyright (C) 2010-2012 Canonical Ltd
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 3 as
11@@ -14,6 +14,7 @@
12 * along with this program. If not, see <http://www.gnu.org/licenses/>.
13 *
14 * Authored by: Jason Smith <jason.smith@canonical.com>
15+ * Marco Trevisan (Treviño) <3v1n0@ubuntu.com>
16 *
17 */
18
19@@ -47,6 +48,10 @@
20 {
21 WnckWindow * legacy_window;
22 char * mini_icon_path;
23+#ifndef USE_GTK3
24+ char * group_name;
25+ char * instance_name;
26+#endif
27 gulong closed_id;
28 gulong name_changed_id;
29 gulong state_changed_id;
30@@ -105,7 +110,6 @@
31 return wnck_window_is_skip_tasklist (self->priv->legacy_window);
32 }
33
34-#ifdef USE_GTK3
35 const char *
36 bamf_legacy_window_get_class_instance_name (BamfLegacyWindow *self)
37 {
38@@ -115,17 +119,29 @@
39
40 window = self->priv->legacy_window;
41
42+ if (BAMF_LEGACY_WINDOW_GET_CLASS (self)->get_class_instance_name)
43+ return BAMF_LEGACY_WINDOW_GET_CLASS (self)->get_class_instance_name (self);
44+
45 if (!window)
46 return NULL;
47
48+#ifdef USE_GTK3
49 return wnck_window_get_class_instance_name (window);
50+
51+#else
52+ if (!self->priv->instance_name)
53+ {
54+ Window xid = wnck_window_get_xid (window);
55+ bamf_xutils_get_window_class_hints (xid, &self->priv->instance_name, NULL);
56+ }
57+
58+ return self->priv->instance_name;
59+#endif
60 }
61-#endif
62
63 const char *
64 bamf_legacy_window_get_class_name (BamfLegacyWindow *self)
65 {
66- WnckClassGroup *group;
67 WnckWindow *window;
68
69 g_return_val_if_fail (BAMF_IS_LEGACY_WINDOW (self), NULL);
70@@ -138,12 +154,18 @@
71 if (!window)
72 return NULL;
73
74- group = wnck_window_get_class_group (window);
75-
76- if (!group)
77- return NULL;
78-
79- return wnck_class_group_get_res_class (group);
80+#ifdef USE_GTK3
81+ return wnck_window_get_class_group_name (window);
82+
83+#else
84+ if (!self->priv->group_name)
85+ {
86+ Window xid = wnck_window_get_xid (window);
87+ bamf_xutils_get_window_class_hints (xid, NULL, &self->priv->group_name);
88+ }
89+
90+ return self->priv->group_name;
91+#endif
92 }
93
94 const char *
95@@ -186,7 +208,7 @@
96 {
97 g_string_append (exec, argv[i]);
98 if (argv[i + 1] != NULL)
99- g_string_append (exec, " ");
100+ g_string_append (exec, " ");
101 g_free (argv[i]);
102 i++;
103 }
104@@ -253,7 +275,6 @@
105 {
106 g_return_val_if_fail (BAMF_IS_LEGACY_WINDOW (self), 0);
107
108-
109 if (BAMF_LEGACY_WINDOW_GET_CLASS (self)->get_xid)
110 return BAMF_LEGACY_WINDOW_GET_CLASS (self)->get_xid (self);
111
112@@ -347,10 +368,10 @@
113 bamf_legacy_window_get_geometry (BamfLegacyWindow *self, gint *x, gint *y,
114 gint *width, gint *height)
115 {
116- *x = 0;
117- *y = 0;
118- *width = 0;
119- *height = 0;
120+ if (x) *x = 0;
121+ if (y) *y = 0;
122+ if (width) *width = 0;
123+ if (height) *height = 0;
124
125 g_return_if_fail (BAMF_IS_LEGACY_WINDOW (self));
126
127@@ -478,6 +499,20 @@
128 self->priv->mini_icon_path = NULL;
129 }
130
131+#ifndef USE_GTK3
132+ if (self->priv->group_name)
133+ {
134+ g_free (self->priv->group_name);
135+ self->priv->group_name = NULL;
136+ }
137+
138+ if (self->priv->instance_name)
139+ {
140+ g_free (self->priv->instance_name);
141+ self->priv->instance_name = NULL;
142+ }
143+#endif
144+
145 if (self->priv->legacy_window)
146 {
147 g_signal_handler_disconnect (self->priv->legacy_window,
148
149=== modified file 'src/bamf-legacy-window.h'
150--- src/bamf-legacy-window.h 2012-02-10 17:33:51 +0000
151+++ src/bamf-legacy-window.h 2012-04-20 17:39:18 +0000
152@@ -70,19 +70,20 @@
153 {
154 GObjectClass parent;
155
156- const char * (*get_name) (BamfLegacyWindow *legacy_window);
157- const char * (*get_class_name) (BamfLegacyWindow *legacy_window);
158- char * (*get_exec_string) (BamfLegacyWindow *legacy_window);
159- char * (*get_app_id) (BamfLegacyWindow *legacy_window);
160- char * (*get_unique_bus_name) (BamfLegacyWindow *legacy_window);
161- char * (*get_menu_object_path) (BamfLegacyWindow *legacy_window);
162- gint (*get_pid) (BamfLegacyWindow *legacy_window);
163- guint32 (*get_xid) (BamfLegacyWindow *legacy_window);
164- gboolean (*needs_attention) (BamfLegacyWindow *legacy_window);
165- gboolean (*is_active) (BamfLegacyWindow *legacy_window);
166- gboolean (*is_skip_tasklist) (BamfLegacyWindow *legacy_window);
167- gboolean (*is_desktop) (BamfLegacyWindow *legacy_window);
168- gboolean (*is_dialog) (BamfLegacyWindow *legacy_window);
169+ const char * (*get_name) (BamfLegacyWindow *legacy_window);
170+ const char * (*get_class_name) (BamfLegacyWindow *legacy_window);
171+ const char * (*get_class_instance_name) (BamfLegacyWindow *legacy_window);
172+ char * (*get_exec_string) (BamfLegacyWindow *legacy_window);
173+ char * (*get_app_id) (BamfLegacyWindow *legacy_window);
174+ char * (*get_unique_bus_name) (BamfLegacyWindow *legacy_window);
175+ char * (*get_menu_object_path) (BamfLegacyWindow *legacy_window);
176+ gint (*get_pid) (BamfLegacyWindow *legacy_window);
177+ guint32 (*get_xid) (BamfLegacyWindow *legacy_window);
178+ gboolean (*needs_attention) (BamfLegacyWindow *legacy_window);
179+ gboolean (*is_active) (BamfLegacyWindow *legacy_window);
180+ gboolean (*is_skip_tasklist) (BamfLegacyWindow *legacy_window);
181+ gboolean (*is_desktop) (BamfLegacyWindow *legacy_window);
182+ gboolean (*is_dialog) (BamfLegacyWindow *legacy_window);
183 BamfWindowMaximizationType (*maximized) (BamfLegacyWindow *legacy_window);
184
185 void (*get_geometry) (BamfLegacyWindow *self,
186@@ -126,9 +127,7 @@
187
188 BamfWindowMaximizationType bamf_legacy_window_maximized (BamfLegacyWindow *self);
189
190-#ifdef USE_GTK3
191 const char * bamf_legacy_window_get_class_instance_name (BamfLegacyWindow *self);
192-#endif
193
194 const char * bamf_legacy_window_get_class_name (BamfLegacyWindow *self);
195
196
197=== modified file 'src/bamf-matcher.c'
198--- src/bamf-matcher.c 2012-03-19 17:11:35 +0000
199+++ src/bamf-matcher.c 2012-04-20 17:39:18 +0000
200@@ -1,5 +1,5 @@
201 /*
202- * Copyright (C) 2010-2011 Canonical Ltd
203+ * Copyright (C) 2010-2012 Canonical Ltd
204 *
205 * This program is free software: you can redistribute it and/or modify
206 * it under the terms of the GNU General Public License version 3 as
207@@ -28,6 +28,7 @@
208 #include "bamf-legacy-screen.h"
209 #include "bamf-indicator-source.h"
210 #include "bamf-xutils.h"
211+#include <strings.h>
212
213 G_DEFINE_TYPE (BamfMatcher, bamf_matcher, BAMF_DBUS_TYPE_MATCHER_SKELETON);
214 #define BAMF_MATCHER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE(obj, \
215@@ -126,6 +127,37 @@
216
217 static void bamf_matcher_unregister_view (BamfMatcher *self, BamfView *view);
218
219+static BamfApplication *
220+bamf_matcher_get_application_by_desktop_file (BamfMatcher *self, const char *desktop_file)
221+{
222+ GList *l;
223+ BamfView *view;
224+
225+ g_return_val_if_fail (BAMF_IS_MATCHER (self), NULL);
226+
227+ if (!desktop_file)
228+ return NULL;
229+
230+ for (l = self->priv->views; l; l = l->next)
231+ {
232+ view = l->data;
233+
234+ if (!BAMF_IS_APPLICATION (view))
235+ continue;
236+
237+ BamfApplication *app = BAMF_APPLICATION (view);
238+ const gchar *app_desktop;
239+ app_desktop = bamf_application_get_desktop_file (app);
240+
241+ if (g_strcmp0 (desktop_file, app_desktop) == 0)
242+ {
243+ return app;
244+ }
245+ }
246+
247+ return NULL;
248+}
249+
250 static gboolean
251 emit_paths_changed (gpointer user_data)
252 {
253@@ -177,11 +209,7 @@
254
255 static void bamf_matcher_prepare_path_change (BamfMatcher *self, const gchar *desktop_file, ViewChangeType change_type)
256 {
257- GList *l;
258- BamfView *view;
259 BamfMatcherPrivate *priv;
260- const char *app_desktop;
261- gboolean found = FALSE;
262
263 if (desktop_file == NULL) return;
264
265@@ -189,26 +217,12 @@
266
267 priv = self->priv;
268
269- for (l = priv->views; l; l = l->next)
270- {
271- view = l->data;
272-
273- if (!BAMF_IS_APPLICATION (view) || !bamf_view_is_running (view))
274- continue;
275-
276- app_desktop = bamf_application_get_desktop_file (BAMF_APPLICATION (view));
277- if (!app_desktop) continue;
278-
279- if (g_strcmp0 (desktop_file, app_desktop) == 0)
280- {
281- found = TRUE;
282- break;
283- }
284- }
285-
286 /* the app was already running (ADDED) / had more instances which are still
287 * there (REMOVED) */
288- if (found) return;
289+ if (bamf_matcher_get_application_by_desktop_file (self, desktop_file))
290+ {
291+ return;
292+ }
293
294 if (!priv->opened_closed_paths_table)
295 {
296@@ -258,7 +272,7 @@
297 self->priv->views = g_list_prepend (self->priv->views, view);
298
299 g_signal_emit_by_name (self, "view-opened", path, type);
300-
301+
302 // trigger manually since this is already active
303 if (bamf_view_is_active (view))
304 on_view_active_changed (view, TRUE, self);
305@@ -1514,59 +1528,134 @@
306 is_web_app_window (BamfMatcher *self, BamfLegacyWindow *window)
307 {
308 const char *window_class = bamf_legacy_window_get_class_name (window);
309-#ifdef USE_GTK3
310 const char *instance_name = bamf_legacy_window_get_class_instance_name(window);
311- // chromium uses url wm_class strings to represent its web apps. These apps
312- // will still have the same parent pid and hints as the main chrome window, so we
313- // skip the hint check. We can tell a window is a chrome web app window if its instance
314- // name is google-chrome but its window class is not Google-chrome
315- return !g_strcmp0(instance_name, "google-chrome") && g_strcmp0(window_class, "Google-chrome");
316-#else
317- return g_str_has_prefix(window_class, "crx_");
318-#endif
319+
320+ // Chrome/Chromium uses url wm_class strings to represent its web apps.
321+ // These apps will still have the same parent pid and hints as the main chrome
322+ // window, so we skip the hint check.
323+ // We can tell a window is a chrome web app window if its instance name is
324+ // not google-chrome but its window class is Google-chrome
325+ // We can tell a window is chromium web app window if its instance name is
326+ // not chromium-browser but its window class is Chromium Browser
327+
328+ gboolean valid_app = FALSE;
329+
330+ if (g_strcmp0 (window_class, "Google-chrome") == 0 &&
331+ g_strcmp0 (instance_name, "google-chrome") != 0)
332+ {
333+ valid_app = TRUE;
334+ }
335+ else if (g_strcmp0 (window_class, "Chromium-browser") == 0 &&
336+ g_strcmp0 (instance_name, "chromium-browser") != 0)
337+ {
338+ valid_app = TRUE;
339+ }
340+
341+ return valid_app;
342+}
343+
344+static gboolean
345+bamf_matcher_window_skips_hint_set (BamfMatcher *self, BamfLegacyWindow *window)
346+{
347+ gboolean skip_hint_set = FALSE;
348+ g_return_val_if_fail (BAMF_IS_MATCHER (self), TRUE);
349+
350+ skip_hint_set = is_web_app_window (self, window);
351+
352+ return skip_hint_set;
353+}
354+
355+static GList *
356+bamf_matcher_get_class_matching_desktop_files (BamfMatcher *self, const gchar *class_name)
357+{
358+ GList* desktop_files = NULL;
359+ gpointer key;
360+ gpointer value;
361+ GHashTableIter iter;
362+
363+ g_return_val_if_fail (BAMF_IS_MATCHER (self), NULL);
364+ g_hash_table_iter_init (&iter, self->priv->desktop_class_table);
365+
366+ while (g_hash_table_iter_next (&iter, &key, &value))
367+ {
368+ gchar* desktop_file = g_strdup (key);
369+ gchar* desktop_class = value;
370+
371+ if (g_strcmp0 (desktop_class, class_name) == 0)
372+ {
373+ desktop_files = g_list_prepend (desktop_files, desktop_file);
374+ }
375+ }
376+
377+ return desktop_files;
378+}
379+
380+static gboolean
381+bamf_matcher_has_instance_class_desktop_file (BamfMatcher *self, const gchar *class_name)
382+{
383+ gpointer key;
384+ gpointer value;
385+ GHashTableIter iter;
386+
387+ g_return_val_if_fail (BAMF_IS_MATCHER (self), FALSE);
388+ g_hash_table_iter_init (&iter, self->priv->desktop_class_table);
389+
390+ while (g_hash_table_iter_next (&iter, &key, &value))
391+ {
392+ gchar* desktop_class = value;
393+
394+ if (g_strcmp0 (desktop_class, class_name) == 0)
395+ {
396+ return TRUE;
397+ }
398+ }
399+
400+ return FALSE;
401 }
402
403 static GList *
404 bamf_matcher_possible_applications_for_window (BamfMatcher *self,
405 BamfWindow *bamf_window)
406 {
407- char *hint = NULL;
408 BamfMatcherPrivate *priv;
409- gint pid;
410 BamfLegacyWindow *window;
411- GList *desktop_files = NULL, *pid_list = NULL, *l;
412+ GList *desktop_files = NULL, *l;
413+ char *desktop_file = NULL;
414+ char *desktop_class = NULL;
415
416 g_return_val_if_fail (BAMF_IS_WINDOW (bamf_window), NULL);
417 g_return_val_if_fail (BAMF_IS_MATCHER (self), NULL);
418
419 priv = self->priv;
420-
421 window = bamf_window_get_window (bamf_window);
422
423- hint = get_window_hint (window, _NET_WM_DESKTOP_FILE);
424- const char *window_class = bamf_legacy_window_get_class_name (window);
425-
426- if (hint)
427+ desktop_file = get_window_hint (window, _NET_WM_DESKTOP_FILE);
428+
429+ const char *class_name = bamf_legacy_window_get_class_name (window);
430+ const char *instance_name = bamf_legacy_window_get_class_instance_name (window);
431+ gboolean known_desktop_class = bamf_matcher_has_instance_class_desktop_file (self, instance_name);
432+
433+ if (desktop_file)
434 {
435- /* whew, hard work, didn't even have to make a copy! */
436- if (hint[0] != '\0' && !is_web_app_window(self, window))
437+ desktop_class = g_hash_table_lookup (priv->desktop_class_table, desktop_file);
438+
439+ if (!known_desktop_class || g_strcmp0 (desktop_class, desktop_file) == 0)
440 {
441- desktop_files = g_list_prepend (desktop_files, hint);
442+ desktop_files = g_list_prepend (desktop_files, desktop_file);
443 }
444 else
445 {
446- g_free (hint);
447+ g_free (desktop_file);
448 }
449 }
450- else
451+
452+ desktop_file = NULL;
453+
454+ if (!desktop_files)
455 {
456-
457- char *desktop_file;
458- char *desktop_class;
459-
460- if (window_class)
461+ if (class_name)
462 {
463- char *window_class_down = g_ascii_strdown (window_class, -1);
464+ char *window_class_down = g_ascii_strdown (class_name, -1);
465 l = g_hash_table_lookup (priv->desktop_id_table, window_class_down);
466 g_free (window_class_down);
467
468@@ -1577,11 +1666,14 @@
469 if (desktop_file)
470 {
471 desktop_class = g_hash_table_lookup (priv->desktop_class_table, desktop_file);
472- if ((desktop_class == NULL || g_strcmp0 (desktop_class, window_class) == 0) &&
473- !g_list_find_custom (desktop_files, desktop_file,
474- (GCompareFunc) g_strcmp0))
475+
476+ if (!known_desktop_class || g_strcmp0 (desktop_class, instance_name) == 0)
477 {
478- desktop_files = g_list_prepend (desktop_files, g_strdup (desktop_file));
479+ if (!g_list_find_custom (desktop_files, desktop_file,
480+ (GCompareFunc) g_strcmp0))
481+ {
482+ desktop_files = g_list_prepend (desktop_files, g_strdup (desktop_file));
483+ }
484 }
485 }
486 }
487@@ -1589,31 +1681,10 @@
488 desktop_files = g_list_reverse (desktop_files);
489 }
490
491- /* Iterate over the desktop class table, and add matching desktop files */
492- gpointer key;
493- gpointer value;
494- GHashTableIter iter;
495- g_hash_table_iter_init (&iter, priv->desktop_class_table);
496-
497- while (g_hash_table_iter_next (&iter, &key, &value))
498- {
499- desktop_file = g_strdup (key);
500- desktop_class = value;
501- if (g_strcmp0 (desktop_class, window_class) == 0 &&
502- !g_list_find_custom (desktop_files, desktop_file, (GCompareFunc) g_strcmp0))
503- {
504- desktop_files = g_list_prepend (desktop_files, desktop_file);
505- }
506- else
507- {
508- g_free (desktop_file);
509- }
510- }
511-
512- pid = bamf_legacy_window_get_pid (window);
513- pid_list = bamf_matcher_possible_applications_for_pid (self, pid);
514+ gint pid = bamf_legacy_window_get_pid (window);
515+ GList *pid_list = bamf_matcher_possible_applications_for_pid (self, pid);
516
517- /* Append these files to the end to give preference to window_class style picking.
518+ /* Append these files to the end to give preference to class_name style picking.
519 This style of matching is prefered and used by GNOME Shell however does not work
520 very well in practice, thus requiring the fallback here */
521 for (l = pid_list; l; l = l->next)
522@@ -1627,10 +1698,10 @@
523 {
524 gboolean append = FALSE;
525
526- if (window_class)
527+ if (instance_name)
528 {
529 desktop_class = g_hash_table_lookup (priv->desktop_class_table, desktop_file);
530- if (desktop_class == NULL || g_strcmp0 (desktop_class, window_class) == 0)
531+ if (!known_desktop_class || g_strcmp0 (desktop_class, instance_name) == 0)
532 {
533 append = TRUE;
534 }
535@@ -1671,7 +1742,12 @@
536
537 g_list_free (pid_list);
538 }
539-
540+
541+ if (!desktop_files && known_desktop_class)
542+ {
543+ desktop_files = bamf_matcher_get_class_matching_desktop_files (self, instance_name);
544+ }
545+
546 return desktop_files;
547 }
548
549@@ -1681,75 +1757,141 @@
550 {
551 GList *possible_apps, *l;
552 BamfLegacyWindow *window;
553- GList *views, *a;
554- const char *win_class;
555- const char *desktop_file;
556- const char *app_class;
557+ const gchar *app_class;
558+ const gchar *app_desktop = NULL;
559 BamfApplication *app = NULL, *best = NULL;
560- BamfView *view;
561
562 g_return_if_fail (BAMF_IS_MATCHER (self));
563 g_return_if_fail (BAMF_IS_WINDOW (bamf_window));
564
565 window = bamf_window_get_window (bamf_window);
566- views = self->priv->views;
567
568 possible_apps = bamf_matcher_possible_applications_for_window (self, bamf_window);
569- win_class = bamf_legacy_window_get_class_name (window);
570-
571- /* Loop over every application, inside that application see if its .desktop file
572- * matches with any of our possible hits. If so we match it. If we have no possible hits
573- * fall back to secondary matching.
574- */
575- for (a = views; a; a = a->next)
576- {
577- view = a->data;
578-
579- if (!BAMF_IS_APPLICATION (view))
580- continue;
581-
582- app = BAMF_APPLICATION (view);
583- app_class = bamf_application_get_wmclass (app);
584- desktop_file = bamf_application_get_desktop_file (app);
585-
586- if (possible_apps)
587- {
588- /* primary matching */
589-
590- for (l = possible_apps; l; l = l->next)
591- {
592- if (g_strcmp0 (desktop_file, l->data) == 0)
593- {
594- if (!best || !g_strcmp0 (win_class, app_class))
595- best = app;
596- break;
597- }
598- }
599- }
600- else if (desktop_file == NULL)
601- {
602- /* secondary matching */
603-
604- if (bamf_application_contains_similar_to_window (app, bamf_window) && (!best || !g_strcmp0 (win_class, app_class)))
605- best = app;
606+ const char *win_instance = bamf_legacy_window_get_class_instance_name (window);
607+ const char *win_class_name = bamf_legacy_window_get_class_name (window);
608+
609+ app_class = win_instance;
610+
611+ /* Loop over every possible desktop file that could match the window, and try
612+ * to reuse an already-opened window that uses it.
613+ * Desktop files are ordered by priority, so we try to use the first possible,
614+ * wm_class matching applications have the priority, btw. */
615+ if (possible_apps)
616+ {
617+ /* primary matching */
618+ for (l = possible_apps; l; l = l->next)
619+ {
620+ const gchar *desktop_file = l->data;
621+ app = bamf_matcher_get_application_by_desktop_file (self, desktop_file);
622+
623+ if (BAMF_IS_APPLICATION (app))
624+ {
625+ const gchar *app_desktop_class;
626+ app_desktop_class = bamf_application_get_wmclass (app);
627+
628+ if (win_instance && app_desktop_class && strcasecmp (win_instance, app_desktop_class) == 0)
629+ {
630+ best = app;
631+ break;
632+ }
633+ else if (!best)
634+ {
635+ best = app;
636+ }
637+ }
638+ }
639+
640+ /* If a "best" application has been found, we should check again if the
641+ * desktop file that is going to be used is really the best one we have.
642+ * To do this, we compare the window class name with the desktop class
643+ * of both candidates to ensure that really is the best one.
644+ * This is important to avoid that very-similar (which differ only by
645+ * StartupWMClass) running desktop files, would be wrongly used to match
646+ * an incompatible window. */
647+ if (BAMF_IS_APPLICATION (best) && possible_apps)
648+ {
649+ const gchar *best_app_desktop = bamf_application_get_desktop_file (best);
650+ const gchar *best_desktop = possible_apps->data;
651+
652+ if (win_class_name && g_strcmp0 (best_app_desktop, best_desktop) != 0)
653+ {
654+ const gchar *best_app_class;
655+ const gchar *best_desktop_class;
656+
657+ best_app_class = bamf_application_get_wmclass (best);
658+ best_desktop_class = g_hash_table_lookup (self->priv->desktop_class_table, best_desktop);
659+
660+ /* We compare the two classes using their "distance" from the
661+ * desidered class value */
662+ if (best_app_class && best_desktop_class)
663+ {
664+ int max_chars = strlen (win_class_name);
665+ int app_diff = strncasecmp (win_class_name, best_app_class, max_chars);
666+ int desktop_diff = strncasecmp (win_class_name, best_desktop_class, max_chars);
667+
668+ if (abs (desktop_diff) < abs (app_diff))
669+ {
670+ best = bamf_matcher_get_application_by_desktop_file (self, best_desktop);
671+ app_desktop = best_desktop;
672+ }
673+ }
674+ }
675+ }
676+ }
677+ else
678+ {
679+ /* secondary matching */
680+ GList *a;
681+ BamfView *view;
682+ const gchar *app_desktop_class;
683+
684+ for (a = self->priv->views; a; a = a->next)
685+ {
686+ view = a->data;
687+
688+ if (!BAMF_IS_APPLICATION (view))
689+ continue;
690+
691+ app = BAMF_APPLICATION (view);
692+ app_desktop_class = bamf_application_get_wmclass (app);
693+
694+ if (bamf_application_contains_similar_to_window (app, bamf_window))
695+ {
696+ if (win_instance && g_strcmp0 (win_instance, app_desktop_class) == 0)
697+ {
698+ best = app;
699+ break;
700+ }
701+ else if (!best)
702+ {
703+ best = app;
704+ }
705+ }
706 }
707 }
708
709 if (!best)
710 {
711- if (possible_apps)
712- best = bamf_application_new_from_desktop_files (possible_apps);
713+ if (app_desktop)
714+ {
715+ best = bamf_application_new_from_desktop_file (app_desktop);
716+ }
717+ else if (possible_apps)
718+ {
719+ best = bamf_application_new_from_desktop_files (possible_apps);
720+ }
721 else
722- best = bamf_application_new ();
723-
724- bamf_application_set_wmclass (best, win_class);
725-
726+ {
727+ best = bamf_application_new ();
728+ }
729+
730+ bamf_application_set_wmclass (best, app_class);
731 bamf_matcher_register_view_stealing_ref (self, BAMF_VIEW (best));
732 }
733
734- g_list_free_full (possible_apps, g_free);
735-
736 bamf_view_add_child (BAMF_VIEW (best), BAMF_VIEW (bamf_window));
737+
738+ g_list_free_full (possible_apps, g_free);
739 }
740
741 /* Ensures that the window hint is set if a registered pid matches, and that set window hints
742@@ -1760,7 +1902,7 @@
743 {
744 GArray *pids;
745 GHashTable *registered_pids;
746- char *window_hint = NULL;
747+ char *desktop_file_hint = NULL;
748 gint i, pid;
749 gpointer key;
750
751@@ -1769,51 +1911,35 @@
752
753 registered_pids = self->priv->registered_pids;
754
755- // web apps can end up forcing their parent browsers to match on them, so remove from pid table if this is the case
756- if (is_web_app_window (self, window))
757- {
758+ /* Some windows such as web applications shares the pid with their parent
759+ * browser so, we have to ignore them */
760+ if (bamf_matcher_window_skips_hint_set (self, window))
761+ {
762+ return;
763+ }
764+
765+ desktop_file_hint = get_window_hint (window, _NET_WM_DESKTOP_FILE);
766+
767+ if (desktop_file_hint)
768+ {
769+ /* already set, make sure we know about this
770+ * fact for future windows of this applications */
771 pid = bamf_legacy_window_get_pid (window);
772
773 if (pid > 0)
774 {
775- gpointer key = GINT_TO_POINTER (pid);
776- const char* result = g_hash_table_lookup (registered_pids, key);
777- if (result && g_str_has_prefix (result, "/home/"))
778+ key = GINT_TO_POINTER (pid);
779+
780+ if (!g_hash_table_lookup (registered_pids, key))
781 {
782- g_hash_table_remove (registered_pids, key);
783+ g_hash_table_insert (registered_pids, key, g_strdup (desktop_file_hint));
784 }
785 }
786
787+ g_free (desktop_file_hint);
788 return;
789 }
790
791- window_hint = get_window_hint (window, _NET_WM_DESKTOP_FILE);
792- if (window_hint)
793- {
794- if (window_hint[0] != '\0')
795- {
796- /* already set, make sure we know about this
797- * fact for future windows of this applications */
798- pid = bamf_legacy_window_get_pid (window);
799-
800- if (pid > 0)
801- {
802- key = GINT_TO_POINTER (pid);
803-
804- if (!g_hash_table_lookup (registered_pids, key))
805- {
806- g_hash_table_insert (registered_pids, key, g_strdup (window_hint));
807- }
808- }
809-
810- g_free (window_hint);
811- return;
812- }
813-
814- g_free (window_hint);
815- window_hint = NULL;
816- }
817-
818 pids = pid_parent_tree (self, bamf_legacy_window_get_pid (window));
819
820 for (i = 0; i < pids->len; i++)
821@@ -1821,15 +1947,15 @@
822 pid = g_array_index (pids, gint, i);
823 key = GINT_TO_POINTER (pid);
824
825- window_hint = g_hash_table_lookup (registered_pids, key);
826- if (window_hint != NULL && window_hint[0] != '\0')
827+ desktop_file_hint = g_hash_table_lookup (registered_pids, key);
828+ if (desktop_file_hint != NULL && desktop_file_hint[0] != '\0')
829 break;
830 }
831
832 g_array_free (pids, TRUE);
833
834- if (window_hint)
835- set_window_hint (window, _NET_WM_DESKTOP_FILE, window_hint);
836+ if (desktop_file_hint)
837+ set_window_hint (window, _NET_WM_DESKTOP_FILE, desktop_file_hint);
838 }
839
840 static void
841@@ -1895,13 +2021,6 @@
842 g_return_val_if_fail (BAMF_IS_LEGACY_WINDOW (window), NULL);
843
844 role = get_window_hint (window, WM_WINDOW_ROLE);
845-
846- if (role && role[0] == '\0')
847- {
848- g_free (role);
849- role = NULL;
850- }
851-
852 exec = g_strconcat ("gnome-control-center", (role ? " " : NULL), role, NULL);
853
854 desktopFileTable = self->priv->desktop_file_table;
855@@ -2013,15 +2132,11 @@
856 bamf_matcher_setup_indicator_state (BamfMatcher *self, BamfIndicator *indicator)
857 {
858 GList *possible_apps, *l;
859- GList *views, *a;
860 const char *desktop_file;
861 BamfApplication *app = NULL, *best = NULL;
862- BamfView *view;
863
864 g_return_if_fail (BAMF_IS_MATCHER (self));
865 g_return_if_fail (BAMF_IS_INDICATOR (indicator));
866-
867- views = self->priv->views;
868
869 possible_apps = bamf_matcher_possible_applications_for_pid (self, bamf_indicator_get_pid (indicator));
870
871@@ -2029,27 +2144,17 @@
872 * matches with any of our possible hits. If so we match it. If we have no possible hits
873 * fall back to secondary matching.
874 */
875- for (a = views; a && !best; a = a->next)
876+ if (possible_apps)
877 {
878- view = a->data;
879-
880- if (!BAMF_IS_APPLICATION (view))
881- continue;
882-
883- app = BAMF_APPLICATION (view);
884- desktop_file = bamf_application_get_desktop_file (app);
885-
886- if (possible_apps)
887+ for (l = possible_apps; l; l = l->next)
888 {
889- /* primary matching */
890+ desktop_file = l->data;
891+ app = bamf_matcher_get_application_by_desktop_file (self, desktop_file);
892
893- for (l = possible_apps; l; l = l->next)
894+ if (BAMF_IS_APPLICATION (app))
895 {
896- if (g_strcmp0 (desktop_file, l->data) == 0)
897- {
898- best = app;
899- break;
900- }
901+ best = app;
902+ break;
903 }
904 }
905 }
906@@ -2270,6 +2375,7 @@
907 }
908 }
909
910+ g_list_free (windows);
911 g_variant_builder_close (&b);
912
913 return g_variant_builder_end (&b);
914@@ -2279,27 +2385,15 @@
915 bamf_matcher_application_is_running (BamfMatcher *matcher,
916 const char *application)
917 {
918- const char * desktop_file;
919- GList *l;
920- BamfView *view;
921- BamfMatcherPrivate *priv;
922+ BamfApplication *app;
923
924 g_return_val_if_fail (BAMF_IS_MATCHER (matcher), FALSE);
925
926- priv = matcher->priv;
927+ app = bamf_matcher_get_application_by_desktop_file (matcher, application);
928
929- for (l = priv->views; l; l = l->next)
930+ if (BAMF_IS_APPLICATION (app))
931 {
932- view = l->data;
933-
934- if (!BAMF_IS_APPLICATION (view))
935- continue;
936-
937- desktop_file = bamf_application_get_desktop_file (BAMF_APPLICATION (view));
938- if (g_strcmp0 (desktop_file, application) == 0)
939- {
940- return bamf_view_is_running (view);
941- }
942+ return bamf_view_is_running (BAMF_VIEW (app));
943 }
944
945 return FALSE;
946@@ -2370,27 +2464,15 @@
947 const char *application)
948 {
949 const char * path = "";
950- const char * desktop_file;
951- GList *l;
952- BamfView *view;
953- BamfMatcherPrivate *priv;
954+ BamfApplication *app;
955
956 g_return_val_if_fail (BAMF_IS_MATCHER (matcher), NULL);
957
958- priv = matcher->priv;
959+ app = bamf_matcher_get_application_by_desktop_file (matcher, application);
960
961- for (l = priv->views; l; l = l->next)
962+ if (BAMF_IS_APPLICATION (app))
963 {
964- view = l->data;
965-
966- if (!BAMF_IS_APPLICATION (view))
967- continue;
968-
969- desktop_file = bamf_application_get_desktop_file (BAMF_APPLICATION (view));
970- if (g_strcmp0 (desktop_file, application) == 0)
971- {
972- path = bamf_view_get_path (view);
973- }
974+ return bamf_view_get_path (BAMF_VIEW (app));
975 }
976
977 return path;
978@@ -2530,29 +2612,16 @@
979 {
980 GVariantBuilder b;
981 GVariant *xids;
982- const char * desktop_file;
983- GList *l;
984- BamfView *view;
985- BamfMatcherPrivate *priv;
986+ BamfApplication *app;
987
988 g_return_val_if_fail (BAMF_IS_MATCHER (matcher), NULL);
989
990- priv = matcher->priv;
991 xids = NULL;
992+ app = bamf_matcher_get_application_by_desktop_file (matcher, application);
993
994- for (l = priv->views; l; l = l->next)
995+ if (BAMF_IS_APPLICATION (app))
996 {
997- view = l->data;
998-
999- if (!BAMF_IS_APPLICATION (view))
1000- continue;
1001-
1002- desktop_file = bamf_application_get_desktop_file (BAMF_APPLICATION (view));
1003- if (g_strcmp0 (desktop_file, application) == 0)
1004- {
1005- xids = bamf_application_get_xids (BAMF_APPLICATION (view));
1006- break;
1007- }
1008+ xids = bamf_application_get_xids (app);
1009 }
1010
1011 if (!xids)
1012
1013=== modified file 'src/bamf-view.c'
1014--- src/bamf-view.c 2012-03-16 19:39:34 +0000
1015+++ src/bamf-view.c 2012-04-20 17:39:18 +0000
1016@@ -465,7 +465,9 @@
1017
1018 ifaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (view));
1019
1020- for (l = ifaces; l; l = l->next)
1021+ /* The dbus object interface list is in reversed order, we try to export
1022+ * the interfaces in bottom to top order (BamfView should be the first) */
1023+ for (l = g_list_last(ifaces); l; l = l->prev)
1024 {
1025 g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (l->data),
1026 connection, path, &error);
1027
1028=== modified file 'src/bamf-xutils.c'
1029--- src/bamf-xutils.c 2012-02-10 20:05:54 +0000
1030+++ src/bamf-xutils.c 2012-04-20 17:39:18 +0000
1031@@ -21,6 +21,29 @@
1032 #include "bamf-xutils.h"
1033 #include <string.h>
1034
1035+static Display *
1036+get_xdisplay (gboolean *opened)
1037+{
1038+ Display *xdisplay;
1039+ xdisplay = gdk_x11_get_default_xdisplay ();
1040+
1041+ if (opened)
1042+ *opened = FALSE;
1043+
1044+ if (!xdisplay)
1045+ {
1046+ xdisplay = XOpenDisplay (NULL);
1047+
1048+ if (xdisplay)
1049+ {
1050+ if (opened)
1051+ *opened = TRUE;
1052+ }
1053+ }
1054+
1055+ return xdisplay;
1056+}
1057+
1058 char *
1059 bamf_xutils_get_window_hint (Window xid, const char *atom_name, Atom type)
1060 {
1061@@ -32,22 +55,17 @@
1062 gulong numItems;
1063 gulong bytesAfter;
1064 unsigned char *buffer;
1065- gboolean close_display = TRUE;
1066+ gboolean close_display = FALSE;
1067
1068 g_return_val_if_fail ((xid != 0), NULL);
1069 g_return_val_if_fail (atom_name, NULL);
1070
1071- XDisplay = XOpenDisplay (NULL);
1072+ XDisplay = get_xdisplay (&close_display);
1073+
1074 if (!XDisplay)
1075 {
1076- XDisplay = gdk_x11_get_default_xdisplay ();
1077- if (!XDisplay)
1078- {
1079- g_warning ("%s: Unable to get a valid XDisplay", G_STRFUNC);
1080- return hint;
1081- }
1082-
1083- close_display = FALSE;
1084+ g_warning ("%s: Unable to get a valid XDisplay", G_STRFUNC);
1085+ return NULL;
1086 }
1087
1088 atom = XInternAtom (XDisplay, atom_name, FALSE);
1089@@ -70,7 +88,9 @@
1090
1091 if (result == Success && numItems > 0)
1092 {
1093- hint = g_strdup ((char*) buffer);
1094+ if (buffer && buffer[0] != '\0')
1095+ hint = g_strdup ((char*) buffer);
1096+
1097 XFree (buffer);
1098 }
1099
1100@@ -81,22 +101,18 @@
1101 bamf_xutils_set_window_hint (Window xid, const char *atom_name, Atom type, const char *data)
1102 {
1103 Display *XDisplay;
1104- gboolean close_display = TRUE;
1105+ gboolean close_display = FALSE;
1106
1107 g_return_if_fail (xid != 0);
1108 g_return_if_fail (atom_name);
1109 g_return_if_fail (data);
1110
1111- XDisplay = XOpenDisplay (NULL);
1112+ XDisplay = get_xdisplay (&close_display);
1113+
1114 if (!XDisplay)
1115 {
1116- XDisplay = gdk_x11_get_default_xdisplay ();
1117- if (!XDisplay)
1118- {
1119- g_warning ("%s: Unable to get a valid XDisplay", G_STRFUNC);
1120- return;
1121- }
1122- close_display = FALSE;
1123+ g_warning ("%s: Unable to get a valid XDisplay", G_STRFUNC);
1124+ return;
1125 }
1126
1127 XChangeProperty (XDisplay,
1128@@ -113,3 +129,35 @@
1129 if (close_display)
1130 XCloseDisplay (XDisplay);
1131 }
1132+
1133+void
1134+bamf_xutils_get_window_class_hints (Window xid, char **class_instance_name, char **class_name)
1135+{
1136+ Display *xdisplay;
1137+ gboolean close_display = FALSE;
1138+
1139+ xdisplay = get_xdisplay (&close_display);
1140+
1141+ if (!xdisplay)
1142+ {
1143+ g_warning ("%s: Unable to get a valid XDisplay", G_STRFUNC);
1144+ return;
1145+ }
1146+
1147+ XClassHint class_hint;
1148+ class_hint.res_name = NULL;
1149+ class_hint.res_class = NULL;
1150+
1151+ XGetClassHint(xdisplay, xid, &class_hint);
1152+
1153+ if (class_name && class_hint.res_class && class_hint.res_class[0] != 0)
1154+ *class_name = g_convert (class_hint.res_class, -1, "utf-8", "iso-8859-1",
1155+ NULL, NULL, NULL);
1156+
1157+ if (class_instance_name && class_hint.res_name && class_hint.res_name[0] != 0)
1158+ *class_instance_name = g_convert (class_hint.res_name, -1, "utf-8", "iso-8859-1",
1159+ NULL, NULL, NULL);
1160+
1161+ XFree (class_hint.res_class);
1162+ XFree (class_hint.res_name);
1163+}
1164
1165=== modified file 'src/bamf-xutils.h'
1166--- src/bamf-xutils.h 2012-02-10 20:05:54 +0000
1167+++ src/bamf-xutils.h 2012-04-20 17:39:18 +0000
1168@@ -18,8 +18,8 @@
1169 */
1170
1171
1172-#ifndef __BAMFXUTILS_H__
1173-#define __BAMFXUTILS_H__
1174+#ifndef __BAMF_XUTILS_H__
1175+#define __BAMF_XUTILS_H__
1176
1177 #include <glib.h>
1178 #include <X11/Xatom.h>
1179@@ -29,4 +29,6 @@
1180 void bamf_xutils_set_window_hint (Window xid, const char *atom_name, Atom type, const char *data);
1181 char* bamf_xutils_get_window_hint (Window xid, const char *atom_name, Atom type);
1182
1183+void bamf_xutils_get_window_class_hints (Window xid, char **class_instance_name, char **class_name);
1184+
1185 #endif

Subscribers

People subscribed via source and target branches