mirror of
https://codeberg.org/dnkl/yambar.git
synced 2025-04-23 04:25:42 +02:00
Introduce a new icon particle. It follows the icon spec (https://specifications.freedesktop.org/icon-theme-spec/latest/index.html). Rendering logic is taken from fuzzel (using nanosvg + libpng), while loading logic is taken from sway. Standard usage is with `use-tag = false` which expands the provided string template and then loads the string as the icon name. There are settings to manually override the base paths, themes, etc. The second usage which is required for tray support is a special icon tag that transfers raw pixmaps. With `use-tag = true` it first expands the string, and then uses that output to find an icon pixmap tag. To reduce memory usage, themes are reference counted so they can be passed down the configuration stack without having to load them in multiple times. For programmability, a fallback particle can be specified if no icon/tag is found `fallback: ...`. And the new icon pixmap tag can be existence checked in map conditions using `+{tag_name}`. Future work to be done in follow up diffs: 1. Icon caching. Currently performs an icon lookup on each instantiation & a render on each refresh. 2. Theme caching. Changing theme directories results in a new "theme collection" being created resulting in the possibility of duplicated theme loading.
502 lines
16 KiB
C
502 lines
16 KiB
C
#include "bar.h"
|
|
#include "icon.h"
|
|
#include "private.h"
|
|
|
|
#include <assert.h>
|
|
#include <pthread.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <threads.h>
|
|
#include <unistd.h>
|
|
|
|
#include <sys/eventfd.h>
|
|
|
|
#define LOG_MODULE "bar"
|
|
#define LOG_ENABLE_DBG 0
|
|
#include "../log.h"
|
|
|
|
#if defined(ENABLE_X11)
|
|
#include "xcb.h"
|
|
#endif
|
|
|
|
#if defined(ENABLE_WAYLAND)
|
|
#include "wayland.h"
|
|
#endif
|
|
|
|
#define max(x, y) ((x) > (y) ? (x) : (y))
|
|
|
|
/*
|
|
* Calculate total width of left/center/right groups.
|
|
* Note: begin_expose() must have been called
|
|
*/
|
|
static void
|
|
calculate_widths(const struct private *b, int *left, int *center, int *right)
|
|
{
|
|
*left = 0;
|
|
*center = 0;
|
|
*right = 0;
|
|
|
|
for (size_t i = 0; i < b->left.count; i++) {
|
|
struct exposable *e = b->left.exps[i];
|
|
if (e->width > 0)
|
|
*left += b->left_spacing + e->width + b->right_spacing;
|
|
}
|
|
|
|
for (size_t i = 0; i < b->center.count; i++) {
|
|
struct exposable *e = b->center.exps[i];
|
|
if (e->width > 0)
|
|
*center += b->left_spacing + e->width + b->right_spacing;
|
|
}
|
|
|
|
for (size_t i = 0; i < b->right.count; i++) {
|
|
struct exposable *e = b->right.exps[i];
|
|
if (e->width > 0)
|
|
*right += b->left_spacing + e->width + b->right_spacing;
|
|
}
|
|
|
|
/* No spacing on the edges (that's what the margins are for) */
|
|
if (*left > 0)
|
|
*left -= b->left_spacing + b->right_spacing;
|
|
if (*center > 0)
|
|
*center -= b->left_spacing + b->right_spacing;
|
|
if (*right > 0)
|
|
*right -= b->left_spacing + b->right_spacing;
|
|
|
|
assert(*left >= 0);
|
|
assert(*center >= 0);
|
|
assert(*right >= 0);
|
|
}
|
|
|
|
static void
|
|
expose(const struct bar *_bar)
|
|
{
|
|
const struct private *bar = _bar->private;
|
|
pixman_image_t *pix = bar->pix;
|
|
|
|
pixman_image_fill_rectangles(PIXMAN_OP_SRC, pix, &bar->background, 1,
|
|
&(pixman_rectangle16_t){0, 0, bar->width, bar->height_with_border});
|
|
|
|
pixman_image_fill_rectangles(
|
|
PIXMAN_OP_OVER, pix, &bar->border.color, 4,
|
|
(pixman_rectangle16_t[]){
|
|
/* Left */
|
|
{0, 0, bar->border.left_width, bar->height_with_border},
|
|
|
|
/* Right */
|
|
{bar->width - bar->border.right_width, 0, bar->border.right_width, bar->height_with_border},
|
|
|
|
/* Top */
|
|
{bar->border.left_width, 0, bar->width - bar->border.left_width - bar->border.right_width,
|
|
bar->border.top_width},
|
|
|
|
/* Bottom */
|
|
{bar->border.left_width, bar->height_with_border - bar->border.bottom_width,
|
|
bar->width - bar->border.left_width - bar->border.right_width, bar->border.bottom_width},
|
|
});
|
|
|
|
for (size_t i = 0; i < bar->left.count; i++) {
|
|
struct module *m = bar->left.mods[i];
|
|
struct exposable *e = bar->left.exps[i];
|
|
if (e != NULL)
|
|
e->destroy(e);
|
|
bar->left.exps[i] = module_begin_expose(m);
|
|
assert(bar->left.exps[i]->width >= 0);
|
|
}
|
|
|
|
for (size_t i = 0; i < bar->center.count; i++) {
|
|
struct module *m = bar->center.mods[i];
|
|
struct exposable *e = bar->center.exps[i];
|
|
if (e != NULL)
|
|
e->destroy(e);
|
|
bar->center.exps[i] = module_begin_expose(m);
|
|
assert(bar->center.exps[i]->width >= 0);
|
|
}
|
|
|
|
for (size_t i = 0; i < bar->right.count; i++) {
|
|
struct module *m = bar->right.mods[i];
|
|
struct exposable *e = bar->right.exps[i];
|
|
if (e != NULL)
|
|
e->destroy(e);
|
|
bar->right.exps[i] = module_begin_expose(m);
|
|
assert(bar->right.exps[i]->width >= 0);
|
|
}
|
|
|
|
int left_width, center_width, right_width;
|
|
calculate_widths(bar, &left_width, ¢er_width, &right_width);
|
|
|
|
int y = bar->border.top_width;
|
|
int x = bar->border.left_width + bar->left_margin - bar->left_spacing;
|
|
pixman_region32_t clip;
|
|
pixman_region32_init_rect(
|
|
&clip, bar->border.left_width + bar->left_margin, bar->border.top_width,
|
|
(bar->width - bar->left_margin - bar->right_margin - bar->border.left_width - bar->border.right_width),
|
|
bar->height);
|
|
pixman_image_set_clip_region32(pix, &clip);
|
|
pixman_region32_fini(&clip);
|
|
|
|
for (size_t i = 0; i < bar->left.count; i++) {
|
|
const struct exposable *e = bar->left.exps[i];
|
|
e->expose(e, pix, x + bar->left_spacing, y, bar->height);
|
|
if (e->width > 0)
|
|
x += bar->left_spacing + e->width + bar->right_spacing;
|
|
}
|
|
|
|
x = bar->width / 2 - center_width / 2 - bar->left_spacing;
|
|
for (size_t i = 0; i < bar->center.count; i++) {
|
|
const struct exposable *e = bar->center.exps[i];
|
|
e->expose(e, pix, x + bar->left_spacing, y, bar->height);
|
|
if (e->width > 0)
|
|
x += bar->left_spacing + e->width + bar->right_spacing;
|
|
}
|
|
|
|
x = bar->width - (right_width + bar->left_spacing + bar->right_margin + bar->border.right_width);
|
|
|
|
for (size_t i = 0; i < bar->right.count; i++) {
|
|
const struct exposable *e = bar->right.exps[i];
|
|
e->expose(e, pix, x + bar->left_spacing, y, bar->height);
|
|
if (e->width > 0)
|
|
x += bar->left_spacing + e->width + bar->right_spacing;
|
|
}
|
|
|
|
bar->backend.iface->commit(_bar);
|
|
}
|
|
|
|
static void
|
|
refresh(const struct bar *bar)
|
|
{
|
|
const struct private *b = bar->private;
|
|
b->backend.iface->refresh(bar);
|
|
}
|
|
|
|
static void
|
|
set_cursor(struct bar *bar, const char *cursor)
|
|
{
|
|
struct private *b = bar->private;
|
|
b->backend.iface->set_cursor(bar, cursor);
|
|
}
|
|
|
|
static const char *
|
|
output_name(const struct bar *bar)
|
|
{
|
|
const struct private *b = bar->private;
|
|
return b->backend.iface->output_name(bar);
|
|
}
|
|
|
|
static void
|
|
on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, int x, int y)
|
|
{
|
|
struct private *bar = _bar->private;
|
|
|
|
if ((y < bar->border.top_width || y >= (bar->height_with_border - bar->border.bottom_width))
|
|
|| (x < bar->border.left_width || x >= (bar->width - bar->border.right_width))) {
|
|
set_cursor(_bar, "left_ptr");
|
|
return;
|
|
}
|
|
|
|
int left_width, center_width, right_width;
|
|
calculate_widths(bar, &left_width, ¢er_width, &right_width);
|
|
|
|
int mx = bar->border.left_width + bar->left_margin - bar->left_spacing;
|
|
for (size_t i = 0; i < bar->left.count; i++) {
|
|
struct exposable *e = bar->left.exps[i];
|
|
|
|
if (e->width == 0)
|
|
continue;
|
|
|
|
mx += bar->left_spacing;
|
|
if (x >= mx && x < mx + e->width) {
|
|
if (e->on_mouse != NULL)
|
|
e->on_mouse(e, _bar, event, btn, x - mx, y);
|
|
return;
|
|
}
|
|
|
|
mx += e->width + bar->right_spacing;
|
|
}
|
|
|
|
mx = bar->width / 2 - center_width / 2 - bar->left_spacing;
|
|
for (size_t i = 0; i < bar->center.count; i++) {
|
|
struct exposable *e = bar->center.exps[i];
|
|
|
|
if (e->width == 0)
|
|
continue;
|
|
|
|
mx += bar->left_spacing;
|
|
if (x >= mx && x < mx + e->width) {
|
|
if (e->on_mouse != NULL)
|
|
e->on_mouse(e, _bar, event, btn, x - mx, y);
|
|
return;
|
|
}
|
|
|
|
mx += e->width + bar->right_spacing;
|
|
}
|
|
|
|
mx = bar->width - (right_width + bar->left_spacing + bar->right_margin + bar->border.right_width);
|
|
|
|
for (size_t i = 0; i < bar->right.count; i++) {
|
|
struct exposable *e = bar->right.exps[i];
|
|
|
|
if (e->width == 0)
|
|
continue;
|
|
|
|
mx += bar->left_spacing;
|
|
if (x >= mx && x < mx + e->width) {
|
|
if (e->on_mouse != NULL)
|
|
e->on_mouse(e, _bar, event, btn, x - mx, y);
|
|
return;
|
|
}
|
|
|
|
mx += e->width + bar->right_spacing;
|
|
}
|
|
|
|
set_cursor(_bar, "left_ptr");
|
|
}
|
|
|
|
static void
|
|
set_module_thread_name(thrd_t id, struct module *mod)
|
|
{
|
|
char title[16];
|
|
if (mod->description != NULL)
|
|
strncpy(title, mod->description(mod), sizeof(title));
|
|
else
|
|
strncpy(title, "mod:<unknown>", sizeof(title));
|
|
|
|
title[15] = '\0';
|
|
|
|
if (pthread_setname_np(id, title) < 0)
|
|
LOG_ERRNO("failed to set thread title");
|
|
}
|
|
|
|
static int
|
|
run(struct bar *_bar)
|
|
{
|
|
struct private *bar = _bar->private;
|
|
|
|
bar->height_with_border = bar->height + bar->border.top_width + bar->border.bottom_width;
|
|
|
|
if (!bar->backend.iface->setup(_bar)) {
|
|
bar->backend.iface->cleanup(_bar);
|
|
if (write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t))
|
|
LOG_ERRNO("failed to signal abort");
|
|
return 1;
|
|
}
|
|
|
|
set_cursor(_bar, "left_ptr");
|
|
expose(_bar);
|
|
|
|
/* Start modules */
|
|
thrd_t thrd_left[max(bar->left.count, 1)];
|
|
thrd_t thrd_center[max(bar->center.count, 1)];
|
|
thrd_t thrd_right[max(bar->right.count, 1)];
|
|
|
|
for (size_t i = 0; i < bar->left.count; i++) {
|
|
struct module *mod = bar->left.mods[i];
|
|
|
|
mod->abort_fd = _bar->abort_fd;
|
|
thrd_create(&thrd_left[i], (int (*)(void *))bar->left.mods[i]->run, mod);
|
|
set_module_thread_name(thrd_left[i], mod);
|
|
}
|
|
for (size_t i = 0; i < bar->center.count; i++) {
|
|
struct module *mod = bar->center.mods[i];
|
|
|
|
mod->abort_fd = _bar->abort_fd;
|
|
thrd_create(&thrd_center[i], (int (*)(void *))bar->center.mods[i]->run, mod);
|
|
set_module_thread_name(thrd_center[i], mod);
|
|
}
|
|
for (size_t i = 0; i < bar->right.count; i++) {
|
|
struct module *mod = bar->right.mods[i];
|
|
|
|
mod->abort_fd = _bar->abort_fd;
|
|
thrd_create(&thrd_right[i], (int (*)(void *))bar->right.mods[i]->run, mod);
|
|
set_module_thread_name(thrd_right[i], mod);
|
|
}
|
|
|
|
LOG_DBG("all modules started");
|
|
|
|
bar->backend.iface->loop(_bar, &expose, &on_mouse);
|
|
|
|
LOG_DBG("shutting down");
|
|
|
|
/* Wait for modules to terminate */
|
|
int ret = 0;
|
|
int mod_ret;
|
|
for (size_t i = 0; i < bar->left.count; i++) {
|
|
thrd_join(thrd_left[i], &mod_ret);
|
|
if (mod_ret != 0) {
|
|
const struct module *m = bar->left.mods[i];
|
|
LOG_ERR("module: LEFT #%zu (%s): non-zero exit value: %d", i, m->description(m), mod_ret);
|
|
}
|
|
ret = ret == 0 && mod_ret != 0 ? mod_ret : ret;
|
|
}
|
|
for (size_t i = 0; i < bar->center.count; i++) {
|
|
thrd_join(thrd_center[i], &mod_ret);
|
|
if (mod_ret != 0) {
|
|
const struct module *m = bar->center.mods[i];
|
|
LOG_ERR("module: CENTER #%zu (%s): non-zero exit value: %d", i, m->description(m), mod_ret);
|
|
}
|
|
ret = ret == 0 && mod_ret != 0 ? mod_ret : ret;
|
|
}
|
|
for (size_t i = 0; i < bar->right.count; i++) {
|
|
thrd_join(thrd_right[i], &mod_ret);
|
|
if (mod_ret != 0) {
|
|
const struct module *m = bar->right.mods[i];
|
|
LOG_ERR("module: RIGHT #%zu (%s): non-zero exit value: %d", i, m->description(m), mod_ret);
|
|
}
|
|
ret = ret == 0 && mod_ret != 0 ? mod_ret : ret;
|
|
}
|
|
|
|
LOG_DBG("modules joined");
|
|
|
|
bar->backend.iface->cleanup(_bar);
|
|
|
|
LOG_DBG("bar exiting");
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
destroy(struct bar *bar)
|
|
{
|
|
struct private *b = bar->private;
|
|
|
|
for (size_t i = 0; i < b->left.count; i++) {
|
|
struct module *m = b->left.mods[i];
|
|
struct exposable *e = b->left.exps[i];
|
|
if (e != NULL)
|
|
e->destroy(e);
|
|
m->destroy(m);
|
|
}
|
|
for (size_t i = 0; i < b->center.count; i++) {
|
|
struct module *m = b->center.mods[i];
|
|
struct exposable *e = b->center.exps[i];
|
|
if (e != NULL)
|
|
e->destroy(e);
|
|
m->destroy(m);
|
|
}
|
|
for (size_t i = 0; i < b->right.count; i++) {
|
|
struct module *m = b->right.mods[i];
|
|
struct exposable *e = b->right.exps[i];
|
|
if (e != NULL)
|
|
e->destroy(e);
|
|
m->destroy(m);
|
|
}
|
|
|
|
free(b->left.mods);
|
|
free(b->left.exps);
|
|
free(b->center.mods);
|
|
free(b->center.exps);
|
|
free(b->right.mods);
|
|
free(b->right.exps);
|
|
free(b->monitor);
|
|
free(b->backend.data);
|
|
|
|
free(bar->private);
|
|
free(bar);
|
|
}
|
|
|
|
struct bar *
|
|
bar_new(const struct bar_config *config)
|
|
{
|
|
void *backend_data = NULL;
|
|
const struct backend *backend_iface = NULL;
|
|
|
|
switch (config->backend) {
|
|
case BAR_BACKEND_AUTO:
|
|
#if defined(ENABLE_X11) && !defined(ENABLE_WAYLAND)
|
|
backend_data = bar_backend_xcb_new();
|
|
backend_iface = &xcb_backend_iface;
|
|
#elif !defined(ENABLE_X11) && defined(ENABLE_WAYLAND)
|
|
backend_data = bar_backend_wayland_new();
|
|
backend_iface = &wayland_backend_iface;
|
|
#else
|
|
if (getenv("WAYLAND_DISPLAY") != NULL) {
|
|
backend_data = bar_backend_wayland_new();
|
|
backend_iface = &wayland_backend_iface;
|
|
} else {
|
|
backend_data = bar_backend_xcb_new();
|
|
backend_iface = &xcb_backend_iface;
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case BAR_BACKEND_XCB:
|
|
#if defined(ENABLE_X11)
|
|
backend_data = bar_backend_xcb_new();
|
|
backend_iface = &xcb_backend_iface;
|
|
#else
|
|
LOG_ERR("yambar was compiled without the XCB backend");
|
|
return NULL;
|
|
#endif
|
|
break;
|
|
|
|
case BAR_BACKEND_WAYLAND:
|
|
#if defined(ENABLE_WAYLAND)
|
|
backend_data = bar_backend_wayland_new();
|
|
backend_iface = &wayland_backend_iface;
|
|
#else
|
|
LOG_ERR("yambar was compiled without the Wayland backend");
|
|
return NULL;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
if (backend_data == NULL)
|
|
return NULL;
|
|
|
|
struct private *priv = calloc(1, sizeof(*priv));
|
|
priv->monitor = config->monitor != NULL ? strdup(config->monitor) : NULL;
|
|
priv->layer = config->layer;
|
|
priv->location = config->location;
|
|
priv->height = config->height;
|
|
priv->background = config->background;
|
|
priv->left_spacing = config->left_spacing;
|
|
priv->right_spacing = config->right_spacing;
|
|
priv->left_margin = config->left_margin;
|
|
priv->right_margin = config->right_margin;
|
|
priv->trackpad_sensitivity = config->trackpad_sensitivity;
|
|
priv->border.left_width = config->border.left_width;
|
|
priv->border.right_width = config->border.right_width;
|
|
priv->border.top_width = config->border.top_width;
|
|
priv->border.bottom_width = config->border.bottom_width;
|
|
priv->border.color = config->border.color;
|
|
priv->border.left_margin = config->border.left_margin;
|
|
priv->border.right_margin = config->border.right_margin;
|
|
priv->border.top_margin = config->border.top_margin;
|
|
priv->border.bottom_margin = config->border.bottom_margin;
|
|
priv->left.mods = malloc(config->left.count * sizeof(priv->left.mods[0]));
|
|
priv->left.exps = calloc(config->left.count, sizeof(priv->left.exps[0]));
|
|
priv->center.mods = malloc(config->center.count * sizeof(priv->center.mods[0]));
|
|
priv->center.exps = calloc(config->center.count, sizeof(priv->center.exps[0]));
|
|
priv->right.mods = malloc(config->right.count * sizeof(priv->right.mods[0]));
|
|
priv->right.exps = calloc(config->right.count, sizeof(priv->right.exps[0]));
|
|
priv->left.count = config->left.count;
|
|
priv->center.count = config->center.count;
|
|
priv->right.count = config->right.count;
|
|
priv->backend.data = backend_data;
|
|
priv->backend.iface = backend_iface;
|
|
|
|
for (size_t i = 0; i < priv->left.count; i++)
|
|
priv->left.mods[i] = config->left.mods[i];
|
|
for (size_t i = 0; i < priv->center.count; i++)
|
|
priv->center.mods[i] = config->center.mods[i];
|
|
for (size_t i = 0; i < priv->right.count; i++)
|
|
priv->right.mods[i] = config->right.mods[i];
|
|
|
|
struct bar *bar = calloc(1, sizeof(*bar));
|
|
bar->private = priv;
|
|
bar->run = &run;
|
|
bar->destroy = &destroy;
|
|
bar->refresh = &refresh;
|
|
bar->set_cursor = &set_cursor;
|
|
bar->output_name = &output_name;
|
|
|
|
for (size_t i = 0; i < priv->left.count; i++)
|
|
priv->left.mods[i]->bar = bar;
|
|
for (size_t i = 0; i < priv->center.count; i++)
|
|
priv->center.mods[i]->bar = bar;
|
|
for (size_t i = 0; i < priv->right.count; i++)
|
|
priv->right.mods[i]->bar = bar;
|
|
|
|
return bar;
|
|
}
|