yambar/bar/bar.c
Daniel Eklöf 4f3aa8c6b0
bar: do a synchronous “refresh” *before* starting the modules
This ensures the surface has been mapped, and our “current” output is
known.

This fixes an issue where modules depending on the current output
being known failed to update correctly during startup.
2021-10-22 18:05:20 +02:00

505 lines
15 KiB
C

#include "bar.h"
#include "private.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <threads.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.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/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];
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, &center_width, &right_width);
int y = bar->border.top_width;
int x = bar->border.left_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);
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, &center_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)
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(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;
}