forked from external/yambar
450 lines
13 KiB
C
450 lines
13 KiB
C
#include "bar.h"
|
|
#include "private.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <threads.h>
|
|
#include <assert.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
|
|
|
|
/*
|
|
* Calculate total width of left/center/rigth 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];
|
|
assert(e != NULL);
|
|
*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];
|
|
assert(e != NULL);
|
|
*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];
|
|
assert(e != NULL);
|
|
*right += b->left_spacing + e->width + b->right_spacing;
|
|
}
|
|
|
|
/* No spacing on the edges (that's what the margins are for) */
|
|
*left -= b->left_spacing + b->right_spacing;
|
|
*center -= b->left_spacing + b->right_spacing;
|
|
*right -= b->left_spacing + b->right_spacing;
|
|
}
|
|
|
|
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});
|
|
|
|
if (bar->border.width > 0) {
|
|
pixman_image_fill_rectangles(
|
|
PIXMAN_OP_OVER, pix, &bar->border.color, 4,
|
|
(pixman_rectangle16_t[]){
|
|
{0, 0, bar->width, bar->border.width},
|
|
{0, 0, bar->border.width, bar->height_with_border},
|
|
{bar->width - bar->border.width, 0, bar->border.width, bar->height_with_border},
|
|
{0, bar->height_with_border - bar->border.width, bar->width, bar->border.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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
int left_width, center_width, right_width;
|
|
calculate_widths(bar, &left_width, ¢er_width, &right_width);
|
|
|
|
int y = bar->border.width;
|
|
int x = bar->border.width + bar->left_margin - bar->left_spacing;
|
|
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);
|
|
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);
|
|
x += bar->left_spacing + e->width + bar->right_spacing;
|
|
}
|
|
|
|
x = bar->width - (
|
|
right_width +
|
|
bar->left_spacing +
|
|
bar->right_margin +
|
|
bar->border.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);
|
|
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;
|
|
|
|
if (b->cursor_name != NULL && strcmp(b->cursor_name, cursor) == 0)
|
|
return;
|
|
|
|
free(b->cursor_name);
|
|
b->cursor_name = strdup(cursor);
|
|
|
|
b->backend.iface->set_cursor(bar, cursor);
|
|
}
|
|
|
|
static void
|
|
on_mouse(struct bar *_bar, enum mouse_event event, int x, int y)
|
|
{
|
|
struct private *bar = _bar->private;
|
|
|
|
if ((y < bar->border.width ||
|
|
y >= (bar->height_with_border - bar->border.width)) ||
|
|
(x < bar->border.width || x >= (bar->width - bar->border.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.width + bar->left_margin - bar->left_spacing;
|
|
for (size_t i = 0; i < bar->left.count; i++) {
|
|
struct exposable *e = bar->left.exps[i];
|
|
|
|
mx += bar->left_spacing;
|
|
if (x >= mx && x < mx + e->width) {
|
|
if (e->on_mouse != NULL)
|
|
e->on_mouse(e, _bar, event, 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];
|
|
|
|
mx += bar->left_spacing;
|
|
if (x >= mx && x < mx + e->width) {
|
|
if (e->on_mouse != NULL)
|
|
e->on_mouse(e, _bar, event, x - mx, y);
|
|
return;
|
|
}
|
|
|
|
mx += e->width + bar->right_spacing;
|
|
}
|
|
|
|
mx = bar->width - (right_width
|
|
+ bar->left_spacing +
|
|
bar->right_margin +
|
|
bar->border.width);
|
|
|
|
for (size_t i = 0; i < bar->right.count; i++) {
|
|
struct exposable *e = bar->right.exps[i];
|
|
|
|
mx += bar->left_spacing;
|
|
if (x >= mx && x < mx + e->width) {
|
|
if (e->on_mouse != NULL)
|
|
e->on_mouse(e, _bar, event, x - mx, y);
|
|
return;
|
|
}
|
|
|
|
mx += e->width + bar->right_spacing;
|
|
}
|
|
|
|
set_cursor(_bar, "left_ptr");
|
|
}
|
|
|
|
|
|
static int
|
|
run(struct bar *_bar)
|
|
{
|
|
struct private *bar = _bar->private;
|
|
|
|
bar->height_with_border = bar->height + 2 * bar->border.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");
|
|
|
|
/* Start modules */
|
|
thrd_t thrd_left[bar->left.count];
|
|
thrd_t thrd_center[bar->center.count];
|
|
thrd_t thrd_right[bar->right.count];
|
|
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
|
|
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)
|
|
LOG_ERR("module: LEFT #%zu: non-zero exit value: %d", i, 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)
|
|
LOG_ERR("module: CENTER #%zu: non-zero exit value: %d", i, 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)
|
|
LOG_ERR("module: RIGHT #%zu: non-zero exit value: %d", i, 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(BAR_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->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->border.width = config->border.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;
|
|
|
|
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;
|
|
}
|