From b875ca369c69970b68f65e0eee78b9a2d000780e Mon Sep 17 00:00:00 2001 From: Michael Webster Date: Wed, 20 Nov 2019 10:57:50 -0500 Subject: [PATCH] xapp-status-icon: Track and notify changes in types of support for the icon - notify when a status icon is picked up by a native applet, a traditional system tray, or is not being picked up by anything. FIXME: Cinnamon currently doesn't notify when the tray applet is removed because the underlying plug/socket are still retained in cinnamon's tray subsystem. We should maybe eventually propagate the applet no longer displaying icons down to cinnamon-tray-manager and na-tray-manager to remove the icon entirely (this is done correctly in mate-panel and xfce4). --- libxapp/xapp-enums.c.template | 1 + libxapp/xapp-status-icon.c | 81 ++++++++++++++++++- libxapp/xapp-status-icon.h | 17 ++++ .../xapp-status-icon-activate-and-menu | 4 + .../xapp-status-icon-all-menus | 4 + .../xapp-status-icon-no-menus | 4 + .../xapp-status-icon-raw-events | 4 + 7 files changed, 114 insertions(+), 1 deletion(-) diff --git a/libxapp/xapp-enums.c.template b/libxapp/xapp-enums.c.template index d974436..6847490 100644 --- a/libxapp/xapp-enums.c.template +++ b/libxapp/xapp-enums.c.template @@ -3,6 +3,7 @@ #include "config.h" #include "xapp-enums.h" #include "xapp-icon-chooser-dialog.h" +#include "xapp-status-icon.h" #define C_ENUM(v) ((gint) v) #define C_FLAGS(v) ((guint) v) diff --git a/libxapp/xapp-status-icon.c b/libxapp/xapp-status-icon.c index c77aa16..ae9f550 100644 --- a/libxapp/xapp-status-icon.c +++ b/libxapp/xapp-status-icon.c @@ -15,6 +15,7 @@ #include "xapp-status-icon.h" #include "xapp-statusicon-interface.h" +#include "xapp-enums.h" #define FDO_DBUS_NAME "org.freedesktop.DBus" #define FDO_DBUS_PATH "/org/freedesktop/DBus" @@ -33,6 +34,7 @@ enum BUTTON_PRESS, BUTTON_RELEASE, ACTIVATE, + STATE_CHANGED, LAST_SIGNAL }; @@ -69,6 +71,8 @@ typedef struct GtkWidget *primary_menu; GtkWidget *secondary_menu; + XAppStatusIconState state; + gchar *name; gchar *icon_name; gchar *tooltip_text; @@ -94,6 +98,8 @@ static void refresh_icon (XAppStatusIcon *self); static void use_gtk_status_icon (XAppStatusIcon *self); static void tear_down_dbus (XAppStatusIcon *self); +#define GET_STATE_STR(i) (g_enum_to_string(XAPP_TYPE_STATUS_ICON_STATE, i->priv->state)) + static void cancellable_reset (XAppStatusIcon *self) { @@ -634,9 +640,12 @@ on_name_acquired (GDBusConnection *connection, { XAppStatusIcon *self = XAPP_STATUS_ICON (user_data); - g_debug ("XAppStatusIcon: name acquired on dbus, syncing icon properties"); + self->priv->state = XAPP_STATUS_ICON_STATE_NATIVE; sync_skeleton (self); + + g_debug ("XAppStatusIcon: name acquired on dbus, syncing icon properties. State is now: %s", GET_STATE_STR (self)); + g_signal_emit (self, signals[STATE_CHANGED], 0, self->priv->state); } typedef struct @@ -729,6 +738,29 @@ update_fallback_icon (XAppStatusIcon *self, } } +static void +on_gtk_status_icon_embedded_changed (GtkStatusIcon *icon, + GParamSpec *pspec, + gpointer user_data) +{ + g_return_if_fail (GTK_IS_STATUS_ICON (icon)); + + XAppStatusIcon *self = XAPP_STATUS_ICON (user_data); + XAppStatusIconPrivate *priv = self->priv; + + if (gtk_status_icon_is_embedded (icon)) + { + priv->state = XAPP_STATUS_ICON_STATE_FALLBACK; + } + else + { + priv->state = XAPP_STATUS_ICON_STATE_NO_SUPPORT; + } + + g_debug ("XAppStatusIcon fallback icon embedded_changed. State is now %s", GET_STATE_STR (self)); + g_signal_emit (self, signals[STATE_CHANGED], 0, priv->state); +} + static void use_gtk_status_icon (XAppStatusIcon *self) { @@ -751,6 +783,10 @@ use_gtk_status_icon (XAppStatusIcon *self) "button-release-event", G_CALLBACK (on_gtk_status_icon_button_release), self); + g_signal_connect (priv->gtk_status_icon, + "notify::embedded", + G_CALLBACK (on_gtk_status_icon_embedded_changed), + self); update_fallback_icon (self, priv->icon_name ? priv->icon_name : ""); gtk_status_icon_set_tooltip_text (self->priv->gtk_status_icon, priv->tooltip_text); @@ -979,6 +1015,7 @@ xapp_status_icon_init (XAppStatusIcon *self) self->priv = xapp_status_icon_get_instance_private (self); self->priv->name = g_strdup_printf("%s", g_get_application_name()); + self->priv->state = XAPP_STATUS_ICON_STATE_NO_SUPPORT; g_debug ("XAppStatusIcon: init: application name: '%s'", self->priv->name); @@ -1176,6 +1213,24 @@ xapp_status_icon_class_init (XAppStatusIconClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + + /** + * XAppStatusIcon::state-changed: + * @icon: The #XAppStatusIcon + * @new_state: The new #XAppStatusIconState of the icon + * + * Gets emitted when the state of the icon changes. If you wish + * to react to changes in how the status icon is being handled + * (perhaps to alter the menu or other click behavior), you should + * connect to this - see #XAppStatusIconState for more details. + */ + signals [STATE_CHANGED] = + g_signal_new ("state-changed", + XAPP_TYPE_STATUS_ICON, + G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, XAPP_TYPE_STATUS_ICON_STATE); } /** @@ -1424,6 +1479,30 @@ xapp_status_icon_new (void) return g_object_new (XAPP_TYPE_STATUS_ICON, NULL); } +/** + * xapp_status_icon_get_state: + * @icon: an #XAppStatusIcon + * + * Gets the current #XAppStatusIconState of icon. The state is determined by whether + * the icon is being displayed by an #XAppStatusMonitor client, a fallback tray icon, + * or not being displayed at all. + * + * See #XAppStatusIconState for more details. + * + * Returns: the icon's state. + * + * Since: 1.6 + */ +XAppStatusIconState +xapp_status_icon_get_state (XAppStatusIcon *icon) +{ + g_return_val_if_fail (XAPP_IS_STATUS_ICON (icon), XAPP_STATUS_ICON_STATE_NO_SUPPORT); + + g_debug ("XAppStatusIcon get_state: %s", GET_STATE_STR (icon)); + + return icon->priv->state; +} + /** * xapp_status_icon_any_monitors: * diff --git a/libxapp/xapp-status-icon.h b/libxapp/xapp-status-icon.h index 98dd35f..60d1d67 100644 --- a/libxapp/xapp-status-icon.h +++ b/libxapp/xapp-status-icon.h @@ -12,6 +12,22 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (XAppStatusIcon, xapp_status_icon, XAPP, STATUS_ICON, GObject) +/** + * XAppStatusIconState: + * @XAPP_STATUS_ICON_STATE_NATIVE: The #XAppStatusIcon is currently being handled + * by an #XAppStatusIconMonitor (usually in an applet). + * @XAPP_STATUS_ICON_STATE_FALLBACK: The #XAppStatusIcon is currently being handled + * by a legacy system tray implementation (using GtkStatusIcon). + * @XAPP_STATUS_ICON_STATE_NO_SUPPORT: The #XAppStatusIcon is not currently being handled by any + * kind of status icon implementation. + */ +typedef enum +{ + XAPP_STATUS_ICON_STATE_NATIVE, + XAPP_STATUS_ICON_STATE_FALLBACK, + XAPP_STATUS_ICON_STATE_NO_SUPPORT +} XAppStatusIconState; + XAppStatusIcon *xapp_status_icon_new (void); void xapp_status_icon_set_name (XAppStatusIcon *icon, const gchar *name); void xapp_status_icon_set_icon_name (XAppStatusIcon *icon, const gchar *icon_name); @@ -22,6 +38,7 @@ void xapp_status_icon_set_primary_menu (XAppStatusIcon *icon, GtkMe GtkWidget *xapp_status_icon_get_primary_menu (XAppStatusIcon *icon); void xapp_status_icon_set_secondary_menu (XAppStatusIcon *icon, GtkMenu *menu); GtkWidget *xapp_status_icon_get_secondary_menu (XAppStatusIcon *icon); +XAppStatusIconState xapp_status_icon_get_state (XAppStatusIcon *icon); /* static */ gboolean xapp_status_icon_any_monitors (void); diff --git a/test-scripts/xapp-status-icon-variants/xapp-status-icon-activate-and-menu b/test-scripts/xapp-status-icon-variants/xapp-status-icon-activate-and-menu index cc2e845..164739c 100755 --- a/test-scripts/xapp-status-icon-variants/xapp-status-icon-activate-and-menu +++ b/test-scripts/xapp-status-icon-variants/xapp-status-icon-activate-and-menu @@ -16,6 +16,7 @@ class App(GObject.Object): def __init__(self): super(App, self).__init__() self.status_icon = XApp.StatusIcon() + self.status_icon.connect("state-changed", self.on_icon_state_changed) self.status_icon.set_icon_name("folder-symbolic") self.status_icon.set_tooltip_text("Testing primary activate and secondary menu") @@ -35,6 +36,9 @@ class App(GObject.Object): GLib.timeout_add_seconds(2, self.on_timeout_cb) + def on_icon_state_changed(self, icon, new_state): + print("Icon state changed - the state is now: %s" % new_state) + def on_timeout_cb(self): self.counter += 1 self.status_icon.set_label("label %d" % self.counter) diff --git a/test-scripts/xapp-status-icon-variants/xapp-status-icon-all-menus b/test-scripts/xapp-status-icon-variants/xapp-status-icon-all-menus index 8cbd8c8..2da0a02 100755 --- a/test-scripts/xapp-status-icon-variants/xapp-status-icon-all-menus +++ b/test-scripts/xapp-status-icon-variants/xapp-status-icon-all-menus @@ -16,6 +16,7 @@ class App(GObject.Object): def __init__(self): super(App, self).__init__() self.status_icon = XApp.StatusIcon() + self.status_icon.connect("state-changed", self.on_icon_state_changed) self.status_icon.set_icon_name("folder-symbolic") self.status_icon.set_tooltip_text("Testing primary and secondary menus") @@ -42,6 +43,9 @@ class App(GObject.Object): GLib.timeout_add_seconds(2, self.on_timeout_cb) + def on_icon_state_changed(self, icon, new_state): + print("Icon state changed - the state is now: %s" % new_state) + def on_timeout_cb(self): self.counter += 1 self.status_icon.set_label("label %d" % self.counter) diff --git a/test-scripts/xapp-status-icon-variants/xapp-status-icon-no-menus b/test-scripts/xapp-status-icon-variants/xapp-status-icon-no-menus index e1e4c8e..fc21f26 100755 --- a/test-scripts/xapp-status-icon-variants/xapp-status-icon-no-menus +++ b/test-scripts/xapp-status-icon-variants/xapp-status-icon-no-menus @@ -16,6 +16,7 @@ class App(GObject.Object): def __init__(self): super(App, self).__init__() self.status_icon = XApp.StatusIcon() + self.status_icon.connect("state-changed", self.on_icon_state_changed) self.status_icon.set_icon_name("folder-symbolic") self.status_icon.set_tooltip_text("Testing primary activate and secondary menu") @@ -28,6 +29,9 @@ class App(GObject.Object): GLib.timeout_add_seconds(2, self.on_timeout_cb) + def on_icon_state_changed(self, icon, new_state): + print("Icon state changed - the state is now: %s" % new_state) + def on_timeout_cb(self): self.counter += 1 self.status_icon.set_label("label %d" % self.counter) diff --git a/test-scripts/xapp-status-icon-variants/xapp-status-icon-raw-events b/test-scripts/xapp-status-icon-variants/xapp-status-icon-raw-events index 94aded8..6ac352d 100755 --- a/test-scripts/xapp-status-icon-variants/xapp-status-icon-raw-events +++ b/test-scripts/xapp-status-icon-variants/xapp-status-icon-raw-events @@ -16,6 +16,7 @@ class App(GObject.Object): def __init__(self): super(App, self).__init__() self.status_icon = XApp.StatusIcon() + self.status_icon.connect("state-changed", self.on_icon_state_changed) self.status_icon.set_icon_name("folder-symbolic") self.status_icon.set_tooltip_text("Testing raw button press and release events") @@ -35,6 +36,9 @@ class App(GObject.Object): GLib.timeout_add_seconds(2, self.on_timeout_cb) + def on_icon_state_changed(self, icon, new_state): + print("Icon state changed - the state is now: %s" % new_state) + def on_timeout_cb(self): self.counter += 1 self.status_icon.set_label("label %d" % self.counter)