diff --git a/include/swaybar/tray/item.h b/include/swaybar/tray/item.h index 73937a0c..4ee6b422 100644 --- a/include/swaybar/tray/item.h +++ b/include/swaybar/tray/item.h @@ -15,6 +15,13 @@ struct swaybar_pixmap { unsigned char pixels[]; }; +struct swaybar_sni_tool_tip { + char *icon_name; + list_t *icon_pixmap; // struct swaybar_pixmap * + char *title; + char *description; // can contain HTML subset , see https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/Markup/ +}; + struct swaybar_sni_slot { struct wl_list link; // swaybar_sni::slots struct swaybar_sni *sni; @@ -45,6 +52,10 @@ struct swaybar_sni { list_t *attention_icon_pixmap; // struct swaybar_pixmap * bool item_is_menu; char *menu; + struct swaybar_sni_tool_tip *tool_tip; + char *title; + char *category; + char *id; char *icon_theme_path; // non-standard KDE property struct wl_list slots; // swaybar_sni_slot::link diff --git a/swaybar/input.c b/swaybar/input.c index ada4bc86..de8719b8 100644 --- a/swaybar/input.c +++ b/swaybar/input.c @@ -5,6 +5,7 @@ #include #include "list.h" #include "log.h" +#include "pango.h" #include "swaybar/bar.h" #include "swaybar/config.h" #include "swaybar/input.h" @@ -142,8 +143,68 @@ static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { struct swaybar_seat *seat = data; - seat->pointer.x = wl_fixed_to_double(surface_x); - seat->pointer.y = wl_fixed_to_double(surface_y); + struct swaybar_pointer *pointer = &seat->pointer; + struct swaybar_output *output = pointer->current; + + pointer->x = wl_fixed_to_double(surface_x); + pointer->y = wl_fixed_to_double(surface_y); + + double px = pointer->x * output->scale; + double py = pointer->y * output->scale; + + sway_log(SWAY_DEBUG, "wl_pointer_motion"); + struct swaybar_hotspot *hotspot; + wl_list_for_each(hotspot, &output->hotspots, link) { + if (px >= hotspot->x && py >= hotspot->y + && px < hotspot->x + hotspot->width + && py < hotspot->y + hotspot->height) { + // cursor in hotspot + sway_log(SWAY_DEBUG, "wl_pointer_motion cursor in hotspot %d %d", surface_x, surface_y); + + //struct swaybar *bar = seat->bar; + struct swaybar *bar = output->bar; + struct swaybar_config *config = bar->config; + + // create cairo/pango graphics + cairo_surface_t *recorder = cairo_recording_surface_create(CAIRO_CONTENT_COLOR_ALPHA, NULL); + cairo_t *cairo = cairo_create(recorder); + + cairo_set_source_u32(cairo, config->colors.focused_statusline); + + //pango_printf(cairo, config->font, output->scale, false, "%s", (struct swaybar_sni *)(hotspot->data)->watcher_id); + pango_printf(cairo, config->font, output->scale, false, "%d %d pointer is hovering", surface_x, surface_y); + + // draw icon or menu indicator if needed + int text_width, text_height; + get_text_size(cairo, config->font, &text_width, &text_height, NULL, + output->scale, false, "%d %d pointer is hovering", px ,py); + //int padding = config->tray_padding * output->scale; + //int width = 2 * padding + text_width; + //int height = 2 * padding + text_height; + + //int size = 16; + //int x = -2 * padding - size; + //int y = height + padding + (text_height - size + 1) / 2; + + cairo_t *shm = output->current_buffer->cairo; + + cairo_save(shm); + cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR); + cairo_paint(shm); + cairo_restore(shm); + + cairo_set_source_surface(shm, recorder, surface_x, surface_y); + cairo_paint(shm); + + wl_surface_damage(output->surface, surface_x, surface_y, text_width, text_height); + wl_surface_commit(output->surface); + + cairo_surface_destroy(recorder); + cairo_destroy(cairo); + + return; + } + } } static bool check_bindings(struct swaybar *bar, uint32_t button, @@ -320,7 +381,7 @@ static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { for (uint32_t axis = 0; axis < 2; ++axis) { if (seat->axis[axis].discrete_steps) { for (uint32_t step = 0; step < seat->axis[axis].discrete_steps; ++step) { - // Honestly, it would probabyl make sense to pass in + // Honestly, it would probably make sense to pass in // 'seat->axis[axis].value / seat->axis[axi].discrete_steps' here, // but it's only used to check whether it's positive or negative // so I don't think it's worth the risk of rounding errors. diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 12929743..460f076f 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -43,12 +43,13 @@ static int read_pixmap(sd_bus_message *msg, struct swaybar_sni *sni, if (sd_bus_message_at_end(msg, 0)) { sway_log(SWAY_DEBUG, "%s %s no. of icons = 0", sni->watcher_id, prop); - return ret; + goto finish; } list_t *pixmaps = create_list(); if (!pixmaps) { - return -12; // -ENOMEM + ret = -12; // -ENOMEM + goto finish; } while (!sd_bus_message_at_end(msg, 0)) { @@ -93,7 +94,7 @@ static int read_pixmap(sd_bus_message *msg, struct swaybar_sni *sni, } if (pixmaps->length < 1) { - sway_log(SWAY_DEBUG, "%s %s no. of icons = 0", sni->watcher_id, prop); + sway_log(SWAY_ERROR, "%s %s no. of icons = 0", sni->watcher_id, prop); goto error; } @@ -102,9 +103,55 @@ static int read_pixmap(sd_bus_message *msg, struct swaybar_sni *sni, sway_log(SWAY_DEBUG, "%s %s no. of icons = %d", sni->watcher_id, prop, pixmaps->length); - return ret; + goto finish; error: list_free_items_and_destroy(pixmaps); +finish: + sd_bus_message_exit_container(msg); + return ret; +} + +static int read_sni_tool_tip(sd_bus_message *msg, struct swaybar_sni *sni, + const char *prop, struct swaybar_sni_tool_tip *tool_tip) { + int ret = sd_bus_message_enter_container(msg, 'r', "sa(iiay)ss"); + if (ret < 0) { + sway_log(SWAY_ERROR, "%s %s: %s", sni->watcher_id, prop, strerror(-ret)); + return ret; + } + + free(tool_tip->icon_name); + ret = sd_bus_message_read(msg, "s", &tool_tip->icon_name); + if (ret < 0) { + sway_log(SWAY_ERROR, "%s %s IconName: %s", sni->watcher_id, prop, strerror(-ret)); + goto error; + } + tool_tip->icon_name = strdup(tool_tip->icon_name); + sway_log(SWAY_DEBUG, "%s %s IconName = '%s'", sni->watcher_id, prop, tool_tip->icon_name); + // free(tool_tip->icon_pixmap); // need to free pixmaps first?? + ret = read_pixmap(msg, sni, prop, &tool_tip->icon_pixmap); + if (ret < 0) { + goto error; + } + free(tool_tip->title); + free(tool_tip->description); + ret = sd_bus_message_read(msg, "ss", &tool_tip->title, &tool_tip->description); + if (ret < 0) { + sway_log(SWAY_ERROR, "%s %s Title and Description: %s", sni->watcher_id, prop, strerror(-ret)); + goto error; + } + tool_tip->title = strdup(tool_tip->title); + tool_tip->description = strdup(tool_tip->description); + sway_log(SWAY_DEBUG, "%s %s Title = '%s'", sni->watcher_id, prop, tool_tip->title); + sway_log(SWAY_DEBUG, "%s %s Description = '%s'", sni->watcher_id, prop, tool_tip->description); + + // list_free_items_and_destroy(*dest); + // *dest = pixmaps; + + goto finish; +error: + // list_free_items_and_destroy(pixmaps); +finish: + sd_bus_message_exit_container(msg); return ret; } @@ -136,9 +183,16 @@ static int get_property_callback(sd_bus_message *msg, void *data, } if (!type) { - ret = read_pixmap(msg, sni, prop, dest); - if (ret < 0) { - goto cleanup; + if (strcmp(prop, "ToolTip") == 0) { + ret = read_sni_tool_tip(msg, sni, prop, dest); + if (ret < 0) { + goto cleanup; + } + } else { + ret = read_pixmap(msg, sni, prop, dest); + if (ret < 0) { + goto cleanup; + } } } else { if (*type == 's' || *type == 'o') { @@ -166,6 +220,7 @@ static int get_property_callback(sd_bus_message *msg, void *data, set_sni_dirty(sni); } cleanup: + // sd_bus_message_exit_container(msg); wl_list_remove(&d->link); free(data); return ret; @@ -256,6 +311,29 @@ static int handle_new_status(sd_bus_message *msg, void *data, sd_bus_error *erro return ret; } +static int handle_new_title(sd_bus_message *msg, void *data, sd_bus_error *error) { + // NOTE(nms): unsure if sni_check_msg_sender is valid (I assume this is to check if title property is sent with the + // NewTitle message) + struct swaybar_sni *sni = data; + int ret = sni_check_msg_sender(sni, msg, "title"); + if (ret == 1) { + char *title; + int r = sd_bus_message_read(msg, "s", &title); + if (r < 0) { + sway_log(SWAY_ERROR, "%s new title error: %s", sni->watcher_id, strerror(-ret)); + ret = r; + } else { + free(sni->title); + sni->title = strdup(title); + sway_log(SWAY_DEBUG, "%s has new title = '%s'", sni->watcher_id, title); + set_sni_dirty(sni); + } + } else { + sni_get_property_async(sni, "Title", "s", &sni->title); + } + return ret; +} + static void sni_match_signal_async(struct swaybar_sni *sni, char *signal, sd_bus_message_handler_t callback) { struct swaybar_sni_slot *slot = calloc(1, sizeof(struct swaybar_sni_slot)); @@ -290,8 +368,7 @@ struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray) { sni_get_property_async(sni, "IconThemePath", "s", &sni->icon_theme_path); } - // Ignored: Category, Id, Title, WindowId, OverlayIconName, - // OverlayIconPixmap, AttentionMovieName, ToolTip + // Ignored: WindowId, OverlayIconName, OverlayIconPixmap, AttentionMovieName 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); @@ -299,10 +376,16 @@ struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray) { 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_get_property_async(sni, "Title", "s", &sni->title); + sni_get_property_async(sni, "ToolTip", NULL, &sni->tool_tip); + sni_get_property_async(sni, "Category", "s", &sni->category); + sni_get_property_async(sni, "Id", "s", &sni->id); sni_match_signal_async(sni, "NewIcon", handle_new_icon); sni_match_signal_async(sni, "NewAttentionIcon", handle_new_attention_icon); sni_match_signal_async(sni, "NewStatus", handle_new_status); + sni_match_signal_async(sni, "NewTitle", handle_new_title); + //sni_match_signal_async(sni, "NewToolTip", handle_new_tool_tip); return sni; } @@ -322,6 +405,10 @@ void destroy_sni(struct swaybar_sni *sni) { free(sni->attention_icon_name); list_free_items_and_destroy(sni->attention_icon_pixmap); free(sni->menu); + free(sni->title); + free(sni->tool_tip); + free(sni->category); + free(sni->id); free(sni->icon_theme_path); struct swaybar_sni_slot *slot, *slot_tmp;