yambar/bar/bar.c
Aivars Vaivods 6212f98dd3 Fixed bar not reappearing after being fully hidden
Somewhere between versions 0.16 and 0.18 wlroots implemented rendering
code optimizations. One of optimizations was to not call frame callback
for invisible surfaces. This caused bar to not reappear, if it was fully
hidden behind non-transparent window.
2025-07-11 22:49:28 +03:00

515 lines
16 KiB
C

#include "bar.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, &center_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 void
set_visible(struct bar *bar, bool visible)
{
struct private *b = bar->private;
if (b->layer != BAR_LAYER_HIDDEN)
return;
b->visible = visible;
b->backend.iface->update_visibility(bar);
}
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) {
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->visible = false;
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->set_visible = &set_visible;
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;
}