diff options
Diffstat (limited to 'systray/tray.c')
-rw-r--r-- | systray/tray.c | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/systray/tray.c b/systray/tray.c new file mode 100644 index 0000000..7f9b1b0 --- /dev/null +++ b/systray/tray.c @@ -0,0 +1,237 @@ +#include "tray.h" + +#include "icon.h" +#include "item.h" +#include "menu.h" +#include "watcher.h" + +#include <fcft/fcft.h> +#include <pixman.h> +#include <wayland-util.h> + +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#define PIXMAN_COLOR(hex) \ + { .red = ((hex >> 24) & 0xff) * 0x101, \ + .green = ((hex >> 16) & 0xff) * 0x101, \ + .blue = ((hex >> 8) & 0xff) * 0x101, \ + .alpha = (hex & 0xff) * 0x101 } + +static Watcher * +tray_get_watcher(const Tray *tray) +{ + if (!tray) + return NULL; + + return tray->watcher; +} + +static pixman_image_t * +createcanvas(int width, int height, int bgcolor) +{ + pixman_image_t *src, *dest; + pixman_color_t bgcolor_pix = PIXMAN_COLOR(bgcolor); + + dest = pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height, NULL, + 0); + src = pixman_image_create_solid_fill(&bgcolor_pix); + + pixman_image_composite32(PIXMAN_OP_SRC, src, NULL, dest, 0, 0, 0, 0, 0, + 0, width, height); + + pixman_image_unref(src); + return dest; +} + +void +tray_update(Tray *tray) +{ + Item *item; + Watcher *watcher; + int icon_size, i = 0, canvas_width, canvas_height, n_items, spacing; + pixman_image_t *canvas = NULL, *img; + + watcher = tray_get_watcher(tray); + n_items = watcher_get_n_items(watcher); + + if (!n_items) { + if (tray->image) { + pixman_image_unref(tray->image); + tray->image = NULL; + } + tray->cb(tray->monitor); + return; + } + + icon_size = tray->height; + spacing = tray->spacing; + canvas_width = n_items * (icon_size + spacing) + spacing; + canvas_height = tray->height; + + canvas = createcanvas(canvas_width, canvas_height, tray->scheme[1]); + if (!canvas) + goto fail; + + wl_list_for_each(item, &watcher->items, link) { + int slot_x_start = spacing + i * (icon_size + spacing); + int slot_x_end = slot_x_start + icon_size + spacing; + int slot_x_width = slot_x_end - slot_x_start; + + int slot_y_start = 0; + int slot_y_end = canvas_height; + int slot_y_width = slot_y_end - slot_y_start; + + if (item->icon) { + /* Real icon */ + img = item->icon->img; + if (resize_image(img, icon_size, icon_size) < 0) + goto fail; + pixman_image_composite32(PIXMAN_OP_OVER, img, NULL, + canvas, 0, 0, 0, 0, + slot_x_start, 0, canvas_width, + canvas_height); + + } else if (item->appid) { + /* Font glyph alpha mask */ + const struct fcft_glyph *g; + int pen_y, pen_x; + pixman_color_t fg_color = PIXMAN_COLOR(tray->scheme[0]); + pixman_image_t *fg; + + if (item->fallback_icon) { + g = item->fallback_icon; + } else { + g = createfallbackicon(item->appid, + item->fgcolor, + tray->font); + if (!g) + goto fail; + item->fallback_icon = g; + } + + pen_x = slot_x_start + (slot_x_width - g->width) / 2; + pen_y = slot_y_start + (slot_y_width - g->height) / 2; + + fg = pixman_image_create_solid_fill(&fg_color); + pixman_image_composite32(PIXMAN_OP_OVER, fg, g->pix, + canvas, 0, 0, 0, 0, pen_x, + pen_y, canvas_width, + canvas_height); + pixman_image_unref(fg); + } + i++; + } + + if (tray->image) + pixman_image_unref(tray->image); + tray->image = canvas; + tray->cb(tray->monitor); + + return; + +fail: + if (canvas) + pixman_image_unref(canvas); + return; +} + +void +destroytray(Tray *tray) +{ + if (tray->image) + pixman_image_unref(tray->image); + if (tray->font) + fcft_destroy(tray->font); + free(tray); +} + +Tray * +createtray(void *monitor, int height, int spacing, uint32_t *colorscheme, + const char **fonts, const char *fontattrs, TrayNotifyCb cb, + Watcher *watcher) +{ + Tray *tray = NULL; + char fontattrs_my[128]; + struct fcft_font *font = NULL; + + sprintf(fontattrs_my, "%s:%s", fontattrs, "weight:bold"); + + tray = calloc(1, sizeof(Tray)); + font = fcft_from_name(1, fonts, fontattrs_my); + if (!tray || !font) + goto fail; + + tray->monitor = monitor; + tray->height = height; + tray->spacing = spacing; + tray->scheme = colorscheme; + tray->cb = cb; + tray->watcher = watcher; + tray->font = font; + + return tray; + +fail: + if (font) + fcft_destroy(font); + free(tray); + return NULL; +} + +int +tray_get_width(const Tray *tray) +{ + if (tray && tray->image) + return pixman_image_get_width(tray->image); + else + return 0; +} + +int +tray_get_icon_width(const Tray *tray) +{ + if (!tray) + return 0; + + return tray->height; +} + +void +tray_rightclicked(Tray *tray, unsigned int index, const char **menucmd) +{ + Item *item; + Watcher *watcher; + unsigned int count = 0; + + watcher = tray_get_watcher(tray); + + wl_list_for_each(item, &watcher->items, link) { + if (count == index) { + menu_show(watcher->conn, watcher->loop, item->busname, + item->menu_busobj, menucmd); + return; + } + count++; + } +} + +void +tray_leftclicked(Tray *tray, unsigned int index) +{ + Item *item; + Watcher *watcher; + unsigned int count = 0; + + watcher = tray_get_watcher(tray); + + wl_list_for_each(item, &watcher->items, link) { + if (count == index) { + item_activate(item); + return; + } + count++; + } +} |