Eliminate all appindicator-specific code from xapp-status-icon.c,
Add a whitelist for xapp-sn-watcher to list ai icons that should
left-click to activate.
Michael Webster
4 years ago
308 | 308 | return do_activate; |
309 | 309 | } |
310 | 310 | |
311 | static gboolean | |
312 | appindicator_can_activate (XAppStatusIcon *icon) | |
313 | { | |
314 | gpointer ptr; | |
315 | gboolean has_activate = FALSE; | |
316 | ||
317 | ptr = g_object_get_data (G_OBJECT (icon), "app-indicator-has-secondary-activate"); | |
318 | ||
319 | if (ptr && GPOINTER_TO_INT (ptr)) | |
320 | { | |
321 | has_activate = TRUE; | |
322 | } | |
323 | ||
324 | return has_activate; | |
325 | } | |
326 | ||
327 | static gboolean | |
328 | handle_appindicator_button_press (XAppStatusIcon *icon, | |
329 | guint button, | |
330 | guint _time) | |
331 | { | |
332 | if (g_object_get_data (G_OBJECT (icon), "app-indicator")) | |
333 | { | |
334 | if (button == GDK_BUTTON_MIDDLE || (button == GDK_BUTTON_PRIMARY && appindicator_can_activate (icon))) | |
335 | { | |
336 | g_debug ("XAppStatusIcon: sending activate for left- or middle-click event (libappindicator)"); | |
337 | ||
338 | g_signal_emit (icon, signals[ACTIVATE], 0, | |
339 | button, | |
340 | _time); | |
341 | } | |
342 | else | |
343 | { | |
344 | g_debug ("XAppStatusIcon: GtkStatusIcon (app-indicator) ignoring %s button press.", button_to_str (button)); | |
345 | } | |
346 | ||
347 | return TRUE; | |
348 | } | |
349 | ||
350 | return FALSE; | |
351 | } | |
352 | ||
353 | 311 | static GtkWidget * |
354 | 312 | get_menu_to_use (XAppStatusIcon *icon, |
355 | 313 | guint button) |
359 | 317 | switch (button) |
360 | 318 | { |
361 | 319 | case GDK_BUTTON_PRIMARY: |
362 | if (g_object_get_data (G_OBJECT (icon), "app-indicator")) | |
363 | { | |
364 | if (!appindicator_can_activate (icon)) | |
365 | { | |
366 | menu_to_use = icon->priv->secondary_menu; | |
367 | break; | |
368 | } | |
369 | } | |
370 | ||
371 | 320 | menu_to_use = icon->priv->primary_menu; |
372 | 321 | break; |
373 | 322 | case GDK_BUTTON_SECONDARY: |
397 | 346 | g_dbus_method_invocation_get_sender (invocation), |
398 | 347 | x, y, button_to_str (button), _time, panel_position_to_str (panel_position)); |
399 | 348 | |
400 | if (!handle_appindicator_button_press (icon, button, _time)) | |
349 | if (should_send_activate (icon, button)) | |
401 | 350 | { |
402 | if (should_send_activate (icon, button)) | |
403 | { | |
404 | g_debug ("XAppStatusIcon: native sending 'activate' for %s button", button_to_str (button)); | |
405 | g_signal_emit (icon, signals[ACTIVATE], 0, | |
406 | button, | |
407 | _time); | |
408 | } | |
351 | g_debug ("XAppStatusIcon: native sending 'activate' for %s button", button_to_str (button)); | |
352 | g_signal_emit (icon, signals[ACTIVATE], 0, | |
353 | button, | |
354 | _time); | |
409 | 355 | } |
410 | 356 | |
411 | 357 | icon->priv->have_button_press = TRUE; |
578 | 524 | button = event->button.button; |
579 | 525 | _time = event->button.time; |
580 | 526 | |
581 | /* Button press equates to activate when there's no menu, | |
582 | * but for appindicator, left click and right click will always | |
583 | * bring up the same menu, and only a middle click should activate. */ | |
584 | ||
585 | 527 | g_debug ("XAppStatusIcon: GtkStatusIcon button-press-event with %s button", button_to_str (button)); |
586 | 528 | |
587 | if (!handle_appindicator_button_press (icon, button, _time)) | |
588 | { | |
589 | 529 | /* We always send 'activate' for a button that has no corresponding menu, |
590 | 530 | * and for middle clicks. */ |
591 | if (should_send_activate (icon, button)) | |
592 | { | |
593 | g_debug ("XAppStatusIcon: GtkStatusIcon activated by %s button", button_to_str (button)); | |
594 | ||
595 | g_signal_emit (icon, signals[ACTIVATE], 0, | |
596 | button, | |
597 | _time); | |
598 | } | |
531 | if (should_send_activate (icon, button)) | |
532 | { | |
533 | g_debug ("XAppStatusIcon: GtkStatusIcon activated by %s button", button_to_str (button)); | |
534 | ||
535 | g_signal_emit (icon, signals[ACTIVATE], 0, | |
536 | button, | |
537 | _time); | |
599 | 538 | } |
600 | 539 | |
601 | 540 | calculate_gtk_status_icon_position_and_orientation (icon, |
1618 | 1557 | GtkMenu *menu) |
1619 | 1558 | { |
1620 | 1559 | g_return_if_fail (XAPP_IS_STATUS_ICON (icon)); |
1621 | g_return_if_fail (GTK_IS_MENU (menu)); | |
1560 | g_return_if_fail (GTK_IS_MENU (menu) || menu == NULL); | |
1622 | 1561 | |
1623 | 1562 | if (menu == GTK_MENU (icon->priv->primary_menu)) |
1624 | 1563 | { |
1670 | 1609 | GtkMenu *menu) |
1671 | 1610 | { |
1672 | 1611 | g_return_if_fail (XAPP_IS_STATUS_ICON (icon)); |
1673 | g_return_if_fail (GTK_IS_MENU (menu)); | |
1612 | g_return_if_fail (GTK_IS_MENU (menu) || menu == NULL); | |
1674 | 1613 | |
1675 | 1614 | if (menu == GTK_MENU (icon->priv->secondary_menu)) |
1676 | 1615 | { |
0 | 0 | <?xml version="1.0"?> |
1 | 1 | <schemalist> |
2 | 2 | <schema id="org.x.apps" path="/org/x/apps/"> |
3 | ||
3 | <key name="left-click-activate-apps" type="as"> | |
4 | <default>["Onboard"]</default> | |
5 | <summary>A list of appindicator-based apps where left-click should trigger the 'secondary activation' action, rather than open the menu.</summary> | |
6 | <description>The name to be put here will be the icon's "Name" (not icon name). You can see what name to use by running xapp-sn-watcher from a terminal (or in .xsession-properties in some DEs). | |
7 | </description> | |
8 | </key> | |
4 | 9 | </schema> |
5 | 10 | </schemalist> |
40 | 40 | gchar *png_path; |
41 | 41 | |
42 | 42 | gint current_icon_id; |
43 | gchar *sortable_name; | |
43 | 44 | |
44 | 45 | gboolean is_ai; |
45 | 46 | }; |
51 | 52 | static void update_tooltip (SnItem *item); |
52 | 53 | static void update_icon (SnItem *item); |
53 | 54 | |
55 | static gboolean | |
56 | should_activate (SnItem *item) | |
57 | { | |
58 | gboolean should; | |
59 | ||
60 | gchar **whitelist = g_settings_get_strv (xapp_settings, | |
61 | WHITELIST_KEY); | |
62 | ||
63 | should = g_strv_contains ((const gchar * const *) whitelist, item->sortable_name); | |
64 | g_strfreev (whitelist); | |
65 | ||
66 | return should; | |
67 | } | |
68 | ||
54 | 69 | static void |
55 | 70 | sn_item_init (SnItem *self) |
56 | 71 | { |
76 | 91 | item->last_png_path = NULL; |
77 | 92 | } |
78 | 93 | |
94 | g_clear_pointer (&item->sortable_name, g_free); | |
79 | 95 | g_clear_object (&item->status_icon); |
80 | 96 | g_clear_object (&item->menu); |
81 | 97 | g_clear_object (&item->prop_proxy); |
518 | 534 | { |
519 | 535 | gchar *menu_path; |
520 | 536 | |
537 | g_clear_object (&item->menu); | |
538 | ||
539 | xapp_status_icon_set_primary_menu (item->status_icon, NULL); | |
540 | xapp_status_icon_set_secondary_menu (item->status_icon, NULL); | |
541 | ||
521 | 542 | menu_path = get_string_property (item, "Menu"); |
522 | 543 | |
523 | 544 | if (menu_path == NULL) |
524 | 545 | { |
525 | g_clear_object (&item->menu); | |
526 | ||
527 | xapp_status_icon_set_secondary_menu (item->status_icon, NULL); | |
528 | 546 | return; |
529 | 547 | } |
530 | 548 | |
531 | 549 | item->menu = GTK_WIDGET (dbusmenu_gtkmenu_new ((gchar *) g_dbus_proxy_get_name (item->sn_item_proxy), menu_path)); |
532 | 550 | g_object_ref_sink (item->menu); |
551 | ||
552 | if (item->is_ai && !should_activate (item)) | |
553 | { | |
554 | xapp_status_icon_set_primary_menu (item->status_icon, GTK_MENU (item->menu)); | |
555 | } | |
533 | 556 | |
534 | 557 | xapp_status_icon_set_secondary_menu (item->status_icon, GTK_MENU (item->menu)); |
535 | 558 | |
728 | 751 | { |
729 | 752 | SnItem *item = SN_ITEM (user_data); |
730 | 753 | |
731 | GError *error = NULL; | |
732 | ||
733 | 754 | if (button == GDK_BUTTON_PRIMARY) |
734 | 755 | { |
735 | /* This sucks, nothing is consistent. Most programs don't have a primary | |
736 | activate (all appindicator ones). One that I checked that does, claims | |
737 | (according to proxyinfo.get_method_info()) it only accepts SecondaryActivate, | |
738 | but only listens for "Activate", so we attempt a sync primary call, and async | |
739 | secondary if needed. Otherwise we're waiting for the first to finish in a | |
740 | callback before we can try the secondary. Maybe we just call secondary always?? */ | |
741 | ||
742 | sn_item_interface_call_activate_sync (SN_ITEM_INTERFACE (item->sn_item_proxy), x, y, NULL, &error); | |
743 | ||
744 | if (error != NULL) | |
745 | { | |
746 | g_error_free (error); | |
747 | ||
748 | sn_item_interface_call_secondary_activate (SN_ITEM_INTERFACE (item->sn_item_proxy), x, y, NULL, NULL, NULL); | |
756 | if (item->is_ai) | |
757 | { | |
758 | if (should_activate (item)) | |
759 | { | |
760 | sn_item_interface_call_secondary_activate (SN_ITEM_INTERFACE (item->sn_item_proxy), x, y, NULL, NULL, NULL); | |
761 | return; | |
762 | } | |
763 | } else | |
764 | { | |
765 | sn_item_interface_call_activate (SN_ITEM_INTERFACE (item->sn_item_proxy), x, y, NULL, NULL, NULL); | |
749 | 766 | } |
750 | 767 | } |
751 | 768 | else |
828 | 845 | g_debug ("Sort name for %s is '%s'", g_dbus_proxy_get_name (G_DBUS_PROXY (item->sn_item_proxy)), sortable_name); |
829 | 846 | xapp_status_icon_set_name (status_icon, sortable_name); |
830 | 847 | |
831 | g_free (sortable_name); | |
848 | item->sortable_name = sortable_name; | |
832 | 849 | } |
833 | 850 | |
834 | 851 | static void |
894 | 911 | |
895 | 912 | initialize_item (item); |
896 | 913 | return item; |
914 | } | |
915 | ||
916 | void | |
917 | sn_item_update_menus (SnItem *item) | |
918 | { | |
919 | update_menu (item); | |
897 | 920 | }⏎ |
12 | 12 | G_DECLARE_FINAL_TYPE (SnItem, sn_item, SN, ITEM, GObject) |
13 | 13 | |
14 | 14 | SnItem *sn_item_new (GDBusProxy *sn_item_proxy, gboolean is_ai); |
15 | void sn_item_update_menus (SnItem *item); | |
16 | ||
17 | #define WHITELIST_KEY "left-click-activate-apps" | |
18 | extern GSettings *xapp_settings; | |
15 | 19 | |
16 | 20 | G_END_DECLS |
17 | 21 |
36 | 36 | |
37 | 37 | #define STATUS_ICON_MONITOR_MATCH "org.x.StatusIconMonitor" |
38 | 38 | #define APPINDICATOR_PATH_PREFIX "/org/ayatana/NotificationItem/" |
39 | ||
40 | GSettings *xapp_settings; | |
39 | 41 | |
40 | 42 | static void continue_startup (XAppSnWatcher *watcher); |
41 | 43 | static void update_published_items (XAppSnWatcher *watcher); |
405 | 407 | } |
406 | 408 | |
407 | 409 | static void |
410 | update_item_menus (const gchar *key, | |
411 | gpointer item, | |
412 | gpointer user_data) | |
413 | { | |
414 | sn_item_update_menus (SN_ITEM (item)); | |
415 | } | |
416 | ||
417 | static void | |
418 | whitelist_changed (XAppSnWatcher *watcher) | |
419 | { | |
420 | g_hash_table_foreach (watcher->items, (GHFunc) update_item_menus, NULL); | |
421 | } | |
422 | ||
423 | static void | |
408 | 424 | continue_startup (XAppSnWatcher *watcher) |
409 | 425 | { |
410 | 426 | g_debug ("Trying to acquire session bus connection"); |
431 | 447 | |
432 | 448 | G_APPLICATION_CLASS (xapp_sn_watcher_parent_class)->startup (application); |
433 | 449 | |
450 | xapp_settings = g_settings_new ("org.x.apps"); | |
451 | g_signal_connect_swapped (xapp_settings, | |
452 | "changed::" WHITELIST_KEY, | |
453 | G_CALLBACK (whitelist_changed), | |
454 | watcher); | |
455 | ||
434 | 456 | watcher->items = g_hash_table_new_full (g_str_hash, g_str_equal, |
435 | 457 | g_free, g_object_unref); |
436 | 458 | |
474 | 496 | watcher_shutdown (GApplication *application) |
475 | 497 | { |
476 | 498 | XAppSnWatcher *watcher = (XAppSnWatcher *) application; |
499 | ||
500 | g_clear_object (&xapp_settings); | |
477 | 501 | |
478 | 502 | if (watcher->name_listener_id > 0) |
479 | 503 | { |