From c79ffbe057f8d9d664f960e262185cc64e9ec195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 22 Jun 2021 19:04:13 +0200 Subject: [PATCH] Add support binding on-click handlers to other buttons than LEFT One can now bind the left/middle/right mouse buttons to on-click. In fact, you can have all three buttons bound to different handlers for the same particle. The new syntax is on-click: left: middle: right: Leaving one out is the same thing as not mapping it at all. Furthermore, on-click: is still valid, and is a shorthand for on-click: left: --- bar/wayland.c | 27 +++++++++-------- config-verify.c | 18 ++++++++++++ config-verify.h | 1 + config.c | 31 ++++++++++++++++++-- particle.c | 63 +++++++++++++++++++++++++++++----------- particle.h | 47 ++++++++++++++++-------------- particles/dynlist.c | 2 +- particles/empty.c | 6 +--- particles/list.c | 8 ++--- particles/map.c | 7 ++--- particles/progress-bar.c | 19 +++++++----- particles/ramp.c | 7 ++--- particles/string.c | 6 +--- tag.c | 8 +++++ tag.h | 3 ++ 15 files changed, 163 insertions(+), 90 deletions(-) diff --git a/bar/wayland.c b/bar/wayland.c index 05a12a9..1124ab2 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -272,26 +272,25 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { - if (state != WL_POINTER_BUTTON_STATE_PRESSED) - return; - struct seat *seat = data; struct wayland_backend *backend = seat->backend; - backend->active_seat = seat; + if (state == WL_POINTER_BUTTON_STATE_PRESSED) + backend->active_seat = seat; + else { + enum mouse_button btn; - enum mouse_button btn; + switch (button) { + case BTN_LEFT: btn = MOUSE_BTN_LEFT; break; + case BTN_MIDDLE: btn = MOUSE_BTN_MIDDLE; break; + case BTN_RIGHT: btn = MOUSE_BTN_RIGHT; break; + default: + return; + } - switch (button) { - case BTN_LEFT: btn = MOUSE_BTN_LEFT; break; - case BTN_MIDDLE: btn = MOUSE_BTN_MIDDLE; break; - case BTN_RIGHT: btn = MOUSE_BTN_RIGHT; break; - default: - return; + backend->bar_on_mouse( + backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); } - - backend->bar_on_mouse( - backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); } static void diff --git a/config-verify.c b/config-verify.c index 7e87d42..6a5278b 100644 --- a/config-verify.c +++ b/config-verify.c @@ -152,6 +152,24 @@ conf_verify_dict(keychain_t *chain, const struct yml_node *node, return true; } +bool +conf_verify_on_click(keychain_t *chain, const struct yml_node *node) +{ + /* on-click: */ + const char *s = yml_value_as_string(node); + if (s != NULL) + return true; + + static const struct attr_info info[] = { + {"left", false, &conf_verify_string}, + {"middle", false, &conf_verify_string}, + {"right", false, &conf_verify_string}, + {NULL, false, NULL}, + }; + + return conf_verify_dict(chain, node, info); +} + bool conf_verify_color(keychain_t *chain, const struct yml_node *node) { diff --git a/config-verify.h b/config-verify.h index 44a6a66..dccaf5f 100644 --- a/config-verify.h +++ b/config-verify.h @@ -40,6 +40,7 @@ bool conf_verify_list(keychain_t *chain, const struct yml_node *node, bool conf_verify_dict(keychain_t *chain, const struct yml_node *node, const struct attr_info info[]); /* NULL-terminated list */ +bool conf_verify_on_click(keychain_t *chain, const struct yml_node *node); bool conf_verify_color(keychain_t *chain, const struct yml_node *node); bool conf_verify_font(keychain_t *chain, const struct yml_node *node); diff --git a/config.c b/config.c index aaa62f9..b2e131c 100644 --- a/config.c +++ b/config.c @@ -139,8 +139,33 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) int right = margin != NULL ? yml_value_as_int(margin) : right_margin != NULL ? yml_value_as_int(right_margin) : 0; - const char *on_click_template - = on_click != NULL ? yml_value_as_string(on_click) : NULL; + const char *on_click_templates[MOUSE_BTN_COUNT] = {NULL}; + if (on_click != NULL) { + const char *legacy = yml_value_as_string(on_click); + + if (legacy != NULL) + on_click_templates[MOUSE_BTN_LEFT] = legacy; + + if (yml_is_dict(on_click)) { + for (struct yml_dict_iter it = yml_dict_iter(on_click); + it.key != NULL; + yml_dict_next(&it)) + { + const char *key = yml_value_as_string(it.key); + const char *template = yml_value_as_string(it.value); + + if (strcmp(key, "left") == 0) + on_click_templates[MOUSE_BTN_LEFT] = template; + else if (strcmp(key, "middle") == 0) + on_click_templates[MOUSE_BTN_MIDDLE] = template; + else if (strcmp(key, "right") == 0) + on_click_templates[MOUSE_BTN_RIGHT] = template; + else + assert(false); + } + } + } + struct deco *deco = deco_node != NULL ? conf_to_deco(deco_node) : NULL; /* @@ -159,7 +184,7 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) /* Instantiate base/common particle */ struct particle *common = particle_common_new( - left, right, on_click_template, font, foreground, deco); + left, right, on_click_templates, font, foreground, deco); const struct particle_iface *iface = plugin_load_particle(type); diff --git a/particle.c b/particle.c index ad76965..ea4bb62 100644 --- a/particle.c +++ b/particle.c @@ -22,31 +22,41 @@ particle_default_destroy(struct particle *particle) if (particle->deco != NULL) particle->deco->destroy(particle->deco); fcft_destroy(particle->font); - free(particle->on_click_template); + for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) + free(particle->on_click_templates[i]); free(particle); } struct particle * particle_common_new(int left_margin, int right_margin, - const char *on_click_template, + const char **on_click_templates, struct fcft_font *font, pixman_color_t foreground, struct deco *deco) { struct particle *p = calloc(1, sizeof(*p)); p->left_margin = left_margin; p->right_margin = right_margin; - p->on_click_template = - on_click_template != NULL ? strdup(on_click_template) : NULL; p->foreground = foreground; p->font = font; p->deco = deco; + + if (on_click_templates != NULL) { + for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) { + if (on_click_templates[i] != NULL) { + p->have_on_click_template = true; + p->on_click_templates[i] = strdup(on_click_templates[i]); + } + } + } + return p; } void exposable_default_destroy(struct exposable *exposable) { - free(exposable->on_click); + for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) + free(exposable->on_click[i]); free(exposable); } @@ -144,19 +154,32 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) { +#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG + static const char *button_name[] = { + [MOUSE_BTN_NONE] = "none", + [MOUSE_BTN_LEFT] = "left", + [MOUSE_BTN_MIDDLE] = "middle", + [MOUSE_BTN_RIGHT] = "right", + [MOUSE_BTN_COUNT] = "count", + }; LOG_DBG("on_mouse: exposable=%p, event=%s, btn=%s, x=%d, y=%d (on-click=%s)", exposable, event == ON_MOUSE_MOTION ? "motion" : "click", - btn == MOUSE_BTN_NONE ? "none" : btn == MOUSE_BTN_LEFT ? "left" : btn == MOUSE_BTN_MIDDLE ? "middle" : "right", - x, y, exposable->on_click); + button_name[btn], x, y, exposable->on_click[btn]); +#endif /* If we have a handler, change cursor to a hand */ - bar->set_cursor(bar, exposable->on_click == NULL ? "left_ptr" : "hand2"); + const char *cursor = + (exposable->particle != NULL && + exposable->particle->have_on_click_template) + ? "hand2" + : "left_ptr"; + bar->set_cursor(bar, cursor); /* If this is a mouse click, and we have a handler, execute it */ - if (exposable->on_click != NULL && event == ON_MOUSE_CLICK) { + if (exposable->on_click[btn] != NULL && event == ON_MOUSE_CLICK) { /* Need a writeable copy, whose scope *we* control */ - char *cmd = strdup(exposable->on_click); - LOG_DBG("cmd = \"%s\"", exposable->on_click); + char *cmd = strdup(exposable->on_click[btn]); + LOG_DBG("cmd = \"%s\"", exposable->on_click[btn]); char **argv; if (!tokenize_cmdline(cmd, &argv)) { @@ -174,15 +197,15 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, int wstatus; if (waitpid(pid, &wstatus, 0) == -1) - LOG_ERRNO("%s: failed to wait for on_click handler", exposable->on_click); + LOG_ERRNO("%s: failed to wait for on_click handler", exposable->on_click[btn]); if (WIFEXITED(wstatus)) { if (WEXITSTATUS(wstatus) != 0) - LOG_ERRNO_P("%s: failed to execute", WEXITSTATUS(wstatus), exposable->on_click); + LOG_ERRNO_P("%s: failed to execute", WEXITSTATUS(wstatus), exposable->on_click[btn]); } else - LOG_ERR("%s: did not exit normally", exposable->on_click); + LOG_ERR("%s: did not exit normally", exposable->on_click[btn]); - LOG_DBG("%s: launched", exposable->on_click); + LOG_DBG("%s: launched", exposable->on_click[btn]); } else { /* * Use a pipe with O_CLOEXEC to communicate exec() failure @@ -283,11 +306,17 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, } struct exposable * -exposable_common_new(const struct particle *particle, const char *on_click) +exposable_common_new(const struct particle *particle, const struct tag_set *tags) { struct exposable *exposable = calloc(1, sizeof(*exposable)); exposable->particle = particle; - exposable->on_click = on_click != NULL ? strdup(on_click) : NULL; + + if (particle != NULL && particle->have_on_click_template) { + tags_expand_templates( + exposable->on_click, + (const char **)particle->on_click_templates, + MOUSE_BTN_COUNT, tags); + } exposable->destroy = &exposable_default_destroy; exposable->on_mouse = &exposable_default_on_mouse; return exposable; diff --git a/particle.h b/particle.h index 1fc582a..4e449f3 100644 --- a/particle.h +++ b/particle.h @@ -7,23 +7,6 @@ #include "decoration.h" #include "tag.h" -struct bar; - -struct particle { - void *private; - - int left_margin, right_margin; - char *on_click_template; - - pixman_color_t foreground; - struct fcft_font *font; - struct deco *deco; - - void (*destroy)(struct particle *particle); - struct exposable *(*instantiate)(const struct particle *particle, - const struct tag_set *tags); -}; - enum mouse_event { ON_MOUSE_MOTION, ON_MOUSE_CLICK, @@ -34,14 +17,36 @@ enum mouse_button { MOUSE_BTN_LEFT, MOUSE_BTN_MIDDLE, MOUSE_BTN_RIGHT, + + MOUSE_BTN_COUNT, }; +struct bar; + +struct particle { + void *private; + + int left_margin, right_margin; + + bool have_on_click_template; + char *on_click_templates[MOUSE_BTN_COUNT]; + + pixman_color_t foreground; + struct fcft_font *font; + struct deco *deco; + + void (*destroy)(struct particle *particle); + struct exposable *(*instantiate)(const struct particle *particle, + const struct tag_set *tags); +}; + + struct exposable { const struct particle *particle; void *private; int width; /* Should be set by begin_expose(), at latest */ - char *on_click; + char *on_click[MOUSE_BTN_COUNT]; void (*destroy)(struct exposable *exposable); int (*begin_expose)(struct exposable *exposable); @@ -53,13 +58,13 @@ struct exposable { }; struct particle *particle_common_new( - int left_margin, int right_margin, const char *on_click_template, + int left_margin, int right_margin, const char *on_click_templates[], struct fcft_font *font, pixman_color_t foreground, struct deco *deco); void particle_default_destroy(struct particle *particle); struct exposable *exposable_common_new( - const struct particle *particle, const char *on_click); + const struct particle *particle, const struct tag_set *tags); void exposable_default_destroy(struct exposable *exposable); void exposable_render_deco( const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height); @@ -73,7 +78,7 @@ void exposable_default_on_mouse( {"margin", false, &conf_verify_int}, \ {"left-margin", false, &conf_verify_int}, \ {"right-margin", false, &conf_verify_int}, \ - {"on-click", false, &conf_verify_string}, \ + {"on-click", false, &conf_verify_on_click}, \ {"font", false, &conf_verify_font}, \ {"foreground", false, &conf_verify_color}, \ {"deco", false, &conf_verify_decoration}, \ diff --git a/particles/dynlist.c b/particles/dynlist.c index 00bb41f..bd002d1 100644 --- a/particles/dynlist.c +++ b/particles/dynlist.c @@ -72,7 +72,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, //const struct particle *p = exposable->particle; const struct private *e = exposable->private; - if (exposable->on_click != NULL) { + if (exposable->on_click[btn] != NULL) { exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; } diff --git a/particles/empty.c b/particles/empty.c index e97f929..5c0be16 100644 --- a/particles/empty.c +++ b/particles/empty.c @@ -22,13 +22,9 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int static struct exposable * instantiate(const struct particle *particle, const struct tag_set *tags) { - char *on_click = tags_expand_template(particle->on_click_template, tags); - - struct exposable *exposable = exposable_common_new(particle, on_click); + struct exposable *exposable = exposable_common_new(particle, tags); exposable->begin_expose = &begin_expose; exposable->expose = &expose; - - free(on_click); return exposable; } diff --git a/particles/list.c b/particles/list.c index f9e61f6..43bd3be 100644 --- a/particles/list.c +++ b/particles/list.c @@ -80,7 +80,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if (exposable->on_click != NULL) { + if (exposable->on_click[btn] != NULL) { /* We have our own handler */ exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; @@ -121,16 +121,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags) assert(e->exposables[i] != NULL); } - char *on_click = tags_expand_template(particle->on_click_template, tags); - - struct exposable *exposable = exposable_common_new(particle, on_click); + struct exposable *exposable = exposable_common_new(particle, tags); exposable->private = e; exposable->destroy = &exposable_destroy; exposable->begin_expose = &begin_expose; exposable->expose = &expose; exposable->on_mouse = &on_mouse; - - free(on_click); return exposable; } diff --git a/particles/map.c b/particles/map.c index cc26f77..3710f72 100644 --- a/particles/map.c +++ b/particles/map.c @@ -66,7 +66,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if (exposable->on_click != NULL) { + if (exposable->on_click[btn] != NULL) { /* We have our own handler */ exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; @@ -119,15 +119,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags) assert(e->exposable != NULL); - char *on_click = tags_expand_template(particle->on_click_template, tags); - struct exposable *exposable = exposable_common_new(particle, on_click); + struct exposable *exposable = exposable_common_new(particle, tags); exposable->private = e; exposable->destroy = &exposable_destroy; exposable->begin_expose = &begin_expose; exposable->expose = &expose; exposable->on_mouse = &on_mouse; - - free(on_click); return exposable; } diff --git a/particles/progress-bar.c b/particles/progress-bar.c index 62a58b9..74c8ebe 100644 --- a/particles/progress-bar.c +++ b/particles/progress-bar.c @@ -148,7 +148,9 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, } /* Remember the original handler, so that we can restore it */ - char *original = exposable->on_click; + char *original[MOUSE_BTN_COUNT]; + for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) + original[i] = exposable->on_click[i]; if (event == ON_MOUSE_CLICK) { long where = clickable_width > 0 @@ -160,7 +162,9 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, .count = 1, }; - exposable->on_click = tags_expand_template(exposable->on_click, &tags); + tags_expand_templates( + exposable->on_click, (const char **)exposable->on_click, + MOUSE_BTN_COUNT, &tags); tag_set_destroy(&tags); } @@ -169,8 +173,10 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, if (event == ON_MOUSE_CLICK) { /* Reset handler string */ - free(exposable->on_click); - exposable->on_click = original; + for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) { + free(exposable->on_click[i]); + exposable->on_click[i] = original[i]; + } } } @@ -213,10 +219,7 @@ instantiate(const struct particle *particle, const struct tag_set *tags) for (size_t i = 0; i < epriv->count; i++) assert(epriv->exposables[i] != NULL); - char *on_click = tags_expand_template(particle->on_click_template, tags); - - struct exposable *exposable = exposable_common_new(particle, on_click); - free(on_click); + struct exposable *exposable = exposable_common_new(particle, tags); exposable->private = epriv; exposable->destroy = &exposable_destroy; diff --git a/particles/ramp.c b/particles/ramp.c index 8087217..14594a2 100644 --- a/particles/ramp.c +++ b/particles/ramp.c @@ -62,7 +62,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if (exposable->on_click != NULL) { + if (exposable->on_click[btn] != NULL) { /* We have our own handler */ exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; @@ -146,15 +146,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags) e->exposable = pp->instantiate(pp, tags); assert(e->exposable != NULL); - char *on_click = tags_expand_template(particle->on_click_template, tags); - struct exposable *exposable = exposable_common_new(particle, on_click); + struct exposable *exposable = exposable_common_new(particle, tags); exposable->private = e; exposable->destroy = &exposable_destroy; exposable->begin_expose = &begin_expose; exposable->expose = &expose; exposable->on_mouse = &on_mouse; - - free(on_click); return exposable; } diff --git a/particles/string.c b/particles/string.c index 653c8ef..fbed826 100644 --- a/particles/string.c +++ b/particles/string.c @@ -269,15 +269,11 @@ instantiate(const struct particle *particle, const struct tag_set *tags) } } - char *on_click = tags_expand_template(particle->on_click_template, tags); - - struct exposable *exposable = exposable_common_new(particle, on_click); + struct exposable *exposable = exposable_common_new(particle, tags); exposable->private = e; exposable->destroy = &exposable_destroy; exposable->begin_expose = &begin_expose; exposable->expose = &expose; - - free(on_click); return exposable; } diff --git a/tag.c b/tag.c index 5b00218..8ca7e52 100644 --- a/tag.c +++ b/tag.c @@ -533,3 +533,11 @@ tags_expand_template(const char *template, const struct tag_set *tags) return formatted.s; } + +void +tags_expand_templates(char *expanded[], const char *template[], size_t nmemb, + const struct tag_set *tags) +{ + for (size_t i = 0; i < nmemb; i++) + expanded[i] = tags_expand_template(template[i], tags); +} diff --git a/tag.h b/tag.h index 2c629c5..d6bfe6a 100644 --- a/tag.h +++ b/tag.h @@ -50,3 +50,6 @@ void tag_set_destroy(struct tag_set *set); /* Utility functions */ char *tags_expand_template(const char *template, const struct tag_set *tags); +void tags_expand_templates( + char *expanded[], const char *template[], size_t nmemb, + const struct tag_set *tags);