diff options
Diffstat (limited to 'swaybar/tray')
-rw-r--r-- | swaybar/tray/dbus.c | 197 | ||||
-rw-r--r-- | swaybar/tray/icon.c | 400 | ||||
-rw-r--r-- | swaybar/tray/sni.c | 481 | ||||
-rw-r--r-- | swaybar/tray/sni_watcher.c | 497 | ||||
-rw-r--r-- | swaybar/tray/tray.c | 398 |
5 files changed, 0 insertions, 1973 deletions
diff --git a/swaybar/tray/dbus.c b/swaybar/tray/dbus.c deleted file mode 100644 index 8e719fd9..00000000 --- a/swaybar/tray/dbus.c +++ /dev/null @@ -1,197 +0,0 @@ -#define _XOPEN_SOURCE 700 -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include <stdbool.h> -#include <poll.h> -#include <signal.h> -#include <time.h> -#include <dbus/dbus.h> -#include "swaybar/tray/dbus.h" -#include "swaybar/event_loop.h" -#include "log.h" - -DBusConnection *conn = NULL; - -static void dispatch_watch(int fd, short mask, void *data) { - sway_log(L_DEBUG, "Dispatching watch"); - DBusWatch *watch = data; - - if (!dbus_watch_get_enabled(watch)) { - return; - } - - uint32_t flags = 0; - - if (mask & POLLIN) { - flags |= DBUS_WATCH_READABLE; - } if (mask & POLLOUT) { - flags |= DBUS_WATCH_WRITABLE; - } if (mask & POLLHUP) { - flags |= DBUS_WATCH_HANGUP; - } if (mask & POLLERR) { - flags |= DBUS_WATCH_ERROR; - } - - dbus_watch_handle(watch, flags); -} - -static dbus_bool_t add_watch(DBusWatch *watch, void *_data) { - if (!dbus_watch_get_enabled(watch)) { - // Watch should not be polled - return TRUE; - } - - short mask = 0; - uint32_t flags = dbus_watch_get_flags(watch); - - if (flags & DBUS_WATCH_READABLE) { - mask |= POLLIN; - } if (flags & DBUS_WATCH_WRITABLE) { - mask |= POLLOUT; - } - - int fd = dbus_watch_get_unix_fd(watch); - - sway_log(L_DEBUG, "Adding DBus watch fd: %d", fd); - add_event(fd, mask, dispatch_watch, watch); - - return TRUE; -} - -static void remove_watch(DBusWatch *watch, void *_data) { - int fd = dbus_watch_get_unix_fd(watch); - - remove_event(fd); -} - -static void dispatch_timeout(timer_t timer, void *data) { - sway_log(L_DEBUG, "Dispatching DBus timeout"); - DBusTimeout *timeout = data; - - if (dbus_timeout_get_enabled(timeout)) { - dbus_timeout_handle(timeout); - } -} - -static dbus_bool_t add_timeout(DBusTimeout *timeout, void *_data) { - if (!dbus_timeout_get_enabled(timeout)) { - return TRUE; - } - - timer_t *timer = malloc(sizeof(timer_t)); - if (!timer) { - sway_log(L_ERROR, "Cannot allocate memory"); - return FALSE; - } - struct sigevent ev = { - .sigev_notify = SIGEV_NONE, - }; - - if (timer_create(CLOCK_MONOTONIC, &ev, timer)) { - sway_log(L_ERROR, "Could not create DBus timer"); - return FALSE; - } - - int interval = dbus_timeout_get_interval(timeout); - int interval_sec = interval / 1000; - int interval_msec = (interval_sec * 1000) - interval; - - struct timespec period = { - (time_t) interval_sec, - ((long) interval_msec) * 1000 * 1000, - }; - struct itimerspec time = { - period, - period, - }; - - timer_settime(*timer, 0, &time, NULL); - - dbus_timeout_set_data(timeout, timer, NULL); - - sway_log(L_DEBUG, "Adding DBus timeout. Interval: %ds %dms", interval_sec, interval_msec); - add_timer(*timer, dispatch_timeout, timeout); - - return TRUE; -} -static void remove_timeout(DBusTimeout *timeout, void *_data) { - timer_t *timer = (timer_t *) dbus_timeout_get_data(timeout); - sway_log(L_DEBUG, "Removing DBus timeout."); - - if (timer) { - remove_timer(*timer); - timer_delete(*timer); - free(timer); - } -} - -static bool should_dispatch = true; - -static void dispatch_status(DBusConnection *connection, DBusDispatchStatus new_status, - void *_data) { - if (new_status == DBUS_DISPATCH_DATA_REMAINS) { - should_dispatch = true; - } -} - -/* Public functions below */ - -void dispatch_dbus() { - if (!should_dispatch || !conn) { - return; - } - - DBusDispatchStatus status; - - do { - status = dbus_connection_dispatch(conn); - } while (status == DBUS_DISPATCH_DATA_REMAINS); - - if (status != DBUS_DISPATCH_COMPLETE) { - sway_log(L_ERROR, "Cannot dispatch dbus events: %d", status); - } - - should_dispatch = false; -} - -int dbus_init() { - DBusError error; - dbus_error_init(&error); - - conn = dbus_bus_get(DBUS_BUS_SESSION, &error); - if (conn == NULL) { - sway_log(L_INFO, "Compiled with dbus support, but unable to connect to dbus"); - sway_log(L_INFO, "swaybar will be unable to display tray icons."); - return -1; - } - - dbus_connection_set_exit_on_disconnect(conn, FALSE); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "Cannot get bus connection: %s\n", error.message); - conn = NULL; - return -1; - } - - sway_log(L_INFO, "Unique name: %s\n", dbus_bus_get_unique_name(conn)); - - // Will be called if dispatch status changes - dbus_connection_set_dispatch_status_function(conn, dispatch_status, NULL, NULL); - - if (!dbus_connection_set_watch_functions(conn, add_watch, remove_watch, - NULL, NULL, NULL)) { - dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL); - sway_log(L_ERROR, "Failed to activate DBUS watch functions"); - return -1; - } - - if (!dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, - NULL, NULL, NULL)) { - dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL); - dbus_connection_set_timeout_functions(conn, NULL, NULL, NULL, NULL, NULL); - sway_log(L_ERROR, "Failed to activate DBUS timeout functions"); - return -1; - } - - return 0; -} diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c deleted file mode 100644 index c146bf32..00000000 --- a/swaybar/tray/icon.c +++ /dev/null @@ -1,400 +0,0 @@ -#define _XOPEN_SOURCE 700 -#define _POSIX_C_SOURCE 200809L -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <dirent.h> -#include <sys/stat.h> -#include <stdbool.h> -#include <stdint.h> -#include <limits.h> -#include "swaybar/tray/icon.h" -#include "swaybar/bar.h" -#include "swaybar/config.h" -#include "stringop.h" -#include "log.h" - -/** - * REVIEW: - * This file repeats lots of "costly" operations that are the same for every - * icon. It's possible to create a dictionary or some other structure to cache - * these, though it may complicate things somewhat. - * - * Also parsing (index.theme) is currently pretty messy, so that could be made - * much better as well. Over all, things work, but are not optimal. - */ - -/* Finds all themes that the given theme inherits */ -static list_t *find_inherits(const char *theme_dir) { - const char inherits[] = "Inherits"; - const char index_name[] = "index.theme"; - list_t *themes = create_list(); - FILE *index = NULL; - char *path = malloc(strlen(theme_dir) + sizeof(index_name)); - if (!path) { - goto fail; - } - if (!themes) { - goto fail; - } - - strcpy(path, theme_dir); - strcat(path, index_name); - - index = fopen(path, "r"); - if (!index) { - goto fail; - } - - char *buf = NULL; - size_t n = 0; - while (!feof(index) && getline(&buf, &n, index) != -1) { - if (n <= sizeof(inherits) + 1) { - continue; - } - if (strncmp(inherits, buf, sizeof(inherits) - 1) == 0) { - char *themestr = buf + sizeof(inherits); - themes = split_string(themestr, ","); - break; - } - } - free(buf); - -fail: - free(path); - if (index) { - fclose(index); - } - return themes; -} - -static bool isdir(const char *path) { - struct stat statbuf; - if (stat(path, &statbuf) != -1) { - if (S_ISDIR(statbuf.st_mode)) { - return true; - } - } - return false; - -} - -/** - * Returns the directory of a given theme if it exists. - * The returned pointer must be freed. - */ -static char *find_theme_dir(const char *theme) { - char *basedir; - char *icon_dir; - - if (!theme) { - return NULL; - } - - if (!(icon_dir = malloc(1024))) { - sway_log(L_ERROR, "Out of memory!"); - goto fail; - } - - if ((basedir = getenv("HOME"))) { - if (snprintf(icon_dir, 1024, "%s/.icons/%s", basedir, theme) >= 1024) { - sway_log(L_ERROR, "Path too long to render"); - // XXX perhaps just goto trying in /usr/share? This - // shouldn't happen anyway, but might with a long global - goto fail; - } - - if (isdir(icon_dir)) { - return icon_dir; - } - } - - 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 - goto fail; - } - - if (isdir(icon_dir)) { - return icon_dir; - } - } - - // Spec says use "/usr/share/pixmaps/", but I see everything in - // "/usr/share/icons/" look it both, I suppose. - if (snprintf(icon_dir, 1024, "/usr/share/pixmaps/%s", theme) >= 1024) { - sway_log(L_ERROR, "Path too long to render"); - goto fail; - } - if (isdir(icon_dir)) { - return icon_dir; - } - - if (snprintf(icon_dir, 1024, "/usr/share/icons/%s", theme) >= 1024) { - sway_log(L_ERROR, "Path too long to render"); - goto fail; - } - if (isdir(icon_dir)) { - return icon_dir; - } - -fail: - free(icon_dir); - sway_log(L_ERROR, "Could not find dir for theme: %s", theme); - return NULL; -} - -/** - * Returns all theme dirs needed to be looked in for an icon. - * Does not check for duplicates - */ -static list_t *find_all_theme_dirs(const char *theme) { - list_t *dirs = create_list(); - if (!dirs) { - return NULL; - } - char *dir = find_theme_dir(theme); - 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); - } - - return dirs; -} - -struct subdir { - int size; - char name[]; -}; - -static int subdir_str_cmp(const void *_subdir, const void *_str) { - const struct subdir *subdir = _subdir; - const char *str = _str; - return strcmp(subdir->name, str); -} -/** - * Helper to find_subdirs. Acts similar to `split_string(subdirs, ",")` but - * generates a list of struct subdirs - */ -static list_t *split_subdirs(char *subdir_str) { - list_t *subdir_list = create_list(); - char *copy = strdup(subdir_str); - if (!subdir_list || !copy) { - list_free(subdir_list); - free(copy); - return NULL; - } - - char *token; - token = strtok(copy, ","); - while(token) { - int len = strlen(token) + 1; - struct subdir *subdir = - malloc(sizeof(struct subdir) + sizeof(char [len])); - if (!subdir) { - // Return what we have - return subdir_list; - } - subdir->size = 0; - strcpy(subdir->name, token); - - list_add(subdir_list, subdir); - - token = strtok(NULL, ","); - } - free(copy); - - return subdir_list; -} -/** - * Returns a list of all subdirectories of a theme. - * Take note: the subdir names are all relative to `theme_dir` and must be - * combined with it to form a valid directory. - * - * Each member of the list is of type (struct subdir *) this struct contains - * the name of the subdir, along with size information. These must be freed - * bye the caller. - * - * This currently ignores min and max sizes of icons. - */ -static list_t* find_theme_subdirs(const char *theme_dir) { - const char index_name[] = "/index.theme"; - list_t *dirs = NULL; - char *path = malloc(strlen(theme_dir) + sizeof(index_name)); - FILE *index = NULL; - if (!path) { - sway_log(L_ERROR, "Failed to allocate memory"); - goto fail; - } - - strcpy(path, theme_dir); - strcat(path, index_name); - - index = fopen(path, "r"); - if (!index) { - sway_log(L_ERROR, "Could not open file: %s", path); - goto fail; - } - - char *buf = NULL; - size_t n = 0; - const char directories[] = "Directories"; - while (!feof(index) && getline(&buf, &n, index) != -1) { - if (n <= sizeof(directories) + 1) { - continue; - } - if (strncmp(directories, buf, sizeof(directories) - 1) == 0) { - char *dirstr = buf + sizeof(directories); - dirs = split_subdirs(dirstr); - break; - } - } - // Now, find the size of each dir - struct subdir *current_subdir = NULL; - const char size[] = "Size"; - while (!feof(index) && getline(&buf, &n, index) != -1) { - if (buf[0] == '[') { - int len = strlen(buf); - if (buf[len-1] == '\n') { - len--; - } - // replace ']' - buf[len-1] = '\0'; - - int index; - if ((index = list_seq_find(dirs, subdir_str_cmp, buf+1)) != -1) { - current_subdir = (dirs->items[index]); - } - } - - if (strncmp(size, buf, sizeof(size) - 1) == 0) { - if (current_subdir) { - current_subdir->size = atoi(buf + sizeof(size)); - } - } - } - free(buf); -fail: - free(path); - if (index) { - fclose(index); - } - return dirs; -} - -/* 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); - list_t *dirs = find_all_theme_dirs(swaybar.config->icon_theme); - if (!dirs) { - return NULL; - } - int min_size_diff = INT_MAX; - char *current_file = NULL; - - for (int i = 0; i < dirs->length; ++i) { - char *dir = dirs->items[i]; - list_t *subdirs = find_theme_subdirs(dir); - - if (!subdirs) { - continue; - } - - for (int i = 0; i < subdirs->length; ++i) { - struct subdir *subdir = subdirs->items[i]; - - // Only use an unsized if we don't already have a - // canidate this should probably change to allow svgs - if (!subdir->size && current_file) { - continue; - } - - int size_diff = abs(size - subdir->size); - - if (size_diff >= min_size_diff) { - continue; - } - - char *path = malloc(strlen(subdir->name) + strlen(dir) + 2); - - strcpy(path, dir); - path[strlen(dir)] = '/'; - strcpy(path + strlen(dir) + 1, subdir->name); - - DIR *icons = opendir(path); - if (!icons) { - free(path); - continue; - } - - struct dirent *direntry; - while ((direntry = readdir(icons)) != NULL) { - int len = strlen(direntry->d_name); - if (len <= namelen + 2) { //must have some ext - continue; - } - if (strncmp(direntry->d_name, name, namelen) == 0) { - char *ext = direntry->d_name + namelen + 1; -#ifdef WITH_GDK_PIXBUF - if (strcmp(ext, "png") == 0 || - strcmp(ext, "xpm") == 0 || - strcmp(ext, "svg") == 0) { -#else - if (strcmp(ext, "png") == 0) { -#endif - free(current_file); - char *icon_path = malloc(strlen(path) + len + 2); - - strcpy(icon_path, path); - icon_path[strlen(path)] = '/'; - strcpy(icon_path + strlen(path) + 1, direntry->d_name); - current_file = icon_path; - min_size_diff = size_diff; - } - } - } - free(path); - closedir(icons); - } - free_flat_list(subdirs); - } - free_flat_list(dirs); - - return current_file; -} - -cairo_surface_t *find_icon(const char *name, int size) { - char *image_path = find_icon_file(name, size); - if (image_path == NULL) { - return NULL; - } - - cairo_surface_t *image = NULL; -#ifdef WITH_GDK_PIXBUF - GError *err = NULL; - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(image_path, &err); - if (!pixbuf) { - sway_log(L_ERROR, "Failed to load icon image: %s", err->message); - } - image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); - g_object_unref(pixbuf); -#else - // TODO make svg work? cairo supports it. maybe remove gdk alltogether - image = cairo_image_surface_create_from_png(image_path); -#endif //WITH_GDK_PIXBUF - if (!image) { - sway_log(L_ERROR, "Could not read icon image"); - return NULL; - } - - free(image_path); - return image; -} diff --git a/swaybar/tray/sni.c b/swaybar/tray/sni.c deleted file mode 100644 index c9d00657..00000000 --- a/swaybar/tray/sni.c +++ /dev/null @@ -1,481 +0,0 @@ -#define _XOPEN_SOURCE 500 -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <stdint.h> -#include <stdbool.h> -#include <dbus/dbus.h> -#include <arpa/inet.h> -#include <netinet/in.h> -#include "swaybar/tray/dbus.h" -#include "swaybar/tray/sni.h" -#include "swaybar/tray/icon.h" -#include "swaybar/bar.h" -#include "client/cairo.h" -#include "log.h" - -// Not sure what this is but cairo needs it. -static const cairo_user_data_key_t cairo_user_data_key; - -struct sni_icon_ref *sni_icon_ref_create(struct StatusNotifierItem *item, - int height) { - struct sni_icon_ref *sni_ref = malloc(sizeof(struct sni_icon_ref)); - if (!sni_ref) { - return NULL; - } - sni_ref->icon = cairo_image_surface_scale(item->image, height, height); - sni_ref->ref = item; - - return sni_ref; -} - -void sni_icon_ref_free(struct sni_icon_ref *sni_ref) { - if (!sni_ref) { - return; - } - cairo_surface_destroy(sni_ref->icon); - free(sni_ref); -} - -/* Gets the pixmap of an icon */ -static void reply_icon(DBusPendingCall *pending, void *_data) { - struct StatusNotifierItem *item = _data; - - DBusMessage *reply = dbus_pending_call_steal_reply(pending); - - if (!reply) { - sway_log(L_ERROR, "Did not get reply"); - goto bail; - } - - int message_type = dbus_message_get_type(reply); - - if (message_type == DBUS_MESSAGE_TYPE_ERROR) { - char *msg; - - dbus_message_get_args(reply, NULL, - DBUS_TYPE_STRING, &msg, - DBUS_TYPE_INVALID); - - sway_log(L_ERROR, "Message is error: %s", msg); - goto bail; - } - - DBusMessageIter iter; - DBusMessageIter variant; /* v[a(iiay)] */ - DBusMessageIter array; /* a(iiay) */ - DBusMessageIter d_struct; /* (iiay) */ - DBusMessageIter icon; /* ay */ - - dbus_message_iter_init(reply, &iter); - - // Each if here checks the types above before recursing - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { - sway_log(L_ERROR, "Relpy type incorrect"); - sway_log(L_ERROR, "Should be \"v\", is \"%s\"", - dbus_message_iter_get_signature(&iter)); - goto bail; - } - dbus_message_iter_recurse(&iter, &variant); - - if (strcmp("a(iiay)", dbus_message_iter_get_signature(&variant)) != 0) { - sway_log(L_ERROR, "Relpy type incorrect"); - sway_log(L_ERROR, "Should be \"a(iiay)\", is \"%s\"", - dbus_message_iter_get_signature(&variant)); - goto bail; - } - - if (dbus_message_iter_get_element_count(&variant) == 0) { - // Can't recurse if there are no items - sway_log(L_INFO, "Item has no icon"); - goto bail; - } - dbus_message_iter_recurse(&variant, &array); - - dbus_message_iter_recurse(&array, &d_struct); - - int width; - dbus_message_iter_get_basic(&d_struct, &width); - dbus_message_iter_next(&d_struct); - - int height; - dbus_message_iter_get_basic(&d_struct, &height); - dbus_message_iter_next(&d_struct); - - int len = dbus_message_iter_get_element_count(&d_struct); - - if (!len) { - sway_log(L_ERROR, "No icon data"); - goto bail; - } - - // Also implies len % 4 == 0, useful below - if (len != width * height * 4) { - sway_log(L_ERROR, "Incorrect array size passed"); - goto bail; - } - - dbus_message_iter_recurse(&d_struct, &icon); - - int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - // FIXME support a variable stride - // (works on my machine though for all tested widths) - if (!sway_assert(stride == width * 4, "Stride must be equal to byte length")) { - goto bail; - } - - // Data is by reference, no need to free - uint8_t *message_data; - dbus_message_iter_get_fixed_array(&icon, &message_data, &len); - - uint8_t *image_data = malloc(stride * height); - if (!image_data) { - sway_log(L_ERROR, "Could not allocate memory for icon"); - goto bail; - } - - // Transform from network byte order to host byte order - // Assumptions are safe because the equality above - uint32_t *network = (uint32_t *) message_data; - uint32_t *host = (uint32_t *)image_data; - for (int i = 0; i < width * height; ++i) { - host[i] = ntohl(network[i]); - } - - cairo_surface_t *image = cairo_image_surface_create_for_data( - image_data, CAIRO_FORMAT_ARGB32, - width, height, stride); - - if (image) { - if (item->image) { - cairo_surface_destroy(item->image); - } - item->image = image; - // Free the image data on surface destruction - cairo_surface_set_user_data(image, - &cairo_user_data_key, - image_data, - free); - item->dirty = true; - dirty = true; - - dbus_message_unref(reply); - dbus_pending_call_unref(pending); - return; - } else { - sway_log(L_ERROR, "Could not create image surface"); - free(image_data); - } - -bail: - if (reply) { - dbus_message_unref(reply); - } - dbus_pending_call_unref(pending); - sway_log(L_ERROR, "Could not get icon from item"); - return; -} -static void send_icon_msg(struct StatusNotifierItem *item) { - DBusPendingCall *pending; - DBusMessage *message = dbus_message_new_method_call( - item->name, - "/StatusNotifierItem", - "org.freedesktop.DBus.Properties", - "Get"); - const char *iface; - if (item->kde_special_snowflake) { - iface = "org.kde.StatusNotifierItem"; - } else { - iface = "org.freedesktop.StatusNotifierItem"; - } - const char *prop = "IconPixmap"; - - dbus_message_append_args(message, - DBUS_TYPE_STRING, &iface, - DBUS_TYPE_STRING, &prop, - DBUS_TYPE_INVALID); - - bool status = - dbus_connection_send_with_reply(conn, message, &pending, -1); - - dbus_message_unref(message); - - if (!(pending || status)) { - sway_log(L_ERROR, "Could not get item icon"); - return; - } - - dbus_pending_call_set_notify(pending, reply_icon, item, NULL); -} - -/* Get an icon by its name */ -static void reply_icon_name(DBusPendingCall *pending, void *_data) { - struct StatusNotifierItem *item = _data; - - DBusMessage *reply = dbus_pending_call_steal_reply(pending); - - if (!reply) { - sway_log(L_INFO, "Got no icon name reply from item"); - goto bail; - } - - int message_type = dbus_message_get_type(reply); - - if (message_type == DBUS_MESSAGE_TYPE_ERROR) { - char *msg; - - dbus_message_get_args(reply, NULL, - DBUS_TYPE_STRING, &msg, - DBUS_TYPE_INVALID); - - sway_log(L_INFO, "Could not get icon name: %s", msg); - goto bail; - } - - DBusMessageIter iter; /* v[s] */ - DBusMessageIter variant; /* s */ - - dbus_message_iter_init(reply, &iter); - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { - sway_log(L_ERROR, "Relpy type incorrect"); - sway_log(L_ERROR, "Should be \"v\", is \"%s\"", - dbus_message_iter_get_signature(&iter)); - goto bail; - } - dbus_message_iter_recurse(&iter, &variant); - - - if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_STRING) { - sway_log(L_ERROR, "Relpy type incorrect"); - sway_log(L_ERROR, "Should be \"s\", is \"%s\"", - dbus_message_iter_get_signature(&iter)); - goto bail; - } - - char *icon_name; - dbus_message_iter_get_basic(&variant, &icon_name); - - cairo_surface_t *image = find_icon(icon_name, 256); - - if (image) { - sway_log(L_DEBUG, "Icon for %s found with size %d", icon_name, - cairo_image_surface_get_width(image)); - if (item->image) { - cairo_surface_destroy(item->image); - } - item->image = image; - item->dirty = true; - dirty = true; - - dbus_message_unref(reply); - dbus_pending_call_unref(pending); - return; - } - -bail: - if (reply) { - dbus_message_unref(reply); - } - dbus_pending_call_unref(pending); - // Now try the pixmap - send_icon_msg(item); - return; -} -static void send_icon_name_msg(struct StatusNotifierItem *item) { - DBusPendingCall *pending; - DBusMessage *message = dbus_message_new_method_call( - item->name, - "/StatusNotifierItem", - "org.freedesktop.DBus.Properties", - "Get"); - const char *iface; - if (item->kde_special_snowflake) { - iface = "org.kde.StatusNotifierItem"; - } else { - iface = "org.freedesktop.StatusNotifierItem"; - } - const char *prop = "IconName"; - - dbus_message_append_args(message, - DBUS_TYPE_STRING, &iface, - DBUS_TYPE_STRING, &prop, - DBUS_TYPE_INVALID); - - bool status = - dbus_connection_send_with_reply(conn, message, &pending, -1); - - dbus_message_unref(message); - - if (!(pending || status)) { - sway_log(L_ERROR, "Could not get item icon name"); - return; - } - - dbus_pending_call_set_notify(pending, reply_icon_name, item, NULL); -} - -void get_icon(struct StatusNotifierItem *item) { - send_icon_name_msg(item); -} - -void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) { - const char *iface = - (item->kde_special_snowflake ? "org.kde.StatusNotifierItem" - : "org.freedesktop.StatusNotifierItem"); - DBusMessage *message = dbus_message_new_method_call( - item->name, - "/StatusNotifierItem", - iface, - "Activate"); - - dbus_message_append_args(message, - DBUS_TYPE_INT32, &x, - DBUS_TYPE_INT32, &y, - DBUS_TYPE_INVALID); - - dbus_connection_send(conn, message, NULL); - - dbus_message_unref(message); -} - -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"); - DBusMessage *message = dbus_message_new_method_call( - item->name, - "/StatusNotifierItem", - iface, - "ContextMenu"); - - dbus_message_append_args(message, - DBUS_TYPE_INT32, &x, - DBUS_TYPE_INT32, &y, - DBUS_TYPE_INVALID); - - dbus_connection_send(conn, message, NULL); - - dbus_message_unref(message); -} -void sni_secondary(struct StatusNotifierItem *item, uint32_t x, uint32_t y) { - const char *iface = - (item->kde_special_snowflake ? "org.kde.StatusNotifierItem" - : "org.freedesktop.StatusNotifierItem"); - DBusMessage *message = dbus_message_new_method_call( - item->name, - "/StatusNotifierItem", - iface, - "SecondaryActivate"); - - dbus_message_append_args(message, - DBUS_TYPE_INT32, &x, - DBUS_TYPE_INT32, &y, - DBUS_TYPE_INVALID); - - dbus_connection_send(conn, message, NULL); - - dbus_message_unref(message); -} - -static void get_unique_name(struct StatusNotifierItem *item) { - // I think that we're fine being sync here becaues the message is - // directly to the message bus. Could be async though. - DBusMessage *message = dbus_message_new_method_call( - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "GetNameOwner"); - - dbus_message_append_args(message, - DBUS_TYPE_STRING, &item->name, - DBUS_TYPE_INVALID); - - DBusMessage *reply = dbus_connection_send_with_reply_and_block( - conn, message, -1, NULL); - - dbus_message_unref(message); - - if (!reply) { - sway_log(L_ERROR, "Could not get unique name for item: %s", - item->name); - return; - } - - char *unique_name; - if (!dbus_message_get_args(reply, NULL, - DBUS_TYPE_STRING, &unique_name, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error parsing method args"); - } else { - if (item->unique_name) { - free(item->unique_name); - } - item->unique_name = strdup(unique_name); - } - - dbus_message_unref(reply); -} - -struct StatusNotifierItem *sni_create(const char *name) { - // Make sure `name` is well formed - if (!dbus_validate_bus_name(name, NULL)) { - sway_log(L_INFO, "Name (%s) is not a bus name. We cannot create an item.", name); - return NULL; - } - - struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem)); - item->name = strdup(name); - item->unique_name = NULL; - item->image = NULL; - item->dirty = false; - - // If it doesn't use this name then assume that it uses the KDE spec - // This is because xembed-sni-proxy uses neither "org.freedesktop" nor - // "org.kde" and just gives us the items "unique name" - // - // We could use this to our advantage and fill out the "unique name" - // field with the given name if it is neither freedesktop or kde, but - // that's makes us rely on KDE hackyness which is bad practice - const char freedesktop_name[] = "org.freedesktop"; - if (strncmp(name, freedesktop_name, sizeof(freedesktop_name) - 1) != 0) { - item->kde_special_snowflake = true; - } else { - item->kde_special_snowflake = false; - } - - get_icon(item); - - get_unique_name(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; - const char *str = _str; - - return strcmp(item->name, str); -} -/* Returns 0 if `item` has a unique name of `str` */ -int sni_uniq_cmp(const void *_item, const void *_str) { - const struct StatusNotifierItem *item = _item; - const char *str = _str; - - if (!item->unique_name) { - return false; - } - return strcmp(item->unique_name, str); -} -void sni_free(struct StatusNotifierItem *item) { - if (!item) { - return; - } - free(item->name); - if (item->unique_name) { - free(item->unique_name); - } - if (item->image) { - cairo_surface_destroy(item->image); - } - free(item); -} diff --git a/swaybar/tray/sni_watcher.c b/swaybar/tray/sni_watcher.c deleted file mode 100644 index 86453e70..00000000 --- a/swaybar/tray/sni_watcher.c +++ /dev/null @@ -1,497 +0,0 @@ -#define _XOPEN_SOURCE 500 -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdbool.h> -#include <dbus/dbus.h> -#include "swaybar/tray/dbus.h" -#include "list.h" -#include "log.h" - -static list_t *items = NULL; -static list_t *hosts = NULL; - -/** - * Describes the function of the StatusNotifierWatcher - * See https://freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierWatcher/ - * - * 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. - */ -static const char *interface_xml = - "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'" - "'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>" - "<node>" - " <interface name='org.freedesktop.DBus.Introspectable'>" - " <method name='Introspect'>" - " <arg name='xml_data' direction='out' type='s'/>" - " </method>" - " </interface>" - " <interface name='org.freedesktop.DBus.Properties'>" - " <method name='Get'>" - " <arg name='interface' direction='in' type='s'/>" - " <arg name='propname' direction='in' type='s'/>" - " <arg name='value' direction='out' type='v'/>" - " </method>" - " <method name='Set'>" - " <arg name='interface' direction='in' type='s'/>" - " <arg name='propname' direction='in' type='s'/>" - " <arg name='value' direction='in' type='v'/>" - " </method>" - " <method name='GetAll'>" - " <arg name='interface' direction='in' type='s'/>" - " <arg name='props' direction='out' type='a{sv}'/>" - " </method>" - " </interface>" - " <interface name='org.freedesktop.StatusNotifierWatcher'>" - " <method name='RegisterStatusNotifierItem'>" - " <arg type='s' name='service' direction='in'/>" - " </method>" - " <method name='RegisterStatusNotifierHost'>" - " <arg type='s' name='service' direction='in'/>" - " </method>" - " <property name='RegisteredStatusNotifierItems' type='as' access='read'/>" - " <property name='IsStatusNotifierHostRegistered' type='b' access='read'/>" - " <property name='ProtocolVersion' type='i' access='read'/>" - " <signal name='StatusNotifierItemRegistered'>" - " <arg type='s' name='service' direction='out'/>" - " </signal>" - " <signal name='StatusNotifierItemUnregistered'>" - " <arg type='s' name='service' direction='out'/>" - " </signal>" - " <signal name='StatusNotifierHostRegistered'>" - " <arg type='' name='service' direction='out'/>" - " </signal>" - " </interface>" - "</node>"; - -static void host_registered_signal(DBusConnection *connection) { - // Send one signal for each protocol - DBusMessage *signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.freedesktop.StatusNotifierWatcher", - "StatusNotifierHostRegistered"); - - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); - - - signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.kde.StatusNotifierWatcher", - "StatusNotifierHostRegistered"); - - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); -} -static void item_registered_signal(DBusConnection *connection, const char *name) { - DBusMessage *signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.freedesktop.StatusNotifierWatcher", - "StatusNotifierItemRegistered"); - dbus_message_append_args(signal, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); - - signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.kde.StatusNotifierWatcher", - "StatusNotifierItemRegistered"); - dbus_message_append_args(signal, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); -} -static void item_unregistered_signal(DBusConnection *connection, const char *name) { - DBusMessage *signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.freedesktop.StatusNotifierWatcher", - "StatusNotifierItemUnregistered"); - dbus_message_append_args(signal, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); - - signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.kde.StatusNotifierWatcher", - "StatusNotifierItemUnregistered"); - dbus_message_append_args(signal, - DBUS_TYPE_STRING, &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; - - reply = dbus_message_new_method_return(request); - dbus_message_append_args(reply, - DBUS_TYPE_STRING, &interface_xml, - DBUS_TYPE_INVALID); - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); -} - -static void register_item(DBusConnection *connection, DBusMessage *message) { - DBusError error; - char *name; - - dbus_error_init(&error); - if (!dbus_message_get_args(message, &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error parsing method args: %s\n", error.message); - } - - sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"\n", 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 (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); - - // It's silly, but xembedsniproxy wants a reply for this function - DBusMessage *reply = dbus_message_new_method_return(message); - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); -} - -static void register_host(DBusConnection *connection, DBusMessage *message) { - DBusError error; - char *name; - - dbus_error_init(&error); - if (!dbus_message_get_args(message, &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error parsing method args: %s\n", error.message); - } - - sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"\n", name); - - // Don't add duplicate or not real host - if (!dbus_validate_bus_name(name, NULL)) { - sway_log(L_INFO, "This item is not valid, we cannot keep track of it."); - return; - } - - - if (list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name) != -1) { - return; - } - if (!dbus_bus_name_has_owner(connection, name, &error)) { - return; - } - - list_add(hosts, strdup(name)); - host_registered_signal(connection); -} - -static void get_property(DBusConnection *connection, DBusMessage *message) { - DBusError error; - char *interface; - char *property; - - dbus_error_init(&error); - if (!dbus_message_get_args(message, &error, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &property, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error parsing prop args: %s\n", error.message); - return; - } - - if (strcmp(property, "RegisteredStatusNotifierItems") == 0) { - sway_log(L_INFO, "Replying with items\n"); - DBusMessage *reply; - reply = dbus_message_new_method_return(message); - DBusMessageIter iter; - DBusMessageIter sub; - DBusMessageIter subsub; - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, - "as", &sub); - dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, - "s", &subsub); - - for (int i = 0; i < items->length; ++i) { - dbus_message_iter_append_basic(&subsub, - DBUS_TYPE_STRING, &items->items[i]); - } - - dbus_message_iter_close_container(&sub, &subsub); - dbus_message_iter_close_container(&iter, &sub); - - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } else if (strcmp(property, "IsStatusNotifierHostRegistered") == 0) { - DBusMessage *reply; - DBusMessageIter iter; - DBusMessageIter sub; - int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1; - - reply = dbus_message_new_method_return(message); - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, - "b", &sub); - dbus_message_iter_append_basic(&sub, - DBUS_TYPE_BOOLEAN, ®istered); - - dbus_message_iter_close_container(&iter, &sub); - - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } else if (strcmp(property, "ProtocolVersion") == 0) { - DBusMessage *reply; - DBusMessageIter iter; - DBusMessageIter sub; - const int version = 0; - - reply = dbus_message_new_method_return(message); - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, - "i", &sub); - dbus_message_iter_append_basic(&sub, - DBUS_TYPE_INT32, &version); - - dbus_message_iter_close_container(&iter, &sub); - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } -} - -static void set_property(DBusConnection *connection, DBusMessage *message) { - // All properties are read only and we don't allow new properties - return; -} - -static void get_all(DBusConnection *connection, DBusMessage *message) { - DBusMessage *reply; - reply = dbus_message_new_method_return(message); - DBusMessageIter iter; /* a{v} */ - DBusMessageIter arr; - DBusMessageIter dict; - DBusMessageIter sub; - DBusMessageIter subsub; - int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1; - const int version = 0; - const char *prop; - - // Could clean this up with a function for each prop - dbus_message_iter_init_append(reply, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - "{sv}", &arr); - - prop = "RegisteredStatusNotifierItems"; - dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY, - NULL, &dict); - dbus_message_iter_append_basic(&dict, - DBUS_TYPE_STRING, &prop); - dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, - "as", &sub); - dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, - "s", &subsub); - for (int i = 0; i < items->length; ++i) { - dbus_message_iter_append_basic(&subsub, - DBUS_TYPE_STRING, &items->items[i]); - } - dbus_message_iter_close_container(&sub, &subsub); - dbus_message_iter_close_container(&dict, &sub); - dbus_message_iter_close_container(&arr, &dict); - - prop = "IsStatusNotifierHostRegistered"; - dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY, - NULL, &dict); - dbus_message_iter_append_basic(&dict, - DBUS_TYPE_STRING, &prop); - dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, - "b", &sub); - dbus_message_iter_append_basic(&sub, - DBUS_TYPE_BOOLEAN, ®istered); - dbus_message_iter_close_container(&dict, &sub); - dbus_message_iter_close_container(&arr, &dict); - - prop = "ProtocolVersion"; - dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY, - NULL, &dict); - dbus_message_iter_append_basic(&dict, - DBUS_TYPE_STRING, &prop); - dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, - "i", &sub); - dbus_message_iter_append_basic(&sub, - DBUS_TYPE_INT32, &version); - dbus_message_iter_close_container(&dict, &sub); - dbus_message_iter_close_container(&arr, &dict); - - dbus_message_iter_close_container(&iter, &arr); - - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); -} - -static DBusHandlerResult message_handler(DBusConnection *connection, - DBusMessage *message, void *data) { - const char *interface_name = dbus_message_get_interface(message); - const char *member_name = dbus_message_get_member(message); - - // In order of the xml above - if (strcmp(interface_name, "org.freedesktop.DBus.Introspectable") == 0 && - strcmp(member_name, "Introspect") == 0) { - // We don't have an introspect for KDE - respond_to_introspect(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (strcmp(interface_name, "org.freedesktop.DBus.Properties") == 0) { - if (strcmp(member_name, "Get") == 0) { - get_property(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (strcmp(member_name, "Set") == 0) { - set_property(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (strcmp(member_name, "GetAll") == 0) { - get_all(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else { - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - } else if (strcmp(interface_name, "org.freedesktop.StatusNotifierWatcher") == 0 || - strcmp(interface_name, "org.kde.StatusNotifierWatcher") == 0) { - if (strcmp(member_name, "RegisterStatusNotifierItem") == 0) { - register_item(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (strcmp(member_name, "RegisterStatusNotifierHost") == 0) { - register_host(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else { - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static DBusHandlerResult signal_handler(DBusConnection *connection, - DBusMessage *message, void *_data) { - if (dbus_message_is_signal(message, "org.freedesktop.DBus", "NameOwnerChanged")) { - // Only eat the message if it is name that we are watching - const char *name; - const char *old_owner; - const char *new_owner; - int index; - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &old_owner, - DBUS_TYPE_STRING, &new_owner, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error getting LostName args"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - if (strcmp(new_owner, "") != 0) { - // Name is not lost - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - if ((index = list_seq_find(items, (int (*)(const void *, const void *))strcmp, name)) != -1) { - sway_log(L_INFO, "Status Notifier Item lost %s", name); - free(items->items[index]); - list_del(items, index); - item_unregistered_signal(connection, name); - - return DBUS_HANDLER_RESULT_HANDLED; - } - if ((index = list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name)) != -1) { - sway_log(L_INFO, "Status Notifier Host lost %s", name); - free(hosts->items[index]); - list_del(hosts, index); - - return DBUS_HANDLER_RESULT_HANDLED; - } - } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static const DBusObjectPathVTable vtable = { - .message_function = message_handler, - .unregister_function = NULL, -}; - -int init_sni_watcher() { - DBusError error; - dbus_error_init(&error); - if (!conn) { - sway_log(L_ERROR, "Connection is null, cannot initiate StatusNotifierWatcher"); - return -1; - } - - items = create_list(); - hosts = create_list(); - - int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher", - DBUS_NAME_FLAG_REPLACE_EXISTING, - &error); - if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - sway_log(L_DEBUG, "Got watcher name"); - } else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) { - 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); - return -1; - } - - status = dbus_bus_request_name(conn, "org.kde.StatusNotifierWatcher", - DBUS_NAME_FLAG_REPLACE_EXISTING, - &error); - if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - sway_log(L_DEBUG, "Got kde watcher name"); - } else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) { - 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); - return -1; - } - - dbus_connection_try_register_object_path(conn, - "/StatusNotifierWatcher", - &vtable, NULL, &error); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus_err: %s\n", error.message); - return -1; - } - - dbus_bus_add_match(conn, - "type='signal',\ - sender='org.freedesktop.DBus',\ - interface='org.freedesktop.DBus',\ - member='NameOwnerChanged'", - &error); - - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "DBus error getting match args: %s", error.message); - } - - dbus_connection_add_filter(conn, signal_handler, NULL, NULL); - return 0; -} diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c deleted file mode 100644 index 91c3af06..00000000 --- a/swaybar/tray/tray.c +++ /dev/null @@ -1,398 +0,0 @@ -#define _XOPEN_SOURCE 700 -#include <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <sys/wait.h> -#include <dbus/dbus.h> -#include "swaybar/bar.h" -#include "swaybar/tray/tray.h" -#include "swaybar/tray/dbus.h" -#include "swaybar/tray/sni.h" -#include "swaybar/tray/sni_watcher.h" -#include "swaybar/bar.h" -#include "swaybar/config.h" -#include "list.h" -#include "log.h" - -struct tray *tray; - -static void register_host(char *name) { - DBusMessage *message; - - message = dbus_message_new_method_call( - "org.freedesktop.StatusNotifierWatcher", - "/StatusNotifierWatcher", - "org.freedesktop.StatusNotifierWatcher", - "RegisterStatusNotifierHost"); - if (!message) { - sway_log(L_ERROR, "Cannot allocate dbus method call"); - return; - } - - dbus_message_append_args(message, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - - dbus_connection_send(conn, message, NULL); - - 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; - 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; - } - - // 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); - - dbus_message_iter_recurse(&variant, &array); - for (int i = 0; i < len; i++) { - const char *name; - dbus_message_iter_get_basic(&array, &name); - - 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; - } - } - -bail: - dbus_message_unref(reply); - dbus_pending_call_unref(pending); - return; -} -static void get_items() { - DBusPendingCall *pending; - DBusMessage *message = dbus_message_new_method_call( - "org.freedesktop.StatusNotifierWatcher", - "/StatusNotifierWatcher", - "org.freedesktop.DBus.Properties", - "Get"); - - const char *iface = "org.freedesktop.StatusNotifierWatcher"; - const char *prop = "RegisteredStatusNotifierItems"; - dbus_message_append_args(message, - DBUS_TYPE_STRING, &iface, - DBUS_TYPE_STRING, &prop, - DBUS_TYPE_INVALID); - - bool status = - dbus_connection_send_with_reply(conn, message, &pending, -1); - dbus_message_unref(message); - - if (!(pending || status)) { - sway_log(L_ERROR, "Could not get items"); - return; - } - - dbus_pending_call_set_notify(pending, get_items_reply, NULL, NULL); -} - -static DBusHandlerResult signal_handler(DBusConnection *connection, - DBusMessage *message, void *_data) { - if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher", - "StatusNotifierItemRegistered")) { - const char *name; - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error getting StatusNotifierItemRegistered args"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - if (list_seq_find(tray->items, sni_str_cmp, name) == -1) { - struct StatusNotifierItem *item = sni_create(name); - - if (item) { - list_add(tray->items, item); - dirty = true; - } - } - - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher", - "StatusNotifierItemUnregistered")) { - const char *name; - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error getting StatusNotifierItemUnregistered args"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - int index; - if ((index = list_seq_find(tray->items, sni_str_cmp, name)) != -1) { - sni_free(tray->items->items[index]); - list_del(tray->items, index); - dirty = true; - } else { - // 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"); - get_items(); - } - - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierItem", - "NewIcon") || dbus_message_is_signal(message, - "org.kde.StatusNotifierItem", "NewIcon")) { - const char *name; - int index; - struct StatusNotifierItem *item; - - name = dbus_message_get_sender(message); - if ((index = list_seq_find(tray->items, sni_uniq_cmp, name)) != -1) { - item = tray->items->items[index]; - sway_log(L_INFO, "NewIcon signal from item %s", item->name); - get_icon(item); - } - - return DBUS_HANDLER_RESULT_HANDLED; - } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static int init_host() { - tray = (struct tray *)malloc(sizeof(tray)); - - tray->items = create_list(); - - DBusError error; - dbus_error_init(&error); - char *name = NULL; - if (!conn) { - sway_log(L_ERROR, "Connection is null, cannot init SNI host"); - goto err; - } - name = calloc(sizeof(char), 256); - - if (!name) { - sway_log(L_ERROR, "Cannot allocate name"); - goto err; - } - - pid_t pid = getpid(); - if (snprintf(name, 256, "org.freedesktop.StatusNotifierHost-%d", pid) - >= 256) { - sway_log(L_ERROR, "Cannot get host name because string is too short." - "This should not happen"); - goto err; - } - - // We want to be the sole owner of this name - if (dbus_bus_request_name(conn, name, DBUS_NAME_FLAG_DO_NOT_QUEUE, - &error) != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - sway_log(L_ERROR, "Cannot get host name and start the tray"); - goto err; - } - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "Dbus err getting host name: %s\n", error.message); - goto err; - } - sway_log(L_DEBUG, "Got host name"); - - register_host(name); - - get_items(); - - // Perhaps use addmatch helper functions like wlc does? - dbus_bus_add_match(conn, - "type='signal',\ - sender='org.freedesktop.StatusNotifierWatcher',\ - member='StatusNotifierItemRegistered'", - &error); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus_err: %s", error.message); - goto err; - } - dbus_bus_add_match(conn, - "type='signal',\ - sender='org.freedesktop.StatusNotifierWatcher',\ - member='StatusNotifierItemUnregistered'", - &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, - "type='signal',\ - interface='org.freedesktop.StatusNotifierItem',\ - member='NewIcon'", - &error); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus_err %s", error.message); - goto err; - } - dbus_bus_add_match(conn, - "type='signal',\ - interface='org.kde.StatusNotifierItem',\ - member='NewIcon'", - &error); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus_err %s", error.message); - goto err; - } - - dbus_connection_add_filter(conn, signal_handler, NULL, NULL); - - free(name); - return 0; - -err: - // TODO better handle errors - free(name); - return -1; -} - -void tray_mouse_event(struct output *output, int x, int y, - uint32_t button, uint32_t state) { - - struct window *window = output->window; - uint32_t tray_padding = swaybar.config->tray_padding; - int tray_width = window->width * window->scale; - - for (int i = 0; i < output->items->length; ++i) { - struct sni_icon_ref *item = - output->items->items[i]; - int icon_width = cairo_image_surface_get_width(item->icon); - - tray_width -= tray_padding; - if (x <= tray_width && x >= tray_width - icon_width) { - if (button == swaybar.config->activate_button) { - sni_activate(item->ref, x, y); - } else if (button == swaybar.config->context_button) { - sni_context_menu(item->ref, x, y); - } else if (button == swaybar.config->secondary_button) { - sni_secondary(item->ref, x, y); - } - break; - } - tray_width -= icon_width; - } -} - -uint32_t tray_render(struct output *output, struct config *config) { - struct window *window = output->window; - cairo_t *cairo = window->cairo; - - // Tray icons - uint32_t tray_padding = config->tray_padding; - uint32_t tray_width = window->width * window->scale; - const int item_size = (window->height * window->scale) - (2 * tray_padding); - - if (item_size < 0) { - // Can't render items if the padding is too large - return tray_width; - } - - if (config->tray_output && strcmp(config->tray_output, output->name) != 0) { - return tray_width; - } - - for (int i = 0; i < tray->items->length; ++i) { - struct StatusNotifierItem *item = - tray->items->items[i]; - if (!item->image) { - continue; - } - - struct sni_icon_ref *render_item = NULL; - int j; - for (j = i; j < output->items->length; ++j) { - struct sni_icon_ref *ref = - output->items->items[j]; - if (ref->ref == item) { - render_item = ref; - break; - } else { - sni_icon_ref_free(ref); - list_del(output->items, j); - } - } - - if (!render_item) { - render_item = sni_icon_ref_create(item, item_size); - list_add(output->items, render_item); - } else if (item->dirty) { - // item needs re-render - sni_icon_ref_free(render_item); - output->items->items[j] = render_item = - sni_icon_ref_create(item, item_size); - } - - tray_width -= tray_padding; - tray_width -= item_size; - - cairo_operator_t op = cairo_get_operator(cairo); - cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); - cairo_set_source_surface(cairo, render_item->icon, tray_width, tray_padding); - cairo_rectangle(cairo, tray_width, tray_padding, item_size, item_size); - cairo_fill(cairo); - cairo_set_operator(cairo, op); - - item->dirty = false; - } - - - if (tray_width != window->width * window->scale) { - tray_width -= tray_padding; - } - - return tray_width; -} - -void init_tray(struct bar *bar) { - if (!bar->config->tray_output || strcmp(bar->config->tray_output, "none") != 0) { - /* Connect to the D-Bus */ - dbus_init(); - - /* Start the SNI watcher */ - init_sni_watcher(); - - /* Start the SNI host */ - init_host(); - } -} |