From 74655f835aa9fe0e976473d443f62d253602696c Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 7 Dec 2018 12:33:45 +0000 Subject: swaybar: add StatusNotifierItem to tray --- swaybar/tray/item.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 swaybar/tray/item.c (limited to 'swaybar/tray/item.c') diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c new file mode 100644 index 00000000..561a3425 --- /dev/null +++ b/swaybar/tray/item.c @@ -0,0 +1,236 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include "swaybar/tray/host.h" +#include "swaybar/tray/item.h" +#include "swaybar/tray/tray.h" +#include "list.h" +#include "log.h" + +// TODO menu + +static int read_pixmap(sd_bus_message *msg, struct swaybar_sni *sni, + const char *prop, list_t **dest) { + int ret = sd_bus_message_enter_container(msg, 'a', "(iiay)"); + if (ret < 0) { + wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, strerror(-ret)); + return ret; + } + + if (sd_bus_message_at_end(msg, 0)) { + return ret; + } + + list_t *pixmaps = create_list(); + if (!pixmaps) { + return -12; // -ENOMEM + } + + while (!sd_bus_message_at_end(msg, 0)) { + ret = sd_bus_message_enter_container(msg, 'r', "iiay"); + if (ret < 0) { + wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, strerror(-ret)); + goto error; + } + + int size; + ret = sd_bus_message_read(msg, "ii", NULL, &size); + if (ret < 0) { + wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, strerror(-ret)); + goto error; + } + + const void *pixels; + size_t npixels; + ret = sd_bus_message_read_array(msg, 'y', &pixels, &npixels); + if (ret < 0) { + wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, strerror(-ret)); + goto error; + } + + struct swaybar_pixmap *pixmap = + malloc(sizeof(struct swaybar_pixmap) + npixels); + pixmap->size = size; + memcpy(pixmap->pixels, pixels, npixels); + list_add(pixmaps, pixmap); + + sd_bus_message_exit_container(msg); + } + *dest = pixmaps; + + return ret; +error: + list_free_items_and_destroy(pixmaps); + return ret; +} + +struct get_property_data { + struct swaybar_sni *sni; + const char *prop; + const char *type; + void *dest; +}; + +static int get_property_callback(sd_bus_message *msg, void *data, + sd_bus_error *error) { + struct get_property_data *d = data; + struct swaybar_sni *sni = d->sni; + const char *prop = d->prop; + const char *type = d->type; + void *dest = d->dest; + + int ret; + if (sd_bus_message_is_method_error(msg, NULL)) { + sd_bus_error err = *sd_bus_message_get_error(msg); + wlr_log(WLR_DEBUG, "Failed to get property %s: %s", prop, err.message); + ret = -sd_bus_error_get_errno(&err); + goto cleanup; + } + + ret = sd_bus_message_enter_container(msg, 'v', type); + if (ret < 0) { + wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, strerror(-ret)); + goto cleanup; + } + + if (!type) { + ret = read_pixmap(msg, sni, prop, dest); + if (ret < 0) { + goto cleanup; + } + } else { + ret = sd_bus_message_read(msg, type, dest); + if (ret < 0) { + wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, + strerror(-ret)); + goto cleanup; + } else if (*type == 's' || *type == 'o') { + char **str = dest; + *str = strdup(*str); + } + } +cleanup: + free(data); + return ret; +} + +static void sni_get_property_async(struct swaybar_sni *sni, const char *prop, + const char *type, void *dest) { + struct get_property_data *data = malloc(sizeof(struct get_property_data)); + data->sni = sni; + data->prop = prop; + data->type = type; + data->dest = dest; + int ret = sd_bus_call_method_async(sni->tray->bus, NULL, sni->service, + sni->path, "org.freedesktop.DBus.Properties", "Get", + get_property_callback, data, "ss", sni->interface, prop); + if (ret < 0) { + wlr_log(WLR_DEBUG, "Failed to get property %s: %s", prop, strerror(-ret)); + } +} + +static int handle_new_icon(sd_bus_message *msg, void *data, sd_bus_error *error) { + struct swaybar_sni *sni = data; + wlr_log(WLR_DEBUG, "%s has new IconName", sni->watcher_id); + + free(sni->icon_name); + sni->icon_name = NULL; + sni_get_property_async(sni, "IconName", "s", &sni->icon_name); + + list_free_items_and_destroy(sni->icon_pixmap); + sni->icon_pixmap = NULL; + sni_get_property_async(sni, "IconPixmap", NULL, &sni->icon_pixmap); + + return 0; +} + +static int handle_new_attention_icon(sd_bus_message *msg, void *data, + sd_bus_error *error) { + struct swaybar_sni *sni = data; + wlr_log(WLR_DEBUG, "%s has new AttentionIconName", sni->watcher_id); + + free(sni->attention_icon_name); + sni->attention_icon_name = NULL; + sni_get_property_async(sni, "AttentionIconName", "s", &sni->attention_icon_name); + + list_free_items_and_destroy(sni->attention_icon_pixmap); + sni->attention_icon_pixmap = NULL; + sni_get_property_async(sni, "AttentionIconPixmap", NULL, &sni->attention_icon_pixmap); + + return 0; +} + +static int handle_new_status(sd_bus_message *msg, void *data, sd_bus_error *error) { + char *status; + int ret = sd_bus_message_read(msg, "s", &status); + if (ret < 0) { + wlr_log(WLR_DEBUG, "Failed to read new status message: %s", strerror(-ret)); + } else { + struct swaybar_sni *sni = data; + free(sni->status); + sni->status = strdup(status); + wlr_log(WLR_DEBUG, "%s has new Status '%s'", sni->watcher_id, status); + } + return ret; +} + +static void sni_match_signal(struct swaybar_sni *sni, char *signal, + sd_bus_message_handler_t callback) { + int ret = sd_bus_match_signal(sni->tray->bus, NULL, sni->service, sni->path, + sni->interface, signal, callback, sni); + if (ret < 0) { + wlr_log(WLR_DEBUG, "Failed to subscribe to signal %s: %s", signal, + strerror(-ret)); + } +} + +struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray) { + struct swaybar_sni *sni = calloc(1, sizeof(struct swaybar_sni)); + if (!sni) { + return NULL; + } + sni->tray = tray; + sni->watcher_id = strdup(id); + char *path_ptr = strchr(id, '/'); + if (!path_ptr) { + sni->service = strdup(id); + sni->path = strdup("/StatusNotifierItem"); + sni->interface = "org.freedesktop.StatusNotifierItem"; + } else { + sni->service = strndup(id, path_ptr - id); + sni->path = strdup(path_ptr); + sni->interface = "org.kde.StatusNotifierItem"; + } + + // Ignored: Category, Id, Title, WindowId, OverlayIconName, + // OverlayIconPixmap, AttentionMovieName, ToolTip + sni_get_property_async(sni, "Status", "s", &sni->status); + sni_get_property_async(sni, "IconName", "s", &sni->icon_name); + sni_get_property_async(sni, "IconPixmap", NULL, &sni->icon_pixmap); + sni_get_property_async(sni, "AttentionIconName", "s", &sni->attention_icon_name); + sni_get_property_async(sni, "AttentionIconPixmap", NULL, &sni->attention_icon_pixmap); + sni_get_property_async(sni, "ItemIsMenu", "b", &sni->item_is_menu); + sni_get_property_async(sni, "Menu", "o", &sni->menu); + + sni_match_signal(sni, "NewIcon", handle_new_icon); + sni_match_signal(sni, "NewAttentionIcon", handle_new_attention_icon); + sni_match_signal(sni, "NewStatus", handle_new_status); + + return sni; +} + +void destroy_sni(struct swaybar_sni *sni) { + if (!sni) { + return; + } + + free(sni->watcher_id); + free(sni->service); + free(sni->path); + free(sni->status); + free(sni->icon_name); + free(sni->icon_pixmap); + free(sni->attention_icon_name); + free(sni->menu); + free(sni); +} -- cgit v1.2.3 From fa2c5282c1d09eced82a9c15a4ee26e7b00a37c4 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 7 Dec 2018 12:37:33 +0000 Subject: swaybar: implement tray rendering --- include/swaybar/tray/item.h | 6 ++++ swaybar/tray/item.c | 84 +++++++++++++++++++++++++++++++++++++++++++++ swaybar/tray/tray.c | 25 +++++++++++++- 3 files changed, 114 insertions(+), 1 deletion(-) (limited to 'swaybar/tray/item.c') diff --git a/include/swaybar/tray/item.h b/include/swaybar/tray/item.h index 57affb78..53d7e48c 100644 --- a/include/swaybar/tray/item.h +++ b/include/swaybar/tray/item.h @@ -1,10 +1,14 @@ #ifndef _SWAYBAR_TRAY_ITEM_H #define _SWAYBAR_TRAY_ITEM_H +#include #include +#include #include "swaybar/tray/tray.h" #include "list.h" +struct swaybar_output; + struct swaybar_pixmap { int size; unsigned char pixels[]; @@ -34,5 +38,7 @@ struct swaybar_sni { struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray); void destroy_sni(struct swaybar_sni *sni); +uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x, + struct swaybar_sni *sni); #endif diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 561a3425..019e8ed0 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -1,14 +1,27 @@ #define _POSIX_C_SOURCE 200809L +#include +#include #include #include +#include "swaybar/bar.h" +#include "swaybar/config.h" #include "swaybar/tray/host.h" +#include "swaybar/tray/icon.h" #include "swaybar/tray/item.h" #include "swaybar/tray/tray.h" +#include "background-image.h" +#include "cairo.h" #include "list.h" #include "log.h" // TODO menu +static bool sni_ready(struct swaybar_sni *sni) { + return sni->status && (sni->status[0] == 'N' ? + sni->attention_icon_name || sni->attention_icon_pixmap : + sni->icon_name || sni->icon_pixmap); +} + static int read_pixmap(sd_bus_message *msg, struct swaybar_sni *sni, const char *prop, list_t **dest) { int ret = sd_bus_message_enter_container(msg, 'a', "(iiay)"); @@ -234,3 +247,74 @@ void destroy_sni(struct swaybar_sni *sni) { free(sni->menu); free(sni); } + +uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x, + struct swaybar_sni *sni) { + uint32_t height = output->height * output->scale; + int padding = output->bar->config->tray_padding; + int ideal_size = height - 2*padding; + if ((ideal_size < sni->min_size || ideal_size > sni->max_size) && sni_ready(sni)) { + bool icon_found = false; + char *icon_name = sni->status[0] == 'N' ? + sni->attention_icon_name : sni->icon_name; + if (icon_name) { + char *icon_path = find_icon(sni->tray->themes, sni->tray->basedirs, + icon_name, ideal_size, output->bar->config->icon_theme, + &sni->min_size, &sni->max_size); + if (icon_path) { + cairo_surface_destroy(sni->icon); + sni->icon = load_background_image(icon_path); + free(icon_path); + icon_found = true; + } + } + if (!icon_found) { + list_t *pixmaps = sni->status[0] == 'N' ? + sni->attention_icon_pixmap : sni->icon_pixmap; + if (pixmaps) { + int idx = -1; + unsigned smallest_error = -1; // UINT_MAX + for (int i = 0; i < pixmaps->length; ++i) { + struct swaybar_pixmap *pixmap = pixmaps->items[i]; + unsigned error = (ideal_size - pixmap->size) * + (ideal_size < pixmap->size ? -1 : 1); + if (error < smallest_error) { + smallest_error = error; + idx = i; + } + } + struct swaybar_pixmap *pixmap = pixmaps->items[idx]; + cairo_surface_destroy(sni->icon); + sni->icon = cairo_image_surface_create_for_data(pixmap->pixels, + CAIRO_FORMAT_ARGB32, pixmap->size, pixmap->size, + cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pixmap->size)); + } + } + } + + if (!sni->icon) { + // TODO fallback + return 0; + } + + cairo_surface_t *icon; + int actual_size = cairo_image_surface_get_height(sni->icon); + int icon_size = actual_size < ideal_size ? + actual_size * (ideal_size / actual_size) : ideal_size; + icon = cairo_image_surface_scale(sni->icon, icon_size, icon_size); + + int padded_size = icon_size + 2*padding; + *x -= padded_size; + int y = floor((height - padded_size) / 2.0); + + cairo_operator_t op = cairo_get_operator(cairo); + cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); + cairo_set_source_surface(cairo, icon, *x + padding, y + padding); + cairo_rectangle(cairo, *x, y, padded_size, padded_size); + cairo_fill(cairo); + cairo_set_operator(cairo, op); + + cairo_surface_destroy(icon); + + return output->height; +} diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index 4ef28a78..f186ed86 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -2,6 +2,7 @@ #include #include #include +#include "swaybar/config.h" #include "swaybar/bar.h" #include "swaybar/tray/icon.h" #include "swaybar/tray/host.h" @@ -70,6 +71,28 @@ void tray_in(int fd, short mask, void *data) { } } +static int cmp_output(const void *item, const void *cmp_to) { + return strcmp(item, cmp_to); +} + uint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x) { - return 0; // placeholder + struct swaybar_config *config = output->bar->config; + if (config->tray_outputs) { + if (list_seq_find(config->tray_outputs, cmp_output, output->name) == -1) { + return 0; + } + } // else display on all + + if ((int) output->height*output->scale <= 2*config->tray_padding) { + return 2*config->tray_padding + 1; + } + + uint32_t max_height = 0; + struct swaybar_tray *tray = output->bar->tray; + for (int i = 0; i < tray->items->length; ++i) { + uint32_t h = render_sni(cairo, output, x, tray->items->items[i]); + max_height = h > max_height ? h : max_height; + } + + return max_height; } -- cgit v1.2.3 From 6becde024680503100c94702ed7d1fbf4d01576e Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 7 Dec 2018 12:39:35 +0000 Subject: swaybar: implement mouse events for tray --- include/swaybar/bar.h | 2 ++ swaybar/bar.c | 8 +++-- swaybar/tray/item.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 2 deletions(-) (limited to 'swaybar/tray/item.c') diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index ef27012d..ad942242 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -70,6 +70,8 @@ struct swaybar_output { struct pool_buffer *current_buffer; bool dirty; bool frame_scheduled; + + uint32_t output_height, output_width, output_x, output_y; }; struct swaybar_workspace { diff --git a/swaybar/bar.c b/swaybar/bar.c index 668168eb..ebb9bc12 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -215,12 +215,16 @@ struct wl_output_listener output_listener = { static void xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) { - // Who cares + struct swaybar_output *output = data; + output->output_x = x; + output->output_y = y; } static void xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) { - // Who cares + struct swaybar_output *output = data; + output->output_height = height; + output->output_width = width; } static void xdg_output_handle_done(void *data, diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 019e8ed0..450b6882 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -5,6 +5,7 @@ #include #include "swaybar/bar.h" #include "swaybar/config.h" +#include "swaybar/input.h" #include "swaybar/tray/host.h" #include "swaybar/tray/icon.h" #include "swaybar/tray/item.h" @@ -13,6 +14,7 @@ #include "cairo.h" #include "list.h" #include "log.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" // TODO menu @@ -248,6 +250,83 @@ void destroy_sni(struct swaybar_sni *sni) { free(sni); } +static void handle_click(struct swaybar_sni *sni, int x, int y, + enum x11_button button, int delta) { + const char *method = sni->tray->bar->config->tray_bindings[button]; + if (!method) { + static const char *default_bindings[10] = { + "nop", + "Activate", + "SecondaryActivate", + "ContextMenu", + "ScrollUp", + "ScrollDown", + "ScrollLeft", + "ScrollRight", + "nop", + "nop" + }; + method = default_bindings[button]; + } + if (strcmp(method, "nop") == 0) { + return; + } + if (sni->item_is_menu && strcmp(method, "Activate") == 0) { + method = "ContextMenu"; + } + + if (strncmp(method, "Scroll", strlen("Scroll")) == 0) { + char dir = method[strlen("Scroll")]; + char *orientation = (dir = 'U' || dir == 'D') ? "vertical" : "horizontal"; + int sign = (dir == 'U' || dir == 'L') ? -1 : 1; + + int ret = sd_bus_call_method_async(sni->tray->bus, NULL, sni->service, + sni->path, sni->interface, "Scroll", NULL, NULL, "is", + delta*sign, orientation); + if (ret < 0) { + wlr_log(WLR_DEBUG, "Failed to scroll on SNI: %s", strerror(-ret)); + } + } else { + int ret = sd_bus_call_method_async(sni->tray->bus, NULL, sni->service, + sni->path, sni->interface, method, NULL, NULL, "ii", x, y); + if (ret < 0) { + wlr_log(WLR_DEBUG, "Failed to click on SNI: %s", strerror(-ret)); + } + } +} + +static int cmp_sni_id(const void *item, const void *cmp_to) { + const struct swaybar_sni *sni = item; + return strcmp(sni->watcher_id, cmp_to); +} + +static enum hotspot_event_handling icon_hotspot_callback( + struct swaybar_output *output, struct swaybar_hotspot *hotspot, + int x, int y, enum x11_button button, void *data) { + wlr_log(WLR_DEBUG, "Clicked on Status Notifier Item '%s'", (char *)data); + + struct swaybar_tray *tray = output->bar->tray; + int idx = list_seq_find(tray->items, cmp_sni_id, data); + + if (idx != -1) { + struct swaybar_sni *sni = tray->items->items[idx]; + // guess global position since wayland doesn't expose it + struct swaybar_config *config = tray->bar->config; + int global_x = output->output_x + config->gaps.left + x; + bool top_bar = config->position & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; + int global_y = output->output_y + (top_bar ? config->gaps.top + y: + (int) output->output_height - config->gaps.bottom - y); + + wlr_log(WLR_DEBUG, "Guessing click at (%d, %d)", global_x, global_y); + handle_click(sni, global_x, global_y, button, 1); // TODO get delta from event + return HOTSPOT_IGNORE; + } else { + wlr_log(WLR_DEBUG, "but it doesn't exist"); + } + + return HOTSPOT_PROCESS; +} + uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x, struct swaybar_sni *sni) { uint32_t height = output->height * output->scale; @@ -316,5 +395,15 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x, cairo_surface_destroy(icon); + struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); + hotspot->x = *x; + hotspot->y = 0; + hotspot->width = height; + hotspot->height = height; + hotspot->callback = icon_hotspot_callback; + hotspot->destroy = free; + hotspot->data = strdup(sni->watcher_id); + wl_list_insert(&output->hotspots, &hotspot->link); + return output->height; } -- cgit v1.2.3 From 2fd41fe9c85a866173fc2770ed7669871258bced Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 7 Dec 2018 12:40:45 +0000 Subject: swaybar: set bar dirty on SNI event --- include/swaybar/bar.h | 2 ++ swaybar/bar.c | 2 +- swaybar/tray/host.c | 2 ++ swaybar/tray/item.c | 13 +++++++++++++ 4 files changed, 18 insertions(+), 1 deletion(-) (limited to 'swaybar/tray/item.c') diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index ad942242..e377b8de 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -88,6 +88,8 @@ bool bar_setup(struct swaybar *bar, const char *socket_path); void bar_run(struct swaybar *bar); void bar_teardown(struct swaybar *bar); +void set_bar_dirty(struct swaybar *bar); + /* * Determines whether the bar should be visible and changes it to be so. * If the current visibility of the bar is the different to what it should be, diff --git a/swaybar/bar.c b/swaybar/bar.c index ebb9bc12..4fd9c488 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -124,7 +124,7 @@ static void destroy_layer_surface(struct swaybar_output *output) { output->frame_scheduled = false; } -static void set_bar_dirty(struct swaybar *bar) { +void set_bar_dirty(struct swaybar *bar) { struct swaybar_output *output; wl_list_for_each(output, &bar->outputs, link) { set_output_dirty(output); diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c index 8ab896d4..c5756f17 100644 --- a/swaybar/tray/host.c +++ b/swaybar/tray/host.c @@ -4,6 +4,7 @@ #include #include #include +#include "swaybar/bar.h" #include "swaybar/tray/host.h" #include "swaybar/tray/item.h" #include "swaybar/tray/tray.h" @@ -56,6 +57,7 @@ static int handle_sni_unregistered(sd_bus_message *msg, void *data, wlr_log(WLR_DEBUG, "Unregistering Status Notifier Item '%s'", id); destroy_sni(tray->items->items[idx]); list_del(tray->items, idx); + set_bar_dirty(tray->bar); } return ret; } diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 450b6882..98d3dc1f 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -24,6 +24,13 @@ static bool sni_ready(struct swaybar_sni *sni) { sni->icon_name || sni->icon_pixmap); } +static void set_sni_dirty(struct swaybar_sni *sni) { + if (sni_ready(sni)) { + sni->min_size = sni->max_size = 0; // invalidate previous icon + set_bar_dirty(sni->tray->bar); + } +} + static int read_pixmap(sd_bus_message *msg, struct swaybar_sni *sni, const char *prop, list_t **dest) { int ret = sd_bus_message_enter_container(msg, 'a', "(iiay)"); @@ -124,6 +131,11 @@ static int get_property_callback(sd_bus_message *msg, void *data, *str = strdup(*str); } } + + if (strcmp(prop, "Status") == 0 || (sni->status && (sni->status[0] == 'N' ? + prop[0] == 'A' : strncmp(prop, "Icon", 4) == 0))) { + set_sni_dirty(sni); + } cleanup: free(data); return ret; @@ -185,6 +197,7 @@ static int handle_new_status(sd_bus_message *msg, void *data, sd_bus_error *erro free(sni->status); sni->status = strdup(status); wlr_log(WLR_DEBUG, "%s has new Status '%s'", sni->watcher_id, status); + set_sni_dirty(sni); } return ret; } -- cgit v1.2.3 From 8ffb7f05293d306959d709063f430182dfbffb7a Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Sun, 16 Dec 2018 18:43:40 +0000 Subject: swaybar: draw a sad face if SNI has no icon --- swaybar/tray/item.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) (limited to 'swaybar/tray/item.c') diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 98d3dc1f..d00339e2 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -384,16 +384,32 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x, } } - if (!sni->icon) { - // TODO fallback - return 0; - } - + int icon_size; cairo_surface_t *icon; - int actual_size = cairo_image_surface_get_height(sni->icon); - int icon_size = actual_size < ideal_size ? - actual_size * (ideal_size / actual_size) : ideal_size; - icon = cairo_image_surface_scale(sni->icon, icon_size, icon_size); + if (sni->icon) { + int actual_size = cairo_image_surface_get_height(sni->icon); + icon_size = actual_size < ideal_size ? + actual_size*(ideal_size/actual_size) : ideal_size; + icon = cairo_image_surface_scale(sni->icon, icon_size, icon_size); + } else { // draw a sad face + icon_size = ideal_size*0.8; + icon = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, icon_size, icon_size); + cairo_t *cairo_icon = cairo_create(icon); + cairo_set_source_u32(cairo_icon, 0xFF0000FF); + cairo_translate(cairo_icon, icon_size/2, icon_size/2); + cairo_scale(cairo_icon, icon_size/2, icon_size/2); + cairo_arc(cairo_icon, 0, 0, 1, 0, 7); + cairo_fill(cairo_icon); + cairo_set_operator(cairo_icon, CAIRO_OPERATOR_CLEAR); + cairo_arc(cairo_icon, 0.35, -0.3, 0.1, 0, 7); + cairo_fill(cairo_icon); + cairo_arc(cairo_icon, -0.35, -0.3, 0.1, 0, 7); + cairo_fill(cairo_icon); + cairo_arc(cairo_icon, 0, 0.75, 0.5, 3.71238898038469, 5.71238898038469); + cairo_set_line_width(cairo_icon, 0.1); + cairo_stroke(cairo_icon); + cairo_destroy(cairo_icon); + } int padded_size = icon_size + 2*padding; *x -= padded_size; -- cgit v1.2.3 From 9e31f5d79ce9de8f1c73013b792aa94f355a1983 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Mon, 17 Dec 2018 23:52:15 +0000 Subject: swaybar: use KDE's SNI IconThemePath property --- include/swaybar/tray/item.h | 1 + swaybar/tray/item.c | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'swaybar/tray/item.c') diff --git a/include/swaybar/tray/item.h b/include/swaybar/tray/item.h index 53d7e48c..9bba7951 100644 --- a/include/swaybar/tray/item.h +++ b/include/swaybar/tray/item.h @@ -34,6 +34,7 @@ struct swaybar_sni { list_t *attention_icon_pixmap; // struct swaybar_pixmap * bool item_is_menu; char *menu; + char *icon_theme_path; // non-standard KDE property }; struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray); diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index d00339e2..41cacd16 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -228,6 +228,7 @@ struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray) { sni->service = strndup(id, path_ptr - id); sni->path = strdup(path_ptr); sni->interface = "org.kde.StatusNotifierItem"; + sni_get_property_async(sni, "IconThemePath", "s", &sni->icon_theme_path); } // Ignored: Category, Id, Title, WindowId, OverlayIconName, @@ -353,6 +354,10 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x, char *icon_path = find_icon(sni->tray->themes, sni->tray->basedirs, icon_name, ideal_size, output->bar->config->icon_theme, &sni->min_size, &sni->max_size); + if (!icon_path && sni->icon_theme_path) { + icon_path = find_icon_in_dir(icon_name, sni->icon_theme_path, + &sni->min_size, &sni->max_size); + } if (icon_path) { cairo_surface_destroy(sni->icon); sni->icon = load_background_image(icon_path); -- cgit v1.2.3