bar: call exposable's on_mouse() on mouse motion events

For this to be doable, the bar no longer destroys the module's
exposables after rendering them. Instead, we keep them around, letting
them represent the "current" content of the bar.

Then, when we receive a mouse motion event, we can iterate the
exposables and calculate which one of them the mouse is over, and call
its on_mouse() function.
This commit is contained in:
Daniel Eklöf 2018-12-29 12:56:10 +01:00
parent 1cb268f2e7
commit 7670473735

314
bar.c
View file

@ -23,6 +23,7 @@
#include <cairo-xcb.h>
#define LOG_MODULE "bar"
#define LOG_ENABLE_DBG 1
#include "log.h"
#include "xcb.h"
@ -42,14 +43,17 @@ struct private {
struct {
struct module **mods;
struct module_expose_context *exps;
size_t count;
} left;
struct {
struct module **mods;
struct module_expose_context *exps;
size_t count;
} center;
struct {
struct module **mods;
struct module_expose_context *exps;
size_t count;
} right;
@ -67,10 +71,46 @@ struct private {
xcb_gc_t gc;
xcb_cursor_context_t *cursor_ctx;
xcb_cursor_t cursor;
char *cursor_name;
cairo_t *cairo;
};
/*
* 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 module_expose_context *e = &b->left.exps[i];
assert(e->exposable != NULL);
*left += b->left_spacing + e->width + b->right_spacing;
}
for (size_t i = 0; i < b->center.count; i++) {
struct module_expose_context *e = &b->center.exps[i];
assert(e->exposable != NULL);
*center += b->left_spacing + e->width + b->right_spacing;
}
for (size_t i = 0; i < b->right.count; i++) {
struct module_expose_context *e = &b->right.exps[i];
assert(e->exposable != 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)
{
@ -100,49 +140,54 @@ expose(const struct bar *_bar)
cairo_stroke(bar->cairo);
}
int left_width = 0;
int center_width = 0;
int right_width = 0;
struct module_expose_context ctx_left[bar->left.count];
for (size_t i = 0; i < bar->left.count; i++) {
struct module *m = bar->left.mods[i];
ctx_left[i] = m->begin_expose(m, bar->cairo);
left_width += bar->left_spacing + ctx_left[i].width + bar->right_spacing;
struct module_expose_context *e = &bar->left.exps[i];
if (e->exposable != NULL)
m->end_expose(m, e);
*e = m->begin_expose(m, bar->cairo);
}
struct module_expose_context ctx_center[bar->center.count];
for (size_t i = 0; i < bar->center.count; i++) {
struct module *m = bar->center.mods[i];
ctx_center[i] = m->begin_expose(m, bar->cairo);
center_width += bar->left_spacing + ctx_center[i].width + bar->right_spacing;
struct module_expose_context *e = &bar->center.exps[i];
if (e->exposable != NULL)
m->end_expose(m, e);
*e = m->begin_expose(m, bar->cairo);
}
struct module_expose_context ctx_right[bar->right.count];
for (size_t i = 0; i < bar->right.count; i++) {
struct module *m = bar->right.mods[i];
ctx_right[i] = m->begin_expose(m, bar->cairo);
right_width += bar->left_spacing + ctx_right[i].width + bar->right_spacing;
struct module_expose_context *e = &bar->right.exps[i];
if (e->exposable != NULL)
m->end_expose(m, e);
*e = m->begin_expose(m, bar->cairo);
}
/* No spacing on the edges (that's what the margins are for) */
left_width -= bar->left_spacing + bar->right_spacing;
center_width -= bar->left_spacing + bar->right_spacing;
right_width -= bar->left_spacing + bar->right_spacing;
int left_width, center_width, right_width;
calculate_widths(bar, &left_width, &center_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 module *m = bar->left.mods[i];
m->expose(m, &ctx_left[i], bar->cairo, x + bar->left_spacing, y, bar->height);
x += bar->left_spacing + ctx_left[i].width + bar->right_spacing;
const struct module_expose_context *e = &bar->left.exps[i];
m->expose(m, e, bar->cairo, 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 module *m = bar->center.mods[i];
m->expose(m, &ctx_center[i], bar->cairo, x + bar->left_spacing, y, bar->height);
x += bar->left_spacing + ctx_center[i].width + bar->right_spacing;
const struct module_expose_context *e = &bar->center.exps[i];
m->expose(m, e, bar->cairo, x + bar->left_spacing, y, bar->height);
x += bar->left_spacing + e->width + bar->right_spacing;
}
x = bar->width - (
@ -153,29 +198,125 @@ expose(const struct bar *_bar)
for (size_t i = 0; i < bar->right.count; i++) {
const struct module *m = bar->right.mods[i];
m->expose(m, &ctx_right[i], bar->cairo, x + bar->left_spacing, y, bar->height);
x += bar->left_spacing + ctx_right[i].width + bar->right_spacing;
const struct module_expose_context *e = &bar->right.exps[i];
m->expose(m, e, bar->cairo, x + bar->left_spacing, y, bar->height);
x += bar->left_spacing + e->width + bar->right_spacing;
}
xcb_copy_area(bar->conn, bar->pixmap, bar->win, bar->gc,
0, 0, 0, 0, bar->width, bar->height_with_border);
for (size_t i = 0; i < bar->left.count; i++) {
const struct module *m = bar->left.mods[i];
m->end_expose(m, &ctx_left[i]);
}
for (size_t i = 0; i < bar->center.count; i++) {
const struct module *m = bar->center.mods[i];
m->end_expose(m, &ctx_center[i]);
}
for (size_t i = 0; i < bar->right.count; i++) {
const struct module *m = bar->right.mods[i];
m->end_expose(m, &ctx_right[i]);
}
}
static void refresh(const struct bar *bar);
static void set_cursor(struct bar *bar, const char *cursor);
static void
refresh(const struct bar *bar)
{
const struct private *b = bar->private;
/* Send an event to handle refresh from main thread */
/* Note: docs say that all X11 events are 32 bytes, reglardless of
* the size of the event structure */
xcb_expose_event_t *evt = calloc(32, 1);
*evt = (xcb_expose_event_t){
.response_type = XCB_EXPOSE,
.window = b->win,
.x = 0,
.y = 0,
.width = b->width,
.height = b->height,
.count = 1
};
xcb_send_event(b->conn, false, b->win, XCB_EVENT_MASK_EXPOSURE, (char *)evt);
xcb_flush(b->conn);
free(evt);
}
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;
if (b->cursor_ctx == NULL)
return;
if (b->cursor != 0) {
xcb_free_cursor(b->conn, b->cursor);
free(b->cursor_name);
b->cursor_name = NULL;
}
b->cursor_name = strdup(cursor);
b->cursor = xcb_cursor_load_cursor(b->cursor_ctx, cursor);
xcb_change_window_attributes(b->conn, b->win, XCB_CW_CURSOR, &b->cursor);
}
static void
on_mouse(struct bar *bar, enum mouse_event event, int x, int y)
{
struct private *b = bar->private;
if ((y < b->border.width || y >= (b->height_with_border - b->border.width)) ||
(x < b->border.width || x >= (b->width - b->border.width))) {
LOG_DBG("mouse at border");
}
int left_width, center_width, right_width;
calculate_widths(b, &left_width, &center_width, &right_width);
int mx = b->border.width + b->left_margin - b->left_spacing;
for (size_t i = 0; i < b->left.count; i++) {
const struct module_expose_context *e = &b->left.exps[i];
mx += b->left_spacing;
if (x >= mx && x < mx + e->width) {
assert(e->exposable != NULL);
if (e->exposable->on_mouse != NULL)
e->exposable->on_mouse(e->exposable, bar, event, x - mx, y);
return;
}
mx += e->width + b->right_spacing;
}
mx = b->width / 2 - center_width / 2 - b->left_spacing;
for (size_t i = 0; i < b->center.count; i++) {
const struct module_expose_context *e = &b->center.exps[i];
mx += b->left_spacing;
if (x >= mx && x < mx + e->width) {
assert(e->exposable != NULL);
if (e->exposable->on_mouse != NULL)
e->exposable->on_mouse(e->exposable, bar, event, x - mx, y);
return;
}
mx += e->width + b->right_spacing;
}
mx = b->width - (right_width + b->left_spacing + b->right_margin + b->border.width);
for (size_t i = 0; i < b->right.count; i++) {
const struct module_expose_context *e = &b->right.exps[i];
mx += b->left_spacing;
if (x >= mx && x < mx + e->width) {
assert(e->exposable != NULL);
if (e->exposable->on_mouse != NULL)
e->exposable->on_mouse(e->exposable, bar, event, x - mx, y);
return;
}
mx += e->width + b->right_spacing;
}
LOG_DBG("mouse at NOTHING");
set_cursor(bar, "left_ptr");
}
static int
run(struct bar_run_context *run_ctx)
@ -465,9 +606,14 @@ run(struct bar_run_context *run_ctx)
expose(_bar);
break;
case XCB_MOTION_NOTIFY: {
const xcb_motion_notify_event_t *evt = (void *)e;
on_mouse(_bar, ON_MOUSE_MOTION, evt->event_x, evt->event_y);
break;
}
case XCB_BUTTON_RELEASE:
case XCB_BUTTON_PRESS:
case XCB_MOTION_NOTIFY:
case XCB_DESTROY_NOTIFY:
case XCB_REPARENT_NOTIFY:
case XCB_CONFIGURE_NOTIFY:
@ -503,6 +649,9 @@ run(struct bar_run_context *run_ctx)
if (bar->cursor_ctx != NULL) {
xcb_free_cursor(bar->conn, bar->cursor);
xcb_cursor_context_free(bar->cursor_ctx);
free(bar->cursor_name);
bar->cursor_name = NULL;
}
xcb_free_gc(bar->conn, bar->gc);
@ -517,59 +666,42 @@ run(struct bar_run_context *run_ctx)
return 0;
}
static void
refresh(const struct bar *bar)
{
const struct private *b = bar->private;
/* Send an event to handle refresh from main thread */
/* Note: docs say that all X11 events are 32 bytes, reglardless of
* the size of the event structure */
xcb_expose_event_t *evt = calloc(32, 1);
*evt = (xcb_expose_event_t){
.response_type = XCB_EXPOSE,
.window = b->win,
.x = 0,
.y = 0,
.width = b->width,
.height = b->height,
.count = 1
};
xcb_send_event(b->conn, false, b->win, XCB_EVENT_MASK_EXPOSURE, (char *)evt);
xcb_flush(b->conn);
free(evt);
}
static void
set_cursor(struct bar *bar, const char *cursor)
{
struct private *b = bar->private;
if (b->cursor_ctx == NULL)
return;
b->cursor = xcb_cursor_load_cursor(b->cursor_ctx, cursor);
xcb_change_window_attributes(b->conn, b->win, XCB_CW_CURSOR, &b->cursor);
}
static void
destroy(struct bar *bar)
{
struct private *b = bar->private;
for (size_t i = 0; i < b->left.count; i++)
b->left.mods[i]->destroy(b->left.mods[i]);
for (size_t i = 0; i < b->center.count; i++)
b->center.mods[i]->destroy(b->center.mods[i]);
for (size_t i = 0; i < b->right.count; i++)
b->right.mods[i]->destroy(b->right.mods[i]);
for (size_t i = 0; i < b->left.count; i++) {
struct module *m = b->left.mods[i];
struct module_expose_context *e = &b->left.exps[i];
if (e->exposable != NULL)
m->end_expose(m, e);
m->destroy(m);
}
for (size_t i = 0; i < b->center.count; i++) {
struct module *m = b->center.mods[i];
struct module_expose_context *e = &b->center.exps[i];
if (e->exposable != NULL)
m->end_expose(m, e);
m->destroy(m);
}
for (size_t i = 0; i < b->right.count; i++) {
struct module *m = b->right.mods[i];
struct module_expose_context *e = &b->right.exps[i];
if (e->exposable != NULL)
m->end_expose(m, 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(bar->private);
free(bar);
@ -589,20 +721,30 @@ bar_new(const struct bar_config *config)
priv->border.width = config->border.width;
priv->border.color = config->border.color;
priv->left.mods = malloc(config->left.count * sizeof(priv->left.mods[0]));
priv->left.exps = malloc(config->left.count * sizeof(priv->left.exps[0]));
priv->center.mods = malloc(config->center.count * sizeof(priv->center.mods[0]));
priv->center.exps = malloc(config->center.count * sizeof(priv->center.exps[0]));
priv->right.mods = malloc(config->right.count * sizeof(priv->right.mods[0]));
priv->right.exps = malloc(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->cursor_ctx = NULL;
priv->cursor = 0;
priv->cursor_name = NULL;
for (size_t i = 0; i < priv->left.count; i++)
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->left.exps[i].exposable = NULL;
}
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->center.exps[i].exposable = NULL;
}
for (size_t i = 0; i < priv->right.count; i++) {
priv->right.mods[i] = config->right.mods[i];
priv->right.exps[i].exposable = NULL;
}
struct bar *bar = malloc(sizeof(*bar));
bar->private = priv;