Merge pull request #97 from otommod/stacking-clicks

Allow having clickable areas one inside another
This commit is contained in:
Giuseppe 2015-02-11 15:05:33 +01:00
commit 0affee3d2f
1 changed files with 34 additions and 23 deletions

57
bar.c
View File

@ -34,6 +34,7 @@ typedef struct monitor_t {
} monitor_t; } monitor_t;
typedef struct area_t { typedef struct area_t {
bool active;
int begin, end, align, button; int begin, end, align, button;
xcb_window_t window; xcb_window_t window;
char *cmd; char *cmd;
@ -219,11 +220,15 @@ set_attribute (const char modifier, const char attribute)
area_t * 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++) // Looping backwards ensures that we get the innermost area first
if (astack.slot[i].window == win && x >= astack.slot[i].begin && x < astack.slot[i].end) for (int i = astack.pos; i >= 0; i--) {
return &astack.slot[i]; area_t *a = &astack.slot[i];
if (a->window == win && a->button == btn
&& x >= a->begin && x < a->end)
return a;
}
return NULL; return NULL;
} }
@ -246,18 +251,18 @@ area_shift (xcb_window_t win, const int align, int delta)
bool bool
area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x, const int align, const int button) area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x, const int align, const int button)
{ {
char *p = str; int i;
char *trail; char *trail;
area_t *a = &astack.slot[astack.pos]; area_t *a;
if (astack.pos == N) {
fprintf(stderr, "astack overflow!\n");
return false;
}
// A wild close area tag appeared! // A wild close area tag appeared!
if (*p != ':') { if (*str != ':') {
*end = p; *end = str;
// Find most recent unclosed area.
for (i = astack.pos - 1; i >= 0 && !astack.slot[i].active; i--)
;
a = &astack.slot[i];
// Basic safety checks // Basic safety checks
if (!a->cmd || a->align != align || a->window != mon->window) if (!a->cmd || a->align != align || a->window != mon->window)
@ -280,25 +285,30 @@ area_add (char *str, const char *optend, char **end, monitor_t *mon, const int x
break; break;
} }
astack.pos++; a->active = false;
return true; 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 // Found the closing : and check if it's just an escaped one
for (trail = strchr(++p, ':'); trail && trail[-1] == '\\'; trail = strchr(trail + 1, ':')) for (trail = strchr(++str, ':'); trail && trail[-1] == '\\'; trail = strchr(trail + 1, ':'))
; ;
// Find the trailing : and make sure it's whitin the formatting block, also reject empty commands // Find the trailing : and make sure it's within the formatting block, also reject empty commands
if (!trail || p == trail || trail > optend) { if (!trail || str == trail || trail > optend) {
*end = p; *end = str;
return false; return false;
} }
*trail = '\0'; *trail = '\0';
// Sanitize the user command by unescaping all the : // Sanitize the user command by unescaping all the :
for (char *needle = p; *needle; needle++) { for (char *needle = str; *needle; needle++) {
int delta = trail - &needle[1]; int delta = trail - &needle[1];
if (needle[0] == '\\' && needle[1] == ':') { if (needle[0] == '\\' && needle[1] == ':') {
memmove(&needle[0], &needle[1], delta); memmove(&needle[0], &needle[1], delta);
@ -307,7 +317,8 @@ 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 // This is a pointer to the string buffer allocated in the main
a->cmd = p; a->cmd = str;
a->active = true;
a->align = align; a->align = align;
a->begin = x; a->begin = x;
a->window = mon->window; a->window = mon->window;
@ -1191,9 +1202,9 @@ main (int argc, char **argv)
case XCB_BUTTON_PRESS: case XCB_BUTTON_PRESS:
press_ev = (xcb_button_press_event_t *)ev; 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 // Respond to the click
if (area && area->button == press_ev->detail) { if (area) {
write(STDOUT_FILENO, area->cmd, strlen(area->cmd)); write(STDOUT_FILENO, area->cmd, strlen(area->cmd));
write(STDOUT_FILENO, "\n", 1); write(STDOUT_FILENO, "\n", 1);
} }