commit c7f278b85ef9d554586ebec0007fc439eee546f7
parent bee1afec5e7536ddeda0d5a80fd4b46df23b10ab
Author: Janne Veteläinen <janne.vetelainen@elisanet.fi>
Date: Mon, 20 May 2024 22:50:35 +0300
Move 'AboutToShow' to sndbusmenu
Diffstat:
3 files changed, 185 insertions(+), 117 deletions(-)
diff --git a/systray/sndbusmenu.c b/systray/sndbusmenu.c
@@ -1,3 +1,4 @@
+#include <stdlib.h>
#include <stdint.h>
#include <glib.h>
@@ -19,6 +20,7 @@ struct _SnDbusmenu {
uint32_t revision;
gboolean reschedule;
+ gboolean updating;
};
G_DEFINE_FINAL_TYPE(SnDbusmenu, sn_dbusmenu, G_TYPE_OBJECT)
@@ -32,7 +34,14 @@ enum
N_PROPERTIES
};
+enum
+{
+ ABOUT_TO_SHOW_HANDLED,
+ LAST_SIGNAL
+};
+
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
+static uint signals[LAST_SIGNAL];
typedef struct {
@@ -69,10 +78,11 @@ check_menuitem_visible(GVariant *data)
return isvisible;
}
-static gboolean
-check_has_sections(GVariant *data)
+
+static int
+get_n_sections(GVariant *data)
{
- gboolean ret = FALSE;
+ int nsections = 1;
char *val;
GVariant *menu_data;
@@ -82,14 +92,51 @@ check_has_sections(GVariant *data)
GVariant *menuitem_data = g_variant_get_child_value(menu_data, 1);
gboolean check = g_variant_lookup(menuitem_data, "type", "&s", &val);
if (check && strcmp(val, "separator") == 0)
- ret = TRUE;
+ nsections++;
g_variant_unref(menuitem_data);
g_variant_unref(menu_data);
}
- return ret;
+ if (nsections == 1)
+ nsections = 0;
+
+ return nsections;
}
+
+/*
+ * static int
+ * get_depth(GVariant *data)
+ * {
+ * int depth = 1;
+ * int localdepth = 1;
+ * char *val;
+ * GVariant *menu_data;
+ *
+ * GVariantIter iter;
+ * g_variant_iter_init(&iter, data);
+ * while ((g_variant_iter_next(&iter, "v", &menu_data))) {
+ * GVariant *menuitem_data = g_variant_get_child_value(menu_data, 1);
+ * gboolean check = g_variant_lookup(menuitem_data, "children-display", "&s", &val);
+ * if (check) {
+ * localdepth++;
+ * //depth = depth + 1;
+ * GVariant *child = g_variant_get_child_value(menu_data, 2);
+ * get_depth(child);
+ * g_variant_unref(child);
+ * }
+ * g_variant_unref(menuitem_data);
+ * g_variant_unref(menu_data);
+ *
+ * if (localdepth > depth)
+ * depth = localdepth;
+ * localdepth = 1;
+ * }
+ *
+ * return depth;
+ * }
+ */
+
static void
action_activated_cb(GSimpleAction *action, GVariant* param, ActionCallbackData *data)
{
@@ -228,9 +275,9 @@ create_menumodel(GVariant *data, SnDbusmenu *self)
GMenu *ret = g_menu_new();
GVariantIter iter;
GVariant *menuitem_data;
- gboolean has_sections = check_has_sections(data);
+ int nsections = get_n_sections(data);
- if (has_sections) {
+ if (nsections > 0) {
GMenu *section = g_menu_new();
g_variant_iter_init(&iter, data);
while ((g_variant_iter_next(&iter, "v", &menuitem_data))) {
@@ -284,24 +331,28 @@ on_layout_updated(GDBusProxy *proxy, GAsyncResult *res, void *data)
// "No such object path '/MenuBar'
if (err && g_error_matches(err, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_OBJECT)) {
g_error_free(err);
+ dbusmenu->updating = FALSE;
g_object_unref(dbusmenu);
return;
// "The name is not activatable"
} else if (err && g_error_matches(err, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) {
g_error_free(err);
+ dbusmenu->updating = FALSE;
g_object_unref(dbusmenu);
return;
// "Remote peer disconnected"
} else if (err && g_error_matches(err, G_DBUS_ERROR, G_DBUS_ERROR_NO_REPLY)) {
g_error_free(err);
+ dbusmenu->updating = FALSE;
g_object_unref(dbusmenu);
return;
} else if (err) {
g_warning("%s\n", err->message);
g_error_free(err);
+ dbusmenu->updating = FALSE;
g_object_unref(dbusmenu);
return;
}
@@ -338,6 +389,7 @@ on_layout_updated(GDBusProxy *proxy, GAsyncResult *res, void *data)
}
g_variant_unref(retvariant);
+ dbusmenu->updating = FALSE;
g_object_unref(dbusmenu);
}
@@ -354,6 +406,7 @@ reschedule_update(SnItem *snitem, GParamSpec *pspec, void *data)
dbusmenu->reschedule = FALSE;
g_debug("sending reschedule call %s", dbusmenu->busname);
+ dbusmenu->updating = TRUE;
g_dbus_proxy_call(dbusmenu->proxy,
"GetLayout",
g_variant_new("(iias)", 0, -1, NULL),
@@ -397,6 +450,7 @@ on_menuproxy_signal(GDBusProxy *proxy,
dbusmenu->revision = revision;
g_debug("sending menuupdate call %s", dbusmenu->busname);
+ dbusmenu->updating = TRUE;
g_dbus_proxy_call(dbusmenu->proxy,
"GetLayout",
g_variant_new("(iias)", 0, -1, NULL),
@@ -413,6 +467,7 @@ on_menuproxy_signal(GDBusProxy *proxy,
return;
}
g_debug("sending menuupdate call %s", dbusmenu->busname);
+ dbusmenu->updating = TRUE;
g_dbus_proxy_call(dbusmenu->proxy,
"GetLayout",
g_variant_new("(iias)", 0, -1, NULL),
@@ -469,15 +524,7 @@ on_menulayout_ready(GDBusProxy *proxy, GAsyncResult *res, void *data)
static void
sn_dbusmenu_get_property(GObject *object, uint property_id, GValue *value, GParamSpec *pspec)
{
- SnDbusmenu *self = SN_DBUSMENU(object);
-
- switch (property_id) {
- case PROP_PROXY:
- g_value_set_object(value, self->proxy);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
- }
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
}
static void
@@ -508,6 +555,12 @@ sn_dbusmenu_class_init(SnDbusmenuClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ object_class->set_property = sn_dbusmenu_set_property;
+ object_class->get_property = sn_dbusmenu_get_property;
+ object_class->constructed = sn_dbusmenu_constructed;
+ object_class->dispose = sn_dbusmenu_dispose;
+ object_class->finalize = sn_dbusmenu_finalize;
+
obj_properties[PROP_BUSNAME] =
g_param_spec_string("busname", NULL, NULL,
NULL,
@@ -531,18 +584,20 @@ sn_dbusmenu_class_init(SnDbusmenuClass *klass)
obj_properties[PROP_PROXY] =
g_param_spec_object("proxy", NULL, NULL,
G_TYPE_DBUS_PROXY,
- G_PARAM_READWRITE |
+ G_PARAM_WRITABLE |
G_PARAM_STATIC_STRINGS);
-
- object_class->set_property = sn_dbusmenu_set_property;
- object_class->get_property = sn_dbusmenu_get_property;
-
- object_class->constructed = sn_dbusmenu_constructed;
- object_class->dispose = sn_dbusmenu_dispose;
- object_class->finalize = sn_dbusmenu_finalize;
-
g_object_class_install_properties(object_class, N_PROPERTIES, obj_properties);
+
+ signals[ABOUT_TO_SHOW_HANDLED] = g_signal_new("abouttoshowhandled",
+ SN_TYPE_DBUSMENU,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 0);
}
static void
@@ -579,11 +634,60 @@ menuproxy_ready_cb(GObject *obj, GAsyncResult *res, void *data)
g_object_unref(self);
}
-
static void
sn_dbusmenu_init(SnDbusmenu *self)
{
self->reschedule = FALSE;
+ self->updating = FALSE;
+}
+
+static void
+on_timeout(void *data)
+{
+ SnDbusmenu *self = SN_DBUSMENU(data);
+ g_signal_emit(self, signals[ABOUT_TO_SHOW_HANDLED], 0);
+ g_object_unref(self);
+}
+
+static void
+about_to_show_handler(GObject *obj, GAsyncResult *res, void *data)
+{
+ SnDbusmenu *self = SN_DBUSMENU(data);
+
+ GError *err = NULL;
+ GVariant *val = g_dbus_proxy_call_finish(self->proxy, res, &err);
+
+ // Discord generates the following error here:
+ // 'G_DBUS_ERROR' 'G_DBUS_ERROR_FAILED' 'error occurred in AboutToShow'
+ // We ignore it.
+ if (err && !g_error_matches(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED) &&
+ g_strrstr(err->message, "error occured in AboutToShow") != 0) {
+ g_warning("%s\n", err->message);
+
+ } else {
+ // This dbusmenu call might have triggered a menu update,
+ // give it a chance to finish.
+ g_timeout_add_once(200, on_timeout, g_object_ref(self));
+ }
+
+ err ? g_error_free(err) : g_variant_unref(val);
+ g_object_unref(self);
+}
+
+
+static void
+about_to_show_request(GObject *obj, void *data)
+{
+ SnDbusmenu *self = SN_DBUSMENU(data);
+ g_assert(SN_IS_DBUSMENU(self));
+ g_dbus_proxy_call(self->proxy,
+ "AboutToShow",
+ g_variant_new("(i)", 0),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ about_to_show_handler,
+ g_object_ref(self));
}
static void
@@ -605,6 +709,8 @@ sn_dbusmenu_constructed(GObject *obj)
g_signal_connect(self->snitem, "notify::menuvisible", G_CALLBACK(reschedule_update), self);
+ g_signal_connect(self->snitem, "rightclick", G_CALLBACK(about_to_show_request), self);
+
G_OBJECT_CLASS(sn_dbusmenu_parent_class)->constructed(obj);
}
@@ -632,15 +738,6 @@ sn_dbusmenu_finalize(GObject *obj)
G_OBJECT_CLASS(sn_dbusmenu_parent_class)->finalize(obj);
}
-GDBusProxy*
-sn_dbusmenu_get_proxy(SnDbusmenu *self)
-{
- GDBusProxy *proxy;
- g_object_get(self, "proxy", &proxy, NULL);
-
- return proxy;
-}
-
SnDbusmenu*
sn_dbusmenu_new(const char *busname, const char *busobj, SnItem *snitem)
{
diff --git a/systray/snhost.c b/systray/snhost.c
@@ -1,3 +1,4 @@
+#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
diff --git a/systray/snitem.c b/systray/snitem.c
@@ -1,9 +1,11 @@
+#include <stdlib.h>
#include <stdint.h>
#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>
#include <gdk/gdk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gtk/gtk.h>
#include "snitem.h"
@@ -46,7 +48,14 @@ enum
N_PROPERTIES
};
+enum
+{
+ RIGHTCLICK,
+ LAST_SIGNAL
+};
+
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
+static uint signals[LAST_SIGNAL];
static void sn_item_constructed (GObject *obj);
static void sn_item_dispose (GObject *obj);
@@ -305,6 +314,14 @@ sn_item_proxy_signal_handler(GDBusProxy *proxy,
}
}
+void
+sn_item_popup(SnItem *self)
+{
+ g_object_set(self, "menuvisible", TRUE, NULL);
+ g_debug("popping up %s", self->busname);
+ gtk_popover_popup(GTK_POPOVER(self->popovermenu));
+}
+
static void
sn_item_proxy_ready_handler(GObject *obj, GAsyncResult *res, void *data)
{
@@ -380,6 +397,8 @@ sn_item_proxy_ready_handler(GObject *obj, GAsyncResult *res, void *data)
g_variant_get(menu_buspath_v, "&o", &menu_buspath);
SnDbusmenu *dbusmenu = sn_dbusmenu_new(self->busname, menu_buspath, g_object_ref(self));
g_object_set(self, "dbusmenu", dbusmenu, NULL);
+
+ g_signal_connect_swapped(self->dbusmenu, "abouttoshowhandled", G_CALLBACK(sn_item_popup), self);
g_variant_unref(menu_buspath_v);
}
self->ready = TRUE;
@@ -387,14 +406,6 @@ sn_item_proxy_ready_handler(GObject *obj, GAsyncResult *res, void *data)
}
void
-sn_item_popup(SnItem *self)
-{
- g_object_set(self, "menuvisible", TRUE, NULL);
- g_debug("popping up %s", self->busname);
- gtk_popover_popup(GTK_POPOVER(self->popovermenu));
-}
-
-void
sn_item_notify_closed(GtkPopover *popover, void *data)
{
SnItem *self = SN_ITEM(data);
@@ -412,53 +423,13 @@ sn_item_leftclick_handler(GtkGestureClick *click,
SnItem *self = SN_ITEM(data);
g_dbus_proxy_call(self->proxy,
- "Activate",
- g_variant_new("(ii)", 0, 0),
- G_DBUS_CALL_FLAGS_NONE,
- -1,
- NULL,
- NULL,
- NULL);
-}
-
-static void
-sn_item_rightclick_handler_helper(GObject *obj, GAsyncResult *res, void *data)
-{
- SnItem *self = SN_ITEM(data);
- if (!self->ready)
- return;
- GDBusProxy *proxy = G_DBUS_PROXY(obj);
-
- GError *err = NULL;
- GVariant *val = g_dbus_proxy_call_finish(proxy, res, &err);
-
- // This error is generated when answer for the call arrives after
- // icon was finalized.
- if (err && g_error_matches(err, G_DBUS_ERROR, G_DBUS_ERROR_NO_REPLY)) {
- g_error_free(err);
- g_object_unref(self);
- return;
-
- // Discord generates the following error here:
- // 'G_DBUS_ERROR' 'G_DBUS_ERROR_FAILED' 'error occurred in AboutToShow'
- // We ignore it.
- } else if (err && g_error_matches(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED) &&
- g_strrstr(err->message, "error occured in AboutToShow") == 0) {
- g_error_free(err);
-
- sn_item_popup(self);
- // Report rest of possible errors
- } else if (err) {
- g_warning("%s\n", err->message);
- g_error_free(err);
-
- } else {
- g_variant_unref(val);
-
- sn_item_popup(self);
- }
-
- g_object_unref(self);
+ "Activate",
+ g_variant_new("(ii)", 0, 0),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ NULL,
+ NULL);
}
static void
@@ -472,19 +443,7 @@ sn_item_rightclick_handler(GtkGestureClick *click,
if (!self->ready)
return;
- GDBusProxy *menuproxy = sn_dbusmenu_get_proxy(self->dbusmenu);
- if (!G_IS_DBUS_PROXY(menuproxy))
- return;
-
- g_dbus_proxy_call(menuproxy,
- "AboutToShow",
- g_variant_new("(i)", 0),
- G_DBUS_CALL_FLAGS_NONE,
- -1,
- NULL,
- sn_item_rightclick_handler_helper,
- g_object_ref(self));
- g_object_unref(menuproxy);
+ g_signal_emit(self, signals[RIGHTCLICK], 0);
}
static void
@@ -592,6 +551,17 @@ sn_item_class_init(SnItemClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS(klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+ widget_class->measure = sn_item_measure;
+ widget_class->size_allocate = sn_item_size_allocate;
+
+ object_class->set_property = sn_item_set_property;
+ object_class->get_property = sn_item_get_property;
+
+ object_class->constructed = sn_item_constructed;
+ object_class->dispose = sn_item_dispose;
+ object_class->finalize = sn_item_finalize;
+
+
obj_properties[PROP_BUSNAME] =
g_param_spec_string("busname", NULL, NULL,
NULL,
@@ -635,22 +605,22 @@ sn_item_class_init(SnItemClass *klass)
obj_properties[PROP_MENUVISIBLE] =
g_param_spec_boolean("menuvisible", NULL, NULL,
- FALSE,
- G_PARAM_CONSTRUCT |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
-
- widget_class->measure = sn_item_measure;
- widget_class->size_allocate = sn_item_size_allocate;
-
- object_class->set_property = sn_item_set_property;
- object_class->get_property = sn_item_get_property;
-
- object_class->constructed = sn_item_constructed;
- object_class->dispose = sn_item_dispose;
- object_class->finalize = sn_item_finalize;
+ FALSE,
+ G_PARAM_CONSTRUCT |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(object_class, N_PROPERTIES, obj_properties);
+
+ signals[RIGHTCLICK] = g_signal_new("rightclick",
+ SN_TYPE_ITEM,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 0);
}
static void