Codebase list xapp / 9c4d5e9
Make property-retrieval fully asynchronous. There's little consistency between apps that use the StatusNotifier interface. In many cases using qt5, code that was probably ok when using xembed to display tray icons ends up causing trouble using dbus with blocking and duplicate calls, and more, stranger things. In order to interact with the client application asynchronously, while avoiding a lot of extra complexity, use GetAll to fetch all properties at once, and act on any changed ones. Fix a couple of issues with the recently-added asynchronous proxy creation as well. Michael Webster 3 years ago
3 changed file(s) with 601 addition(s) and 364 deletion(s). Raw diff Collapse all Expand all
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
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 }
174250 }
175251
176252 return FALLBACK_ICON_SIZE;
177 }
178
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;
247253 }
248254
249255 static cairo_surface_t *
289295 }
290296 }
291297
292 static gboolean
293 process_pixmaps (SnItem *item,
294 GVariant *pixmaps,
295 gchar **image_path)
298 static cairo_surface_t *
299 get_icon_surface (SnItem *item,
300 GVariant *pixmaps,
301 gchar **md5)
296302 {
297303 GVariantIter *iter;
298304 cairo_surface_t *surface;
299305 gint width, height;
300306 gint largest_width, largest_height;
307 gsize largest_data_size;
301308 GVariant *byte_array_var;
302309 gconstpointer data;
303310 guchar *best_image_array = NULL;
304311
305312 largest_width = largest_height = 0;
306313
314 *md5 = NULL;
315
307316 g_variant_get (pixmaps, "a(iiay)", &iter);
308317
309318 if (iter == NULL)
310319 {
311 return FALSE;
320 return NULL;
312321 }
313322
314323 while (g_variant_iter_loop (iter, "(ii@ay)", &width, &height, &byte_array_var))
331340
332341 best_image_array = g_memdup (data, data_size);
333342
343 largest_data_size = data_size;
334344 largest_width = width;
335345 largest_height = height;
336346 }
338348 }
339349 }
340350
351 g_variant_iter_free (iter);
352
341353 if (best_image_array == NULL)
342354 {
343 g_warning ("No valid pixmaps found.");
344 return FALSE;
355 return NULL;
345356 }
346357
347358 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);
348360
349361 if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
350362 {
351363 cairo_surface_destroy (surface);
352 return FALSE;
353 }
354
355 item->last_png_path = item->png_path;
356
357 gchar *filename = g_strdup_printf ("xapp-tmp-%p-%d.png", item, get_icon_id (item));
358 gchar *save_filename = g_build_path ("/", g_get_tmp_dir (), filename, NULL);
359 g_free (filename);
360
361 cairo_status_t status = CAIRO_STATUS_SUCCESS;
362 status = cairo_surface_write_to_png (surface, save_filename);
363
364 if (status != CAIRO_STATUS_SUCCESS)
365 {
366 g_warning ("Failed to save png of status icon");
367 g_free (image_path);
368 cairo_surface_destroy (surface);
369 return FALSE;
370 }
371
372 *image_path = save_filename;
373 cairo_surface_destroy (surface);
374
375 return TRUE;
376 }
377
378 static void
379 set_icon_from_pixmap (SnItem *item)
380 {
381 GVariant *pixmaps = NULL;
382 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;
383380
384381 if (item->status == STATUS_ACTIVE)
385382 {
386 pixmaps = get_pixmap_property (item, "IconPixmap");
383 if (new_props->icon_surface)
384 {
385 surface = new_props->icon_surface;
386 }
387387 }
388388 else
389389 if (item->status == STATUS_NEEDS_ATTENTION)
390390 {
391 pixmaps = get_pixmap_property (item, "AttentionIconPixmap");
392
393 if (!pixmaps)
394 {
395 pixmaps = get_pixmap_property (item, "IconPixmap");
396 }
397 }
398
399 if (!pixmaps)
400 {
401 xapp_status_icon_set_icon_name (item->status_icon, "image-missing");
402 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 if (status != CAIRO_STATUS_SUCCESS)
409 {
410 g_warning ("Failed to save png of status icon");
411 }
412
413 xapp_status_icon_set_icon_name (item->status_icon, save_filename);
414 g_free (save_filename);
403415 return;
404416 }
405417
406 if (process_pixmaps (item, pixmaps, &image_path))
407 {
408 xapp_status_icon_set_icon_name (item->status_icon, image_path);
409 g_free (image_path);
410 }
411
412 g_variant_unref (pixmaps);
418 g_debug ("No pixmaps to use");
419 xapp_status_icon_set_icon_name (item->status_icon, "image-missing");
413420 }
414421
415422 static gchar *
416 get_icon_filename_from_theme (SnItem *item,
423 get_icon_filename_from_theme (SnItem *item,
417424 const gchar *theme_path,
418425 const gchar *icon_name)
419426 {
420427 GtkIconInfo *info;
421428 gchar *filename;
422429 const gchar *array[2];
430 gint host_icon_size;
423431
424432 array[0] = icon_name;
425433 array[1] = NULL;
426434
427435 // We have a theme path, but try the system theme first
428436 GtkIconTheme *theme = gtk_icon_theme_get_default ();
437 host_icon_size = get_icon_size (item);
429438
430439 info = gtk_icon_theme_choose_icon_for_scale (theme,
431440 array,
432 get_icon_size (item),
441 host_icon_size,
433442 lookup_ui_scale (),
434443 GTK_ICON_LOOKUP_FORCE_SVG | GTK_ICON_LOOKUP_FORCE_SYMBOLIC);
435444
442451
443452 info = gtk_icon_theme_choose_icon_for_scale (theme,
444453 array,
445 get_icon_size (item),
454 host_icon_size,
446455 lookup_ui_scale (),
447456 GTK_ICON_LOOKUP_FORCE_SVG | GTK_ICON_LOOKUP_FORCE_SYMBOLIC);
448457
460469 return filename;
461470 }
462471
463 static void
464 process_icon_name (SnItem *item,
465 const gchar *icon_theme_path,
466 const gchar *icon_name)
467 {
468 if (g_path_is_absolute (icon_name) || !icon_theme_path)
472 static gboolean
473 set_icon_name (SnItem *item,
474 const gchar *icon_theme_path,
475 const gchar *icon_name)
476 {
477 g_debug ("Checking for icon name for %s",
478 item->sortable_name);
479
480 if (icon_name == NULL)
481 {
482 return FALSE;
483 }
484
485 if (g_path_is_absolute (icon_name))
469486 {
470487 xapp_status_icon_set_icon_name (item->status_icon, icon_name);
488
489 return TRUE;
471490 }
472491 else
473492 {
477496 {
478497 xapp_status_icon_set_icon_name (item->status_icon, filename);
479498 g_free (filename);
480 }
481 else
482 {
483 xapp_status_icon_set_icon_name (item->status_icon, "image-missing");
484 }
485 }
486 }
487
488 static void
489 set_icon_name_or_path (SnItem *item,
490 const gchar *icon_theme_path,
491 const gchar *icon_name,
492 const gchar *att_icon_name,
493 const gchar *olay_icon_name)
499 return TRUE;
500 }
501 }
502
503 return FALSE;
504 }
505
506 static gboolean
507 set_icon_name_or_path (SnItem *item,
508 SnItemPropertiesResult *new_props)
494509 {
495510 const gchar *name_to_use = NULL;
496511
497512 if (item->status == STATUS_ACTIVE)
498513 {
499 if (icon_name)
500 {
501 name_to_use = icon_name;
514 if (new_props->icon_name)
515 {
516 name_to_use = new_props->icon_name;
502517 }
503518 }
504519 else
505520 if (item->status == STATUS_NEEDS_ATTENTION)
506521 {
507 if (att_icon_name)
508 {
509 name_to_use = att_icon_name;
522 if (new_props->attention_icon_name)
523 {
524 name_to_use = new_props->attention_icon_name;
510525 }
511526 else
512 if (icon_name)
513 {
514 name_to_use = icon_name;
515 }
516 }
517
518 if (name_to_use == NULL)
519 {
520 name_to_use = "image-missing";
521 }
522
523 process_icon_name (item, icon_theme_path, name_to_use);
524 }
525
526 static void
527 update_icon (SnItem *item)
528 {
529 gchar *icon_theme_path;
530 gchar *icon_name, *att_icon_name, *olay_icon_name;
531
532 icon_theme_path = get_string_property (item, "IconThemePath");
533 icon_name = get_string_property (item, "IconName");
534 att_icon_name = get_string_property (item, "AttentionIconName");
535 olay_icon_name = get_string_property (item, "OverlayIconName");
536
537 if (icon_name || att_icon_name || olay_icon_name)
538 {
539 // g_printerr ("icon name '%s' '%s' '%s'\n", icon_name, att_icon_name, olay_icon_name);
540 set_icon_name_or_path (item,
541 icon_theme_path,
542 icon_name,
543 att_icon_name,
544 olay_icon_name);
545 }
546 else
547 {
548 set_icon_from_pixmap (item);
549 }
550
551 g_free (icon_theme_path);
552 g_free (icon_name);
553 g_free (att_icon_name);
554 g_free (olay_icon_name);
555 }
556
557 static void
558 update_menu (SnItem *item)
559 {
560 gchar *menu_path;
561
527 if (new_props->icon_name)
528 {
529 name_to_use = new_props->icon_name;
530 }
531 }
532
533 return set_icon_name (item, new_props->icon_theme_path, name_to_use);
534 }
535
536 static void
537 update_icon (SnItem *item, SnItemPropertiesResult *new_props)
538 {
539 if (!set_icon_name_or_path (item, new_props))
540 {
541 set_icon_from_pixmap (item, new_props);
542 }
543 }
544
545 static void
546 update_menu (SnItem *item, SnItemPropertiesResult *new_props)
547 {
562548 g_clear_object (&item->menu);
563549
564550 xapp_status_icon_set_primary_menu (item->status_icon, NULL);
565551 xapp_status_icon_set_secondary_menu (item->status_icon, NULL);
566552
567 menu_path = get_string_property (item, "Menu");
568
569 if (menu_path == NULL)
553 if (new_props->menu_path == NULL)
570554 {
571555 return;
572556 }
573557
574 item->menu = GTK_WIDGET (dbusmenu_gtkmenu_new ((gchar *) g_dbus_proxy_get_name (item->sn_item_proxy), menu_path));
558 item->menu = GTK_WIDGET (dbusmenu_gtkmenu_new ((gchar *) g_dbus_proxy_get_name (item->sn_item_proxy),
559 new_props->menu_path));
575560 g_object_ref_sink (item->menu);
576561
562 g_debug ("New menu for '%s'", item->sortable_name);
563
577564 if (item->is_ai && !item->should_activate)
578565 {
579566 xapp_status_icon_set_primary_menu (item->status_icon, GTK_MENU (item->menu));
580567 }
581568
582569 xapp_status_icon_set_secondary_menu (item->status_icon, GTK_MENU (item->menu));
583
584 g_free (menu_path);
585570 }
586571
587572 static gchar *
608593 }
609594
610595 static void
611 update_tooltip (SnItem *item)
612 {
613 g_autoptr(GVariant) tt_var = NULL;
596 update_tooltip (SnItem *item, SnItemPropertiesResult *new_props)
597 {
598 if (new_props->title)
599 {
600 assign_sortable_name (item, new_props->title);
601 }
614602
615603 if (!item->should_replace_tooltip)
616604 {
617 tt_var = get_property (item, "ToolTip");
618 }
619
620 if (tt_var)
621 {
622 const gchar *type_str;
623 type_str = g_variant_get_type_string (tt_var);
624
625 if (g_strcmp0 (type_str, "(sa(iiay)ss)") == 0)
626 {
627 const gchar *tooltip_title, *tooltip_body;
628
629 g_variant_get (tt_var, "(sa(iiay)&s&s)", NULL, NULL, &tooltip_title, &tooltip_body);
630
631 if (g_strcmp0 (tooltip_title, "") != 0)
632 {
633
634 if (g_strcmp0 (tooltip_body, "") != 0)
635 {
636 gchar *text;
637 text = g_strdup_printf ("%s\n%s", tooltip_title, tooltip_body);
638
639 xapp_status_icon_set_tooltip_text (item->status_icon, text);
640 g_debug ("Tooltip text from ToolTip: %s", text);
641
642 g_free (text);
643 }
644 else
645 {
646 g_debug ("Tooltip text from ToolTip: %s", tooltip_title);
647 xapp_status_icon_set_tooltip_text (item->status_icon, tooltip_title);
648 }
649
650 return;
651 }
652 }
653 }
654
655 gchar *title_string;
656 title_string = get_string_property (item, "Title");
657
658 if (title_string != NULL)
605 if (new_props->tooltip_heading != NULL)
606 {
607 if (new_props->tooltip_body != NULL)
608 {
609 gchar *text;
610 text = g_strdup_printf ("%s\n%s",
611 new_props->tooltip_heading,
612 new_props->tooltip_body);
613
614 xapp_status_icon_set_tooltip_text (item->status_icon, text);
615 g_debug ("Tooltip text for '%s' from ToolTip: %s",
616 item->sortable_name,
617 text);
618
619 g_free (text);
620 }
621 else
622 {
623 g_debug ("Tooltip text for '%s' from ToolTip: %s",
624 item->sortable_name,
625 new_props->tooltip_heading);
626
627 xapp_status_icon_set_tooltip_text (item->status_icon, new_props->tooltip_heading);
628 }
629
630 return;
631 }
632 }
633
634 if (new_props->title != NULL)
659635 {
660636 gchar *capped_string;
661637
662 capped_string = capitalize (title_string);
638 capped_string = capitalize (new_props->title);
663639 xapp_status_icon_set_tooltip_text (item->status_icon, capped_string);
664 g_debug ("Tooltip text from Title: %s", capped_string);
665
666 g_free (title_string);
640
641 g_debug ("Tooltip text for '%s' from Title: %s",
642 item->sortable_name,
643 capped_string);
644
667645 g_free (capped_string);
668646 return;
669647 }
672650 }
673651
674652 static void
675 update_status (SnItem *item)
676 {
677 Status old_status;
678 gchar *status;
679
680 old_status = item->status;
681
682 status = get_string_property (item, "Status");
683
684 if (g_strcmp0 (status, "Passive") == 0)
653 update_status (SnItem *item, SnItemPropertiesResult *new_props)
654 {
655 if (g_strcmp0 (new_props->status, "Passive") == 0)
685656 {
686657 item->status = STATUS_PASSIVE;
687658 xapp_status_icon_set_visible (item->status_icon, FALSE);
688659 }
689 else if (g_strcmp0 (status, "NeedsAttention") == 0)
660 else
661 if (g_strcmp0 (new_props->status, "NeedsAttention") == 0)
690662 {
691663 item->status = STATUS_NEEDS_ATTENTION;
692664 xapp_status_icon_set_visible (item->status_icon, TRUE);
697669 xapp_status_icon_set_visible (item->status_icon, TRUE);
698670 }
699671
700 g_free (status);
701
702 if (old_status != item->status)
703 {
704 update_icon (item);
705 }
672 g_debug ("Status for '%s' is now '%s'", item->sortable_name, new_props->status);
673 }
674
675 static gchar *
676 null_or_string_from_string (const gchar *str)
677 {
678 if (str != NULL && strlen(str) > 0)
679 {
680 return g_strdup (str);
681 }
682 else
683 {
684 return NULL;
685 }
686 }
687
688 static gchar *
689 null_or_string_from_variant (GVariant *variant)
690 {
691 const gchar *str;
692
693 str = g_variant_get_string (variant, NULL);
694
695 return null_or_string_from_string (str);
696 }
697
698 static void
699 get_all_properties_callback (GObject *source_object,
700 GAsyncResult *res,
701 gpointer user_data)
702 {
703 SnItem *item = SN_ITEM (user_data);
704 SnItemPropertiesResult *new_props;
705 GError *error = NULL;
706 GVariant *properties;
707 GVariantIter *iter = NULL;
708 const gchar *name;
709 GVariant *value;
710
711 properties = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error);
712
713 if (error != NULL)
714 {
715 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
716 {
717 g_critical ("Could get propertyies for %s: %s\n",
718 g_dbus_proxy_get_name (item->sn_item_proxy),
719 error->message);
720 }
721
722 g_error_free (error);
723 return;
724 }
725
726 if (properties == NULL)
727 {
728 return;
729 }
730
731 new_props = g_slice_new0 (SnItemPropertiesResult);
732
733 g_variant_get (properties, "(a{sv})", &iter);
734
735 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
736 {
737 if (g_strcmp0 (name, "Title") == 0)
738 {
739 new_props->title = null_or_string_from_variant (value);
740
741 if (g_strcmp0 (new_props->title, item->current_props->title) != 0)
742 {
743 new_props->update_tooltip = TRUE;
744 }
745 }
746 else
747 if (g_strcmp0 (name, "Status") == 0)
748 {
749 new_props->status = null_or_string_from_variant (value);
750
751 if (g_strcmp0 (new_props->status, item->current_props->status) != 0)
752 {
753 new_props->update_status = TRUE;
754 }
755 }
756 else
757 if (g_strcmp0 (name, "ToolTip") == 0)
758 {
759 const gchar *ts;
760
761 ts = g_variant_get_type_string (value);
762
763 if (g_strcmp0 (ts, "(sa(iiay)ss)") == 0)
764 {
765 gchar *heading, *body;
766
767 g_variant_get (value, "(sa(iiay)ss)", NULL, NULL,
768 &heading,
769 &body);
770
771 new_props->tooltip_heading = null_or_string_from_string (heading);
772 new_props->tooltip_body = null_or_string_from_string (body);
773
774 g_free (heading);
775 g_free (body);
776 }
777 else
778 if (g_strcmp0 (ts, "s") == 0)
779 {
780 new_props->tooltip_body = null_or_string_from_variant (value);
781 }
782
783 if (g_strcmp0 (new_props->tooltip_heading, item->current_props->tooltip_heading) != 0 ||
784 g_strcmp0 (new_props->tooltip_body, item->current_props->tooltip_body) != 0)
785 {
786 new_props->update_tooltip = TRUE;
787 }
788 }
789 else
790 if (g_strcmp0 (name, "Menu") == 0)
791 {
792 new_props->menu_path = null_or_string_from_variant (value);
793
794 if (g_strcmp0 (new_props->menu_path, item->current_props->menu_path) != 0)
795 {
796 new_props->update_menu = TRUE;
797 }
798 }
799 else
800 if (g_strcmp0 (name, "IconThemePath") == 0)
801 {
802 new_props->icon_theme_path = null_or_string_from_variant (value);
803
804 if (g_strcmp0 (new_props->icon_theme_path, item->current_props->icon_theme_path) != 0)
805 {
806 new_props->update_icon = TRUE;
807 }
808 }
809 else
810 if (g_strcmp0 (name, "IconName") == 0)
811 {
812 new_props->icon_name = null_or_string_from_variant (value);
813 if (g_strcmp0 (new_props->icon_name, item->current_props->icon_name) != 0)
814 {
815 new_props->update_icon = TRUE;
816 }
817 }
818 else
819 if (g_strcmp0 (name, "IconPixmap") == 0)
820 {
821 new_props->icon_surface = get_icon_surface (item, value, &new_props->icon_md5);
822
823 if (g_strcmp0 (new_props->icon_md5, item->current_props->icon_md5) != 0)
824 {
825 new_props->update_icon = TRUE;
826 }
827 }
828 else
829 if (g_strcmp0 (name, "AttentionIconName") == 0)
830 {
831 new_props->attention_icon_name = null_or_string_from_variant (value);
832
833 if (g_strcmp0 (new_props->attention_icon_name, item->current_props->attention_icon_name) != 0)
834 {
835 new_props->update_icon = TRUE;
836 }
837 }
838 else
839 if (g_strcmp0 (name, "AttentionIconPixmap") == 0)
840 {
841 new_props->attention_icon_surface = get_icon_surface (item, value, &new_props->attention_icon_md5);
842
843 if (g_strcmp0 (new_props->attention_icon_md5, item->current_props->attention_icon_md5) != 0)
844 {
845 new_props->update_icon = TRUE;
846 }
847 }
848 else
849 if (g_strcmp0 (name, "OverlayIconName") == 0)
850 {
851 new_props->overlay_icon_name = null_or_string_from_variant (value);
852
853 if (g_strcmp0 (new_props->overlay_icon_name, item->current_props->overlay_icon_name) != 0)
854 {
855 new_props->update_icon = TRUE;
856 }
857 }
858 else
859 if (g_strcmp0 (name, "OverlayIconPixmap") == 0)
860 {
861 new_props->overlay_icon_surface = get_icon_surface (item, value, &new_props->overlay_icon_md5);
862
863 if (g_strcmp0 (new_props->overlay_icon_md5, item->current_props->overlay_icon_md5) != 0)
864 {
865 new_props->update_icon = TRUE;
866 }
867 }
868 }
869
870 g_variant_iter_free (iter);
871 g_variant_unref (properties);
872
873 if (new_props->update_status)
874 {
875 update_status (item, new_props);
876 }
877
878 if (new_props->update_tooltip)
879 {
880 update_tooltip (item, new_props);
881 }
882
883 if (new_props->update_menu)
884 {
885 update_menu (item, new_props);
886 }
887
888 if (new_props->update_icon)
889 {
890 update_icon (item, new_props);
891 }
892
893 props_free (item->current_props);
894 item->current_props = new_props;
895 }
896
897 static gboolean
898 update_all_properties (gpointer data)
899 {
900 SnItem *item = SN_ITEM (data);
901
902 g_dbus_proxy_call (item->prop_proxy,
903 "GetAll",
904 g_variant_new ("(s)",
905 g_dbus_proxy_get_interface_name (item->sn_item_proxy)),
906 G_DBUS_CALL_FLAGS_NONE,
907 5 * 1000,
908 item->cancellable,
909 get_all_properties_callback,
910 item);
911
912 item->update_properties_timeout = 0;
913
914 return G_SOURCE_REMOVE;
915 }
916
917 static void
918 queue_update_properties (SnItem *item, gboolean force)
919 {
920 if (item->update_properties_timeout > 0)
921 {
922 g_source_remove (item->update_properties_timeout);
923 }
924
925 if (force)
926 {
927 props_free (item->current_props);
928 item->current_props = g_slice_new0 (SnItemPropertiesResult);
929 }
930
931 item->update_properties_timeout = g_timeout_add (10, G_SOURCE_FUNC (update_all_properties), item);
706932 }
707933
708934 static void
721947
722948 if (g_strcmp0 (signal_name, "NewIcon") == 0 ||
723949 g_strcmp0 (signal_name, "NewAttentionIcon") == 0 ||
724 g_strcmp0 (signal_name, "NewOverlayIcon") == 0)
725 {
726 update_icon (item);
727 }
728 else
729 if (g_strcmp0 (signal_name, "NewStatus") == 0)
730 {
731 update_status (item); // This will update_icon(item) also.
732 }
733 else
734 if (g_strcmp0 (signal_name, "NewMenu") == 0)
735 {
736 update_menu (item);
737 }
738 else
739 if (g_strcmp0 (signal_name, "NewToolTip") ||
740 g_strcmp0 (signal_name, "NewTitle"))
741 {
742 update_tooltip (item);
950 g_strcmp0 (signal_name, "NewOverlayIcon") == 0 ||
951 g_strcmp0 (signal_name, "NewToolTip") == 0 ||
952 g_strcmp0 (signal_name, "NewTitle") == 0 ||
953 g_strcmp0 (signal_name, "NewStatus") == 0 ||
954 g_strcmp0 (signal_name, "NewMenu") == 0)
955 {
956 queue_update_properties (item, FALSE);
743957 }
744958 }
745959
8341048 return;
8351049 }
8361050
837 update_icon (item);
838 update_status (item);
839 update_icon (item);
840 update_tooltip (item);
1051 queue_update_properties (item, TRUE);
8411052 }
8421053
8431054 static void
8441055 assign_sortable_name (SnItem *item,
845 XAppStatusIcon *status_icon)
846 {
847 gchar *init_name, *normalized, *sortable_name;
1056 const gchar *title)
1057 {
1058 gchar *init_name, *normalized;
1059 gchar *sortable_name, *old_sortable_name;
8481060
8491061 init_name = sn_item_interface_dup_id (SN_ITEM_INTERFACE (item->sn_item_proxy));
8501062
851 if (init_name == NULL)
852 {
853 init_name = get_string_property (item, "Title");
1063 if (init_name == NULL && title != NULL)
1064 {
1065 init_name = g_strdup (title);
1066 }
1067 else
1068 {
1069 init_name = g_strdup (g_dbus_proxy_get_name (G_DBUS_PROXY (item->sn_item_proxy)));
8541070 }
8551071
8561072 normalized = g_utf8_normalize (init_name,
8591075
8601076 sortable_name = g_utf8_strdown (normalized, -1);
8611077
862 g_debug ("Sort name for %s is '%s'", g_dbus_proxy_get_name (G_DBUS_PROXY (item->sn_item_proxy)), sortable_name);
863 xapp_status_icon_set_name (status_icon, sortable_name);
864
865 item->sortable_name = sortable_name;
866
8671078 g_free (init_name);
8681079 g_free (normalized);
1080
1081 if (g_strcmp0 (sortable_name, item->sortable_name) == 0)
1082 {
1083 g_free (sortable_name);
1084 return;
1085 }
1086
1087 g_debug ("Sort name for '%s' is '%s'",
1088 g_dbus_proxy_get_name (G_DBUS_PROXY (item->sn_item_proxy)),
1089 sortable_name);
1090
1091 xapp_status_icon_set_name (item->status_icon, sortable_name);
1092
1093 old_sortable_name = item->sortable_name;
1094 item->sortable_name = sortable_name;
1095 g_free (old_sortable_name);
1096
1097 update_conditionals (item);
8691098 }
8701099
8711100 static void
8811110
8821111 if (error != NULL)
8831112 {
884 g_printerr ("Could not get prop proxy: %s\n", error->message);
885 g_error_free (error);
886 return;
1113 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1114 {
1115 g_critical ("Could not get property proxy for %s: %s\n",
1116 g_dbus_proxy_get_name (item->sn_item_proxy),
1117 error->message);
1118 g_error_free (error);
1119 return;
1120 }
8871121 }
8881122
8891123 g_signal_connect (item->sn_item_proxy,
8931127
8941128 item->status_icon = xapp_status_icon_new ();
8951129
896 json = g_strdup_printf ("{ 'highlight-both-menus': %s }", item->is_ai ? "true" : "false");
1130 json = g_strdup_printf ("{ \"highlight-both-menus\": %s }", item->is_ai ? "true" : "false");
8971131 xapp_status_icon_set_metadata (item->status_icon, json);
8981132 g_free (json);
8991133
9031137 g_signal_connect (item->status_icon, "scroll-event", G_CALLBACK (xapp_icon_scroll), item);
9041138 g_signal_connect (item->status_icon, "state-changed", G_CALLBACK (xapp_icon_state_changed), item);
9051139
906 assign_sortable_name (item, item->status_icon);
907
908 item->should_activate = should_activate (item);
909 item->should_replace_tooltip = should_replace_tooltip (item);
910
911 update_status (item);
912 update_menu (item);
913 update_tooltip (item);
914 update_icon (item);
1140 assign_sortable_name (item, NULL);
1141 update_conditionals (item);
1142
1143 queue_update_properties (item, TRUE);
9151144 }
9161145
9171146 static void
9231152 g_dbus_proxy_get_name (item->sn_item_proxy),
9241153 g_dbus_proxy_get_object_path (item->sn_item_proxy),
9251154 "org.freedesktop.DBus.Properties",
926 NULL,
1155 item->cancellable,
9271156 property_proxy_acquired,
9281157 item);
9291158 }
9361165
9371166 item->sn_item_proxy = sn_item_proxy;
9381167 item->is_ai = is_ai;
1168 item->cancellable = g_cancellable_new ();
9391169
9401170 initialize_item (item);
9411171 return item;
9421172 }
9431173
944 void
945 sn_item_update_menus (SnItem *item)
946 {
947 update_menu (item);
948 }
1717
1818 SnWatcherInterface *skeleton;
1919 GDBusConnection *connection;
20 GCancellable *cancellable;
2021
2122 guint owner_id;
2223 guint name_listener_id;
313314 NewSnProxyData *data = (NewSnProxyData *) user_data;
314315 XAppSnWatcher *watcher = data->watcher;
315316 SnItem *item;
317 gpointer stolen_ptr;
316318 GError *error = NULL;
317319
318320 SnItemInterface *proxy;
321323
322324 if (error != NULL)
323325 {
324 g_debug ("Could not create new status notifier proxy item for item at %s: %s",
325 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);
326339 return;
327340 }
328341
329342 item = sn_item_new ((GDBusProxy *) proxy,
330343 g_str_has_prefix (data->path, APPINDICATOR_PATH_PREFIX));
331344
345 g_hash_table_steal_extended (watcher->items,
346 data->key,
347 &stolen_ptr,
348 NULL);
349
332350 g_hash_table_insert (watcher->items,
333 g_strdup (data->key),
351 stolen_ptr,
334352 item);
335353
336354 update_published_items (watcher);
355373 const gchar* service,
356374 XAppSnWatcher *watcher)
357375 {
358 SnItem *item;
359376 GError *error;
360377 const gchar *sender;
361378 g_autofree gchar *key = NULL, *bus_name = NULL, *path = NULL;
372389 return FALSE;
373390 }
374391
375 item = g_hash_table_lookup (watcher->items, key);
376
377 if (item == NULL)
392 if (!g_hash_table_contains (watcher->items, (const gchar *) key))
378393 {
379394 NewSnProxyData *data;
380395 error = NULL;
381 g_debug ("Key: '%s'", key);
396 // g_debug ("Key: '%s'", key);
382397
383398 data = g_slice_new0 (NewSnProxyData);
384399 data->watcher = watcher;
388403 data->service = g_strdup (service);
389404 data->invocation = g_object_ref (invocation);
390405
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);
411
391412 sn_item_interface_proxy_new (watcher->connection,
392413 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
393414 bus_name,
394415 path,
395 NULL,
416 watcher->cancellable,
396417 sn_item_proxy_new_completed,
397418 data);
419 }
420 else
421 {
422 sn_watcher_interface_complete_register_status_notifier_item (watcher->skeleton,
423 invocation);
398424 }
399425
400426 return TRUE;
448474 }
449475
450476 static void
451 update_item_menus (const gchar *key,
452 gpointer item,
453 gpointer user_data)
454 {
455 sn_item_update_menus (SN_ITEM (item));
456 }
457
458 static void
459 whitelist_changed (XAppSnWatcher *watcher)
460 {
461 g_hash_table_foreach (watcher->items, (GHFunc) update_item_menus, NULL);
462 }
463
464 static void
465477 continue_startup (XAppSnWatcher *watcher)
466478 {
467479 g_debug ("Trying to acquire session bus connection");
489501 G_APPLICATION_CLASS (xapp_sn_watcher_parent_class)->startup (application);
490502
491503 xapp_settings = g_settings_new (STATUS_ICON_SCHEMA);
492 g_signal_connect_swapped (xapp_settings,
493 "changed::" WHITELIST_KEY,
494 G_CALLBACK (whitelist_changed),
495 watcher);
496504
497505 watcher->items = g_hash_table_new_full (g_str_hash, g_str_equal,
498506 g_free, g_object_unref);
502510 g_application_hold (application);
503511 g_application_release (application);
504512
513 watcher->cancellable = g_cancellable_new ();
514
505515 error = NULL;
506516
507517 watcher->connection = g_bus_get_sync (G_BUS_TYPE_SESSION,
568578 }
569579
570580 g_clear_object (&watcher->connection);
581 g_clear_object (&watcher->cancellable);
571582
572583 G_APPLICATION_CLASS (xapp_sn_watcher_parent_class)->shutdown (application);
573584 }