From 7d2c7ab438afa5cde93969a1ade4fc8d39d2f1e1 Mon Sep 17 00:00:00 2001 From: Otto Modinos Date: Fri, 16 Jan 2015 01:37:45 +0200 Subject: [PATCH] Allow having clickable areas inside another Previously, if you started several areas, one inside another, only the inermost one would get registered and eventually triggered. This patch fixes that, allowing you to use multiple areas around text to respond to several different buttons, for example, both scroll-up and scroll-down. If you define two areas that respond to the same button, only the innermost one would get triggered. I think this makes sense. --- bar.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/bar.c b/bar.c index 90c2229..a4492e9 100644 --- a/bar.c +++ b/bar.c @@ -34,6 +34,7 @@ typedef struct monitor_t { } monitor_t; typedef struct area_t { + bool active; int begin, end, align, button; xcb_window_t window; char *cmd; @@ -219,11 +220,15 @@ set_attribute (const char modifier, const char attribute) area_t * -area_get (xcb_window_t win, const int x) +area_get (xcb_window_t win, const int btn, const int x) { - for (int i = 0; i < astack.pos; i++) - if (astack.slot[i].window == win && x >= astack.slot[i].begin && x < astack.slot[i].end) - return &astack.slot[i]; + /* Looping backwards ensures that we get the innermost area first */ + for (int i = astack.pos; i >= 0; i--) { + area_t *a = &astack.slot[i]; + if (a->window == win && a->button == btn + && x >= a->begin && x < a->end) + return a; + } return NULL; } @@ -248,17 +253,18 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x { char *p = str; char *trail; - area_t *a = &astack.slot[astack.pos]; - - if (astack.pos == N) { - fprintf(stderr, "astack overflow!\n"); - return false; - } + area_t *a; // A wild close area tag appeared! if (*p != ':') { *end = p; + /* Find most recent unclosed area. */ + int i; + for (i = astack.pos - 1; i >= 0 && !astack.slot[i].active; i--) + ; + a = &astack.slot[i]; + // Basic safety checks if (!a->cmd || a->align != align || a->window != mon->window) return false; @@ -280,11 +286,16 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x break; } - astack.pos++; - + a->active = false; return true; } + if (astack.pos >= N) { + fprintf(stderr, "astack overflow!\n"); + return false; + } + a = &astack.slot[astack.pos++]; + // Found the closing : and check if it's just an escaped one for (trail = strchr(++p, ':'); trail && trail[-1] == '\\'; trail = strchr(trail + 1, ':')) ; @@ -308,6 +319,7 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x // This is a pointer to the string buffer allocated in the main a->cmd = p; + a->active = true; a->align = align; a->begin = x; a->window = mon->window; @@ -1191,7 +1203,7 @@ main (int argc, char **argv) case XCB_BUTTON_PRESS: press_ev = (xcb_button_press_event_t *)ev; { - area_t *area = area_get(press_ev->event, press_ev->event_x); + area_t *area = area_get(press_ev->event, press_ev->detail, press_ev->event_x); // Respond to the click if (area && area->button == press_ev->detail) { write(STDOUT_FILENO, area->cmd, strlen(area->cmd));