aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2017-12-29 14:24:23 -0500
committerGitHub <noreply@github.com>2017-12-29 14:24:23 -0500
commit1e87c90923eef75b7d032322dfef38def944c2bb (patch)
treeed17f848e7ac95c919d23424671a8a8ad5651a86
parentd85ad05fa857cd7d7af2c8e2c33616d7895e1955 (diff)
parent4d34bc393e7b144fbc105537f17bee329c44814d (diff)
downloadsway-1e87c90923eef75b7d032322dfef38def944c2bb.zip
sway-1e87c90923eef75b7d032322dfef38def944c2bb.tar.gz
sway-1e87c90923eef75b7d032322dfef38def944c2bb.tar.bz2
Merge pull request #1431 from 4e554c4c/sni_sucks
Support libappindicator
-rw-r--r--include/swaybar/bar.h4
-rw-r--r--include/swaybar/tray/dbus.h31
-rw-r--r--include/swaybar/tray/sni.h19
-rw-r--r--swaybar/bar.c7
-rw-r--r--swaybar/tray/dbus.c102
-rw-r--r--swaybar/tray/icon.c68
-rw-r--r--swaybar/tray/sni.c254
-rw-r--r--swaybar/tray/sni_watcher.c182
-rw-r--r--swaybar/tray/tray.c211
9 files changed, 590 insertions, 288 deletions
diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h
index 50d36e7..7ec09e3 100644
--- a/include/swaybar/bar.h
+++ b/include/swaybar/bar.h
@@ -21,6 +21,7 @@ struct bar {
struct output {
struct window *window;
struct registry *registry;
+ struct output_state *state;
list_t *workspaces;
#ifdef ENABLE_TRAY
list_t *items;
@@ -28,6 +29,9 @@ struct output {
char *name;
int idx;
bool focused;
+#ifdef ENABLE_TRAY
+ bool active;
+#endif
};
struct workspace {
diff --git a/include/swaybar/tray/dbus.h b/include/swaybar/tray/dbus.h
index eb9cfea..c693e6f 100644
--- a/include/swaybar/tray/dbus.h
+++ b/include/swaybar/tray/dbus.h
@@ -5,6 +5,37 @@
#include <dbus/dbus.h>
extern DBusConnection *conn;
+enum property_status {
+ PROP_EXISTS, /* Will give iter */
+ PROP_ERROR, /* Will not give iter */
+ PROP_BAD_DATA, /* Will not give iter */
+ PROP_WRONG_SIG, /* Will give iter, please be careful */
+};
+
+/**
+ * Checks the signature of the given iter against `sig`. Prefer to
+ * `dbus_message_iter_get_signature` as this one frees the intermediate string.
+ */
+bool dbus_message_iter_check_signature(DBusMessageIter *iter, const char *sig);
+
+/**
+ * Fetches the property and calls `callback` with a message iter pointing it.
+ * Performs error handling and signature checking.
+ *
+ * Returns: true if message is successfully sent and false otherwise. If there
+ * is an error getting a property, `callback` will still be run, but with
+ * `status` set to the error.
+ *
+ * NOTE: `expected_signature` must remain valid until the message reply is
+ * received, please only use 'static signatures.
+ */
+bool dbus_get_prop_async(const char *destination,
+ const char *path,
+ const char *iface,
+ const char *prop,
+ const char *expected_signature,
+ void(*callback)(DBusMessageIter *iter, void *data, enum property_status status),
+ void *data);
/**
* Should be called in main loop to dispatch events
*/
diff --git a/include/swaybar/tray/sni.h b/include/swaybar/tray/sni.h
index c2544e2..95c10b9 100644
--- a/include/swaybar/tray/sni.h
+++ b/include/swaybar/tray/sni.h
@@ -9,6 +9,8 @@ struct StatusNotifierItem {
char *name;
/* Unique bus name, needed for determining signal origins */
char *unique_name;
+ /* Object path, useful for items not registerd by well known name */
+ char *object_path;
bool kde_special_snowflake;
cairo_surface_t *image;
@@ -31,6 +33,12 @@ void sni_icon_ref_free(struct sni_icon_ref *sni_ref);
* May return `NULL` if `name` is not valid.
*/
struct StatusNotifierItem *sni_create(const char *name);
+/**
+ * Same as sni_create, but takes an object path and unique name instead of
+ * well-known name.
+ */
+struct StatusNotifierItem *sni_create_from_obj_path(const char *unique_name,
+ const char *object_path);
/**
* `item` must be a struct StatusNotifierItem *
@@ -46,6 +54,17 @@ int sni_str_cmp(const void *item, const void *str);
*/
int sni_uniq_cmp(const void *item, const void *str);
+
+struct ObjName {
+ const void *obj_path;
+ const void *name;
+};
+/**
+ * Returns 0 if `item` has a name of `obj_name->name` and object path of
+ * `obj_name->obj_path`.
+ */
+int sni_obj_name_cmp(const void *item, const void *obj_name);
+
/**
* Gets an icon for the given item if found.
*
diff --git a/swaybar/bar.c b/swaybar/bar.c
index f12923a..f1b42d2 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -247,6 +247,8 @@ void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id) {
/* set window height */
set_window_height(bar_output->window, bar->config->height);
+
+ bar_output->state = output;
}
/* spawn status command */
spawn_status_cmd_proc(bar);
@@ -296,6 +298,11 @@ void bar_run(struct bar *bar) {
render(output, bar->config, bar->status);
window_render(output->window);
wl_display_flush(output->registry->display);
+#ifdef ENABLE_TRAY
+ output->active = true;
+ } else {
+ output->active = false;
+#endif
}
}
}
diff --git a/swaybar/tray/dbus.c b/swaybar/tray/dbus.c
index 8e719fd..08abf65 100644
--- a/swaybar/tray/dbus.c
+++ b/swaybar/tray/dbus.c
@@ -1,5 +1,6 @@
#define _XOPEN_SOURCE 700
#include <stdio.h>
+#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
@@ -135,7 +136,106 @@ static void dispatch_status(DBusConnection *connection, DBusDispatchStatus new_s
}
}
-/* Public functions below */
+struct async_prop_data {
+ char const *sig;
+ void(*callback)(DBusMessageIter *, void *, enum property_status);
+ void *usr_data;
+};
+
+static void get_prop_callback(DBusPendingCall *pending, void *_data) {
+ struct async_prop_data *data = _data;
+
+ DBusMessage *reply = dbus_pending_call_steal_reply(pending);
+
+ if (!reply) {
+ sway_log(L_INFO, "Got no icon name reply from item");
+ goto bail;
+ }
+
+ if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+ char *msg;
+
+ dbus_message_get_args(reply, NULL,
+ DBUS_TYPE_STRING, &msg,
+ DBUS_TYPE_INVALID);
+
+ sway_log(L_INFO, "Failure to get property: %s", msg);
+ data->callback(NULL, data->usr_data, PROP_ERROR);
+ goto bail;
+ }
+
+ DBusMessageIter iter;
+ DBusMessageIter variant;
+
+ dbus_message_iter_init(reply, &iter);
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+ sway_log(L_ERROR, "Property relpy type incorrect");
+ data->callback(NULL, data->usr_data, PROP_BAD_DATA);
+ goto bail;
+ }
+ dbus_message_iter_recurse(&iter, &variant);
+
+ if (!dbus_message_iter_check_signature(&variant, data->sig)) {
+ sway_log(L_INFO, "Property returned has incorrect signatue.");
+ data->callback(&variant, data->usr_data, PROP_WRONG_SIG);
+ goto bail;
+ }
+
+ data->callback(&variant, data->usr_data, PROP_EXISTS);
+
+bail:
+ if (reply) {
+ dbus_message_unref(reply);
+ }
+ dbus_pending_call_unref(pending);
+}
+
+/* Public functions below -- see header for docs*/
+
+bool dbus_message_iter_check_signature(DBusMessageIter *iter, const char *sig) {
+ char *msg_sig = dbus_message_iter_get_signature(iter);
+ int result = strcmp(msg_sig, sig);
+ dbus_free(msg_sig);
+ return (result == 0);
+}
+
+bool dbus_get_prop_async(const char *destination,
+ const char *path, const char *iface,
+ const char *prop, const char *expected_signature,
+ void(*callback)(DBusMessageIter *, void *, enum property_status),
+ void *usr_data) {
+ struct async_prop_data *data = malloc(sizeof(struct async_prop_data));
+ if (!data) {
+ return false;
+ }
+ DBusPendingCall *pending;
+ DBusMessage *message = dbus_message_new_method_call(
+ destination, path,
+ "org.freedesktop.DBus.Properties",
+ "Get");
+
+ dbus_message_append_args(message,
+ DBUS_TYPE_STRING, &iface,
+ DBUS_TYPE_STRING, &prop,
+ DBUS_TYPE_INVALID);
+
+ bool status =
+ dbus_connection_send_with_reply(conn, message, &pending, -1);
+
+ dbus_message_unref(message);
+
+ if (!(pending || status)) {
+ sway_log(L_ERROR, "Could not get property");
+ return false;
+ }
+
+ data->sig = expected_signature;
+ data->callback = callback;
+ data->usr_data = usr_data;
+ dbus_pending_call_set_notify(pending, get_prop_callback, data, free);
+
+ return true;
+}
void dispatch_dbus() {
if (!should_dispatch || !conn) {
diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c
index c146bf3..fc9b176 100644
--- a/swaybar/tray/icon.c
+++ b/swaybar/tray/icon.c
@@ -80,6 +80,17 @@ static bool isdir(const char *path) {
}
+static bool isfile(const char *path) {
+ struct stat statbuf;
+ if (stat(path, &statbuf) != -1) {
+ if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
+ return true;
+ }
+ }
+ return false;
+
+}
+
/**
* Returns the directory of a given theme if it exists.
* The returned pointer must be freed.
@@ -111,15 +122,28 @@ static char *find_theme_dir(const char *theme) {
}
if ((basedir = getenv("XDG_DATA_DIRS"))) {
- if (snprintf(icon_dir, 1024, "%s/icons/%s", basedir, theme) >= 1024) {
- sway_log(L_ERROR, "Path too long to render");
- // ditto
+ if (!(basedir = strdup(basedir))) {
+ sway_log_errno(L_ERROR, "Path too long to render");
goto fail;
}
+ char *token = strtok(basedir, ":");
+ while (token) {
+ // By peeking at the spec, there should be a slash at
+ // the end of the data dir.
+ if (snprintf(icon_dir, 1024, "%sicons/%s", token, theme) >= 1024) {
+ sway_log(L_ERROR, "Path too long to render");
+ // ditto
+ free(basedir);
+ goto fail;
+ }
- if (isdir(icon_dir)) {
- return icon_dir;
+ if (isdir(icon_dir)) {
+ free(basedir);
+ return icon_dir;
+ }
+ token = strtok(NULL, ":");
}
+ free(basedir);
}
// Spec says use "/usr/share/pixmaps/", but I see everything in
@@ -162,6 +186,15 @@ static list_t *find_all_theme_dirs(const char *theme) {
list_cat(dirs, inherits);
list_free(inherits);
}
+ // 'default' usually inherits the default theme. I don't believe it has
+ // any icons, but look for them anyway
+ dir = find_theme_dir("default");
+ if (dir) {
+ list_add(dirs, dir);
+ list_t *inherits = find_inherits(dir);
+ list_cat(dirs, inherits);
+ list_free(inherits);
+ }
dir = find_theme_dir("hicolor");
if (dir) {
list_add(dirs, dir);
@@ -290,6 +323,24 @@ fail:
return dirs;
}
+/* Returns true if full path and file exists */
+static bool is_valid_path(const char *file) {
+ if (strstr(file, "/") == NULL || !isfile(file)) {
+ return false;
+ }
+#ifdef WITH_GDK_PIXBUF
+ if (strstr(file, ".png") == NULL &&
+ strstr(file, ".xpm") == NULL &&
+ strstr(file, ".svg") == NULL) {
+#else
+ if (strstr(file, ".png") == NULL) {
+#endif
+ return false;
+ }
+
+ return true;
+}
+
/* Returns the file of an icon given its name and size */
static char *find_icon_file(const char *name, int size) {
int namelen = strlen(name);
@@ -372,7 +423,12 @@ static char *find_icon_file(const char *name, int size) {
}
cairo_surface_t *find_icon(const char *name, int size) {
- char *image_path = find_icon_file(name, size);
+ char *image_path;
+ if (is_valid_path(name)) {
+ image_path = strdup(name);
+ } else {
+ image_path = find_icon_file(name, size);
+ }
if (image_path == NULL) {
return NULL;
}
diff --git a/swaybar/tray/sni.c b/swaybar/tray/sni.c
index c9d0065..be7d9fd 100644
--- a/swaybar/tray/sni.c
+++ b/swaybar/tray/sni.c
@@ -14,6 +14,9 @@
#include "client/cairo.h"
#include "log.h"
+static const char *KDE_IFACE = "org.kde.StatusNotifierItem";
+static const char *FD_IFACE = "org.freedesktop.StatusNotifierItem";
+
// Not sure what this is but cairo needs it.
static const cairo_user_data_key_t cairo_user_data_key;
@@ -38,90 +41,53 @@ void sni_icon_ref_free(struct sni_icon_ref *sni_ref) {
}
/* Gets the pixmap of an icon */
-static void reply_icon(DBusPendingCall *pending, void *_data) {
- struct StatusNotifierItem *item = _data;
-
- DBusMessage *reply = dbus_pending_call_steal_reply(pending);
-
- if (!reply) {
- sway_log(L_ERROR, "Did not get reply");
- goto bail;
- }
-
- int message_type = dbus_message_get_type(reply);
-
- if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
- char *msg;
-
- dbus_message_get_args(reply, NULL,
- DBUS_TYPE_STRING, &msg,
- DBUS_TYPE_INVALID);
-
- sway_log(L_ERROR, "Message is error: %s", msg);
- goto bail;
+static void reply_icon(DBusMessageIter *iter /* a(iiay) */, void *_data, enum property_status status) {
+ if (status != PROP_EXISTS) {
+ return;
}
+ struct StatusNotifierItem *item = _data;
- DBusMessageIter iter;
- DBusMessageIter variant; /* v[a(iiay)] */
- DBusMessageIter array; /* a(iiay) */
DBusMessageIter d_struct; /* (iiay) */
- DBusMessageIter icon; /* ay */
-
- dbus_message_iter_init(reply, &iter);
-
- // Each if here checks the types above before recursing
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
- sway_log(L_ERROR, "Relpy type incorrect");
- sway_log(L_ERROR, "Should be \"v\", is \"%s\"",
- dbus_message_iter_get_signature(&iter));
- goto bail;
- }
- dbus_message_iter_recurse(&iter, &variant);
-
- if (strcmp("a(iiay)", dbus_message_iter_get_signature(&variant)) != 0) {
- sway_log(L_ERROR, "Relpy type incorrect");
- sway_log(L_ERROR, "Should be \"a(iiay)\", is \"%s\"",
- dbus_message_iter_get_signature(&variant));
- goto bail;
- }
+ DBusMessageIter struct_items;
+ DBusMessageIter icon;
- if (dbus_message_iter_get_element_count(&variant) == 0) {
+ if (dbus_message_iter_get_element_count(iter) == 0) {
// Can't recurse if there are no items
sway_log(L_INFO, "Item has no icon");
- goto bail;
+ return;
}
- dbus_message_iter_recurse(&variant, &array);
- dbus_message_iter_recurse(&array, &d_struct);
+ dbus_message_iter_recurse(iter, &d_struct);
+ dbus_message_iter_recurse(&d_struct, &struct_items);
int width;
- dbus_message_iter_get_basic(&d_struct, &width);
- dbus_message_iter_next(&d_struct);
+ dbus_message_iter_get_basic(&struct_items, &width);
+ dbus_message_iter_next(&struct_items);
int height;
- dbus_message_iter_get_basic(&d_struct, &height);
- dbus_message_iter_next(&d_struct);
+ dbus_message_iter_get_basic(&struct_items, &height);
+ dbus_message_iter_next(&struct_items);
- int len = dbus_message_iter_get_element_count(&d_struct);
+ int len = dbus_message_iter_get_element_count(&struct_items);
if (!len) {
sway_log(L_ERROR, "No icon data");
- goto bail;
+ return;
}
// Also implies len % 4 == 0, useful below
if (len != width * height * 4) {
sway_log(L_ERROR, "Incorrect array size passed");
- goto bail;
+ return;
}
- dbus_message_iter_recurse(&d_struct, &icon);
+ dbus_message_iter_recurse(&struct_items, &icon);
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
// FIXME support a variable stride
// (works on my machine though for all tested widths)
if (!sway_assert(stride == width * 4, "Stride must be equal to byte length")) {
- goto bail;
+ return;
}
// Data is by reference, no need to free
@@ -131,7 +97,7 @@ static void reply_icon(DBusPendingCall *pending, void *_data) {
uint8_t *image_data = malloc(stride * height);
if (!image_data) {
sway_log(L_ERROR, "Could not allocate memory for icon");
- goto bail;
+ return;
}
// Transform from network byte order to host byte order
@@ -159,101 +125,29 @@ static void reply_icon(DBusPendingCall *pending, void *_data) {
item->dirty = true;
dirty = true;
- dbus_message_unref(reply);
- dbus_pending_call_unref(pending);
return;
} else {
sway_log(L_ERROR, "Could not create image surface");
free(image_data);
}
-bail:
- if (reply) {
- dbus_message_unref(reply);
- }
- dbus_pending_call_unref(pending);
sway_log(L_ERROR, "Could not get icon from item");
return;
}
-static void send_icon_msg(struct StatusNotifierItem *item) {
- DBusPendingCall *pending;
- DBusMessage *message = dbus_message_new_method_call(
- item->name,
- "/StatusNotifierItem",
- "org.freedesktop.DBus.Properties",
- "Get");
- const char *iface;
- if (item->kde_special_snowflake) {
- iface = "org.kde.StatusNotifierItem";
- } else {
- iface = "org.freedesktop.StatusNotifierItem";
- }
- const char *prop = "IconPixmap";
-
- dbus_message_append_args(message,
- DBUS_TYPE_STRING, &iface,
- DBUS_TYPE_STRING, &prop,
- DBUS_TYPE_INVALID);
-
- bool status =
- dbus_connection_send_with_reply(conn, message, &pending, -1);
-
- dbus_message_unref(message);
-
- if (!(pending || status)) {
- sway_log(L_ERROR, "Could not get item icon");
- return;
- }
-
- dbus_pending_call_set_notify(pending, reply_icon, item, NULL);
-}
/* Get an icon by its name */
-static void reply_icon_name(DBusPendingCall *pending, void *_data) {
+static void reply_icon_name(DBusMessageIter *iter, void *_data, enum property_status status) {
struct StatusNotifierItem *item = _data;
- DBusMessage *reply = dbus_pending_call_steal_reply(pending);
-
- if (!reply) {
- sway_log(L_INFO, "Got no icon name reply from item");
- goto bail;
- }
-
- int message_type = dbus_message_get_type(reply);
-
- if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
- char *msg;
-
- dbus_message_get_args(reply, NULL,
- DBUS_TYPE_STRING, &msg,
- DBUS_TYPE_INVALID);
-
- sway_log(L_INFO, "Could not get icon name: %s", msg);
- goto bail;
- }
-
- DBusMessageIter iter; /* v[s] */
- DBusMessageIter variant; /* s */
-
- dbus_message_iter_init(reply, &iter);
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
- sway_log(L_ERROR, "Relpy type incorrect");
- sway_log(L_ERROR, "Should be \"v\", is \"%s\"",
- dbus_message_iter_get_signature(&iter));
- goto bail;
- }
- dbus_message_iter_recurse(&iter, &variant);
-
-
- if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_STRING) {
- sway_log(L_ERROR, "Relpy type incorrect");
- sway_log(L_ERROR, "Should be \"s\", is \"%s\"",
- dbus_message_iter_get_signature(&iter));
- goto bail;
+ if (status != PROP_EXISTS) {
+ dbus_get_prop_async(item->name, item->object_path,
+ (item->kde_special_snowflake ? KDE_IFACE : FD_IFACE),
+ "IconPixmap", "a(iiay)", reply_icon, item);
+ return;
}
char *icon_name;
- dbus_message_iter_get_basic(&variant, &icon_name);
+ dbus_message_iter_get_basic(iter, &icon_name);
cairo_surface_t *image = find_icon(icon_name, 256);
@@ -267,55 +161,19 @@ static void reply_icon_name(DBusPendingCall *pending, void *_data) {
item->dirty = true;
dirty = true;
- dbus_message_unref(reply);
- dbus_pending_call_unref(pending);
return;
}
-bail:
- if (reply) {
- dbus_message_unref(reply);
- }
- dbus_pending_call_unref(pending);
// Now try the pixmap
- send_icon_msg(item);
- return;
-}
-static void send_icon_name_msg(struct StatusNotifierItem *item) {
- DBusPendingCall *pending;
- DBusMessage *message = dbus_message_new_method_call(
- item->name,
- "/StatusNotifierItem",
- "org.freedesktop.DBus.Properties",
- "Get");
- const char *iface;
- if (item->kde_special_snowflake) {
- iface = "org.kde.StatusNotifierItem";
- } else {
- iface = "org.freedesktop.StatusNotifierItem";
- }
- const char *prop = "IconName";
-
- dbus_message_append_args(message,
- DBUS_TYPE_STRING, &iface,
- DBUS_TYPE_STRING, &prop,
- DBUS_TYPE_INVALID);
-
- bool status =
- dbus_connection_send_with_reply(conn, message, &pending, -1);
-
- dbus_message_unref(message);
-
- if (!(pending || status)) {
- sway_log(L_ERROR, "Could not get item icon name");
- return;
- }
-
- dbus_pending_call_set_notify(pending, reply_icon_name, item, NULL);
+ dbus_get_prop_async(item->name, item->object_path,
+ (item->kde_special_snowflake ? KDE_IFACE : FD_IFACE),
+ "IconPixmap", "a(iiay)", reply_icon, item);
}
void get_icon(struct StatusNotifierItem *item) {
- send_icon_name_msg(item);
+ dbus_get_prop_async(item->name, item->object_path,
+ (item->kde_special_snowflake ? KDE_IFACE : FD_IFACE),
+ "IconName", "s", reply_icon_name, item);
}
void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
@@ -324,7 +182,7 @@ void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
: "org.freedesktop.StatusNotifierItem");
DBusMessage *message = dbus_message_new_method_call(
item->name,
- "/StatusNotifierItem",
+ item->object_path,
iface,
"Activate");
@@ -342,9 +200,10 @@ void sni_context_menu(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
const char *iface =
(item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
: "org.freedesktop.StatusNotifierItem");
+ sway_log(L_INFO, "Activating context menu for item: (%s,%s)", item->name, item->object_path);
DBusMessage *message = dbus_message_new_method_call(
item->name,
- "/StatusNotifierItem",
+ item->object_path,
iface,
"ContextMenu");
@@ -363,7 +222,7 @@ void sni_secondary(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
: "org.freedesktop.StatusNotifierItem");
DBusMessage *message = dbus_message_new_method_call(
item->name,
- "/StatusNotifierItem",
+ item->object_path,
iface,
"SecondaryActivate");
@@ -426,6 +285,8 @@ struct StatusNotifierItem *sni_create(const char *name) {
struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem));
item->name = strdup(name);
item->unique_name = NULL;
+ // TODO use static str if the default path instead of all these god-damn strdups
+ item->object_path = strdup("/StatusNotifierItem");
item->image = NULL;
item->dirty = false;
@@ -449,6 +310,21 @@ struct StatusNotifierItem *sni_create(const char *name) {
return item;
}
+struct StatusNotifierItem *sni_create_from_obj_path(const char *unique_name,
+ const char *object_path) {
+ struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem));
+ // XXX strdup-ing twice to avoid a double-free; see above todo
+ item->name = strdup(unique_name);
+ item->unique_name = strdup(unique_name);
+ item->object_path = strdup(object_path);
+ item->image = NULL;
+ item->dirty = false;
+ // If they're registering by obj-path they're a special snowflake
+ item->kde_special_snowflake = true;
+
+ get_icon(item);
+ return item;
+}
/* Return 0 if `item` has a name of `str` */
int sni_str_cmp(const void *_item, const void *_str) {
const struct StatusNotifierItem *item = _item;
@@ -466,14 +342,24 @@ int sni_uniq_cmp(const void *_item, const void *_str) {
}
return strcmp(item->unique_name, str);
}
+int sni_obj_name_cmp(const void *_item, const void *_obj_name) {
+ const struct StatusNotifierItem *item = _item;
+ const struct ObjName *obj_name = _obj_name;
+
+ if (strcmp(item->unique_name, obj_name->name) == 0 &&
+ strcmp(item->object_path, obj_name->obj_path) == 0) {
+ return 0;
+ }
+ return 1;
+}
+
void sni_free(struct StatusNotifierItem *item) {
if (!item) {
return;
}
free(item->name);
- if (item->unique_name) {
- free(item->unique_name);
- }
+ free(item->unique_name);
+ free(item->object_path);
if (item->image) {
cairo_surface_destroy(item->image);
}
diff --git a/swaybar/tray/sni_watcher.c b/swaybar/tray/sni_watcher.c
index 86453e7..41a95c4 100644
--- a/swaybar/tray/sni_watcher.c
+++ b/swaybar/tray/sni_watcher.c
@@ -11,6 +11,7 @@
static list_t *items = NULL;
static list_t *hosts = NULL;
+static list_t *object_path_items = NULL;
/**
* Describes the function of the StatusNotifierWatcher
@@ -18,6 +19,10 @@ static list_t *hosts = NULL;
*
* We also implement KDE's special snowflake protocol, it's like this but with
* all occurrences 'freedesktop' replaced with 'kde'. There is no KDE introspect.
+ *
+ * We _also_ support registering items by object path (even though this is a
+ * huge pain in the ass). Hosts that would like to subscribe to these items have
+ * to go through the `org.swaywm.LessSuckyStatusNotifierWatcher` interface.
*/
static const char *interface_xml =
"<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'"
@@ -64,8 +69,59 @@ static const char *interface_xml =
" <arg type='' name='service' direction='out'/>"
" </signal>"
" </interface>"
+ " <interface name='org.swaywm.LessSuckyStatusNotifierWatcher'>"
+ " <property name='RegisteredObjectPathItems' type='a(os)' access='read'/>"
+ " <signal name='ObjPathItemRegistered'>"
+ " <arg type='os' name='service' direction='out'/>"
+ " </signal>"
+ " </interface>"
"</node>";
+struct ObjPathItem {
+ char *obj_path;
+ char *unique_name;
+};
+
+static void free_obj_path_item(struct ObjPathItem *item) {
+ if (!item) {
+ return;
+ }
+ free(item->unique_name);
+ free(item->obj_path);
+ free(item);
+}
+static struct ObjPathItem *create_obj_path_item(const char *unique_name, const char *obj_path) {
+ struct ObjPathItem *item = malloc(sizeof(struct ObjPathItem));
+ if (!item) {
+ return NULL;
+ }
+ item->unique_name = strdup(unique_name);
+ item->obj_path = strdup(obj_path);
+ if (!item->unique_name || !item->obj_path) {
+ free_obj_path_item(item);
+ return NULL;
+ }
+ return item;
+}
+/**
+ * NOTE: This compare function does have ordering, this is because it has to
+ * comapre two strings.
+ */
+static int obj_path_item_cmp(const void *_item1, const void *_item2) {
+ const struct ObjPathItem *item1 = _item1;
+ const struct ObjPathItem *item2 = _item2;
+ if (strcmp(item1->unique_name,item2->unique_name) == 0 &&
+ strcmp(item1->obj_path,item2->obj_path) == 0) {
+ return 0;
+ }
+ return -1;
+}
+static int obj_path_unique_name_cmp(const void *_item, const void *_unique_name) {
+ const struct ObjPathItem *item = _item;
+ const char *unique_name = _unique_name;
+ return strcmp(item->unique_name, unique_name);
+}
+
static void host_registered_signal(DBusConnection *connection) {
// Send one signal for each protocol
DBusMessage *signal = dbus_message_new_signal(
@@ -128,6 +184,19 @@ static void item_unregistered_signal(DBusConnection *connection, const char *nam
dbus_message_unref(signal);
}
+static void obj_path_item_registered_signal(DBusConnection *connection, const struct ObjPathItem *item) {
+ DBusMessage *signal = dbus_message_new_signal(
+ "/StatusNotifierWatcher",
+ "org.swaywm.LessSuckyStatusNotifierWatcher",
+ "ObjPathItemRegistered");
+ dbus_message_append_args(signal,
+ DBUS_TYPE_OBJECT_PATH, &item->obj_path,
+ DBUS_TYPE_STRING, &item->unique_name,
+ DBUS_TYPE_INVALID);
+ dbus_connection_send(connection, signal, NULL);
+ dbus_message_unref(signal);
+}
+
static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) {
DBusMessage *reply;
@@ -141,35 +210,53 @@ static void respond_to_introspect(DBusConnection *connection, DBusMessage *reque
static void register_item(DBusConnection *connection, DBusMessage *message) {
DBusError error;
+ DBusMessage *reply;
char *name;
dbus_error_init(&error);
if (!dbus_message_get_args(message, &error,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_INVALID)) {
- sway_log(L_ERROR, "Error parsing method args: %s\n", error.message);
+ sway_log(L_ERROR, "Error parsing method args: %s", error.message);
}
- sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"\n", name);
+ sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"", name);
// Don't add duplicate or not real item
if (!dbus_validate_bus_name(name, NULL)) {
- sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
- return;
- }
+ if (dbus_validate_path(name, NULL)) {
+ // Item is registered by object path
+ struct ObjPathItem *item =
+ create_obj_path_item(dbus_message_get_sender(message), name);
+
+ // Add ObjPathItem
+ if (list_seq_find(object_path_items, obj_path_item_cmp, item) != -1) {
+ free_obj_path_item(item);
+ return;
+ }
+ list_add(object_path_items, item);
+ obj_path_item_registered_signal(connection, item);
+ goto send_reply;
+ } else {
+ sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
+ return;
+ }
+ } else {
- if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) {
- return;
- }
- if (!dbus_bus_name_has_owner(connection, name, &error)) {
- return;
- }
+ if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) {
+ return;
+ }
+ if (!dbus_bus_name_has_owner(connection, name, &error)) {
+ return;
+ }
- list_add(items, strdup(name));
- item_registered_signal(connection, name);
+ list_add(items, strdup(name));
+ item_registered_signal(connection, name);
+ }
- // It's silly, but xembedsniproxy wants a reply for this function
- DBusMessage *reply = dbus_message_new_method_return(message);
+send_reply:
+ // It's silly, but clients want a reply for this function
+ reply = dbus_message_new_method_return(message);
dbus_connection_send(connection, reply, NULL);
dbus_message_unref(reply);
}
@@ -182,10 +269,10 @@ static void register_host(DBusConnection *connection, DBusMessage *message) {
if (!dbus_message_get_args(message, &error,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_INVALID)) {
- sway_log(L_ERROR, "Error parsing method args: %s\n", error.message);
+ sway_log(L_ERROR, "Error parsing method args: %s", error.message);
}
- sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"\n", name);
+ sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"", name);
// Don't add duplicate or not real host
if (!dbus_validate_bus_name(name, NULL)) {
@@ -215,12 +302,12 @@ static void get_property(DBusConnection *connection, DBusMessage *message) {
DBUS_TYPE_STRING, &interface,
DBUS_TYPE_STRING, &property,
DBUS_TYPE_INVALID)) {
- sway_log(L_ERROR, "Error parsing prop args: %s\n", error.message);
+ sway_log(L_ERROR, "Error parsing prop args: %s", error.message);
return;
}
if (strcmp(property, "RegisteredStatusNotifierItems") == 0) {
- sway_log(L_INFO, "Replying with items\n");
+ sway_log(L_INFO, "Replying with items");
DBusMessage *reply;
reply = dbus_message_new_method_return(message);
DBusMessageIter iter;
@@ -281,6 +368,41 @@ static void get_property(DBusConnection *connection, DBusMessage *message) {
dbus_message_iter_close_container(&iter, &sub);
dbus_connection_send(connection, reply, NULL);
dbus_message_unref(reply);
+ } else if (strcmp(property, "RegisteredObjectPathItems") == 0) {
+ sway_log(L_INFO, "Replying with ObjPathItems");
+ DBusMessage *reply;
+ reply = dbus_message_new_method_return(message);
+ DBusMessageIter iter;
+ DBusMessageIter variant;
+ DBusMessageIter array;
+ DBusMessageIter dstruct;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ "a(os)", &variant);
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ "(os)", &array);
+
+ for (int i = 0; i < object_path_items->length; ++i) {
+ struct ObjPathItem *item = object_path_items->items[i];
+
+ dbus_message_iter_open_container(&array,
+ DBUS_TYPE_STRUCT, NULL, &dstruct);
+
+ dbus_message_iter_append_basic(&dstruct,
+ DBUS_TYPE_OBJECT_PATH, &item->obj_path);
+ dbus_message_iter_append_basic(&dstruct,
+ DBUS_TYPE_STRING, &item->unique_name);
+
+ dbus_message_iter_close_container(&array, &dstruct);
+ }
+
+ dbus_message_iter_close_container(&variant, &array);
+ dbus_message_iter_close_container(&iter, &variant);
+
+ dbus_connection_send(connection, reply, NULL);
+ dbus_message_unref(reply);
}
}
@@ -289,6 +411,8 @@ static void set_property(DBusConnection *connection, DBusMessage *message) {
return;
}
+// TODO clean me up please or get rid of me
+// also add LessSuckyStatusNotifierWatcher props
static void get_all(DBusConnection *connection, DBusMessage *message) {
DBusMessage *reply;
reply = dbus_message_new_method_return(message);
@@ -400,6 +524,8 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
const char *old_owner;
const char *new_owner;
int index;
+ bool found_obj_path_item = false;
+
if (!dbus_message_get_args(message, NULL,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_STRING, &old_owner,
@@ -427,6 +553,17 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
return DBUS_HANDLER_RESULT_HANDLED;
}
+ while ((index = list_seq_find(object_path_items, obj_path_unique_name_cmp, name)) != -1) {
+ found_obj_path_item = true;
+ struct ObjPathItem *item = object_path_items->items[index];
+ sway_log(L_INFO, "ObjPathItem lost %s", item->obj_path);
+ list_del(object_path_items, index);
+ free_obj_path_item(item);
+ }
+ if (found_obj_path_item) {
+ item_unregistered_signal(connection, name);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
@@ -446,6 +583,7 @@ int init_sni_watcher() {
items = create_list();
hosts = create_list();
+ object_path_items = create_list();
int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher",
DBUS_NAME_FLAG_REPLACE_EXISTING,
@@ -456,7 +594,7 @@ int init_sni_watcher() {
sway_log(L_INFO, "Could not get watcher name, it may start later");
}
if (dbus_error_is_set(&error)) {
- sway_log(L_ERROR, "dbus err getting watcher name: %s\n", error.message);
+ sway_log(L_ERROR, "dbus err getting watcher name: %s", error.message);
return -1;
}
@@ -469,7 +607,7 @@ int init_sni_watcher() {
sway_log(L_INFO, "Could not get kde watcher name, it may start later");
}
if (dbus_error_is_set(&error)) {
- sway_log(L_ERROR, "dbus err getting kde watcher name: %s\n", error.message);
+ sway_log(L_ERROR, "dbus err getting kde watcher name: %s", error.message);
return -1;
}
@@ -477,7 +615,7 @@ int init_sni_watcher() {
"/StatusNotifierWatcher",
&vtable, NULL, &error);
if (dbus_error_is_set(&error)) {
- sway_log(L_ERROR, "dbus_err: %s\n", error.message);
+ sway_log(L_ERROR, "dbus_err: %s", error.message);
return -1;
}
diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c
index 91c3af0..a5248f6 100644
--- a/swaybar/tray/tray.c
+++ b/swaybar/tray/tray.c
@@ -38,95 +38,81 @@ static void register_host(char *name) {
dbus_message_unref(message);
}
-static void get_items_reply(DBusPendingCall *pending, void *_data) {
- DBusMessage *reply = dbus_pending_call_steal_reply(pending);
-
- if (!reply) {
- sway_log(L_ERROR, "Got no items reply from sni watcher");
- goto bail;
+static void get_items_reply(DBusMessageIter *iter, void *_data, enum property_status status) {
+ if (status != PROP_EXISTS) {
+ return;
}
+ DBusMessageIter array;
- int message_type = dbus_message_get_type(reply);
+ // O(n) function, could be faster dynamically reading values
+ int len = dbus_message_iter_get_element_count(iter);
- if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
- char *msg;
+ dbus_message_iter_recurse(iter, &array);
+ for (int i = 0; i < len; i++) {
+ const char *name;
+ dbus_message_iter_get_basic(&array, &name);
- dbus_message_get_args(reply, NULL,
- DBUS_TYPE_STRING, &msg,
- DBUS_TYPE_INVALID);
+ if (list_seq_find(tray->items, sni_str_cmp, name) == -1) {
+ struct StatusNotifierItem *item = sni_create(name);
- sway_log(L_ERROR, "Message is error: %s", msg);
- goto bail;
+ if (item) {
+ sway_log(L_DEBUG, "Item registered with host: %s", name);
+ list_add(tray->items, item);
+ dirty = true;
+ }
+ }
+ }
+}
+static void get_obj_items_reply(DBusMessageIter *iter, void *_data, enum property_status status) {
+ if (status != PROP_EXISTS) {
+ return;
}
-
- DBusMessageIter iter;
- DBusMessageIter variant;
DBusMessageIter array;
+ DBusMessageIter dstruct;
- dbus_message_iter_init(reply, &iter);
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
- sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
- goto bail;
- }
- dbus_message_iter_recurse(&iter, &variant);
- if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(&variant) != DBUS_TYPE_STRING) {
- sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
- goto bail;
- }
+ int len = dbus_message_iter_get_element_count(iter);
- // Clear list
- list_foreach(tray->items, (void (*)(void *))sni_free);
- list_free(tray->items);
- tray->items = create_list();
+ dbus_message_iter_recurse(iter, &array);
+ for (int i = 0; i < len; i++) {
+ const char *object_path;
+ const char *unique_name;
- // O(n) function, could be faster dynamically reading values
- int len = dbus_message_iter_get_element_count(&variant);
+ dbus_message_iter_recurse(&array, &dstruct);
- dbus_message_iter_recurse(&variant, &array);
- for (int i = 0; i < len; i++) {
- const char *name;
- dbus_message_iter_get_basic(&array, &name);
+ dbus_message_iter_get_basic(&dstruct, &object_path);
+ dbus_message_iter_next(&dstruct);
+ dbus_message_iter_get_basic(&dstruct, &unique_name);
- struct StatusNotifierItem *item = sni_create(name);
+ struct ObjName obj_name = {
+ object_path,
+ unique_name,
+ };
+ if (list_seq_find(tray->items, sni_obj_name_cmp, &obj_name) == -1) {
+ struct StatusNotifierItem *item =
+ sni_create_from_obj_path(unique_name, object_path);
- if (item) {
- sway_log(L_DEBUG, "Item registered with host: %s", name);
- list_add(tray->items, item);
- dirty = true;
+ if (item) {
+ sway_log(L_DEBUG, "Item registered with host: %s", unique_name);
+ list_add(tray->items, item);
+ dirty = true;
+ }
}
}
-
-bail:
- dbus_message_unref(reply);
- dbus_pending_call_unref(pending);
- return;
}
-static void get_items() {
- DBusPendingCall *pending;
- DBusMessage *message = dbus_message_new_method_call(
- "org.freedesktop.StatusNotifierWatcher",
- "/StatusNotifierWatcher",
- "org.freedesktop.DBus.Properties",
- "Get");
-
- const char *iface = "org.freedesktop.StatusNotifierWatcher";
- const char *prop = "RegisteredStatusNotifierItems";
- dbus_message_append_args(message,
- DBUS_TYPE_STRING, &iface,
- DBUS_TYPE_STRING, &prop,
- DBUS_TYPE_INVALID);
- bool status =
- dbus_connection_send_with_reply(conn, message, &pending, -1);
- dbus_message_unref(message);
+static void get_items() {
+ // Clear list
+ list_foreach(tray->items, (void (*)(void *))sni_free);
+ list_free(tray->items);
+ tray->items = create_list();
- if (!(pending || status)) {
- sway_log(L_ERROR, "Could not get items");
- return;
- }
+ dbus_get_prop_async("org.freedesktop.StatusNotifierWatcher",
+ "/StatusNotifierWatcher","org.freedesktop.StatusNotifierWatcher",
+ "RegisteredStatusNotifierItems", "as", get_items_reply, NULL);
- dbus_pending_call_set_notify(pending, get_items_reply, NULL, NULL);
+ dbus_get_prop_async("org.freedesktop.StatusNotifierWatcher",
+ "/StatusNotifierWatcher","org.swaywm.LessSuckyStatusNotifierWatcher",
+ "RegisteredObjectPathItems", "a(os)", get_obj_items_reply, NULL);
}
static DBusHandlerResult signal_handler(DBusConnection *connection,
@@ -162,11 +148,14 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
}
int index;
- if ((index = list_seq_find(tray->items, sni_str_cmp, name)) != -1) {
+ bool found_item = false;
+ while ((index = list_seq_find(tray->items, sni_str_cmp, name)) != -1) {
+ found_item = true;
sni_free(tray->items->items[index]);
list_del(tray->items, index);
dirty = true;
- } else {
+ }
+ if (found_item == false) {
// If it's not in our list, then our list is incorrect.
// Fetch all items again
sway_log(L_INFO, "Host item list incorrect, refreshing");
@@ -178,17 +167,52 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
"NewIcon") || dbus_message_is_signal(message,
"org.kde.StatusNotifierItem", "NewIcon")) {
const char *name;
+ const char *obj_path;
int index;
struct StatusNotifierItem *item;
name = dbus_message_get_sender(message);
- if ((index = list_seq_find(tray->items, sni_uniq_cmp, name)) != -1) {
+ obj_path = dbus_message_get_path(message);
+ struct ObjName obj_name = {
+ obj_path,
+ name,
+ };
+ if ((index = list_seq_find(tray->items, sni_obj_name_cmp, &obj_name)) != -1) {
item = tray->items->items[index];
sway_log(L_INFO, "NewIcon signal from item %s", item->name);
get_icon(item);
}
return DBUS_HANDLER_RESULT_HANDLED;
+ } else if (dbus_message_is_signal(message,
+ "org.swaywm.LessSuckyStatusNotifierWatcher",
+ "ObjPathItemRegistered")) {
+ const char *object_path;
+ const char *unique_name;
+ if (!dbus_message_get_args(message, NULL,
+ DBUS_TYPE_OBJECT_PATH, &object_path,
+ DBUS_TYPE_STRING, &unique_name,
+ DBUS_TYPE_INVALID)) {
+ sway_log(L_ERROR, "Error getting ObjPathItemRegistered args");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ struct ObjName obj_name = {
+ object_path,
+ unique_name,
+ };
+ if (list_seq_find(tray->items, sni_obj_name_cmp, &obj_name) == -1) {
+ struct StatusNotifierItem *item =
+ sni_create_from_obj_path(unique_name,
+ object_path);
+
+ if (item) {
+ list_add(tray->items, item);
+ dirty = true;
+ }
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
@@ -234,6 +258,9 @@ static int init_host() {
register_host(name);
+ // Chances are if an item is already running, we'll get it two times.
+ // Once from this and another time from queued signals. Still we want
+ // to do this to be a complient sni host just in case.
get_items();
// Perhaps use addmatch helper functions like wlc does?
@@ -255,6 +282,15 @@ static int init_host() {
sway_log(L_ERROR, "dbus_err: %s", error.message);
return -1;
}
+ dbus_bus_add_match(conn,
+ "type='signal',\
+ sender='org.freedesktop.StatusNotifierWatcher',\
+ member='ObjPathItemRegistered'",
+ &error);
+ if (dbus_error_is_set(&error)) {
+ sway_log(L_ERROR, "dbus_err: %s", error.message);
+ return -1;
+ }
// SNI matches
dbus_bus_add_match(conn,
@@ -287,9 +323,13 @@ err:
return -1;
}
-void tray_mouse_event(struct output *output, int x, int y,
+void tray_mouse_event(struct output *output, int rel_x, int rel_y,
uint32_t button, uint32_t state) {
+ int x = rel_x;
+ int y = rel_y + (swaybar.config->position == DESKTOP_SHELL_PANEL_POSITION_TOP
+ ? 0 : (output->state->height - output->window->height));
+
struct window *window = output->window;
uint32_t tray_padding = swaybar.config->tray_padding;
int tray_width = window->width * window->scale;
@@ -332,6 +372,24 @@ uint32_t tray_render(struct output *output, struct config *config) {
return tray_width;
}
+ bool clean_item = false;
+ // Clean item if only one output has tray or this is the last output
+ if (swaybar.outputs->length == 1 || config->tray_output || output->idx == swaybar.outputs->length-1) {
+ clean_item = true;
+ // More trickery is needed in case you plug off secondary outputs on live
+ } else {
+ int active_outputs = 0;
+ for (int i = 0; i < swaybar.outputs->length; i++) {
+ struct output *output = swaybar.outputs->items[i];
+ if (output->active) {
+ active_outputs++;
+ }
+ }
+ if (active_outputs == 1) {
+ clean_item = true;
+ }
+ }
+
for (int i = 0; i < tray->items->length; ++i) {
struct StatusNotifierItem *item =
tray->items->items[i];
@@ -358,6 +416,7 @@ uint32_t tray_render(struct output *output, struct config *config) {
list_add(output->items, render_item);
} else if (item->dirty) {
// item needs re-render
+ sway_log(L_DEBUG, "Redrawing item %d for output %d", i, output->idx);
sni_icon_ref_free(render_item);
output->items->items[j] = render_item =
sni_icon_ref_create(item, item_size);
@@ -373,7 +432,9 @@ uint32_t tray_render(struct output *output, struct config *config) {
cairo_fill(cairo);
cairo_set_operator(cairo, op);
- item->dirty = false;
+ if (clean_item) {
+ item->dirty = false;
+ }
}