This commit is contained in:
Nathan Schulte 2025-10-01 10:32:54 +01:00 committed by GitHub
commit c57f4d9969
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 171 additions and 12 deletions

View file

@ -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 <b><i><u><a href=""><img src=" alt="">, 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

View file

@ -5,6 +5,7 @@
#include <wayland-cursor.h>
#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.

View file

@ -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;