aboutsummaryrefslogtreecommitdiff
path: root/systray/tray.c
diff options
context:
space:
mode:
Diffstat (limited to 'systray/tray.c')
-rw-r--r--systray/tray.c237
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++;
+ }
+}