From 767047373540c199a52b576e962d03ece63f9f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Dec 2018 12:56:10 +0100 Subject: [PATCH] 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. --- bar.c | 314 ++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 228 insertions(+), 86 deletions(-) diff --git a/bar.c b/bar.c index 6eba97e..6e96b18 100644 --- a/bar.c +++ b/bar.c @@ -23,6 +23,7 @@ #include #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, ¢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 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, ¢er_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;