diff options
| -rw-r--r-- | include/swaybar/tray/sni.h | 8 | ||||
| -rw-r--r-- | swaybar/tray/sni.c | 33 | ||||
| -rw-r--r-- | swaybar/tray/sni_watcher.c | 178 | ||||
| -rw-r--r-- | 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 = "<!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; @@ -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; @@ -281,6 +366,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 +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"); @@ -189,6 +278,32 @@ static DBusHandlerResult signal_handler(DBusConnection *connection, } 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, |
