diff options
Diffstat (limited to 'swaybar')
-rw-r--r-- | swaybar/main.c | 335 |
1 files changed, 324 insertions, 11 deletions
diff --git a/swaybar/main.c b/swaybar/main.c index 4323d370..75d043b0 100644 --- a/swaybar/main.c +++ b/swaybar/main.c | |||
@@ -45,6 +45,18 @@ struct workspace { | |||
45 | bool urgent; | 45 | bool urgent; |
46 | }; | 46 | }; |
47 | 47 | ||
48 | struct status_block { | ||
49 | char *full_text, *short_text, *align; | ||
50 | bool urgent; | ||
51 | uint32_t color; | ||
52 | int min_width; | ||
53 | char *name, *instance; | ||
54 | bool separator; | ||
55 | int separator_block_width; | ||
56 | }; | ||
57 | |||
58 | list_t *status_line = NULL; | ||
59 | |||
48 | list_t *workspaces = NULL; | 60 | list_t *workspaces = NULL; |
49 | int socketfd; | 61 | int socketfd; |
50 | pid_t pid; | 62 | pid_t pid; |
@@ -55,6 +67,8 @@ char *output, *status_command; | |||
55 | struct registry *registry; | 67 | struct registry *registry; |
56 | struct window *window; | 68 | struct window *window; |
57 | bool dirty = true; | 69 | bool dirty = true; |
70 | typedef enum {UNDEF, TEXT, I3BAR} command_protocol; | ||
71 | command_protocol protocol = UNDEF; | ||
58 | 72 | ||
59 | struct colors colors = { | 73 | struct colors colors = { |
60 | .background = 0x000000FF, | 74 | .background = 0x000000FF, |
@@ -88,6 +102,20 @@ struct colors colors = { | |||
88 | }, | 102 | }, |
89 | }; | 103 | }; |
90 | 104 | ||
105 | #define I3JSON_MAXDEPTH 4 | ||
106 | #define I3JSON_UNKNOWN 0 | ||
107 | #define I3JSON_ARRAY 1 | ||
108 | #define I3JSON_STRING 2 | ||
109 | struct { | ||
110 | int bufsize; | ||
111 | char *buffer; | ||
112 | char *line_start; | ||
113 | bool escape; | ||
114 | int depth; | ||
115 | int state[I3JSON_MAXDEPTH+1]; | ||
116 | } i3json_state = { 0, NULL, NULL, false, 0, { I3JSON_UNKNOWN } }; | ||
117 | |||
118 | |||
91 | void swaybar_teardown() { | 119 | void swaybar_teardown() { |
92 | window_teardown(window); | 120 | window_teardown(window); |
93 | if (registry) { | 121 | if (registry) { |
@@ -114,7 +142,6 @@ void swaybar_teardown() { | |||
114 | } | 142 | } |
115 | } | 143 | } |
116 | 144 | ||
117 | |||
118 | void sway_terminate(void) { | 145 | void sway_terminate(void) { |
119 | swaybar_teardown(); | 146 | swaybar_teardown(); |
120 | exit(EXIT_FAILURE); | 147 | exit(EXIT_FAILURE); |
@@ -133,9 +160,21 @@ void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { | |||
133 | (color & 0xFF) / 256.0); | 160 | (color & 0xFF) / 256.0); |
134 | } | 161 | } |
135 | 162 | ||
163 | void free_workspace(void *item) { | ||
164 | if (!item) { | ||
165 | return; | ||
166 | } | ||
167 | struct workspace *ws = (struct workspace *)item; | ||
168 | if (ws->name) { | ||
169 | free(ws->name); | ||
170 | } | ||
171 | free(ws); | ||
172 | } | ||
173 | |||
136 | void ipc_update_workspaces() { | 174 | void ipc_update_workspaces() { |
137 | if (workspaces) { | 175 | if (workspaces) { |
138 | free_flat_list(workspaces); | 176 | list_foreach(workspaces, free_workspace); |
177 | list_free(workspaces); | ||
139 | } | 178 | } |
140 | workspaces = create_list(); | 179 | workspaces = create_list(); |
141 | 180 | ||
@@ -362,10 +401,38 @@ void render() { | |||
362 | // Command output | 401 | // Command output |
363 | cairo_set_source_u32(window->cairo, colors.statusline); | 402 | cairo_set_source_u32(window->cairo, colors.statusline); |
364 | int width, height; | 403 | int width, height; |
365 | get_text_size(window, &width, &height, "%s", line); | ||
366 | 404 | ||
367 | cairo_move_to(window->cairo, window->width - margin - width, margin); | 405 | if (protocol == TEXT) { |
368 | pango_printf(window, "%s", line); | 406 | get_text_size(window, &width, &height, "%s", line); |
407 | cairo_move_to(window->cairo, window->width - margin - width, margin); | ||
408 | pango_printf(window, "%s", line); | ||
409 | } else if (protocol == I3BAR && status_line) { | ||
410 | int i, blockpos; | ||
411 | int moved = 0; | ||
412 | bool corner = true; | ||
413 | for (i = status_line->length - 1; i >= 0; --i) { | ||
414 | struct status_block *block = status_line->items[i]; | ||
415 | if (block->full_text && block->full_text[0]) { | ||
416 | get_text_size(window, &width, &height, "%s", block->full_text); | ||
417 | moved += width + block->separator_block_width; | ||
418 | blockpos = window->width - margin - moved; | ||
419 | cairo_move_to(window->cairo, blockpos, margin); | ||
420 | cairo_set_source_u32(window->cairo, block->color); | ||
421 | pango_printf(window, "%s", block->full_text); | ||
422 | if (corner) { | ||
423 | corner = false; | ||
424 | } else if (block->separator) { | ||
425 | cairo_set_source_u32(window->cairo, colors.separator); | ||
426 | cairo_set_line_width(window->cairo, 1); | ||
427 | cairo_move_to(window->cairo, blockpos + width | ||
428 | + block->separator_block_width/2, margin); | ||
429 | cairo_line_to(window->cairo, blockpos + width | ||
430 | + block->separator_block_width/2, window->height - margin); | ||
431 | cairo_stroke(window->cairo); | ||
432 | } | ||
433 | } | ||
434 | } | ||
435 | } | ||
369 | 436 | ||
370 | // Workspaces | 437 | // Workspaces |
371 | cairo_set_line_width(window->cairo, 1.0); | 438 | cairo_set_line_width(window->cairo, 1.0); |
@@ -401,6 +468,220 @@ void render() { | |||
401 | } | 468 | } |
402 | } | 469 | } |
403 | 470 | ||
471 | void free_status_block(void *item) { | ||
472 | if (!item) { | ||
473 | return; | ||
474 | } | ||
475 | struct status_block *sb = (struct status_block*)item; | ||
476 | if (sb->full_text) { | ||
477 | free(sb->full_text); | ||
478 | } | ||
479 | if (sb->short_text) { | ||
480 | free(sb->short_text); | ||
481 | } | ||
482 | if (sb->align) { | ||
483 | free(sb->align); | ||
484 | } | ||
485 | if (sb->name) { | ||
486 | free(sb->name); | ||
487 | } | ||
488 | if (sb->instance) { | ||
489 | free(sb->instance); | ||
490 | } | ||
491 | free(sb); | ||
492 | } | ||
493 | |||
494 | void parse_json(const char *text) { | ||
495 | /* the array of objects looks like this: | ||
496 | * [ { | ||
497 | * "full_text": "E: 10.0.0.1 (1000 Mbit/s)", | ||
498 | * "short_text": "10.0.0.1", | ||
499 | * "color": "#00ff00", | ||
500 | * "min_width": 300, | ||
501 | * "align": "right", | ||
502 | * "urgent": false, | ||
503 | * "name": "ethernet", | ||
504 | * "instance": "eth0", | ||
505 | * "separator": true, | ||
506 | * "separator_block_width": 9 | ||
507 | * }, | ||
508 | * { ... }, ... | ||
509 | * ] | ||
510 | */ | ||
511 | json_object *results = json_tokener_parse(text); | ||
512 | if (!results) { | ||
513 | sway_log(L_DEBUG, "xxx Failed to parse json"); | ||
514 | return; | ||
515 | } | ||
516 | |||
517 | if (json_object_array_length(results) < 1) { | ||
518 | return; | ||
519 | } | ||
520 | |||
521 | if (status_line) { | ||
522 | list_foreach(status_line, free_status_block); | ||
523 | list_free(status_line); | ||
524 | } | ||
525 | |||
526 | status_line = create_list(); | ||
527 | |||
528 | int i; | ||
529 | for (i = 0; i < json_object_array_length(results); ++i) { | ||
530 | json_object *full_text, *short_text, *color, *min_width, *align, *urgent; | ||
531 | json_object *name, *instance, *separator, *separator_block_width; | ||
532 | |||
533 | json_object *json = json_object_array_get_idx(results, i); | ||
534 | if (!json) { | ||
535 | continue; | ||
536 | } | ||
537 | |||
538 | json_object_object_get_ex(json, "full_text", &full_text); | ||
539 | json_object_object_get_ex(json, "short_text", &short_text); | ||
540 | json_object_object_get_ex(json, "color", &color); | ||
541 | json_object_object_get_ex(json, "min_width", &min_width); | ||
542 | json_object_object_get_ex(json, "align", &align); | ||
543 | json_object_object_get_ex(json, "urgent", &urgent); | ||
544 | json_object_object_get_ex(json, "name", &name); | ||
545 | json_object_object_get_ex(json, "instance", &instance); | ||
546 | json_object_object_get_ex(json, "separator", &separator); | ||
547 | json_object_object_get_ex(json, "separator_block_width", &separator_block_width); | ||
548 | |||
549 | struct status_block *new = malloc(sizeof(struct status_block)); | ||
550 | memset(new, 0, sizeof(struct status_block)); | ||
551 | |||
552 | if (full_text) { | ||
553 | new->full_text = strdup(json_object_get_string(full_text)); | ||
554 | } | ||
555 | |||
556 | if (short_text) { | ||
557 | new->short_text = strdup(json_object_get_string(short_text)); | ||
558 | } | ||
559 | |||
560 | if (color) { | ||
561 | new->color = parse_color(json_object_get_string(color)); | ||
562 | } | ||
563 | else { | ||
564 | new->color = colors.statusline; | ||
565 | } | ||
566 | |||
567 | if (min_width) { | ||
568 | new->min_width = json_object_get_int(min_width); | ||
569 | } | ||
570 | |||
571 | if (align) { | ||
572 | new->align = strdup(json_object_get_string(align)); | ||
573 | } | ||
574 | else { | ||
575 | new->align = strdup("left"); | ||
576 | } | ||
577 | |||
578 | if (urgent) { | ||
579 | new->urgent = json_object_get_int(urgent); | ||
580 | } | ||
581 | |||
582 | if (name) { | ||
583 | new->name = strdup(json_object_get_string(name)); | ||
584 | } | ||
585 | |||
586 | if (instance) { | ||
587 | new->instance = strdup(json_object_get_string(instance)); | ||
588 | } | ||
589 | |||
590 | if (separator) { | ||
591 | new->separator = json_object_get_int(separator); | ||
592 | } | ||
593 | else { | ||
594 | new->separator = true; // i3bar spec | ||
595 | } | ||
596 | |||
597 | if (separator_block_width) { | ||
598 | new->separator_block_width = json_object_get_int(separator_block_width); | ||
599 | } | ||
600 | else { | ||
601 | new->separator_block_width = 9; // i3bar spec | ||
602 | } | ||
603 | |||
604 | list_add(status_line, new); | ||
605 | } | ||
606 | |||
607 | json_object_put(results); | ||
608 | } | ||
609 | |||
610 | int i3json_handle(FILE *file) { | ||
611 | char *c; | ||
612 | int handled = 0; | ||
613 | // handle partially buffered data | ||
614 | // make sure 1023+1 bytes at the end are free | ||
615 | if (i3json_state.line_start) { | ||
616 | int len = strlen(i3json_state.line_start); | ||
617 | memmove(i3json_state.buffer, i3json_state.line_start, len+1); | ||
618 | if (i3json_state.bufsize < len+1024) { | ||
619 | i3json_state.bufsize += 1024; | ||
620 | i3json_state.buffer = realloc(i3json_state.buffer, i3json_state.bufsize); | ||
621 | } | ||
622 | c = i3json_state.buffer+len; | ||
623 | i3json_state.line_start = i3json_state.buffer; | ||
624 | } else if (!i3json_state.buffer) { | ||
625 | i3json_state.buffer = malloc(1024); | ||
626 | i3json_state.bufsize = 1024; | ||
627 | c = i3json_state.buffer; | ||
628 | } else { | ||
629 | c = i3json_state.buffer; | ||
630 | } | ||
631 | if (!i3json_state.buffer) { | ||
632 | sway_abort("Could not allocate buffer."); | ||
633 | } | ||
634 | // get fresh data at the end of the buffer | ||
635 | if (!fgets(c, 1023, file)) return -1; | ||
636 | c[1023] = '\0'; | ||
637 | |||
638 | while (*c) { | ||
639 | if (i3json_state.state[i3json_state.depth] == I3JSON_STRING) { | ||
640 | if (!i3json_state.escape && *c == '"') { | ||
641 | --i3json_state.depth; | ||
642 | } | ||
643 | i3json_state.escape = !i3json_state.escape && *c == '\\'; | ||
644 | } else { | ||
645 | switch (*c) { | ||
646 | case '[': | ||
647 | ++i3json_state.depth; | ||
648 | if (i3json_state.depth > I3JSON_MAXDEPTH) { | ||
649 | sway_abort("JSON too deep"); | ||
650 | } | ||
651 | i3json_state.state[i3json_state.depth] = I3JSON_ARRAY; | ||
652 | if (i3json_state.depth == 2) { | ||
653 | i3json_state.line_start = c; | ||
654 | } | ||
655 | break; | ||
656 | case ']': | ||
657 | if (i3json_state.state[i3json_state.depth] != I3JSON_ARRAY) { | ||
658 | sway_abort("JSON malformed"); | ||
659 | } | ||
660 | --i3json_state.depth; | ||
661 | if (i3json_state.depth == 1) { | ||
662 | ssize_t len = c-i3json_state.line_start+1; | ||
663 | char p = c[len]; | ||
664 | c[len] = '\0'; | ||
665 | parse_json(i3json_state.line_start); | ||
666 | c[len] = p; | ||
667 | ++handled; | ||
668 | i3json_state.line_start = c+1; | ||
669 | } | ||
670 | break; | ||
671 | case '"': | ||
672 | ++i3json_state.depth; | ||
673 | if (i3json_state.depth > I3JSON_MAXDEPTH) { | ||
674 | sway_abort("JSON too deep"); | ||
675 | } | ||
676 | i3json_state.state[i3json_state.depth] = I3JSON_STRING; | ||
677 | break; | ||
678 | } | ||
679 | } | ||
680 | ++c; | ||
681 | } | ||
682 | return handled; | ||
683 | } | ||
684 | |||
404 | void poll_for_update() { | 685 | void poll_for_update() { |
405 | fd_set readfds; | 686 | fd_set readfds; |
406 | int activity; | 687 | int activity; |
@@ -436,18 +717,49 @@ void poll_for_update() { | |||
436 | 717 | ||
437 | if (status_command && FD_ISSET(pipefd[0], &readfds)) { | 718 | if (status_command && FD_ISSET(pipefd[0], &readfds)) { |
438 | sway_log(L_DEBUG, "Got update from status command."); | 719 | sway_log(L_DEBUG, "Got update from status command."); |
439 | fgets(line, sizeof(line), command); | 720 | int linelen; |
440 | int l = strlen(line) - 1; | 721 | switch (protocol) { |
441 | if (line[l] == '\n') { | 722 | case I3BAR: |
442 | line[l] = '\0'; | 723 | sway_log(L_DEBUG, "Got i3bar protocol."); |
724 | if (i3json_handle(command) > 0) { | ||
725 | dirty = true; | ||
726 | } | ||
727 | break; | ||
728 | case TEXT: | ||
729 | case UNDEF: | ||
730 | sway_log(L_DEBUG, "Got text protocol."); | ||
731 | fgets(line, sizeof(line), command); | ||
732 | linelen = strlen(line) - 1; | ||
733 | if (line[linelen] == '\n') { | ||
734 | line[linelen] = '\0'; | ||
735 | } | ||
736 | dirty = true; | ||
737 | if (protocol == UNDEF) { | ||
738 | protocol = TEXT; | ||
739 | if (line[0] == '{') { | ||
740 | // detect i3bar json protocol | ||
741 | json_object *proto = json_tokener_parse(line); | ||
742 | json_object *version; | ||
743 | if (proto) { | ||
744 | if (json_object_object_get_ex(proto, "version", &version) | ||
745 | && json_object_get_int(version) == 1 | ||
746 | ) { | ||
747 | sway_log(L_DEBUG, "Switched to i3bar protocol."); | ||
748 | protocol = I3BAR; | ||
749 | line[0] = '\0'; | ||
750 | } | ||
751 | json_object_put(proto); | ||
752 | } | ||
753 | } | ||
754 | } | ||
755 | break; | ||
443 | } | 756 | } |
444 | dirty = true; | ||
445 | } | 757 | } |
446 | } | 758 | } |
447 | } | 759 | } |
448 | 760 | ||
449 | int main(int argc, char **argv) { | 761 | int main(int argc, char **argv) { |
450 | init_log(L_INFO); | 762 | init_log(L_DEBUG); |
451 | 763 | ||
452 | char *socket_path = NULL; | 764 | char *socket_path = NULL; |
453 | char *bar_id = NULL; | 765 | char *bar_id = NULL; |
@@ -538,6 +850,7 @@ int main(int argc, char **argv) { | |||
538 | 850 | ||
539 | close(pipefd[1]); | 851 | close(pipefd[1]); |
540 | command = fdopen(pipefd[0], "r"); | 852 | command = fdopen(pipefd[0], "r"); |
853 | setbuf(command, NULL); | ||
541 | line[0] = '\0'; | 854 | line[0] = '\0'; |
542 | } | 855 | } |
543 | 856 | ||