From ad99d9dff8f8e8ee78b5195d6ca440af130f6ab6 Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Tue, 24 Oct 2017 11:59:42 -0600 Subject: Allow registering StatusNotifierItems by obj path This commit impliments a KDE hidden feature where a SNI could be registered by object path instead of well-known name. This should allow libappindicator programs to work correctly under sway. See #1372 --- include/swaybar/tray/sni.h | 8 ++ swaybar/tray/sni.c | 33 +++++++-- swaybar/tray/sni_watcher.c | 178 +++++++++++++++++++++++++++++++++++++++------ swaybar/tray/tray.c | 128 +++++++++++++++++++++++++++++++- 4 files changed, 316 insertions(+), 31 deletions(-) diff --git a/include/swaybar/tray/sni.h b/include/swaybar/tray/sni.h index c2544e2a..22991e53 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 * diff --git a/swaybar/tray/sni.c b/swaybar/tray/sni.c index c9d00657..44d7ad91 100644 --- a/swaybar/tray/sni.c +++ b/swaybar/tray/sni.c @@ -179,7 +179,7 @@ static void send_icon_msg(struct StatusNotifierItem *item) { DBusPendingCall *pending; DBusMessage *message = dbus_message_new_method_call( item->name, - "/StatusNotifierItem", + item->object_path, "org.freedesktop.DBus.Properties", "Get"); const char *iface; @@ -285,7 +285,7 @@ static void send_icon_name_msg(struct StatusNotifierItem *item) { DBusPendingCall *pending; DBusMessage *message = dbus_message_new_method_call( item->name, - "/StatusNotifierItem", + item->object_path, "org.freedesktop.DBus.Properties", "Get"); const char *iface; @@ -324,7 +324,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 +342,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 +364,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 +427,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 +452,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; @@ -471,9 +489,8 @@ void sni_free(struct StatusNotifierItem *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 86453e70..4c53fb99 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 = "" " " " " + " " + " " + " " + " " + " " + " " ""; +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; @@ -147,28 +216,44 @@ static void register_item(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, "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); + return; + } 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 + // It's silly, but clients want a reply for this function DBusMessage *reply = dbus_message_new_method_return(message); dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); @@ -182,10 +267,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 +300,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; @@ -279,6 +364,41 @@ static void get_property(DBusConnection *connection, DBusMessage *message) { DBUS_TYPE_INT32, &version); 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 +409,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 +522,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 +551,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 +581,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 +592,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 +605,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 +613,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 91c3af06..01532e1c 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -102,6 +102,70 @@ bail: dbus_pending_call_unref(pending); return; } +static void get_obj_items_reply(DBusPendingCall *pending, void *_data) { + DBusMessage *reply = dbus_pending_call_steal_reply(pending); + + if (!reply) { + sway_log(L_ERROR, "Got no object path items reply from sni watcher"); + 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; + } + + 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(a(os))"); + goto bail; + } + dbus_message_iter_recurse(&iter, &variant); + if (strcmp(dbus_message_iter_get_signature(&variant), "a(os)") != 0) { + sway_log(L_ERROR, "Replyed with wrong type not a(os)"); + goto bail; + } + + int len = dbus_message_iter_get_element_count(&variant); + + dbus_message_iter_recurse(&variant, &array); + for (int i = 0; i < len; i++) { + const char *object_path; + const char *unique_name; + + dbus_message_iter_recurse(&array, &dstruct); + + dbus_message_iter_get_basic(&dstruct, &object_path); + dbus_message_iter_get_basic(&dstruct, &unique_name); + + struct StatusNotifierItem *item = + sni_create_from_obj_path(unique_name, object_path); + + 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); +} + static void get_items() { DBusPendingCall *pending; DBusMessage *message = dbus_message_new_method_call( @@ -127,6 +191,28 @@ static void get_items() { } dbus_pending_call_set_notify(pending, get_items_reply, NULL, NULL); + + message = dbus_message_new_method_call( + "org.freedesktop.StatusNotifierWatcher", + "/StatusNotifierWatcher", + "org.freedesktop.DBus.Properties", + "Get"); + + iface = "org.swaywm.LessSuckyStatusNotifierWatcher"; + prop = "RegisteredObjectPathItems"; + dbus_message_append_args(message, + DBUS_TYPE_STRING, &iface, + DBUS_TYPE_STRING, &prop, + DBUS_TYPE_INVALID); + + status = dbus_connection_send_with_reply(conn, message, &pending, -1); + dbus_message_unref(message); + + if (!(pending || status)) { + sway_log(L_ERROR, "Could not get items"); + return; + } + dbus_pending_call_set_notify(pending, get_obj_items_reply, NULL, NULL); } static DBusHandlerResult signal_handler(DBusConnection *connection, @@ -162,11 +248,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"); @@ -188,6 +277,32 @@ static DBusHandlerResult signal_handler(DBusConnection *connection, 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; + } + + // TODO allow one unique name to have multiple items + if (list_seq_find(tray->items, sni_str_cmp, unique_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; @@ -255,6 +370,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, -- cgit v1.2.3 From bd121999cab98b8deefbbff4f39460c08071024e Mon Sep 17 00:00:00 2001 From: gnidorah Date: Wed, 25 Oct 2017 15:04:23 +0300 Subject: Allow paths to icons in iconName property --- swaybar/tray/icon.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c index c146bf32..8a7c0415 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. @@ -290,6 +301,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 +401,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; } -- cgit v1.2.3 From 4231061e4d80a19a6f2dde0bfb63b7a7b277bd4a Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Wed, 25 Oct 2017 12:49:34 -0600 Subject: Allow multiple object paths for each connection --- include/swaybar/tray/sni.h | 11 +++++++++++ swaybar/tray/sni.c | 11 +++++++++++ swaybar/tray/tray.c | 7 +++++-- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/include/swaybar/tray/sni.h b/include/swaybar/tray/sni.h index 22991e53..95c10b9f 100644 --- a/include/swaybar/tray/sni.h +++ b/include/swaybar/tray/sni.h @@ -54,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/tray/sni.c b/swaybar/tray/sni.c index 44d7ad91..7e09f414 100644 --- a/swaybar/tray/sni.c +++ b/swaybar/tray/sni.c @@ -484,6 +484,17 @@ 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->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; diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index 01532e1c..5cc7e902 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -291,8 +291,11 @@ static DBusHandlerResult signal_handler(DBusConnection *connection, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - // TODO allow one unique name to have multiple items - if (list_seq_find(tray->items, sni_str_cmp, unique_name) == -1) { + 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); -- cgit v1.2.3 From 5bc46f458c1ac36aa17979af9583fc3060bac094 Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Thu, 26 Oct 2017 12:27:48 -0600 Subject: Prevent segfault in `get_items()` One segfault resulted from an incorrect dbus call in sni_watcher. The other from duplicate items in the sni host. --- swaybar/tray/sni_watcher.c | 4 ++-- swaybar/tray/tray.c | 44 ++++++++++++++++++++++++++++---------------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/swaybar/tray/sni_watcher.c b/swaybar/tray/sni_watcher.c index 4c53fb99..b89ac812 100644 --- a/swaybar/tray/sni_watcher.c +++ b/swaybar/tray/sni_watcher.c @@ -389,9 +389,9 @@ static void get_property(DBusConnection *connection, DBusMessage *message) { DBUS_TYPE_STRUCT, NULL, &dstruct); dbus_message_iter_append_basic(&dstruct, - DBUS_TYPE_OBJECT_PATH, item->obj_path); + DBUS_TYPE_OBJECT_PATH, &item->obj_path); dbus_message_iter_append_basic(&dstruct, - DBUS_TYPE_STRING, item->unique_name); + DBUS_TYPE_STRING, &item->unique_name); dbus_message_iter_close_container(&array, &dstruct); } diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index 5cc7e902..a95b29fd 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -75,11 +75,6 @@ static void get_items_reply(DBusPendingCall *pending, void *_data) { goto bail; } - // Clear list - list_foreach(tray->items, (void (*)(void *))sni_free); - list_free(tray->items); - tray->items = create_list(); - // O(n) function, could be faster dynamically reading values int len = dbus_message_iter_get_element_count(&variant); @@ -88,12 +83,14 @@ static void get_items_reply(DBusPendingCall *pending, void *_data) { const char *name; dbus_message_iter_get_basic(&array, &name); - struct StatusNotifierItem *item = sni_create(name); + if (list_seq_find(tray->items, sni_str_cmp, name) == -1) { + struct StatusNotifierItem *item = sni_create(name); - 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", name); + list_add(tray->items, item); + dirty = true; + } } } @@ -149,15 +146,22 @@ static void get_obj_items_reply(DBusPendingCall *pending, void *_data) { dbus_message_iter_recurse(&array, &dstruct); 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_from_obj_path(unique_name, object_path); + 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", unique_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; + } } } @@ -167,6 +171,11 @@ bail: } static void get_items() { + // Clear list + list_foreach(tray->items, (void (*)(void *))sni_free); + list_free(tray->items); + tray->items = create_list(); + DBusPendingCall *pending; DBusMessage *message = dbus_message_new_method_call( "org.freedesktop.StatusNotifierWatcher", @@ -352,6 +361,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? -- cgit v1.2.3 From 210e5bb893598f2bcf9011e29d22146850969b1e Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Thu, 26 Oct 2017 12:56:37 -0600 Subject: Improve Icon Theme Implimentation --- swaybar/tray/icon.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c index 8a7c0415..fc9b176d 100644 --- a/swaybar/tray/icon.c +++ b/swaybar/tray/icon.c @@ -122,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 @@ -173,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); -- cgit v1.2.3 From 3137f32711b3df2a650b4a686a601e3bf11f930a Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Fri, 27 Oct 2017 13:17:54 -0600 Subject: Allow correct item to get icon updates --- swaybar/tray/tray.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index a95b29fd..e926faae 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -276,11 +276,17 @@ 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); -- cgit v1.2.3 From 2f1eb30c380a81e712095accddd77815a920a547 Mon Sep 17 00:00:00 2001 From: gnidorah Date: Fri, 27 Oct 2017 22:43:40 +0300 Subject: Reply in case of object path item --- swaybar/tray/sni_watcher.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/swaybar/tray/sni_watcher.c b/swaybar/tray/sni_watcher.c index b89ac812..41a95c47 100644 --- a/swaybar/tray/sni_watcher.c +++ b/swaybar/tray/sni_watcher.c @@ -210,6 +210,7 @@ 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); @@ -235,7 +236,7 @@ static void register_item(DBusConnection *connection, DBusMessage *message) { } list_add(object_path_items, item); obj_path_item_registered_signal(connection, item); - return; + goto send_reply; } else { sway_log(L_INFO, "This item is not valid, we cannot keep track of it."); return; @@ -253,8 +254,9 @@ static void register_item(DBusConnection *connection, DBusMessage *message) { item_registered_signal(connection, name); } +send_reply: // It's silly, but clients want a reply for this function - DBusMessage *reply = dbus_message_new_method_return(message); + reply = dbus_message_new_method_return(message); dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); } -- cgit v1.2.3 From 5c8dc9cb73be8b3fde25475dbc3e5f931f73f642 Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Tue, 31 Oct 2017 12:42:08 -0600 Subject: Correct context menu placement if bar is bottom Originally the context menu would draw at the top of the screen, which is incorrect. --- include/swaybar/bar.h | 1 + swaybar/bar.c | 2 ++ swaybar/tray/tray.c | 6 +++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 50d36e76..6f2a7083 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; diff --git a/swaybar/bar.c b/swaybar/bar.c index f12923a8..9cd17303 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); diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index e926faae..924ff1a0 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -432,9 +432,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; -- cgit v1.2.3 From b9f36716b52d4566609ac64be88b8a1f65602214 Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Wed, 8 Nov 2017 12:59:43 -0700 Subject: Plug memory `dbus_message_iter_get_signature` leak --- include/swaybar/tray/dbus.h | 6 ++++++ swaybar/tray/dbus.c | 8 ++++++++ swaybar/tray/sni.c | 18 +++++------------- swaybar/tray/tray.c | 2 +- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/include/swaybar/tray/dbus.h b/include/swaybar/tray/dbus.h index eb9cfea7..51754464 100644 --- a/include/swaybar/tray/dbus.h +++ b/include/swaybar/tray/dbus.h @@ -5,6 +5,12 @@ #include extern DBusConnection *conn; +/** + * 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); + /** * Should be called in main loop to dispatch events */ diff --git a/swaybar/tray/dbus.c b/swaybar/tray/dbus.c index 8e719fd9..46a1c807 100644 --- a/swaybar/tray/dbus.c +++ b/swaybar/tray/dbus.c @@ -1,5 +1,6 @@ #define _XOPEN_SOURCE 700 #include +#include #include #include #include @@ -137,6 +138,13 @@ static void dispatch_status(DBusConnection *connection, DBusDispatchStatus new_s /* Public functions below */ +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); +} + void dispatch_dbus() { if (!should_dispatch || !conn) { return; diff --git a/swaybar/tray/sni.c b/swaybar/tray/sni.c index 7e09f414..401a0091 100644 --- a/swaybar/tray/sni.c +++ b/swaybar/tray/sni.c @@ -71,17 +71,13 @@ static void reply_icon(DBusPendingCall *pending, void *_data) { // 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)); + sway_log(L_ERROR, "Icon relpy type incorrect"); 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)); + if (dbus_message_iter_check_signature(&variant, "a(iiay)")) { + sway_log(L_ERROR, "Icon relpy type incorrect"); goto bail; } @@ -237,18 +233,14 @@ static void reply_icon_name(DBusPendingCall *pending, void *_data) { 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)); + sway_log(L_ERROR, "Icon name relpy type incorrect"); 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)); + sway_log(L_ERROR, "Icon name relpy type incorrect"); goto bail; } diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index 924ff1a0..f1ecb429 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -131,7 +131,7 @@ static void get_obj_items_reply(DBusPendingCall *pending, void *_data) { goto bail; } dbus_message_iter_recurse(&iter, &variant); - if (strcmp(dbus_message_iter_get_signature(&variant), "a(os)") != 0) { + if (dbus_message_iter_check_signature(&iter, "a(os)")) { sway_log(L_ERROR, "Replyed with wrong type not a(os)"); goto bail; } -- cgit v1.2.3 From 87035380e33c7da10b53c6da713c56f3d89a1577 Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Thu, 9 Nov 2017 12:58:32 -0700 Subject: Add `dbus_get_prop_async` utility This drastically reduces the amount of boilerplate needed to get a property from a bus object. --- include/swaybar/tray/dbus.h | 17 ++++ swaybar/tray/dbus.c | 90 +++++++++++++++++++++- swaybar/tray/sni.c | 184 +++++--------------------------------------- swaybar/tray/tray.c | 139 +++------------------------------ 4 files changed, 137 insertions(+), 293 deletions(-) diff --git a/include/swaybar/tray/dbus.h b/include/swaybar/tray/dbus.h index 51754464..125ce96f 100644 --- a/include/swaybar/tray/dbus.h +++ b/include/swaybar/tray/dbus.h @@ -11,6 +11,23 @@ extern DBusConnection *conn; */ 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 (will not necessarily arrive) + * and false otherwise + * + * 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), + void *data); /** * Should be called in main loop to dispatch events */ diff --git a/swaybar/tray/dbus.c b/swaybar/tray/dbus.c index 46a1c807..4439fb83 100644 --- a/swaybar/tray/dbus.c +++ b/swaybar/tray/dbus.c @@ -136,7 +136,58 @@ static void dispatch_status(DBusConnection *connection, DBusDispatchStatus new_s } } -/* Public functions below */ +struct async_prop_data { + char const *sig; + void(*callback)(DBusMessageIter *, void *); + 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); + 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"); + 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."); + goto bail; + } + + data->callback(&variant, data->usr_data); + +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); @@ -145,6 +196,43 @@ bool dbus_message_iter_check_signature(DBusMessageIter *iter, const char *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 *), 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) { return; diff --git a/swaybar/tray/sni.c b/swaybar/tray/sni.c index 401a0091..200422da 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,57 +41,19 @@ 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) { +static void reply_icon(DBusMessageIter *iter /* a(iiay) */, 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; - } - - 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, "Icon relpy type incorrect"); - goto bail; - } - dbus_message_iter_recurse(&iter, &variant); - - if (dbus_message_iter_check_signature(&variant, "a(iiay)")) { - sway_log(L_ERROR, "Icon relpy type incorrect"); - goto bail; - } - - 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); int width; dbus_message_iter_get_basic(&d_struct, &width); @@ -102,13 +67,13 @@ static void reply_icon(DBusPendingCall *pending, void *_data) { 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); @@ -117,7 +82,7 @@ static void reply_icon(DBusPendingCall *pending, void *_data) { // 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 @@ -127,7 +92,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 @@ -155,97 +120,22 @@ 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, - item->object_path, - "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) { 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, "Icon name relpy type incorrect"); - goto bail; - } - dbus_message_iter_recurse(&iter, &variant); - - - if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_STRING) { - sway_log(L_ERROR, "Icon name relpy type incorrect"); - goto bail; - } - 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); @@ -259,55 +149,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, - item->object_path, - "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) { diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index f1ecb429..89e7c3e2 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -38,47 +38,13 @@ 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; - } - - 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; - } - - DBusMessageIter iter; - DBusMessageIter variant; +static void get_items_reply(DBusMessageIter *iter, void *_data) { DBusMessageIter array; - 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; - } - // O(n) function, could be faster dynamically reading values - int len = dbus_message_iter_get_element_count(&variant); + int len = dbus_message_iter_get_element_count(iter); - dbus_message_iter_recurse(&variant, &array); + dbus_message_iter_recurse(iter, &array); for (int i = 0; i < len; i++) { const char *name; dbus_message_iter_get_basic(&array, &name); @@ -93,52 +59,14 @@ static void get_items_reply(DBusPendingCall *pending, void *_data) { } } } - -bail: - dbus_message_unref(reply); - dbus_pending_call_unref(pending); - return; } -static void get_obj_items_reply(DBusPendingCall *pending, void *_data) { - DBusMessage *reply = dbus_pending_call_steal_reply(pending); - - if (!reply) { - sway_log(L_ERROR, "Got no object path items reply from sni watcher"); - 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; - } - - DBusMessageIter iter; - DBusMessageIter variant; +static void get_obj_items_reply(DBusMessageIter *iter, void *_data) { 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(a(os))"); - goto bail; - } - dbus_message_iter_recurse(&iter, &variant); - if (dbus_message_iter_check_signature(&iter, "a(os)")) { - sway_log(L_ERROR, "Replyed with wrong type not a(os)"); - goto bail; - } - - int len = dbus_message_iter_get_element_count(&variant); + int len = dbus_message_iter_get_element_count(iter); - dbus_message_iter_recurse(&variant, &array); + dbus_message_iter_recurse(iter, &array); for (int i = 0; i < len; i++) { const char *object_path; const char *unique_name; @@ -164,10 +92,6 @@ static void get_obj_items_reply(DBusPendingCall *pending, void *_data) { } } } - -bail: - dbus_message_unref(reply); - dbus_pending_call_unref(pending); } static void get_items() { @@ -176,52 +100,13 @@ static void get_items() { list_free(tray->items); tray->items = create_list(); - 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); - - if (!(pending || status)) { - sway_log(L_ERROR, "Could not get items"); - return; - } - - dbus_pending_call_set_notify(pending, get_items_reply, NULL, NULL); + dbus_get_prop_async("org.freedesktop.StatusNotifierWatcher", + "/StatusNotifierWatcher","org.freedesktop.StatusNotifierWatcher", + "RegisteredStatusNotifierItems", "as", get_items_reply, NULL); - message = dbus_message_new_method_call( - "org.freedesktop.StatusNotifierWatcher", - "/StatusNotifierWatcher", - "org.freedesktop.DBus.Properties", - "Get"); - - iface = "org.swaywm.LessSuckyStatusNotifierWatcher"; - prop = "RegisteredObjectPathItems"; - dbus_message_append_args(message, - DBUS_TYPE_STRING, &iface, - DBUS_TYPE_STRING, &prop, - DBUS_TYPE_INVALID); - - status = dbus_connection_send_with_reply(conn, message, &pending, -1); - dbus_message_unref(message); - - if (!(pending || status)) { - sway_log(L_ERROR, "Could not get items"); - return; - } - dbus_pending_call_set_notify(pending, get_obj_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, -- cgit v1.2.3 From 13b81f9fb91ee20a41ccdd955c2539d46ef6b6e1 Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Wed, 15 Nov 2017 13:16:21 -0700 Subject: Fix `get_icon` Property reply functions are now notified if there was an error with the property instead of silent failure. Also issues in `get_item` were resolved. --- include/swaybar/tray/dbus.h | 14 +++++++++++--- swaybar/tray/dbus.c | 10 +++++++--- swaybar/tray/sni.c | 30 +++++++++++++++++++++--------- swaybar/tray/tray.c | 10 ++++++++-- 4 files changed, 47 insertions(+), 17 deletions(-) diff --git a/include/swaybar/tray/dbus.h b/include/swaybar/tray/dbus.h index 125ce96f..c693e6f7 100644 --- a/include/swaybar/tray/dbus.h +++ b/include/swaybar/tray/dbus.h @@ -5,6 +5,13 @@ #include 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. @@ -15,8 +22,9 @@ 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 (will not necessarily arrive) - * and false otherwise + * 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. @@ -26,7 +34,7 @@ bool dbus_get_prop_async(const char *destination, const char *iface, const char *prop, const char *expected_signature, - void(*callback)(DBusMessageIter *iter, void *data), + void(*callback)(DBusMessageIter *iter, void *data, enum property_status status), void *data); /** * Should be called in main loop to dispatch events diff --git a/swaybar/tray/dbus.c b/swaybar/tray/dbus.c index 4439fb83..08abf65c 100644 --- a/swaybar/tray/dbus.c +++ b/swaybar/tray/dbus.c @@ -138,7 +138,7 @@ static void dispatch_status(DBusConnection *connection, DBusDispatchStatus new_s struct async_prop_data { char const *sig; - void(*callback)(DBusMessageIter *, void *); + void(*callback)(DBusMessageIter *, void *, enum property_status); void *usr_data; }; @@ -160,6 +160,7 @@ static void get_prop_callback(DBusPendingCall *pending, void *_data) { DBUS_TYPE_INVALID); sway_log(L_INFO, "Failure to get property: %s", msg); + data->callback(NULL, data->usr_data, PROP_ERROR); goto bail; } @@ -169,16 +170,18 @@ static void get_prop_callback(DBusPendingCall *pending, void *_data) { 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); + data->callback(&variant, data->usr_data, PROP_EXISTS); bail: if (reply) { @@ -199,7 +202,8 @@ bool dbus_message_iter_check_signature(DBusMessageIter *iter, const char *sig) { 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 *), void *usr_data) { + 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; diff --git a/swaybar/tray/sni.c b/swaybar/tray/sni.c index 200422da..98934e11 100644 --- a/swaybar/tray/sni.c +++ b/swaybar/tray/sni.c @@ -41,11 +41,15 @@ void sni_icon_ref_free(struct sni_icon_ref *sni_ref) { } /* Gets the pixmap of an icon */ -static void reply_icon(DBusMessageIter *iter /* a(iiay) */, void *_data) { +static void reply_icon(DBusMessageIter *iter /* a(iiay) */, void *_data, enum property_status status) { + if (status != PROP_EXISTS) { + return; + } struct StatusNotifierItem *item = _data; DBusMessageIter d_struct; /* (iiay) */ - DBusMessageIter icon; /* ay */ + DBusMessageIter struct_items; + DBusMessageIter icon; if (dbus_message_iter_get_element_count(iter) == 0) { // Can't recurse if there are no items @@ -54,16 +58,17 @@ static void reply_icon(DBusMessageIter *iter /* a(iiay) */, void *_data) { } 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"); @@ -76,7 +81,7 @@ static void reply_icon(DBusMessageIter *iter /* a(iiay) */, void *_data) { 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 @@ -131,9 +136,16 @@ static void reply_icon(DBusMessageIter *iter /* a(iiay) */, void *_data) { } /* Get an icon by its name */ -static void reply_icon_name(DBusMessageIter *iter, void *_data) { +static void reply_icon_name(DBusMessageIter *iter, void *_data, enum property_status status) { struct StatusNotifierItem *item = _data; + 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(iter, &icon_name); diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index 89e7c3e2..3c5492f7 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -38,7 +38,10 @@ static void register_host(char *name) { dbus_message_unref(message); } -static void get_items_reply(DBusMessageIter *iter, void *_data) { +static void get_items_reply(DBusMessageIter *iter, void *_data, enum property_status status) { + if (status != PROP_EXISTS) { + return; + } DBusMessageIter array; // O(n) function, could be faster dynamically reading values @@ -60,7 +63,10 @@ static void get_items_reply(DBusMessageIter *iter, void *_data) { } } } -static void get_obj_items_reply(DBusMessageIter *iter, void *_data) { +static void get_obj_items_reply(DBusMessageIter *iter, void *_data, enum property_status status) { + if (status != PROP_EXISTS) { + return; + } DBusMessageIter array; DBusMessageIter dstruct; -- cgit v1.2.3 From cbd0c49a8cf5a11d1bcc27629a7a71f7fb9d9df2 Mon Sep 17 00:00:00 2001 From: gnidorah Date: Thu, 16 Nov 2017 06:56:56 +0300 Subject: Allow correct item to get icon updates for both kde and gtk implementation --- swaybar/tray/sni.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swaybar/tray/sni.c b/swaybar/tray/sni.c index 98934e11..be7d9fd7 100644 --- a/swaybar/tray/sni.c +++ b/swaybar/tray/sni.c @@ -346,7 +346,7 @@ 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->name, obj_name->name) == 0 && + if (strcmp(item->unique_name, obj_name->name) == 0 && strcmp(item->object_path, obj_name->obj_path) == 0) { return 0; } -- cgit v1.2.3 From 87a55dfe5cd6805906fc08f3f03083a2649e84a0 Mon Sep 17 00:00:00 2001 From: gnidorah Date: Wed, 22 Nov 2017 01:47:05 +0300 Subject: Fix icon updating on secondary outputs --- include/swaybar/bar.h | 1 + swaybar/bar.c | 3 +++ swaybar/tray/tray.c | 23 ++++++++++++++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 6f2a7083..96c4365c 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -29,6 +29,7 @@ struct output { char *name; int idx; bool focused; + bool active; }; struct workspace { diff --git a/swaybar/bar.c b/swaybar/bar.c index 9cd17303..49a698b7 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -295,9 +295,12 @@ void bar_run(struct bar *bar) { for (i = 0; i < bar->outputs->length; ++i) { struct output *output = bar->outputs->items[i]; if (window_prerender(output->window) && output->window->cairo) { + output->active = true; render(output, bar->config, bar->status); window_render(output->window); wl_display_flush(output->registry->display); + } else { + output->active = false; } } } diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index 3c5492f7..a5248f6c 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -372,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]; @@ -398,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); @@ -413,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; + } } -- cgit v1.2.3 From 4d34bc393e7b144fbc105537f17bee329c44814d Mon Sep 17 00:00:00 2001 From: gnidorah Date: Wed, 22 Nov 2017 09:04:46 +0300 Subject: Keep tray separate --- include/swaybar/bar.h | 2 ++ swaybar/bar.c | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 96c4365c..7ec09e3e 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -29,7 +29,9 @@ struct output { char *name; int idx; bool focused; +#ifdef ENABLE_TRAY bool active; +#endif }; struct workspace { diff --git a/swaybar/bar.c b/swaybar/bar.c index 49a698b7..f1b42d2c 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -295,12 +295,14 @@ void bar_run(struct bar *bar) { for (i = 0; i < bar->outputs->length; ++i) { struct output *output = bar->outputs->items[i]; if (window_prerender(output->window) && output->window->cairo) { - output->active = true; 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 } } } -- cgit v1.2.3