Merge lp:~3v1n0/bamf/wmclass-matching into lp:bamf/0.4

Proposed by Marco Trevisan (Treviño)
Status: Merged
Merged at revision: 387
Proposed branch: lp:~3v1n0/bamf/wmclass-matching
Merge into: lp:bamf/0.4
Diff against target: 488 lines (+198/-40)
3 files modified
src/bamf-application.c (+50/-3)
src/bamf-application.h (+6/-0)
src/bamf-matcher.c (+142/-37)
To merge this branch: bzr merge lp:~3v1n0/bamf/wmclass-matching
Reviewer Review Type Date Requested Status
Jason Smith (community) Approve
Review via email: mp+54941@code.launchpad.net

Description of the change

Now bamf uses also the window WMCLASS parameter and the .desktop file StartupWMClass to associate a window to its application.
This causes that each bamf application should now have its own class; a window is considered as part of that application only if its wmclass matches the application one.

A desktop file is now associated to an application also considering the StartupWMClass key value.

To post a comment you must log in.
Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

I am not sure I'm the right person to review this branch, but I got one nitpick nontheless :-)

The get_class() and set_class() methods on BamfApplication are not very binding friendly. If I called app.get_class() from Vala or PyGI it would normally give me the GObjectClass struct for the object, but this patch would override that method.

Maybe get/set_wmclass() instead?

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

Yes, you're right about that. I'll fix it.

lp:~3v1n0/bamf/wmclass-matching updated
392. By Marco Trevisan (Treviño)

application_class renamed as application_wmclass

To avoid gobjet get_class function overriding

393. By Marco Trevisan (Treviño)

Some code spaces cleanup.

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

