diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index c6db5691f9..b98fccfd9b 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -65,6 +65,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/include/swaybar/tray/item_kde.h b/include/swaybar/tray/item_kde.h index b5bcf93751..667d205836 100644 --- a/include/swaybar/tray/item_kde.h +++ b/include/swaybar/tray/item_kde.h @@ -35,5 +35,6 @@ struct swaybar_sni_kde { struct swaybar_sni_kde *create_sni(char *id, struct swaybar_tray *tray); void destroy_sni(struct swaybar_sni_kde *sni); +void sni_handle_click(struct swaybar_sni_kde *sni, int x, int y, enum x11_button button); #endif diff --git a/swaybar/bar.c b/swaybar/bar.c index 050a83478a..3f5cc9bb4d 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -210,12 +210,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_kde.c b/swaybar/tray/item_kde.c index dc2c28e88f..e9179394ee 100644 --- a/swaybar/tray/item_kde.c +++ b/swaybar/tray/item_kde.c @@ -92,3 +92,26 @@ void destroy_sni(struct swaybar_sni_kde *sni) { // free(sni->attention_icon_name); free(sni); } + +void sni_handle_click(struct swaybar_sni_kde *sni, int x, int y, enum x11_button button) { + sd_bus *bus = sni->tray->bus; + + char *method; + switch (button) { + case LEFT: method = "ContextMenu"; break; + case RIGHT: method = "Activate"; break; // TODO use itemismenu prop + case MIDDLE: method = "SecondaryActivate"; break; + default: method = NULL; + } + + if (method) { + sd_bus_error error = SD_BUS_ERROR_NULL; + // Remember: "is" for scroll + int ret = sd_bus_call_method(bus, sni->service, sni->path, interface, + method, &error, NULL, "ii", x, y); + if (ret < 0) { + wlr_log(WLR_DEBUG, "Failed to click button: %s", error.message); + } + sd_bus_error_free(&error); + } +} diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index 59cef0831c..964df1f1a0 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -1,12 +1,17 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include +#include #include "swaybar/bar.h" +#include "swaybar/config.h" +#include "swaybar/input.h" #include "swaybar/tray/icon.h" #include "swaybar/tray/item_kde.h" #include "swaybar/tray/host_kde.h" #include "swaybar/tray/tray.h" #include "swaybar/tray/watcher_kde.h" +#include "cairo.h" #include "log.h" struct swaybar_tray *create_tray(void) { @@ -56,6 +61,80 @@ void tray_in(int fd, short mask, void *data) { } } +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 sni %s", (char *)data); + + struct swaybar_tray *tray = output->bar->tray; + struct swaybar_host_kde *host_kde = tray->host_kde; + + for (int i = 0; i < host_kde->items->length; ++i) { + struct swaybar_sni_kde *sni = host_kde->items->items[i]; + if (strcmp(sni->dbus_id, data) == 0) { + // guess actual position since wayland doesn't expose it + // output pos + output dimensions + position + bar gaps + int global_x = output->output_x + x; + int global_y = output->output_y + (output->bar->config->position & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP ? y : (int) output->output_height - y); + + wlr_log(WLR_DEBUG, "Guessing click at %d %d", global_x, global_y); + + sni_handle_click(sni, global_x, global_y, button); + return HOTSPOT_IGNORE; + } + } + + return HOTSPOT_PROCESS; +} + uint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x) { - return 0; // placeholder + struct swaybar_tray *tray = output->bar->tray; + struct swaybar_host_kde *host_kde = tray->host_kde; + + uint32_t height = output->height * output->scale; + + for (int i = 0; i < host_kde->items->length; ++i) { + struct swaybar_sni_kde *sni = host_kde->items->items[i]; + cairo_surface_t *icon = sni->icon; + + int icon_height = cairo_image_surface_get_height(icon); + int icon_width = cairo_image_surface_get_width(icon); + + + int padding = 2; // TODO get from config + + int padded_height = icon_height + 2*padding; + int padded_width = icon_width + 2*padding; + + if (padded_height > (int) height) { + int width = (double) (height - 2*padding) / icon_height * icon_width; + + icon = cairo_image_surface_scale(icon, width, height - 2*padding); // TODO free + + padded_height = height; + padded_width = width + 2*padding; + } + + *x -= padded_width; + double y = (int) floor((height - padded_height) / 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_width, padded_height); + cairo_fill(cairo); + cairo_set_operator(cairo, op); + + struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); + hotspot->x = *x; + hotspot->y = 0; + hotspot->width = icon_width; + hotspot->height = height; + hotspot->callback = icon_hotspot_callback; + hotspot->destroy = free; + hotspot->data = strdup(sni->dbus_id); + wl_list_insert(&output->hotspots, &hotspot->link); + } + return output->height; }