Codebase list xapp / e40c3b8
Update upstream source from tag 'upstream/1.8.9' Update to upstream version '1.8.9' with Debian dir a9417fe00d34a24cba7cc2d689b69d04dfadebaa Norbert Preining 3 years ago
8 changed file(s) with 696 addition(s) and 414 deletion(s). Raw diff Collapse all Expand all
55 libdeps += dependency('gtk+-3.0', version: '>=3.3.16', required: true)
66 libdeps += dependency('gdk-pixbuf-2.0', version: '>=2.22.0', required: true)
77 libdeps += dependency('cairo', required: true)
8 libdeps += dependency('libgnomekbdui', required: true)
98 libdeps += dependency('x11', required: true)
109
1110 xapp_headers = [
1211 'xapp-gtk-window.h',
1312 'xapp-icon-chooser-button.h',
1413 'xapp-icon-chooser-dialog.h',
15 'xapp-kbd-layout-controller.h',
1614 'xapp-monitor-blanker.h',
1715 'xapp-preferences-window.h',
1816 'xapp-stack-sidebar.h',
2624 'xapp-gtk-window.c',
2725 'xapp-icon-chooser-button.c',
2826 'xapp-icon-chooser-dialog.c',
29 'xapp-kbd-layout-controller.c',
3027 'xapp-monitor-blanker.c',
3128 'xapp-preferences-window.c',
3229 'xapp-stack-sidebar.c',
3431 'xapp-status-icon-monitor.c',
3532 'xapp-util.c'
3633 ]
34
35 if not app_lib_only
36 libdeps += dependency('libgnomekbdui', required: true)
37 xapp_headers += 'xapp-kbd-layout-controller.h'
38 xapp_sources += 'xapp-kbd-layout-controller.c'
39 endif
3740
3841 dbus_headers = []
3942
11491149 self->priv->name = g_strdup_printf("%s", g_get_application_name());
11501150 self->priv->state = XAPP_STATUS_ICON_STATE_NO_SUPPORT;
11511151 self->priv->icon_size = FALLBACK_ICON_SIZE;
1152 self->priv->icon_name = g_strdup (" ");
11521153
11531154 g_debug ("XAppStatusIcon: init: application name: '%s'", self->priv->name);
11541155
00 project('xapp',
11 'c',
2 version : '1.8.8'
2 version : '1.8.9'
33 )
44
55 gnome = import('gnome')
99 dbus_services_dir = dependency('dbus-1').get_pkgconfig_variable('session_bus_services_dir',
1010 define_variable: ['prefix', get_option('prefix')])
1111 libexec_path = join_paths(get_option('prefix'), get_option('libexecdir'), 'xapps', 'sn-watcher')
12
13
1412
1513 cdata = configuration_data()
1614 cdata.set_quoted('GETTEXT_PACKAGE', 'xapp')
3331 )
3432 endif
3533
34 app_lib_only = get_option('app-lib-only')
35
3636 c = configure_file(output : 'config.h',
3737 configuration : cdata
3838 )
4040 top_inc = include_directories('.')
4141 codegen = find_program(join_paths(meson.source_root(), 'meson-scripts', 'g-codegen.py'))
4242
43 subdir('icons')
4443 subdir('libxapp')
4544 subdir('po')
45 subdir('schemas')
4646 subdir('pygobject')
47 subdir('schemas')
48 subdir('status-applets')
49 subdir('scripts')
5047
51 if get_option('status-notifier')
48 if not app_lib_only
49 subdir('icons')
50 subdir('status-applets')
51 subdir('scripts')
52 endif
53
54 if get_option('status-notifier') and not app_lib_only
5255 subdir('xapp-sn-watcher')
5356 endif
5457
1616 type: 'boolean',
1717 value: true,
1818 description: 'Build the XApp StatusNotifier service.'
19 )
19 )
20 option('app-lib-only',
21 type: 'boolean',
22 value: false,
23 description: 'Limit build to core library only and without the keyboard layout controller. This is to allow flatpak bundlings.'
24 )
25
5555 return Gtk.PositionType.LEFT
5656
5757 class StatusWidget(Gtk.ToggleButton):
58 __gsignals__ = {
59 "re-sort": (GObject.SignalFlags.RUN_LAST, None, ())
60 }
61
5862 def __init__(self, icon, orientation, size):
5963 super(Gtk.ToggleButton, self).__init__()
6064 self.theme = Gtk.IconTheme.get_default()
107111 print("Could not read metadata: %s" % e)
108112
109113 self.proxy.connect("notify::icon-name", self._on_icon_name_changed)
114 self.proxy.connect("notify::name", self._on_name_changed)
110115
111116 self.in_widget = False
112117 self.plain_surface = None
125130
126131 def _on_icon_name_changed(self, proxy, gparamspec, data=None):
127132 self.update_icon()
133
134 def _on_name_changed(self, proxy, gparamspec, data=None):
135 self.emit("re-sort")
128136
129137 def update_icon(self):
130138 string = self.proxy.props.icon_name
375383
376384 self.indicators[name] = StatusWidget(proxy, self.applet.get_orient(), self.applet.get_size())
377385 self.indicator_box.add(self.indicators[name])
386 self.indicators[name].connect("re-sort", self.sort_icons)
378387
379388 self.sort_icons()
380389
382391 name = proxy.get_name()
383392
384393 self.indicator_box.remove(self.indicators[name])
394 self.indicators[name].disconnect_by_func(self.sort_icons)
385395 del(self.indicators[name])
386396
387397 self.sort_icons()
424434
425435 self.indicator_box.queue_resize()
426436
427 def sort_icons(self):
437 def sort_icons(self, status_widget=None):
428438 icon_list = list(self.indicators.values())
429439
430440 # for i in icon_list:
1010 DBUS_PATH = "/org/x/StatusIcon"
1111
1212 class StatusWidget(Gtk.ToggleButton):
13 __gsignals__ = {
14 "re-sort": (GObject.SignalFlags.RUN_LAST, None, ())
15 }
1316
1417 def __init__(self, icon):
1518 super(Gtk.ToggleButton, self).__init__()
4144 self.proxy.bind_property("secondary-menu-is-open", self, "active", flags)
4245
4346 self.proxy.connect("notify::icon-name", self.on_icon_name_changed)
47 self.proxy.connect("notify::name", self.on_name_changed)
4448
4549 self.connect("button-press-event", self.on_button_press)
4650 self.connect("button-release-event", self.on_button_release)
5054 string = self.proxy.props.icon_name
5155
5256 self.set_icon(string)
57
58 def on_name_changed(self, proxy, gparamspec, data=None):
59 self.emit("re-sort")
5360
5461 def set_icon(self, string):
5562 if string:
163170
164171 self.indicators[name] = StatusWidget(proxy)
165172 self.indicator_box.add(self.indicators[name])
173 self.indicators[name].connect("re-sort", self.sort_icons)
166174
167175 self.sort_icons()
168176
170178 name = proxy.get_name()
171179
172180 self.indicator_box.remove(self.indicators[name])
181 self.indicators[name].disconnect_by_func(self.sort_icons)
173182 del(self.indicators[name])
174183
175184 self.sort_icons()
2525 STATUS_NEEDS_ATTENTION
2626 } Status;
2727
28 typedef struct
29 {
30 gchar *id;
31 gchar *title;
32 gchar *status;
33 gchar *tooltip_heading;
34 gchar *tooltip_body;
35 gchar *menu_path;
36
37 gchar *icon_theme_path;
38 gchar *icon_name;
39 gchar *attention_icon_name;
40 gchar *overlay_icon_name;
41
42 gchar *icon_md5;
43 gchar *attention_icon_md5;
44 gchar *overlay_icon_md5;
45 cairo_surface_t *icon_surface;
46 cairo_surface_t *attention_icon_surface;
47 cairo_surface_t *overlay_icon_surface;
48
49 gboolean update_status;
50 gboolean update_tooltip;
51 gboolean update_menu;
52 gboolean update_icon;
53 } SnItemPropertiesResult;
54
2855 struct _SnItem
2956 {
3057 GObject parent_instance;
3259 GDBusProxy *sn_item_proxy; // SnItemProxy
3360 GDBusProxy *prop_proxy; // dbus properties (we can't trust SnItemProxy)
3461
35 GtkWidget *menu;
3662 XAppStatusIcon *status_icon;
63
64 SnItemPropertiesResult *current_props;
65 GCancellable *cancellable;
3766
3867 Status status;
3968 gchar *last_png_path;
4069 gchar *png_path;
4170
4271 gint current_icon_id;
72
73 guint update_properties_timeout;
4374 gchar *sortable_name;
4475
4576 gboolean should_activate;
4677 gboolean should_replace_tooltip;
4778 gboolean is_ai;
79
80 GtkWidget *menu;
4881 };
4982
5083 G_DEFINE_TYPE (SnItem, sn_item, G_TYPE_OBJECT)
5184
52 static void update_menu (SnItem *item);
53 static void update_status (SnItem *item);
54 static void update_tooltip (SnItem *item);
55 static void update_icon (SnItem *item);
85 static void update_menu (SnItem *item, SnItemPropertiesResult *new_props);
86 static void update_status (SnItem *item, SnItemPropertiesResult *new_props);
87 static void update_tooltip (SnItem *item, SnItemPropertiesResult *new_props);
88 static void update_icon (SnItem *item, SnItemPropertiesResult *new_props);
89 static void assign_sortable_name (SnItem *item, const gchar *title);
90
91 static void
92 props_free (SnItemPropertiesResult *props)
93 {
94 if (props == NULL)
95 {
96 return;
97 }
98
99 g_free (props->id);
100 g_free (props->title);
101 g_free (props->status);
102 g_free (props->tooltip_heading);
103 g_free (props->tooltip_body);
104 g_free (props->menu_path);
105 g_free (props->icon_theme_path);
106 g_free (props->icon_name);
107 g_free (props->attention_icon_name);
108 g_free (props->overlay_icon_name);
109
110 g_free (props->icon_md5);
111 g_free (props->attention_icon_md5);
112 g_free (props->overlay_icon_md5);
113
114 cairo_surface_destroy (props->icon_surface);
115 cairo_surface_destroy (props->attention_icon_surface);
116 cairo_surface_destroy (props->overlay_icon_surface);
117
118 g_slice_free (SnItemPropertiesResult, props);
119 }
56120
57121 static gboolean
58122 should_activate (SnItem *item)
80144 g_strfreev (ids);
81145
82146 return should;
147 }
148
149 static void
150 update_conditionals (SnItem *item)
151 {
152 item->should_replace_tooltip = should_replace_tooltip (item);
153 item->should_activate = should_activate (item);
83154 }
84155
85156 static void
106177 g_free (item->last_png_path);
107178 item->last_png_path = NULL;
108179 }
180
181 g_clear_handle_id (&item->update_properties_timeout, g_source_remove);
109182
110183 g_clear_pointer (&item->sortable_name, g_free);
111184 g_clear_object (&item->status_icon);
112185 g_clear_object (&item->menu);
113186 g_clear_object (&item->prop_proxy);
114187 g_clear_object (&item->sn_item_proxy);
188 g_clear_object (&item->cancellable);
189
190 props_free (item->current_props);
115191
116192 G_OBJECT_CLASS (sn_item_parent_class)->dispose (object);
117193 }
176252 return FALLBACK_ICON_SIZE;
177253 }
178254
179 static GVariant *
180 get_property (SnItem *item,
181 const gchar *prop_name)
182 {
183 GVariant *res, *var;
184 GError *error = NULL;
185
186 res = g_dbus_proxy_call_sync (item->prop_proxy,
187 "Get",
188 g_variant_new ("(ss)",
189 g_dbus_proxy_get_interface_name (item->sn_item_proxy),
190 prop_name),
191 G_DBUS_CALL_FLAGS_NONE,
192 5 * 1000,
193 NULL,
194 &error);
195
196 if (error != NULL)
197 {
198 g_error_free (error);
199 return NULL;
200 }
201
202 g_variant_get (res, "(v)", &var);
203 g_variant_unref (res);
204
205 return var;
206 }
207
208 static GVariant *
209 get_pixmap_property (SnItem *item,
210 const gchar *name)
211 {
212 GVariant *var = NULL;
213
214 var = get_property (item, name);
215
216 if (var == NULL)
217 {
218 return NULL;
219 }
220
221 return var;
222 }
223
224 static gchar *
225 get_string_property (SnItem *item,
226 const gchar *name)
227 {
228 GVariant *var = NULL;
229 gchar *result = NULL;
230
231 var = get_property (item, name);
232
233 if (var == NULL)
234 {
235 return NULL;
236 }
237
238 result = g_variant_dup_string (var, NULL);
239 g_variant_unref (var);
240
241 if (g_strcmp0 (result, "") == 0)
242 {
243 g_clear_pointer (&result, g_free);
244 }
245
246 return result;
247 }
248
249255 static cairo_surface_t *
250256 surface_from_pixmap_data (gint width,
251257 gint height,
252 GBytes *bytes)
258 guchar *data)
253259 {
254260 cairo_surface_t *surface;
255261 GdkPixbuf *pixbuf;
256262 gint rowstride, i;
257 gsize size;
258 gconstpointer data;
259 guchar *copy;
260263 guchar alpha;
261
262 data = g_bytes_get_data (bytes, &size);
263 copy = g_memdup ((guchar *) data, size);
264264
265265 surface = NULL;
266266 rowstride = width * 4;
268268
269269 while (i < 4 * width * height)
270270 {
271 alpha = copy[i ];
272 copy[i ] = copy[i + 1];
273 copy[i + 1] = copy[i + 2];
274 copy[i + 2] = copy[i + 3];
275 copy[i + 3] = alpha;
271 alpha = data[i ];
272 data[i ] = data[i + 1];
273 data[i + 1] = data[i + 2];
274 data[i + 2] = data[i + 3];
275 data[i + 3] = alpha;
276276 i += 4;
277277 }
278278
279 pixbuf = gdk_pixbuf_new_from_data (copy,
279 pixbuf = gdk_pixbuf_new_from_data (data,
280280 GDK_COLORSPACE_RGB,
281281 TRUE, 8,
282282 width, height,
295295 }
296296 }
297297
298 static gboolean
299 process_pixmaps (SnItem *item,
300 GVariant *pixmaps,
301 gchar **image_path)
302 {
303 GVariantIter iter;
298 static cairo_surface_t *
299 get_icon_surface (SnItem *item,
300 GVariant *pixmaps,
301 gchar **md5)
302 {
303 GVariantIter *iter;
304304 cairo_surface_t *surface;
305305 gint width, height;
306306 gint largest_width, largest_height;
307 gsize largest_data_size;
307308 GVariant *byte_array_var;
308 GBytes *best_image_bytes = NULL;
309 gconstpointer data;
310 guchar *best_image_array = NULL;
309311
310312 largest_width = largest_height = 0;
311313
312 g_variant_iter_init (&iter, pixmaps);
313
314 while (g_variant_iter_loop (&iter, "(ii@ay)", &width, &height, &byte_array_var))
315 {
316 if (width > 0 & height > 0 &&
314 *md5 = NULL;
315
316 g_variant_get (pixmaps, "a(iiay)", &iter);
317
318 if (iter == NULL)
319 {
320 return NULL;
321 }
322
323 while (g_variant_iter_loop (iter, "(ii@ay)", &width, &height, &byte_array_var))
324 {
325 if (width > 0 & height > 0 && byte_array_var != NULL &&
317326 ((width * height) > (largest_width * largest_height)))
318327 {
319328 gsize data_size = g_variant_get_size (byte_array_var);
320329
321330 if (data_size == width * height * 4)
322331 {
323 g_clear_pointer (&best_image_bytes, g_bytes_unref);
324
325 largest_width = width;
326 largest_height = height;
327 best_image_bytes = g_variant_get_data_as_bytes (byte_array_var);
328 }
329 }
330 }
331
332 if (best_image_bytes == NULL)
333 {
334 g_warning ("No valid pixmaps found.");
335 return FALSE;
336 }
337
338 surface = surface_from_pixmap_data (largest_width, largest_height, best_image_bytes);
332 data = g_variant_get_data (byte_array_var);
333
334 if (data != NULL)
335 {
336 if (best_image_array != NULL)
337 {
338 g_free (best_image_array);
339 }
340
341 best_image_array = g_memdup (data, data_size);
342
343 largest_data_size = data_size;
344 largest_width = width;
345 largest_height = height;
346 }
347 }
348 }
349 }
350
351 g_variant_iter_free (iter);
352
353 if (best_image_array == NULL)
354 {
355 return NULL;
356 }
357
358 surface = surface_from_pixmap_data (largest_width, largest_height, best_image_array);
359 *md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5, best_image_array, largest_data_size);
339360
340361 if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
341362 {
342363 cairo_surface_destroy (surface);
343 return FALSE;
344 }
345
346 item->last_png_path = item->png_path;
347
348 gchar *filename = g_strdup_printf ("xapp-tmp-%p-%d.png", item, get_icon_id (item));
349 gchar *save_filename = g_build_path ("/", g_get_tmp_dir (), filename, NULL);
350 g_free (filename);
351
352 cairo_status_t status = CAIRO_STATUS_SUCCESS;
353 status = cairo_surface_write_to_png (surface, save_filename);
354
355 if (status != CAIRO_STATUS_SUCCESS)
356 {
357 g_warning ("Failed to save png of status icon");
358 g_free (image_path);
359 cairo_surface_destroy (surface);
360 return FALSE;
361 }
362
363 *image_path = save_filename;
364 cairo_surface_destroy (surface);
365
366 return TRUE;
367 }
368
369 static void
370 set_icon_from_pixmap (SnItem *item)
371 {
372 GVariant *pixmaps;
373 gchar *image_path;
364 return NULL;
365 }
366
367 return surface;
368 }
369
370 static void
371 set_icon_from_pixmap (SnItem *item, SnItemPropertiesResult *new_props)
372 {
373 cairo_surface_t *surface;
374 gchar *filename, *save_filename;
375
376 g_debug ("Trying to use icon pixmap for %s",
377 item->sortable_name);
378
379 surface = NULL;
374380
375381 if (item->status == STATUS_ACTIVE)
376382 {
377 pixmaps = get_pixmap_property (item, "IconPixmap");
383 if (new_props->icon_surface)
384 {
385 surface = new_props->icon_surface;
386 }
378387 }
379388 else
380389 if (item->status == STATUS_NEEDS_ATTENTION)
381390 {
382 pixmaps = get_pixmap_property (item, "AttentionIconPixmap");
383
384 if (!pixmaps)
385 {
386 pixmaps = get_pixmap_property (item, "IconPixmap");
387 }
388 }
389
390 if (!pixmaps)
391 {
392 xapp_status_icon_set_icon_name (item->status_icon, "image-missing");
393 g_warning ("No pixmaps to use");
391 if (new_props->attention_icon_surface)
392 {
393 surface = new_props->attention_icon_surface;
394 }
395 }
396
397 if (surface != NULL)
398 {
399 item->last_png_path = item->png_path;
400
401 filename = g_strdup_printf ("xapp-tmp-%p-%d.png", item, get_icon_id (item));
402 save_filename = g_build_path ("/", g_get_tmp_dir (), filename, NULL);
403 g_free (filename);
404
405 cairo_status_t status = CAIRO_STATUS_SUCCESS;
406 status = cairo_surface_write_to_png (surface, save_filename);
407
408 g_debug ("Saving tmp image file for '%s' to '%s'", item->sortable_name, save_filename);
409
410 if (status != CAIRO_STATUS_SUCCESS)
411 {
412 g_warning ("Failed to save png of status icon");
413 }
414
415 xapp_status_icon_set_icon_name (item->status_icon, save_filename);
416 g_free (save_filename);
394417 return;
395418 }
396419
397 if (process_pixmaps (item, pixmaps, &image_path))
398 {
399 xapp_status_icon_set_icon_name (item->status_icon, image_path);
400 g_free (image_path);
401 }
402
403 g_variant_unref (pixmaps);
420 g_debug ("No pixmaps to use");
421 xapp_status_icon_set_icon_name (item->status_icon, "image-missing");
404422 }
405423
406424 static gchar *
407 get_icon_filename_from_theme (SnItem *item,
425 get_icon_filename_from_theme (SnItem *item,
408426 const gchar *theme_path,
409427 const gchar *icon_name)
410428 {
411429 GtkIconInfo *info;
412430 gchar *filename;
413431 const gchar *array[2];
432 gint host_icon_size;
414433
415434 array[0] = icon_name;
416435 array[1] = NULL;
417436
418437 // We have a theme path, but try the system theme first
419438 GtkIconTheme *theme = gtk_icon_theme_get_default ();
439 host_icon_size = get_icon_size (item);
420440
421441 info = gtk_icon_theme_choose_icon_for_scale (theme,
422442 array,
423 get_icon_size (item),
443 host_icon_size,
424444 lookup_ui_scale (),
425445 GTK_ICON_LOOKUP_FORCE_SVG | GTK_ICON_LOOKUP_FORCE_SYMBOLIC);
426446
433453
434454 info = gtk_icon_theme_choose_icon_for_scale (theme,
435455 array,
436 get_icon_size (item),
456 host_icon_size,
437457 lookup_ui_scale (),
438458 GTK_ICON_LOOKUP_FORCE_SVG | GTK_ICON_LOOKUP_FORCE_SYMBOLIC);
439459
451471 return filename;
452472 }
453473
454 static void
455 process_icon_name (SnItem *item,
456 const gchar *icon_theme_path,
457 const gchar *icon_name)
458 {
459 if (g_path_is_absolute (icon_name) || !icon_theme_path)
474 static gboolean
475 set_icon_name (SnItem *item,
476 const gchar *icon_theme_path,
477 const gchar *icon_name)
478 {
479 g_debug ("Checking for icon name for %s",
480 item->sortable_name);
481
482 if (icon_name == NULL)
483 {
484 return FALSE;
485 }
486
487 if (g_path_is_absolute (icon_name))
460488 {
461489 xapp_status_icon_set_icon_name (item->status_icon, icon_name);
490
491 return TRUE;
462492 }
463493 else
464494 {
468498 {
469499 xapp_status_icon_set_icon_name (item->status_icon, filename);
470500 g_free (filename);
471 }
472 else
473 {
474 xapp_status_icon_set_icon_name (item->status_icon, "image-missing");
475 }
476 }
477 }
478
479 static void
480 set_icon_name_or_path (SnItem *item,
481 const gchar *icon_theme_path,
482 const gchar *icon_name,
483 const gchar *att_icon_name,
484 const gchar *olay_icon_name)
501 return TRUE;
502 }
503 }
504
505 return FALSE;
506 }
507
508 static gboolean
509 set_icon_name_or_path (SnItem *item,
510 SnItemPropertiesResult *new_props)
485511 {
486512 const gchar *name_to_use = NULL;
487513
488514 if (item->status == STATUS_ACTIVE)
489515 {
490 if (icon_name)
491 {
492 name_to_use = icon_name;
516 if (new_props->icon_name)
517 {
518 name_to_use = new_props->icon_name;
493519 }
494520 }
495521 else
496522 if (item->status == STATUS_NEEDS_ATTENTION)
497523 {
498 if (att_icon_name)
499 {
500 name_to_use = att_icon_name;
524 if (new_props->attention_icon_name)
525 {
526 name_to_use = new_props->attention_icon_name;
501527 }
502528 else
503 if (icon_name)
504 {
505 name_to_use = icon_name;
506 }
507 }
508
509 if (name_to_use == NULL)
510 {
511 name_to_use = "image-missing";
512 }
513
514 process_icon_name (item, icon_theme_path, name_to_use);
515 }
516
517 static void
518 update_icon (SnItem *item)
519 {
520 gchar *icon_theme_path;
521 gchar *icon_name, *att_icon_name, *olay_icon_name;
522
523 icon_theme_path = get_string_property (item, "IconThemePath");
524 icon_name = get_string_property (item, "IconName");
525 att_icon_name = get_string_property (item, "AttentionIconName");
526 olay_icon_name = get_string_property (item, "OverlayIconName");
527
528 if (icon_name || att_icon_name || olay_icon_name)
529 {
530 // g_printerr ("icon name '%s' '%s' '%s'\n", icon_name, att_icon_name, olay_icon_name);
531 set_icon_name_or_path (item,
532 icon_theme_path,
533 icon_name,
534 att_icon_name,
535 olay_icon_name);
536 }
537 else
538 {
539 set_icon_from_pixmap (item);
540 }
541
542 g_free (icon_theme_path);
543 g_free (icon_name);
544 g_free (att_icon_name);
545 g_free (olay_icon_name);
546 }
547
548 static void
549 update_menu (SnItem *item)
550 {
551 gchar *menu_path;
552
529 if (new_props->icon_name)
530 {
531 name_to_use = new_props->icon_name;
532 }
533 }
534
535 return set_icon_name (item, new_props->icon_theme_path, name_to_use);
536 }
537
538 static void
539 update_icon (SnItem *item, SnItemPropertiesResult *new_props)
540 {
541 if (!set_icon_name_or_path (item, new_props))
542 {
543 set_icon_from_pixmap (item, new_props);
544 }
545 }
546
547 static void
548 update_menu (SnItem *item, SnItemPropertiesResult *new_props)
549 {
553550 g_clear_object (&item->menu);
554551
555552 xapp_status_icon_set_primary_menu (item->status_icon, NULL);
556553 xapp_status_icon_set_secondary_menu (item->status_icon, NULL);
557554
558 menu_path = get_string_property (item, "Menu");
559
560 if (menu_path == NULL)
555 if (new_props->menu_path == NULL)
561556 {
562557 return;
563558 }
564559
565 item->menu = GTK_WIDGET (dbusmenu_gtkmenu_new ((gchar *) g_dbus_proxy_get_name (item->sn_item_proxy), menu_path));
560 item->menu = GTK_WIDGET (dbusmenu_gtkmenu_new ((gchar *) g_dbus_proxy_get_name (item->sn_item_proxy),
561 new_props->menu_path));
566562 g_object_ref_sink (item->menu);
567563
564 g_debug ("New menu for '%s'", item->sortable_name);
565
568566 if (item->is_ai && !item->should_activate)
569567 {
570568 xapp_status_icon_set_primary_menu (item->status_icon, GTK_MENU (item->menu));
571569 }
572570
573571 xapp_status_icon_set_secondary_menu (item->status_icon, GTK_MENU (item->menu));
574
575 g_free (menu_path);
576572 }
577573
578574 static gchar *
599595 }
600596
601597 static void
602 update_tooltip (SnItem *item)
603 {
604 g_autoptr(GVariant) tt_var = NULL;
598 update_tooltip (SnItem *item, SnItemPropertiesResult *new_props)
599 {
600 if (new_props->title)
601 {
602 assign_sortable_name (item, new_props->title);
603 }
605604
606605 if (!item->should_replace_tooltip)
607606 {
608 tt_var = get_property (item, "ToolTip");
609 }
610
611 if (tt_var)
612 {
613 const gchar *type_str;
614 type_str = g_variant_get_type_string (tt_var);
615
616 if (g_strcmp0 (type_str, "(sa(iiay)ss)") == 0)
617 {
618 const gchar *tooltip_title, *tooltip_body;
619
620 g_variant_get (tt_var, "(sa(iiay)&s&s)", NULL, NULL, &tooltip_title, &tooltip_body);
621
622 if (g_strcmp0 (tooltip_title, "") != 0)
623 {
624
625 if (g_strcmp0 (tooltip_body, "") != 0)
626 {
627 gchar *text;
628 text = g_strdup_printf ("%s\n%s", tooltip_title, tooltip_body);
629
630 xapp_status_icon_set_tooltip_text (item->status_icon, text);
631 g_debug ("Tooltip text from ToolTip: %s", text);
632
633 g_free (text);
634 }
635 else
636 {
637 g_debug ("Tooltip text from ToolTip: %s", tooltip_title);
638 xapp_status_icon_set_tooltip_text (item->status_icon, tooltip_title);
639 }
640
641 return;
642 }
643 }
644 }
645
646 gchar *title_string;
647 title_string = get_string_property (item, "Title");
648
649 if (title_string != NULL)
607 if (new_props->tooltip_heading != NULL)
608 {
609 if (new_props->tooltip_body != NULL)
610 {
611 gchar *text;
612 text = g_strdup_printf ("%s\n%s",
613 new_props->tooltip_heading,
614 new_props->tooltip_body);
615
616 xapp_status_icon_set_tooltip_text (item->status_icon, text);
617 g_debug ("Tooltip text for '%s' from ToolTip: %s",
618 item->sortable_name,
619 text);
620
621 g_free (text);
622 }
623 else
624 {
625 g_debug ("Tooltip text for '%s' from ToolTip: %s",
626 item->sortable_name,
627 new_props->tooltip_heading);
628
629 xapp_status_icon_set_tooltip_text (item->status_icon, new_props->tooltip_heading);
630 }
631
632 return;
633 }
634 }
635
636 if (new_props->title != NULL)
650637 {
651638 gchar *capped_string;
652639
653 capped_string = capitalize (title_string);
640 capped_string = capitalize (new_props->title);
654641 xapp_status_icon_set_tooltip_text (item->status_icon, capped_string);
655 g_debug ("Tooltip text from Title: %s", capped_string);
656
657 g_free (title_string);
642
643 g_debug ("Tooltip text for '%s' from Title: %s",
644 item->sortable_name,
645 capped_string);
646
658647 g_free (capped_string);
659648 return;
660649 }
663652 }
664653
665654 static void
666 update_status (SnItem *item)
667 {
668 Status old_status;
669 gchar *status;
670
671 old_status = item->status;
672
673 status = get_string_property (item, "Status");
674
675 if (g_strcmp0 (status, "Passive") == 0)
655 update_status (SnItem *item, SnItemPropertiesResult *new_props)
656 {
657 if (g_strcmp0 (new_props->status, "Passive") == 0)
676658 {
677659 item->status = STATUS_PASSIVE;
678660 xapp_status_icon_set_visible (item->status_icon, FALSE);
679661 }
680 else if (g_strcmp0 (status, "NeedsAttention") == 0)
662 else
663 if (g_strcmp0 (new_props->status, "NeedsAttention") == 0)
681664 {
682665 item->status = STATUS_NEEDS_ATTENTION;
683666 xapp_status_icon_set_visible (item->status_icon, TRUE);
688671 xapp_status_icon_set_visible (item->status_icon, TRUE);
689672 }
690673
691 g_free (status);
692
693 if (old_status != item->status)
694 {
695 update_icon (item);
696 }
674 g_debug ("Status for '%s' is now '%s'", item->sortable_name, new_props->status);
675 }
676
677 static gchar *
678 null_or_string_from_string (const gchar *str)
679 {
680 if (str != NULL && strlen(str) > 0)
681 {
682 return g_strdup (str);
683 }
684 else
685 {
686 return NULL;
687 }
688 }
689
690 static gchar *
691 null_or_string_from_variant (GVariant *variant)
692 {
693 const gchar *str;
694
695 str = g_variant_get_string (variant, NULL);
696
697 return null_or_string_from_string (str);
698 }
699
700 static void
701 get_all_properties_callback (GObject *source_object,
702 GAsyncResult *res,
703 gpointer user_data)
704 {
705 SnItem *item = SN_ITEM (user_data);
706 SnItemPropertiesResult *new_props;
707 GError *error = NULL;
708 GVariant *properties;
709 GVariantIter *iter = NULL;
710 const gchar *name;
711 GVariant *value;
712
713 properties = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error);
714
715 if (error != NULL)
716 {
717 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
718 {
719 g_critical ("Could get propertyies for %s: %s\n",
720 g_dbus_proxy_get_name (item->sn_item_proxy),
721 error->message);
722 }
723
724 g_error_free (error);
725 return;
726 }
727
728 if (properties == NULL)
729 {
730 return;
731 }
732
733 new_props = g_slice_new0 (SnItemPropertiesResult);
734
735 g_variant_get (properties, "(a{sv})", &iter);
736
737 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
738 {
739 if (g_strcmp0 (name, "Title") == 0)
740 {
741 new_props->title = null_or_string_from_variant (value);
742
743 if (g_strcmp0 (new_props->title, item->current_props->title) != 0)
744 {
745 new_props->update_tooltip = TRUE;
746 }
747 }
748 else
749 if (g_strcmp0 (name, "Status") == 0)
750 {
751 new_props->status = null_or_string_from_variant (value);
752
753 if (g_strcmp0 (new_props->status, item->current_props->status) != 0)
754 {
755 new_props->update_status = TRUE;
756 }
757 }
758 else
759 if (g_strcmp0 (name, "ToolTip") == 0)
760 {
761 const gchar *ts;
762
763 ts = g_variant_get_type_string (value);
764
765 if (g_strcmp0 (ts, "(sa(iiay)ss)") == 0)
766 {
767 gchar *heading, *body;
768
769 g_variant_get (value, "(sa(iiay)ss)", NULL, NULL,
770 &heading,
771 &body);
772
773 new_props->tooltip_heading = null_or_string_from_string (heading);
774 new_props->tooltip_body = null_or_string_from_string (body);
775
776 g_free (heading);
777 g_free (body);
778 }
779 else
780 if (g_strcmp0 (ts, "s") == 0)
781 {
782 new_props->tooltip_body = null_or_string_from_variant (value);
783 }
784
785 if (g_strcmp0 (new_props->tooltip_heading, item->current_props->tooltip_heading) != 0 ||
786 g_strcmp0 (new_props->tooltip_body, item->current_props->tooltip_body) != 0)
787 {
788 new_props->update_tooltip = TRUE;
789 }
790 }
791 else
792 if (g_strcmp0 (name, "Menu") == 0)
793 {
794 new_props->menu_path = null_or_string_from_variant (value);
795
796 if (g_strcmp0 (new_props->menu_path, item->current_props->menu_path) != 0)
797 {
798 new_props->update_menu = TRUE;
799 }
800 }
801 else
802 if (g_strcmp0 (name, "IconThemePath") == 0)
803 {
804 new_props->icon_theme_path = null_or_string_from_variant (value);
805
806 if (g_strcmp0 (new_props->icon_theme_path, item->current_props->icon_theme_path) != 0)
807 {
808 new_props->update_icon = TRUE;
809 }
810 }
811 else
812 if (g_strcmp0 (name, "IconName") == 0)
813 {
814 new_props->icon_name = null_or_string_from_variant (value);
815 if (g_strcmp0 (new_props->icon_name, item->current_props->icon_name) != 0)
816 {
817 new_props->update_icon = TRUE;
818 }
819 }
820 else
821 if (g_strcmp0 (name, "IconPixmap") == 0)
822 {
823 new_props->icon_surface = get_icon_surface (item, value, &new_props->icon_md5);
824
825 if (g_strcmp0 (new_props->icon_md5, item->current_props->icon_md5) != 0)
826 {
827 new_props->update_icon = TRUE;
828 }
829 }
830 else
831 if (g_strcmp0 (name, "AttentionIconName") == 0)
832 {
833 new_props->attention_icon_name = null_or_string_from_variant (value);
834
835 if (g_strcmp0 (new_props->attention_icon_name, item->current_props->attention_icon_name) != 0)
836 {
837 new_props->update_icon = TRUE;
838 }
839 }
840 else
841 if (g_strcmp0 (name, "AttentionIconPixmap") == 0)
842 {
843 new_props->attention_icon_surface = get_icon_surface (item, value, &new_props->attention_icon_md5);
844
845 if (g_strcmp0 (new_props->attention_icon_md5, item->current_props->attention_icon_md5) != 0)
846 {
847 new_props->update_icon = TRUE;
848 }
849 }
850 else
851 if (g_strcmp0 (name, "OverlayIconName") == 0)
852 {
853 new_props->overlay_icon_name = null_or_string_from_variant (value);
854
855 if (g_strcmp0 (new_props->overlay_icon_name, item->current_props->overlay_icon_name) != 0)
856 {
857 new_props->update_icon = TRUE;
858 }
859 }
860 else
861 if (g_strcmp0 (name, "OverlayIconPixmap") == 0)
862 {
863 new_props->overlay_icon_surface = get_icon_surface (item, value, &new_props->overlay_icon_md5);
864
865 if (g_strcmp0 (new_props->overlay_icon_md5, item->current_props->overlay_icon_md5) != 0)
866 {
867 new_props->update_icon = TRUE;
868 }
869 }
870 }
871
872 g_variant_iter_free (iter);
873 g_variant_unref (properties);
874
875 if (new_props->update_status)
876 {
877 update_status (item, new_props);
878 }
879
880 if (new_props->update_tooltip)
881 {
882 update_tooltip (item, new_props);
883 }
884
885 if (new_props->update_menu)
886 {
887 update_menu (item, new_props);
888 }
889
890 if (new_props->update_icon)
891 {
892 update_icon (item, new_props);
893 }
894
895 props_free (item->current_props);
896 item->current_props = new_props;
897 }
898
899 static gboolean
900 update_all_properties (gpointer data)
901 {
902 SnItem *item = SN_ITEM (data);
903
904 g_dbus_proxy_call (item->prop_proxy,
905 "GetAll",
906 g_variant_new ("(s)",
907 g_dbus_proxy_get_interface_name (item->sn_item_proxy)),
908 G_DBUS_CALL_FLAGS_NONE,
909 5 * 1000,
910 item->cancellable,
911 get_all_properties_callback,
912 item);
913
914 item->update_properties_timeout = 0;
915
916 return G_SOURCE_REMOVE;
917 }
918
919 static void
920 queue_update_properties (SnItem *item, gboolean force)
921 {
922 if (item->update_properties_timeout > 0)
923 {
924 g_source_remove (item->update_properties_timeout);
925 }
926
927 if (force)
928 {
929 props_free (item->current_props);
930 item->current_props = g_slice_new0 (SnItemPropertiesResult);
931 }
932
933 item->update_properties_timeout = g_timeout_add (10, G_SOURCE_FUNC (update_all_properties), item);
697934 }
698935
699936 static void
712949
713950 if (g_strcmp0 (signal_name, "NewIcon") == 0 ||
714951 g_strcmp0 (signal_name, "NewAttentionIcon") == 0 ||
715 g_strcmp0 (signal_name, "NewOverlayIcon") == 0)
716 {
717 update_icon (item);
718 }
719 else
720 if (g_strcmp0 (signal_name, "NewStatus") == 0)
721 {
722 update_status (item); // This will update_icon(item) also.
723 }
724 else
725 if (g_strcmp0 (signal_name, "NewMenu") == 0)
726 {
727 update_menu (item);
728 }
729 else
730 if (g_strcmp0 (signal_name, "NewToolTip") ||
731 g_strcmp0 (signal_name, "NewTitle"))
732 {
733 update_tooltip (item);
952 g_strcmp0 (signal_name, "NewOverlayIcon") == 0 ||
953 g_strcmp0 (signal_name, "NewToolTip") == 0 ||
954 g_strcmp0 (signal_name, "NewTitle") == 0 ||
955 g_strcmp0 (signal_name, "NewStatus") == 0 ||
956 g_strcmp0 (signal_name, "NewMenu") == 0)
957 {
958 queue_update_properties (item, FALSE);
734959 }
735960 }
736961
8251050 return;
8261051 }
8271052
828 update_icon (item);
829 update_status (item);
830 update_icon (item);
831 update_tooltip (item);
1053 queue_update_properties (item, TRUE);
8321054 }
8331055
8341056 static void
8351057 assign_sortable_name (SnItem *item,
836 XAppStatusIcon *status_icon)
837 {
838 gchar *init_name, *normalized, *sortable_name;
1058 const gchar *title)
1059 {
1060 gchar *init_name, *normalized;
1061 gchar *sortable_name, *old_sortable_name;
8391062
8401063 init_name = sn_item_interface_dup_id (SN_ITEM_INTERFACE (item->sn_item_proxy));
8411064
842 if (init_name == NULL)
843 {
844 init_name = get_string_property (item, "Title");
1065 if (init_name == NULL && title != NULL)
1066 {
1067 init_name = g_strdup (title);
1068 }
1069 else
1070 {
1071 init_name = g_strdup (g_dbus_proxy_get_name (G_DBUS_PROXY (item->sn_item_proxy)));
8451072 }
8461073
8471074 normalized = g_utf8_normalize (init_name,
8501077
8511078 sortable_name = g_utf8_strdown (normalized, -1);
8521079
853 g_debug ("Sort name for %s is '%s'", g_dbus_proxy_get_name (G_DBUS_PROXY (item->sn_item_proxy)), sortable_name);
854 xapp_status_icon_set_name (status_icon, sortable_name);
855
856 item->sortable_name = sortable_name;
857
8581080 g_free (init_name);
8591081 g_free (normalized);
1082
1083 if (g_strcmp0 (sortable_name, item->sortable_name) == 0)
1084 {
1085 g_free (sortable_name);
1086 return;
1087 }
1088
1089 g_debug ("Sort name for '%s' is '%s'",
1090 g_dbus_proxy_get_name (G_DBUS_PROXY (item->sn_item_proxy)),
1091 sortable_name);
1092
1093 xapp_status_icon_set_name (item->status_icon, sortable_name);
1094
1095 old_sortable_name = item->sortable_name;
1096 item->sortable_name = sortable_name;
1097 g_free (old_sortable_name);
1098
1099 update_conditionals (item);
8601100 }
8611101
8621102 static void
8721112
8731113 if (error != NULL)
8741114 {
875 g_printerr ("Could not get prop proxy: %s\n", error->message);
876 g_error_free (error);
877 return;
1115 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1116 {
1117 g_critical ("Could not get property proxy for %s: %s\n",
1118 g_dbus_proxy_get_name (item->sn_item_proxy),
1119 error->message);
1120 g_error_free (error);
1121 return;
1122 }
8781123 }
8791124
8801125 g_signal_connect (item->sn_item_proxy,
8841129
8851130 item->status_icon = xapp_status_icon_new ();
8861131
887 json = g_strdup_printf ("{ 'highlight-both-menus': %s }", item->is_ai ? "true" : "false");
1132 json = g_strdup_printf ("{ \"highlight-both-menus\": %s }", item->is_ai ? "true" : "false");
8881133 xapp_status_icon_set_metadata (item->status_icon, json);
8891134 g_free (json);
8901135
8941139 g_signal_connect (item->status_icon, "scroll-event", G_CALLBACK (xapp_icon_scroll), item);
8951140 g_signal_connect (item->status_icon, "state-changed", G_CALLBACK (xapp_icon_state_changed), item);
8961141
897 assign_sortable_name (item, item->status_icon);
898
899 item->should_activate = should_activate (item);
900 item->should_replace_tooltip = should_replace_tooltip (item);
901
902 update_status (item);
903 update_menu (item);
904 update_tooltip (item);
905 update_icon (item);
1142 assign_sortable_name (item, NULL);
1143 update_conditionals (item);
1144
1145 queue_update_properties (item, TRUE);
9061146 }
9071147
9081148 static void
9141154 g_dbus_proxy_get_name (item->sn_item_proxy),
9151155 g_dbus_proxy_get_object_path (item->sn_item_proxy),
9161156 "org.freedesktop.DBus.Properties",
917 NULL,
1157 item->cancellable,
9181158 property_proxy_acquired,
9191159 item);
9201160 }
9271167
9281168 item->sn_item_proxy = sn_item_proxy;
9291169 item->is_ai = is_ai;
1170 item->cancellable = g_cancellable_new ();
9301171
9311172 initialize_item (item);
9321173 return item;
9331174 }
9341175
935 void
936 sn_item_update_menus (SnItem *item)
937 {
938 update_menu (item);
939 }
1717
1818 SnWatcherInterface *skeleton;
1919 GDBusConnection *connection;
20 GCancellable *cancellable;
2021
2122 guint owner_id;
2223 guint name_listener_id;
298299 typedef struct
299300 {
300301 XAppSnWatcher *watcher;
302 GDBusMethodInvocation *invocation;
301303 gchar *key;
302304 gchar *path;
303305 gchar *bus_name;
312314 NewSnProxyData *data = (NewSnProxyData *) user_data;
313315 XAppSnWatcher *watcher = data->watcher;
314316 SnItem *item;
317 gpointer stolen_ptr;
315318 GError *error = NULL;
316319
317320 SnItemInterface *proxy;
320323
321324 if (error != NULL)
322325 {
323 g_debug ("Could not create new status notifier proxy item for item at %s: %s",
324 data->bus_name, error->message);
326 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
327 {
328 g_debug ("Could not create new status notifier proxy item for item at %s: %s",
329 data->bus_name, error->message);
330 }
331
332 g_hash_table_steal_extended (watcher->items,
333 data->key,
334 &stolen_ptr,
335 NULL);
336 g_free (stolen_ptr);
337
338 g_dbus_method_invocation_take_error (data->invocation, error);
325339 return;
326340 }
327341
328342 item = sn_item_new ((GDBusProxy *) proxy,
329343 g_str_has_prefix (data->path, APPINDICATOR_PATH_PREFIX));
330344
345 g_hash_table_steal_extended (watcher->items,
346 data->key,
347 &stolen_ptr,
348 NULL);
349
331350 g_hash_table_insert (watcher->items,
332 g_strdup (data->key),
351 stolen_ptr,
333352 item);
334353
335354 update_published_items (watcher);
336355
337356 sn_watcher_interface_emit_status_notifier_item_registered (watcher->skeleton,
338357 data->service);
358
359 sn_watcher_interface_complete_register_status_notifier_item (watcher->skeleton,
360 data->invocation);
339361
340362 g_free (data->key);
341363 g_free (data->path);
342364 g_free (data->bus_name);
343365 g_free (data->service);
366 g_object_unref (data->invocation);
344367 g_slice_free (NewSnProxyData, data);
345368 }
346369
350373 const gchar* service,
351374 XAppSnWatcher *watcher)
352375 {
353 SnItem *item;
354376 GError *error;
355377 const gchar *sender;
356378 g_autofree gchar *key = NULL, *bus_name = NULL, *path = NULL;
367389 return FALSE;
368390 }
369391
370 item = g_hash_table_lookup (watcher->items, key);
371
372 if (item == NULL)
392 if (!g_hash_table_contains (watcher->items, (const gchar *) key))
373393 {
374394 NewSnProxyData *data;
375395 error = NULL;
376 g_debug ("Key: '%s'", key);
396 // g_debug ("Key: '%s'", key);
377397
378398 data = g_slice_new0 (NewSnProxyData);
379399 data->watcher = watcher;
381401 data->path = g_strdup (path);
382402 data->bus_name = g_strdup (bus_name);
383403 data->service = g_strdup (service);
404 data->invocation = g_object_ref (invocation);
405
406 /* Save a place in the hash table, some programs re-register frequently,
407 * and we don't want the same app have two registrations here. */
408 g_hash_table_insert (watcher->items,
409 g_strdup (key),
410 NULL);
384411
385412 sn_item_interface_proxy_new (watcher->connection,
386413 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
387414 bus_name,
388415 path,
389 NULL,
416 watcher->cancellable,
390417 sn_item_proxy_new_completed,
391418 data);
392419 }
393
394 sn_watcher_interface_complete_register_status_notifier_item (watcher->skeleton,
395 invocation);
420 else
421 {
422 sn_watcher_interface_complete_register_status_notifier_item (watcher->skeleton,
423 invocation);
424 }
396425
397426 return TRUE;
398427 }
445474 }
446475
447476 static void
448 update_item_menus (const gchar *key,
449 gpointer item,
450 gpointer user_data)
451 {
452 sn_item_update_menus (SN_ITEM (item));
453 }
454
455 static void
456 whitelist_changed (XAppSnWatcher *watcher)
457 {
458 g_hash_table_foreach (watcher->items, (GHFunc) update_item_menus, NULL);
459 }
460
461 static void
462477 continue_startup (XAppSnWatcher *watcher)
463478 {
464479 g_debug ("Trying to acquire session bus connection");
486501 G_APPLICATION_CLASS (xapp_sn_watcher_parent_class)->startup (application);
487502
488503 xapp_settings = g_settings_new (STATUS_ICON_SCHEMA);
489 g_signal_connect_swapped (xapp_settings,
490 "changed::" WHITELIST_KEY,
491 G_CALLBACK (whitelist_changed),
492 watcher);
493504
494505 watcher->items = g_hash_table_new_full (g_str_hash, g_str_equal,
495506 g_free, g_object_unref);
499510 g_application_hold (application);
500511 g_application_release (application);
501512
513 watcher->cancellable = g_cancellable_new ();
514
502515 error = NULL;
503516
504517 watcher->connection = g_bus_get_sync (G_BUS_TYPE_SESSION,
565578 }
566579
567580 g_clear_object (&watcher->connection);
581 g_clear_object (&watcher->cancellable);
568582
569583 G_APPLICATION_CLASS (xapp_sn_watcher_parent_class)->shutdown (application);
570584 }