aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/swaybar/tray/sni.h8
-rw-r--r--swaybar/tray/sni.c33
-rw-r--r--swaybar/tray/sni_watcher.c178
-rw-r--r--swaybar/tray/tray.c128
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,