A tiling window manager

bar: Allow multiple buttons/commands in a single ^ca()

This isn't supported by dzen2 but it seems like a natural extension.

It allows a single string of data like a speaker icon, but multiple
commands depending on the button clicked, such as a volume up action
when button 4 is clicked (mouse wheel scrolling up), and a volume
down action when button 5 is clicked (mouse wheel scrolling down).

Defer parsing these until we get an actual click since the parsing
is a bit more complicated now and we're probably going to have many
more updates to the bar than mouse clicks.

+46 -25
+2 -2
README.md
··· 84 84 command is reset with `^command()`. 85 85 Currently supported commands: 86 86 87 - - `^ca(btn,cmd)`: execute `cmd` when mouse button `btn` is clicked on this 88 - area of text. 87 + - `^ca(btn,cmd,btn2,cmd2)`: execute `cmd` when mouse button `btn` is clicked on 88 + this area of text, or `cmd2` if button `btn2` is clicked. 89 89 Closing the area of clickable text can be done with `^ca()`. 90 90 91 91 - `^fg(color)`: color the text following until the next `^fg()` command.
+44 -23
bar.c
··· 61 61 int length; 62 62 char *color; 63 63 char *font; 64 - char *cmd; 65 - int cmd_btn; 64 + char *clickcmd; 66 65 int text_x; 67 66 int text_width; 68 67 struct list_head node; ··· 295 294 struct bar_chunk *chunk; 296 295 struct sbuf *tbuf, *curcmd, *curtxt; 297 296 char *tline, *font, *color, *clickcmd; 298 - int diff = 0, len, cmd = 0, skip = 0, xftx = 0, x, clickcmdbtn = 0; 297 + int diff = 0, len, cmd = 0, skip = 0, xftx = 0, x; 299 298 int width, height; 300 299 301 300 if (!force && (s->full_screen_win || !defaults.bar_sticky || ··· 375 374 free(chunk->color); 376 375 if (chunk->font) 377 376 free(chunk->font); 378 - if (chunk->cmd) 379 - free(chunk->cmd); 377 + if (chunk->clickcmd) 378 + free(chunk->clickcmd); 380 379 list_del(&chunk->node); 381 380 free(chunk); 382 381 } ··· 407 406 font = xstrdup(tline + 3); 408 407 } else if (strncmp(tline, "ca(", 3) == 0) { 409 408 /* ^ca(1,some command)*^ca() */ 410 - char btn[2]; 411 409 if (clickcmd) 412 410 free(clickcmd); 413 411 clickcmd = NULL; 414 - if (strlen(tline) > 4) { 415 - btn[0] = (tline + 3)[0]; 416 - btn[1] = '\0'; 417 - clickcmdbtn = atoi(btn); 418 - clickcmd = xstrdup(tline + 3 + 419 - 2); 420 - } 412 + if (strlen(tline) > 4) 413 + clickcmd = xstrdup(tline + 3); 421 414 } else { 422 415 PRINT_DEBUG(("unsupported bar command " 423 416 "\"%s\", ignoring\n", tline)); ··· 434 427 chunk->length = strlen(chunk->text); 435 428 chunk->color = color ? xstrdup(color) : NULL; 436 429 chunk->font = font ? xstrdup(font) : NULL; 437 - chunk->cmd = clickcmd ? xstrdup(clickcmd) : NULL; 438 - chunk->cmd_btn = clickcmdbtn; 430 + chunk->clickcmd = clickcmd ? xstrdup(clickcmd) : NULL; 439 431 list_add_tail(&chunk->node, &bar_chunks); 440 432 sbuf_clear(curtxt); 441 433 } else { ··· 452 444 chunk->length = strlen(chunk->text); 453 445 chunk->color = color ? xstrdup(color) : NULL; 454 446 chunk->font = font ? xstrdup(font) : NULL; 455 - chunk->cmd = clickcmd ? xstrdup(clickcmd) : NULL; 456 - chunk->cmd_btn = clickcmdbtn; 447 + chunk->clickcmd = clickcmd ? xstrdup(clickcmd) : NULL; 457 448 list_add_tail(&chunk->node, &bar_chunks); 458 449 } 459 450 sbuf_free(curcmd); ··· 505 496 bar_handle_click(rp_screen *s, XButtonEvent *e) 506 497 { 507 498 struct bar_chunk *chunk; 499 + char *cmd, *actcmd; 500 + int btn, len; 508 501 509 502 PRINT_DEBUG(("bar click at %d,%d button %d\n", e->x, e->y, e->button)); 510 503 ··· 512 505 PRINT_DEBUG(("chunk: text_x:%d text_width:%d text:%s\n", 513 506 chunk->text_x, chunk->text_width, chunk->text)); 514 507 515 - if (!chunk->cmd) 508 + if (!chunk->clickcmd) 509 + continue; 510 + 511 + if (e->x < chunk->text_x || 512 + e->x > (chunk->text_x + chunk->text_width)) 516 513 continue; 517 514 518 - if (e->button == chunk->cmd_btn && e->x >= chunk->text_x && 519 - e->x <= (chunk->text_x + chunk->text_width)) { 520 - PRINT_DEBUG(("executing bar click action %s\n", 521 - chunk->cmd)); 522 - spawn(chunk->cmd, current_frame(rp_current_vscreen)); 515 + /* 1,somecmd,2,someothercmd */ 516 + cmd = chunk->clickcmd; 517 + PRINT_DEBUG(("chunk: parsing btns/cmds:%s\n", cmd)); 518 + 519 + while (cmd != NULL && cmd[0] != '\0') { 520 + len = strlen(cmd); 521 + actcmd = xmalloc(len); 522 + memset(actcmd, 0, len); 523 + if (sscanf(cmd, "%d,%[^,]%n", &btn, actcmd, &len) != 2) { 524 + PRINT_DEBUG(("chunk: invalid format\n")); 525 + free(actcmd); 526 + break; 527 + } 528 + 529 + PRINT_DEBUG(("chunk: btn:%d cmd:%s\n", btn, actcmd)); 530 + 531 + if (e->button == btn) { 532 + PRINT_DEBUG(("executing bar click action %s\n", 533 + actcmd)); 534 + spawn(actcmd, current_frame(rp_current_vscreen)); 535 + free(actcmd); 536 + break; 537 + } 538 + 539 + cmd += len; 540 + free(actcmd); 541 + if (cmd[0] != ',') 542 + break; 543 + cmd++; 523 544 } 524 545 } 525 546 }