diff options
author | Mikkel Oscar Lyderik <mikkeloscar@gmail.com> | 2015-12-27 13:18:55 +0100 |
---|---|---|
committer | Mikkel Oscar Lyderik <mikkeloscar@gmail.com> | 2015-12-27 13:18:55 +0100 |
commit | d21b15a4be35524a2021a61c3ec15d73d412c969 (patch) | |
tree | 2f68c3cefd0c2542a594ae09d40eef12e2e835d6 /swaybar | |
parent | Merge pull request #408 from mikkeloscar/add-focus-sibling (diff) | |
download | sway-d21b15a4be35524a2021a61c3ec15d73d412c969.tar.gz sway-d21b15a4be35524a2021a61c3ec15d73d412c969.tar.zst sway-d21b15a4be35524a2021a61c3ec15d73d412c969.zip |
swaybar: Add support for Airblade i3bar extensions
This extends the i3bar protocol implementation with the following
features from @Airblade/i3:
* background
* border
* border_top, border_bottom, border_left, border_right
A block will now be rendered like this:
```
(border_left + margin) + width + (margin + border_right) + sep
```
Where `border_left/border_right` and their related margin is only drawn
if the `border` is specified and the border has a width > 0 (default is
1).
`border_top` and `border_bottom` does not affect the height of the bar
(no margin is added), thus it will be drawn behind the text if it is too
big. The user should specify a bar height if more space between
top/bottom borders are required.
Diffstat (limited to 'swaybar')
-rw-r--r-- | swaybar/main.c | 301 |
1 files changed, 235 insertions, 66 deletions
diff --git a/swaybar/main.c b/swaybar/main.c index 434d7363..c9fc7de7 100644 --- a/swaybar/main.c +++ b/swaybar/main.c | |||
@@ -54,6 +54,13 @@ struct status_block { | |||
54 | char *name, *instance; | 54 | char *name, *instance; |
55 | bool separator; | 55 | bool separator; |
56 | int separator_block_width; | 56 | int separator_block_width; |
57 | // Airblader features | ||
58 | uint32_t background; | ||
59 | uint32_t border; | ||
60 | int border_top; | ||
61 | int border_bottom; | ||
62 | int border_left; | ||
63 | int border_right; | ||
57 | }; | 64 | }; |
58 | 65 | ||
59 | list_t *status_line = NULL; | 66 | list_t *status_line = NULL; |
@@ -397,7 +404,185 @@ void bar_ipc_init(int outputi, const char *bar_id) { | |||
397 | ipc_update_workspaces(); | 404 | ipc_update_workspaces(); |
398 | } | 405 | } |
399 | 406 | ||
407 | /** | ||
408 | * Renders a sharp line of any width and height. | ||
409 | * | ||
410 | * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0 | ||
411 | * if the line has a width/height of one pixel, respectively. | ||
412 | */ | ||
413 | void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) { | ||
414 | cairo_set_source_u32(cairo, color); | ||
415 | |||
416 | if (width > 1 && height > 1) { | ||
417 | cairo_rectangle(cairo, x, y, width, height); | ||
418 | cairo_fill(cairo); | ||
419 | } else { | ||
420 | if (width == 1) { | ||
421 | x += 0.5; | ||
422 | height += y; | ||
423 | width = x; | ||
424 | } | ||
425 | |||
426 | if (height == 1) { | ||
427 | y += 0.5; | ||
428 | width += x; | ||
429 | height = y; | ||
430 | } | ||
431 | |||
432 | cairo_move_to(cairo, x, y); | ||
433 | cairo_set_line_width(cairo, 1.0); | ||
434 | cairo_line_to(cairo, width, height); | ||
435 | cairo_stroke(cairo); | ||
436 | } | ||
437 | } | ||
438 | |||
439 | void render_block(struct status_block *block, double *x, bool edge) { | ||
440 | int width, height; | ||
441 | get_text_size(window, &width, &height, "%s", block->full_text); | ||
442 | |||
443 | int textwidth = width; | ||
444 | double block_width = width; | ||
445 | |||
446 | if (width < block->min_width) { | ||
447 | width = block->min_width; | ||
448 | } | ||
449 | |||
450 | *x -= width; | ||
451 | |||
452 | if (block->border != 0 && block->border_left > 0) { | ||
453 | *x -= (block->border_left + margin); | ||
454 | block_width += block->border_left + margin; | ||
455 | } | ||
456 | |||
457 | if (block->border != 0 && block->border_right > 0) { | ||
458 | *x -= (block->border_right + margin); | ||
459 | block_width += block->border_right + margin; | ||
460 | } | ||
461 | |||
462 | // Add separator | ||
463 | if (!edge) { | ||
464 | *x -= block->separator_block_width; | ||
465 | } else { | ||
466 | *x -= margin; | ||
467 | } | ||
468 | |||
469 | double pos = *x; | ||
470 | |||
471 | // render background | ||
472 | if (block->background != 0x0) { | ||
473 | cairo_set_source_u32(window->cairo, block->background); | ||
474 | cairo_rectangle(window->cairo, pos - 0.5, 1, block_width, window->height - 2); | ||
475 | cairo_fill(window->cairo); | ||
476 | } | ||
477 | |||
478 | // render top border | ||
479 | if (block->border != 0 && block->border_top > 0) { | ||
480 | render_sharp_line(window->cairo, block->border, | ||
481 | pos - 0.5, | ||
482 | 1, | ||
483 | block_width, | ||
484 | block->border_top); | ||
485 | } | ||
486 | |||
487 | // render bottom border | ||
488 | if (block->border != 0 && block->border_bottom > 0) { | ||
489 | render_sharp_line(window->cairo, block->border, | ||
490 | pos - 0.5, | ||
491 | window->height - 1 - block->border_bottom, | ||
492 | block_width, | ||
493 | block->border_bottom); | ||
494 | } | ||
495 | |||
496 | // render left border | ||
497 | if (block->border != 0 && block->border_left > 0) { | ||
498 | render_sharp_line(window->cairo, block->border, | ||
499 | pos - 0.5, | ||
500 | 1, | ||
501 | block->border_left, | ||
502 | window->height - 2); | ||
503 | |||
504 | pos += block->border_left + margin; | ||
505 | } | ||
506 | |||
507 | // render text | ||
508 | double offset = 0; | ||
509 | |||
510 | if (strncmp(block->align, "left", 5) == 0) { | ||
511 | offset = pos; | ||
512 | } else if (strncmp(block->align, "right", 5) == 0) { | ||
513 | offset = pos + width - textwidth; | ||
514 | } else if (strncmp(block->align, "center", 6) == 0) { | ||
515 | offset = pos + (width - textwidth) / 2; | ||
516 | } | ||
517 | |||
518 | cairo_move_to(window->cairo, offset, margin); | ||
519 | cairo_set_source_u32(window->cairo, block->color); | ||
520 | pango_printf(window, "%s", block->full_text); | ||
521 | |||
522 | pos += width; | ||
523 | |||
524 | // render right border | ||
525 | if (block->border != 0 && block->border_right > 0) { | ||
526 | pos += margin; | ||
527 | |||
528 | render_sharp_line(window->cairo, block->border, | ||
529 | pos - 0.5, | ||
530 | 1, | ||
531 | block->border_right, | ||
532 | window->height - 2); | ||
533 | |||
534 | pos += block->border_right; | ||
535 | } | ||
536 | |||
537 | // render separator | ||
538 | // TODO: Handle custom separator | ||
539 | if (!edge && block->separator) { | ||
540 | cairo_set_source_u32(window->cairo, colors.separator); | ||
541 | cairo_set_line_width(window->cairo, 1); | ||
542 | cairo_move_to(window->cairo, pos + block->separator_block_width/2, margin); | ||
543 | cairo_line_to(window->cairo, pos + block->separator_block_width/2, window->height - margin); | ||
544 | cairo_stroke(window->cairo); | ||
545 | } | ||
546 | |||
547 | } | ||
548 | |||
549 | void render_workspace_button(struct workspace *ws, double *x) { | ||
550 | int width, height; | ||
551 | get_text_size(window, &width, &height, "%s", ws->name); | ||
552 | struct box_colors box_colors; | ||
553 | if (ws->urgent) { | ||
554 | box_colors = colors.urgent_workspace; | ||
555 | } else if (ws->focused) { | ||
556 | box_colors = colors.focused_workspace; | ||
557 | } else if (ws->visible) { | ||
558 | box_colors = colors.active_workspace; | ||
559 | } else { | ||
560 | box_colors = colors.inactive_workspace; | ||
561 | } | ||
562 | |||
563 | // background | ||
564 | cairo_set_source_u32(window->cairo, box_colors.background); | ||
565 | cairo_rectangle(window->cairo, *x, 1.5, width + ws_hor_padding * 2 - 1, | ||
566 | height + ws_ver_padding * 2); | ||
567 | cairo_fill(window->cairo); | ||
568 | |||
569 | // border | ||
570 | cairo_set_source_u32(window->cairo, box_colors.border); | ||
571 | cairo_rectangle(window->cairo, *x, 1.5, width + ws_hor_padding * 2 - 1, | ||
572 | height + ws_ver_padding * 2); | ||
573 | cairo_stroke(window->cairo); | ||
574 | |||
575 | // text | ||
576 | cairo_set_source_u32(window->cairo, box_colors.text); | ||
577 | cairo_move_to(window->cairo, (int)*x + ws_hor_padding, margin); | ||
578 | pango_printf(window, "%s", ws->name); | ||
579 | |||
580 | *x += width + ws_hor_padding * 2 + ws_spacing; | ||
581 | } | ||
582 | |||
400 | void render() { | 583 | void render() { |
584 | int i; | ||
585 | |||
401 | // Clear | 586 | // Clear |
402 | cairo_save(window->cairo); | 587 | cairo_save(window->cairo); |
403 | cairo_set_operator(window->cairo, CAIRO_OPERATOR_CLEAR); | 588 | cairo_set_operator(window->cairo, CAIRO_OPERATOR_CLEAR); |
@@ -417,49 +602,13 @@ void render() { | |||
417 | cairo_move_to(window->cairo, window->width - margin - width, margin); | 602 | cairo_move_to(window->cairo, window->width - margin - width, margin); |
418 | pango_printf(window, "%s", line); | 603 | pango_printf(window, "%s", line); |
419 | } else if (protocol == I3BAR && status_line) { | 604 | } else if (protocol == I3BAR && status_line) { |
420 | int i, blockpos; | 605 | double pos = window->width - 0.5; |
421 | int moved = 0; | 606 | bool edge = true; |
422 | bool corner = true; | ||
423 | for (i = status_line->length - 1; i >= 0; --i) { | 607 | for (i = status_line->length - 1; i >= 0; --i) { |
424 | struct status_block *block = status_line->items[i]; | 608 | struct status_block *block = status_line->items[i]; |
425 | if (block->full_text && block->full_text[0]) { | 609 | if (block->full_text && block->full_text[0]) { |
426 | get_text_size(window, &width, &height, "%s", block->full_text); | 610 | render_block(block, &pos, edge); |
427 | 611 | edge = false; | |
428 | int textwidth = width; | ||
429 | |||
430 | if (width < block->min_width) { | ||
431 | width = block->min_width; | ||
432 | } | ||
433 | |||
434 | moved += width + block->separator_block_width; | ||
435 | blockpos = window->width - margin - moved; | ||
436 | |||
437 | int offset = 0; | ||
438 | |||
439 | if (strncmp(block->align, "left", 5) == 0) { | ||
440 | offset = blockpos; | ||
441 | } | ||
442 | else if (strncmp(block->align, "right", 5) == 0) { | ||
443 | offset = blockpos + width - textwidth; | ||
444 | } | ||
445 | else if (strncmp(block->align, "center", 6) == 0) { | ||
446 | offset = blockpos + (width - textwidth) / 2; | ||
447 | } | ||
448 | |||
449 | cairo_move_to(window->cairo, offset, margin); | ||
450 | cairo_set_source_u32(window->cairo, block->color); | ||
451 | pango_printf(window, "%s", block->full_text); | ||
452 | if (corner) { | ||
453 | corner = false; | ||
454 | } else if (block->separator) { | ||
455 | cairo_set_source_u32(window->cairo, colors.separator); | ||
456 | cairo_set_line_width(window->cairo, 1); | ||
457 | cairo_move_to(window->cairo, blockpos + width | ||
458 | + block->separator_block_width/2, margin); | ||
459 | cairo_line_to(window->cairo, blockpos + width | ||
460 | + block->separator_block_width/2, window->height - margin); | ||
461 | cairo_stroke(window->cairo); | ||
462 | } | ||
463 | } | 612 | } |
464 | } | 613 | } |
465 | } | 614 | } |
@@ -467,34 +616,9 @@ void render() { | |||
467 | // Workspaces | 616 | // Workspaces |
468 | cairo_set_line_width(window->cairo, 1.0); | 617 | cairo_set_line_width(window->cairo, 1.0); |
469 | double x = 0.5; | 618 | double x = 0.5; |
470 | int i; | ||
471 | for (i = 0; i < workspaces->length; ++i) { | 619 | for (i = 0; i < workspaces->length; ++i) { |
472 | struct workspace *ws = workspaces->items[i]; | 620 | struct workspace *ws = workspaces->items[i]; |
473 | get_text_size(window, &width, &height, "%s", ws->name); | 621 | render_workspace_button(ws, &x); |
474 | struct box_colors box_colors; | ||
475 | if (ws->urgent) { | ||
476 | box_colors = colors.urgent_workspace; | ||
477 | } else if (ws->focused) { | ||
478 | box_colors = colors.focused_workspace; | ||
479 | } else if (ws->visible) { | ||
480 | box_colors = colors.active_workspace; | ||
481 | } else { | ||
482 | box_colors = colors.inactive_workspace; | ||
483 | } | ||
484 | |||
485 | cairo_set_source_u32(window->cairo, box_colors.background); | ||
486 | cairo_rectangle(window->cairo, x, 1.5, width + ws_hor_padding * 2 - 1, height + ws_ver_padding * 2); | ||
487 | cairo_fill(window->cairo); | ||
488 | |||
489 | cairo_set_source_u32(window->cairo, box_colors.border); | ||
490 | cairo_rectangle(window->cairo, x, 1.5, width + ws_hor_padding * 2 - 1, height + ws_ver_padding * 2); | ||
491 | cairo_stroke(window->cairo); | ||
492 | |||
493 | cairo_set_source_u32(window->cairo, box_colors.text); | ||
494 | cairo_move_to(window->cairo, (int)x + ws_hor_padding, margin); | ||
495 | pango_printf(window, "%s", ws->name); | ||
496 | |||
497 | x += width + ws_hor_padding * 2 + ws_spacing; | ||
498 | } | 622 | } |
499 | } | 623 | } |
500 | 624 | ||
@@ -543,6 +667,8 @@ void parse_json(const char *text) { | |||
543 | for (i = 0; i < json_object_array_length(results); ++i) { | 667 | for (i = 0; i < json_object_array_length(results); ++i) { |
544 | json_object *full_text, *short_text, *color, *min_width, *align, *urgent; | 668 | json_object *full_text, *short_text, *color, *min_width, *align, *urgent; |
545 | json_object *name, *instance, *separator, *separator_block_width; | 669 | json_object *name, *instance, *separator, *separator_block_width; |
670 | json_object *background, *border, *border_top, *border_bottom; | ||
671 | json_object *border_left, *border_right; | ||
546 | 672 | ||
547 | json_object *json = json_object_array_get_idx(results, i); | 673 | json_object *json = json_object_array_get_idx(results, i); |
548 | if (!json) { | 674 | if (!json) { |
@@ -559,6 +685,12 @@ void parse_json(const char *text) { | |||
559 | json_object_object_get_ex(json, "instance", &instance); | 685 | json_object_object_get_ex(json, "instance", &instance); |
560 | json_object_object_get_ex(json, "separator", &separator); | 686 | json_object_object_get_ex(json, "separator", &separator); |
561 | json_object_object_get_ex(json, "separator_block_width", &separator_block_width); | 687 | json_object_object_get_ex(json, "separator_block_width", &separator_block_width); |
688 | json_object_object_get_ex(json, "background", &background); | ||
689 | json_object_object_get_ex(json, "border", &border); | ||
690 | json_object_object_get_ex(json, "border_top", &border_top); | ||
691 | json_object_object_get_ex(json, "border_bottom", &border_bottom); | ||
692 | json_object_object_get_ex(json, "border_left", &border_left); | ||
693 | json_object_object_get_ex(json, "border_right", &border_right); | ||
562 | 694 | ||
563 | struct status_block *new = malloc(sizeof(struct status_block)); | 695 | struct status_block *new = malloc(sizeof(struct status_block)); |
564 | memset(new, 0, sizeof(struct status_block)); | 696 | memset(new, 0, sizeof(struct status_block)); |
@@ -623,6 +755,43 @@ void parse_json(const char *text) { | |||
623 | new->separator_block_width = 9; // i3bar spec | 755 | new->separator_block_width = 9; // i3bar spec |
624 | } | 756 | } |
625 | 757 | ||
758 | // Airblader features | ||
759 | if (background) { | ||
760 | new->background = parse_color(json_object_get_string(background)); | ||
761 | } else { | ||
762 | new->background = 0x0; // transparent | ||
763 | } | ||
764 | |||
765 | if (border) { | ||
766 | new->border = parse_color(json_object_get_string(border)); | ||
767 | } else { | ||
768 | new->border = 0x0; // transparent | ||
769 | } | ||
770 | |||
771 | if (border_top) { | ||
772 | new->border_top = json_object_get_int(border_top); | ||
773 | } else { | ||
774 | new->border_top = 1; | ||
775 | } | ||
776 | |||
777 | if (border_bottom) { | ||
778 | new->border_bottom = json_object_get_int(border_bottom); | ||
779 | } else { | ||
780 | new->border_bottom = 1; | ||
781 | } | ||
782 | |||
783 | if (border_left) { | ||
784 | new->border_left = json_object_get_int(border_left); | ||
785 | } else { | ||
786 | new->border_left = 1; | ||
787 | } | ||
788 | |||
789 | if (border_right) { | ||
790 | new->border_right = json_object_get_int(border_right); | ||
791 | } else { | ||
792 | new->border_right = 1; | ||
793 | } | ||
794 | |||
626 | list_add(status_line, new); | 795 | list_add(status_line, new); |
627 | } | 796 | } |
628 | 797 | ||