Works for me, looks good

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/bamf-application.c'
2--- src/bamf-application.c 2011-01-14 15:53:35 +0000
3+++ src/bamf-application.c 2011-03-29 11:26:03 +0000
4@@ -48,6 +48,7 @@
5 GList * desktop_file_list;
6 char * app_type;
7 char * icon;
8+ char * wmclass;
9 gboolean is_tab_container;
10 gboolean show_stubs;
11 };
12@@ -83,6 +84,19 @@
13 return result;
14 }
15
16+char *
17+bamf_application_get_wmclass (BamfApplication *application)
18+{
19+ BamfApplicationPrivate *priv;
20+ char *result = NULL;
21+
22+ g_return_val_if_fail (BAMF_IS_APPLICATION (application), NULL);
23+ priv = application->priv;
24+
25+ result = g_strdup (priv->wmclass);
26+ return result;
27+}
28+
29 static gboolean
30 icon_name_is_valid (char *name)
31 {
32@@ -212,6 +226,18 @@
33 bamf_application_setup_icon_and_name (application);
34 }
35
36+void
37+bamf_application_set_wmclass (BamfApplication *application,
38+ char *wmclass)
39+{
40+ g_return_if_fail (BAMF_IS_APPLICATION (application));
41+
42+ if (wmclass && wmclass[0] != '\0')
43+ application->priv->wmclass = g_strdup (wmclass);
44+ else
45+ application->priv->wmclass = NULL;
46+}
47+
48 GArray *
49 bamf_application_get_xids (BamfApplication *application)
50 {
51@@ -312,12 +338,15 @@
52 GList *children, *l;
53 BamfView *child;
54
55- g_return_val_if_fail (BAMF_IS_APPLICATION (view), NULL);
56+ g_return_val_if_fail (BAMF_IS_APPLICATION (view), NULL);
57 self = BAMF_APPLICATION (view);
58-
59+
60+ if (self->priv->wmclass)
61+ return g_strdup_printf ("application%i", abs (g_str_hash (self->priv->wmclass)));
62+
63 if (self->priv->desktop_file)
64 return g_strdup_printf ("application%i", abs (g_str_hash (self->priv->desktop_file)));
65-
66+
67 children = bamf_view_get_children (BAMF_VIEW (self));
68 for (l = children; l; l = l->next)
69 {
70@@ -562,6 +591,12 @@
71 g_free (priv->app_type);
72 priv->app_type = NULL;
73 }
74+
75+ if (priv->wmclass)
76+ {
77+ g_free (priv->wmclass);
78+ priv->wmclass = NULL;
79+ }
80
81 g_signal_handlers_disconnect_by_func (G_OBJECT (bamf_matcher_get_default ()), matcher_favorites_changed, object);
82
83@@ -577,6 +612,7 @@
84 priv->is_tab_container = FALSE;
85 priv->app_type = g_strdup ("system");
86 priv->show_stubs = TRUE;
87+ priv->wmclass = NULL;
88
89 g_signal_connect (G_OBJECT (bamf_matcher_get_default ()), "favorites-changed",
90 (GCallback) matcher_favorites_changed, self);
91@@ -651,6 +687,17 @@
92 return application;
93 }
94
95+BamfApplication *
96+bamf_application_new_with_wmclass (char *wmclass)
97+{
98+ BamfApplication *application;
99+ application = (BamfApplication *) g_object_new (BAMF_TYPE_APPLICATION, NULL);
100+
101+ bamf_application_set_wmclass (application, wmclass);
102+
103+ return application;
104+}
105+
106 /**
107 bamf_application_get_show_stubs:
108 @application: Application to check for menu stubs
109
110=== modified file 'src/bamf-application.h'
111--- src/bamf-application.h 2010-09-11 02:52:33 +0000
112+++ src/bamf-application.h 2011-03-29 11:26:03 +0000
113@@ -66,6 +66,10 @@
114 gboolean bamf_application_contains_similar_to_window (BamfApplication *app,
115 BamfWindow *window);
116
117+char * bamf_application_get_wmclass (BamfApplication *application);
118+void bamf_application_set_wmclass (BamfApplication *application,
119+ char *wmclass);
120+
121 BamfApplication * bamf_application_new (void);
122
123 BamfApplication * bamf_application_new_from_desktop_file (char * desktop_file);
124@@ -73,4 +77,6 @@
125
126 BamfApplication * bamf_application_new_from_desktop_files (GList * desktop_files);
127
128+BamfApplication * bamf_application_new_with_wmclass (char *wmclass);
129+
130 #endif
131
132=== modified file 'src/bamf-matcher.c'
133--- src/bamf-matcher.c 2011-03-15 12:09:16 +0000
134+++ src/bamf-matcher.c 2011-03-29 11:26:03 +0000
135@@ -61,6 +61,7 @@
136 GArray * known_pids;
137 GHashTable * desktop_id_table;
138 GHashTable * desktop_file_table;
139+ GHashTable * desktop_class_table;
140 GHashTable * exec_list;
141 GHashTable * registered_pids;
142 GList * views;
143@@ -416,17 +417,43 @@
144 file_list = g_list_append (file_list, datadup);
145 id_list = g_list_append (id_list, datadup);
146 }
147-
148+
149 g_hash_table_insert (desktop_file_table, g_strdup (exec), file_list);
150 g_hash_table_insert (desktop_id_table, g_strdup (desktop_id), id_list);
151-
152+}
153+
154+static void
155+insert_desktop_file_class_into_table (BamfMatcher *self,
156+ const char *desktop_file,
157+ GHashTable *desktop_class_table)
158+{
159+ GKeyFile *desktop_keyfile;
160+ char *class;
161+
162+ g_return_if_fail (desktop_file);
163+
164+ desktop_keyfile = g_key_file_new ();
165+
166+ if (g_key_file_load_from_file (desktop_keyfile, desktop_file, G_KEY_FILE_NONE,
167+ NULL))
168+ {
169+ class = g_key_file_get_string (desktop_keyfile,
170+ G_KEY_FILE_DESKTOP_GROUP,
171+ G_KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS,
172+ NULL);
173+ if (class)
174+ g_hash_table_insert (desktop_class_table, g_strdup (desktop_file), class);
175+
176+ g_key_file_free (desktop_keyfile);
177+ }
178 }
179
180 static void
181 load_desktop_file_to_table (BamfMatcher * self,
182 const char *file,
183 GHashTable *desktop_file_table,
184- GHashTable *desktop_id_table)
185+ GHashTable *desktop_id_table,
186+ GHashTable *desktop_class_table)
187 {
188 GAppInfo *desktop_file;
189 char *exec;
190@@ -467,6 +494,7 @@
191 desktop_id = g_string_truncate (desktop_id, desktop_id->len - 8); /* remove last 8 characters for .desktop */
192
193 insert_data_into_tables (self, file, exec, desktop_id->str, desktop_file_table, desktop_id_table);
194+ insert_desktop_file_class_into_table (self, file, desktop_class_table);
195
196 g_free (exec);
197 g_string_free (desktop_id, TRUE);
198@@ -476,7 +504,8 @@
199 load_directory_to_table (BamfMatcher * self,
200 const char *directory,
201 GHashTable *desktop_file_table,
202- GHashTable *desktop_id_table)
203+ GHashTable *desktop_id_table,
204+ GHashTable *desktop_class_table)
205 {
206 GFile *dir;
207 GFileEnumerator *enumerator;
208@@ -506,7 +535,8 @@
209 load_desktop_file_to_table (self,
210 path,
211 desktop_file_table,
212- desktop_id_table);
213+ desktop_id_table,
214+ desktop_class_table);
215
216 g_free ((gpointer) path);
217 g_object_unref (info);
218@@ -520,7 +550,8 @@
219 load_index_file_to_table (BamfMatcher * self,
220 const char *index_file,
221 GHashTable *desktop_file_table,
222- GHashTable *desktop_id_table)
223+ GHashTable *desktop_id_table,
224+ GHashTable *desktop_class_table)
225 {
226 GFile *file;
227 GFileInputStream *stream;
228@@ -569,6 +600,7 @@
229 g_string_truncate (desktop_id, desktop_id->len - 8);
230
231 insert_data_into_tables (self, filename->str, exec, desktop_id->str, desktop_file_table, desktop_id_table);
232+ insert_desktop_file_class_into_table (self, filename->str, desktop_class_table);
233
234 g_string_free (desktop_id, TRUE);
235 length = 0;
236@@ -612,7 +644,7 @@
237 if (!g_list_find_custom (dirs, "/usr/local/share/applications", (GCompareFunc) g_strcmp0))
238 dirs = g_list_prepend (dirs, g_strdup ("/usr/local/share/applications"));
239
240- dirs = g_list_prepend (dirs, g_strdup (g_build_filename (g_get_home_dir (), ".share/applications", NULL)));
241+ dirs = g_list_prepend (dirs, g_strdup (g_build_filename (g_get_home_dir (), ".local/share/applications", NULL)));
242
243 if (data_dirs)
244 g_strfreev (data_dirs);
245@@ -690,6 +722,7 @@
246 {
247 g_hash_table_foreach_remove (self->priv->desktop_id_table, (GHRFunc) hash_table_remove_values, path);
248 g_hash_table_foreach_remove (self->priv->desktop_file_table, (GHRFunc) hash_table_remove_values, path);
249+ g_hash_table_remove (self->priv->desktop_class_table, path);
250 }
251
252 out:
253@@ -697,7 +730,10 @@
254 }
255
256 static void
257-create_desktop_file_table (BamfMatcher * self, GHashTable **desktop_file_table, GHashTable **desktop_id_table)
258+create_desktop_file_table (BamfMatcher * self,
259+ GHashTable **desktop_file_table,
260+ GHashTable **desktop_id_table,
261+ GHashTable **desktop_class_table)
262 {
263 GList *directories;
264 GList *l;
265@@ -718,6 +754,12 @@
266 (GDestroyNotify) g_free,
267 NULL);
268
269+ *desktop_class_table =
270+ g_hash_table_new_full ((GHashFunc) g_str_hash,
271+ (GEqualFunc) g_str_equal,
272+ (GDestroyNotify) g_free,
273+ (GDestroyNotify) g_free);
274+
275 g_return_if_fail (BAMF_IS_MATCHER (self));
276
277 directories = get_desktop_file_directories (self);
278@@ -740,11 +782,13 @@
279
280 if (g_file_test (bamf_file, G_FILE_TEST_EXISTS))
281 {
282- load_index_file_to_table (self, bamf_file, *desktop_file_table, *desktop_id_table);
283+ load_index_file_to_table (self, bamf_file, *desktop_file_table,
284+ *desktop_id_table, *desktop_class_table);
285 }
286 else
287 {
288- load_directory_to_table (self, directory, *desktop_file_table, *desktop_id_table);
289+ load_directory_to_table (self, directory, *desktop_file_table,
290+ *desktop_id_table, *desktop_class_table);
291 }
292
293 g_free (directory);
294@@ -993,38 +1037,81 @@
295 }
296 else
297 {
298- char *class_name = window_class_name (window);
299-
300- if (class_name)
301+ char *window_class = window_class_name (window);
302+
303+ char *desktop_file;
304+ char *desktop_class;
305+
306+ if (window_class)
307 {
308- class_name = g_ascii_strdown (class_name, -1);
309- l = g_hash_table_lookup (priv->desktop_id_table, class_name);
310+ char *window_class_down = g_ascii_strdown (g_strdup(window_class), -1);
311+ l = g_hash_table_lookup (priv->desktop_id_table, window_class_down);
312+ g_free (window_class_down);
313
314 for (; l; l = l->next)
315 {
316- if (l->data && !g_list_find_custom (desktop_files, l->data, (GCompareFunc) g_strcmp0))
317- desktop_files = g_list_prepend (desktop_files, g_strdup (l->data));
318- }
319-
320- desktop_files = g_list_reverse (desktop_files);
321- g_free (class_name);
322- }
323+ desktop_file = l->data;
324+ if (desktop_file)
325+ {
326+ desktop_class = g_hash_table_lookup (priv->desktop_class_table, desktop_file);
327+ if ((desktop_class == NULL || g_strcmp0 (desktop_class, window_class) == 0) &&
328+ !g_list_find_custom (desktop_files, desktop_file,
329+ (GCompareFunc) g_strcmp0))
330+ {
331+ desktop_files = g_list_prepend (desktop_files, g_strdup (desktop_file));
332+ }
333+ }
334+ }
335+
336+ desktop_files = g_list_reverse (desktop_files);
337+ }
338+
339+ /* Iterate over the desktop class table, and add matching desktop files */
340+ gpointer key;
341+ gpointer value;
342+ GHashTableIter iter;
343+ g_hash_table_iter_init (&iter, priv->desktop_class_table);
344+
345+ while (g_hash_table_iter_next (&iter, &key, &value))
346+ {
347+ desktop_file = g_strdup (key);
348+ desktop_class = value;
349+ if (g_strcmp0 (desktop_class, window_class) == 0 &&
350+ !g_list_find_custom (desktop_files, desktop_file, (GCompareFunc) g_strcmp0))
351+ {
352+ desktop_files = g_list_prepend (desktop_files, desktop_file);
353+ }
354+ }
355
356 pid = bamf_legacy_window_get_pid (window);
357-
358 pid_list = bamf_matcher_possible_applications_for_pid (self, pid);
359
360- /* Append these files to the end to give preference to class_name style picking.
361+ /* Append these files to the end to give preference to window_class style picking.
362 This style of matching is prefered and used by GNOME Shell however does not work
363 very well in practice, thus requiring the fallback here */
364 for (l = pid_list; l; l = l->next)
365 {
366+ desktop_file = l->data;
367 if (g_list_find_custom (desktop_files, l->data, (GCompareFunc) g_strcmp0))
368- g_free (l->data);
369+ g_free (desktop_file);
370 else
371- desktop_files = g_list_append (desktop_files, l->data);
372+ {
373+ if (window_class)
374+ {
375+ desktop_class = g_hash_table_lookup (priv->desktop_class_table, desktop_file);
376+ if ((desktop_class == NULL || g_strcmp0 (desktop_class, window_class) == 0) &&
377+ !g_list_find_custom (desktop_files, desktop_file,
378+ (GCompareFunc) g_strcmp0))
379+ {
380+ desktop_files = g_list_append (desktop_files, desktop_file);
381+ }
382+ }
383+ else
384+ desktop_files = g_list_append (desktop_files, desktop_file);
385+ }
386 }
387-
388+
389+ g_free (window_class);
390 g_list_free (pid_list);
391 }
392
393@@ -1039,6 +1126,8 @@
394 BamfLegacyWindow *window;
395 GList *views, *a;
396 char *desktop_file;
397+ char *win_class;
398+ char *app_class;
399 BamfApplication *app = NULL, *best = NULL;
400 BamfView *view;
401
402@@ -1049,6 +1138,7 @@
403 views = self->priv->views;
404
405 possible_apps = bamf_matcher_possible_applications_for_window (self, bamf_window);
406+ win_class = window_class_name(window);
407
408 /* Loop over every application, inside that application see if its .desktop file
409 * matches with any of our possible hits. If so we match it. If we have no possible hits
410@@ -1062,8 +1152,16 @@
411 continue;
412
413 app = BAMF_APPLICATION (view);
414+ app_class = bamf_application_get_wmclass (app);
415+
416+ if (app_class != NULL && g_strcmp0 (win_class, app_class) != 0)
417+ {
418+ g_free (app_class);
419+ continue;
420+ }
421+
422 desktop_file = bamf_application_get_desktop_file (app);
423-
424+
425 if (possible_apps)
426 {
427 /* primary matching */
428@@ -1084,7 +1182,8 @@
429 if (bamf_application_contains_similar_to_window (app, bamf_window))
430 best = app;
431 }
432-
433+
434+ g_free (app_class);
435 g_free (desktop_file);
436 }
437
438@@ -1095,16 +1194,19 @@
439 else
440 best = bamf_application_new ();
441
442+ bamf_application_set_wmclass (best, win_class);
443+
444 bamf_matcher_register_view (self, BAMF_VIEW (best));
445 g_object_unref (best);
446 }
447
448- for (l = possible_apps; l; l = l->next)
449- {
450- char *str = l->data;
451- g_free (str);
452- }
453+ g_free (win_class);
454
455+ for (l = possible_apps; l; l = l->next)
456+ {
457+ char *str = l->data;
458+ g_free (str);
459+ }
460
461 g_list_free (possible_apps);
462
463@@ -1340,7 +1442,8 @@
464 load_desktop_file_to_table (self,
465 desktop_file,
466 self->priv->desktop_file_table,
467- self->priv->desktop_id_table);
468+ self->priv->desktop_id_table,
469+ self->priv->desktop_class_table);
470 }
471
472 void
473@@ -1731,11 +1834,13 @@
474
475 g_array_free (prefixstrings, TRUE);
476
477- create_desktop_file_table (self, &(priv->desktop_file_table), &(priv->desktop_id_table));
478+ create_desktop_file_table (self, &(priv->desktop_file_table),
479+ &(priv->desktop_id_table),
480+ &(priv->desktop_class_table));
481
482 screen = bamf_legacy_screen_get_default ();
483 g_signal_connect (G_OBJECT (screen), "window-opened",
484- (GCallback) handle_window_opened, self);
485+ (GCallback) handle_window_opened, self);
486
487 approver = bamf_indicator_source_get_default ();
488 g_signal_connect (G_OBJECT (approver), "indicator-opened",

Subscribers

People subscribed via source and target branches