diff options
Diffstat (limited to 'swaybar')
-rw-r--r-- | swaybar/main.c | 233 |
1 files changed, 173 insertions, 60 deletions
diff --git a/swaybar/main.c b/swaybar/main.c index 37218c32..9f735f36 100644 --- a/swaybar/main.c +++ b/swaybar/main.c | |||
@@ -1,3 +1,4 @@ | |||
1 | #include <fcntl.h> | ||
1 | #include <stdio.h> | 2 | #include <stdio.h> |
2 | #include <stdlib.h> | 3 | #include <stdlib.h> |
3 | #include <string.h> | 4 | #include <string.h> |
@@ -60,9 +61,9 @@ list_t *status_line = NULL; | |||
60 | list_t *workspaces = NULL; | 61 | list_t *workspaces = NULL; |
61 | int socketfd; | 62 | int socketfd; |
62 | pid_t pid; | 63 | pid_t pid; |
63 | int pipefd[2]; | 64 | int status_read_fd; |
64 | FILE *command; | ||
65 | char line[1024]; | 65 | char line[1024]; |
66 | char line_rest[1024]; | ||
66 | char *output, *status_command; | 67 | char *output, *status_command; |
67 | struct registry *registry; | 68 | struct registry *registry; |
68 | struct window *window; | 69 | struct window *window; |
@@ -110,10 +111,11 @@ struct { | |||
110 | int bufsize; | 111 | int bufsize; |
111 | char *buffer; | 112 | char *buffer; |
112 | char *line_start; | 113 | char *line_start; |
114 | char *parserpos; | ||
113 | bool escape; | 115 | bool escape; |
114 | int depth; | 116 | int depth; |
115 | int state[I3JSON_MAXDEPTH+1]; | 117 | int state[I3JSON_MAXDEPTH+1]; |
116 | } i3json_state = { 0, NULL, NULL, false, 0, { I3JSON_UNKNOWN } }; | 118 | } i3json_state = { 0, NULL, NULL, NULL, false, 0, { I3JSON_UNKNOWN } }; |
117 | 119 | ||
118 | 120 | ||
119 | void swaybar_teardown() { | 121 | void swaybar_teardown() { |
@@ -122,8 +124,8 @@ void swaybar_teardown() { | |||
122 | registry_teardown(registry); | 124 | registry_teardown(registry); |
123 | } | 125 | } |
124 | 126 | ||
125 | if (command) { | 127 | if (status_read_fd) { |
126 | fclose(command); | 128 | close(status_read_fd); |
127 | } | 129 | } |
128 | 130 | ||
129 | if (pid) { | 131 | if (pid) { |
@@ -137,8 +139,8 @@ void swaybar_teardown() { | |||
137 | } | 139 | } |
138 | } | 140 | } |
139 | 141 | ||
140 | if (pipefd[0]) { | 142 | if (status_read_fd) { |
141 | close(pipefd[0]); | 143 | close(status_read_fd); |
142 | } | 144 | } |
143 | } | 145 | } |
144 | 146 | ||
@@ -445,9 +447,9 @@ void render() { | |||
445 | cairo_set_source_u32(window->cairo, colors.separator); | 447 | cairo_set_source_u32(window->cairo, colors.separator); |
446 | cairo_set_line_width(window->cairo, 1); | 448 | cairo_set_line_width(window->cairo, 1); |
447 | cairo_move_to(window->cairo, blockpos + width | 449 | cairo_move_to(window->cairo, blockpos + width |
448 | + block->separator_block_width/2, margin); | 450 | + block->separator_block_width/2, margin); |
449 | cairo_line_to(window->cairo, blockpos + width | 451 | cairo_line_to(window->cairo, blockpos + width |
450 | + block->separator_block_width/2, window->height - margin); | 452 | + block->separator_block_width/2, window->height - margin); |
451 | cairo_stroke(window->cairo); | 453 | cairo_stroke(window->cairo); |
452 | } | 454 | } |
453 | } | 455 | } |
@@ -619,34 +621,124 @@ void parse_json(const char *text) { | |||
619 | json_object_put(results); | 621 | json_object_put(results); |
620 | } | 622 | } |
621 | 623 | ||
622 | int i3json_handle(FILE *file) { | 624 | // Read line from file descriptor, only show the line tail if it is too long. |
623 | char *c; | 625 | // In non-blocking mode treat "no more data" as a linebreak. |
624 | int handled = 0; | 626 | // If data after a line break has been read, return it in rest. |
625 | // handle partially buffered data | 627 | // If rest is non-empty, then use that as the start of the next line. |
626 | // make sure 1023+1 bytes at the end are free | 628 | int read_line_tail(int fd, char *buf, int nbyte, char *rest) { |
627 | if (i3json_state.line_start) { | 629 | if (fd < 0 || !buf || !nbyte) { |
628 | int len = strlen(i3json_state.line_start); | 630 | return -1; |
629 | memmove(i3json_state.buffer, i3json_state.line_start, len+1); | 631 | } |
630 | if (i3json_state.bufsize < len+1024) { | 632 | int l; |
631 | i3json_state.bufsize += 1024; | 633 | char *buffer = malloc(nbyte*2+1); |
632 | i3json_state.buffer = realloc(i3json_state.buffer, i3json_state.bufsize); | 634 | char *readpos = buffer; |
635 | char *lf; | ||
636 | // prepend old data to new line if necessary | ||
637 | if (rest) { | ||
638 | l = strlen(rest); | ||
639 | if (l > nbyte) { | ||
640 | strcpy(buffer, rest + l - nbyte); | ||
641 | readpos += nbyte; | ||
642 | } else if (l) { | ||
643 | strcpy(buffer, rest); | ||
644 | readpos += l; | ||
645 | } | ||
646 | } | ||
647 | // read until a linefeed is found or no more data is available | ||
648 | while ((l = read(fd, readpos, nbyte)) > 0) { | ||
649 | readpos[l] = '\0'; | ||
650 | lf = strchr(readpos, '\n'); | ||
651 | if (lf) { | ||
652 | // linefeed found, replace with \0 | ||
653 | *lf = '\0'; | ||
654 | // give data from the end of the line, try to fill the buffer | ||
655 | if (lf-buffer > nbyte) { | ||
656 | strcpy(buf, lf - nbyte + 1); | ||
657 | } else { | ||
658 | strcpy(buf, buffer); | ||
659 | } | ||
660 | // we may have read data from the next line, save it to rest | ||
661 | if (rest) { | ||
662 | rest[0] = '\0'; | ||
663 | strcpy(rest, lf + 1); | ||
664 | } | ||
665 | free(buffer); | ||
666 | return strlen(buf); | ||
667 | } else { | ||
668 | // no linefeed found, slide data back. | ||
669 | int overflow = readpos - buffer + l - nbyte; | ||
670 | if (overflow > 0) { | ||
671 | memmove(buffer, buffer + overflow , nbyte + 1); | ||
672 | } | ||
633 | } | 673 | } |
634 | c = i3json_state.buffer+len; | 674 | } |
635 | i3json_state.line_start = i3json_state.buffer; | 675 | if (l < 0) { |
636 | } else if (!i3json_state.buffer) { | 676 | free(buffer); |
637 | i3json_state.buffer = malloc(1024); | 677 | return l; |
638 | i3json_state.bufsize = 1024; | 678 | } |
639 | c = i3json_state.buffer; | 679 | readpos[l]='\0'; |
680 | if (rest) { | ||
681 | rest[0] = '\0'; | ||
682 | } | ||
683 | if (nbyte < readpos - buffer + l - 1) { | ||
684 | memcpy(buf, readpos - nbyte + l + 1, nbyte); | ||
640 | } else { | 685 | } else { |
641 | c = i3json_state.buffer; | 686 | strncpy(buf, buffer, nbyte); |
687 | } | ||
688 | buf[nbyte-1] = '\0'; | ||
689 | free(buffer); | ||
690 | return strlen(buf); | ||
691 | } | ||
692 | |||
693 | // make sure that enough buffer space is available starting from parserpos | ||
694 | void i3json_ensure_free(int min_free) { | ||
695 | int _step = 10240; | ||
696 | int r = min_free % _step; | ||
697 | if (r) { | ||
698 | min_free += _step - r; | ||
699 | } | ||
700 | if (!i3json_state.buffer) { | ||
701 | i3json_state.buffer = malloc(min_free); | ||
702 | i3json_state.bufsize = min_free; | ||
703 | i3json_state.parserpos = i3json_state.buffer; | ||
704 | } else { | ||
705 | int len = 0; | ||
706 | int pos = 0; | ||
707 | if (i3json_state.line_start) { | ||
708 | len = strlen(i3json_state.line_start); | ||
709 | pos = i3json_state.parserpos - i3json_state.line_start; | ||
710 | if (i3json_state.line_start != i3json_state.buffer) { | ||
711 | memmove(i3json_state.buffer, i3json_state.line_start, len+1); | ||
712 | } | ||
713 | } else { | ||
714 | len = strlen(i3json_state.buffer); | ||
715 | } | ||
716 | if (i3json_state.bufsize < len+min_free) { | ||
717 | i3json_state.bufsize += min_free; | ||
718 | if (i3json_state.bufsize > 1024000) { | ||
719 | sway_abort("Status line json too long or malformed."); | ||
720 | } | ||
721 | i3json_state.buffer = realloc(i3json_state.buffer, i3json_state.bufsize); | ||
722 | if (!i3json_state.buffer) { | ||
723 | sway_abort("Could not allocate json buffer"); | ||
724 | } | ||
725 | } | ||
726 | if (i3json_state.line_start) { | ||
727 | i3json_state.line_start = i3json_state.buffer; | ||
728 | i3json_state.parserpos = i3json_state.buffer + pos; | ||
729 | } else { | ||
730 | i3json_state.parserpos = i3json_state.buffer; | ||
731 | } | ||
642 | } | 732 | } |
643 | if (!i3json_state.buffer) { | 733 | if (!i3json_state.buffer) { |
644 | sway_abort("Could not allocate buffer."); | 734 | sway_abort("Could not allocate buffer."); |
645 | } | 735 | } |
646 | // get fresh data at the end of the buffer | 736 | } |
647 | if (!fgets(c, 1023, file)) return -1; | ||
648 | c[1023] = '\0'; | ||
649 | 737 | ||
738 | // continue parsing from last parserpos | ||
739 | int i3json_parse() { | ||
740 | char *c = i3json_state.parserpos; | ||
741 | int handled = 0; | ||
650 | while (*c) { | 742 | while (*c) { |
651 | if (i3json_state.state[i3json_state.depth] == I3JSON_STRING) { | 743 | if (i3json_state.state[i3json_state.depth] == I3JSON_STRING) { |
652 | if (!i3json_state.escape && *c == '"') { | 744 | if (!i3json_state.escape && *c == '"') { |
@@ -671,11 +763,11 @@ int i3json_handle(FILE *file) { | |||
671 | } | 763 | } |
672 | --i3json_state.depth; | 764 | --i3json_state.depth; |
673 | if (i3json_state.depth == 1) { | 765 | if (i3json_state.depth == 1) { |
674 | ssize_t len = c-i3json_state.line_start+1; | 766 | // c[1] is valid since c[0] != '\0' |
675 | char p = c[len]; | 767 | char p = c[1]; |
676 | c[len] = '\0'; | 768 | c[1] = '\0'; |
677 | parse_json(i3json_state.line_start); | 769 | parse_json(i3json_state.line_start); |
678 | c[len] = p; | 770 | c[1] = p; |
679 | ++handled; | 771 | ++handled; |
680 | i3json_state.line_start = c+1; | 772 | i3json_state.line_start = c+1; |
681 | } | 773 | } |
@@ -691,9 +783,30 @@ int i3json_handle(FILE *file) { | |||
691 | } | 783 | } |
692 | ++c; | 784 | ++c; |
693 | } | 785 | } |
786 | i3json_state.parserpos = c; | ||
694 | return handled; | 787 | return handled; |
695 | } | 788 | } |
696 | 789 | ||
790 | // append data and parse it. | ||
791 | int i3json_handle_data(char *data) { | ||
792 | int len = strlen(data); | ||
793 | i3json_ensure_free(len); | ||
794 | strcpy(i3json_state.parserpos, data); | ||
795 | return i3json_parse(); | ||
796 | } | ||
797 | |||
798 | // read data from fd and parse it. | ||
799 | int i3json_handle_fd(int fd) { | ||
800 | i3json_ensure_free(10240); | ||
801 | // get fresh data at the end of the buffer | ||
802 | int readlen = read(fd, i3json_state.parserpos, 10239); | ||
803 | if (readlen < 0) { | ||
804 | return readlen; | ||
805 | } | ||
806 | i3json_state.parserpos[readlen] = '\0'; | ||
807 | return i3json_parse(); | ||
808 | } | ||
809 | |||
697 | void poll_for_update() { | 810 | void poll_for_update() { |
698 | fd_set readfds; | 811 | fd_set readfds; |
699 | int activity; | 812 | int activity; |
@@ -710,7 +823,7 @@ void poll_for_update() { | |||
710 | dirty = false; | 823 | dirty = false; |
711 | FD_ZERO(&readfds); | 824 | FD_ZERO(&readfds); |
712 | FD_SET(socketfd, &readfds); | 825 | FD_SET(socketfd, &readfds); |
713 | FD_SET(pipefd[0], &readfds); | 826 | FD_SET(status_read_fd, &readfds); |
714 | 827 | ||
715 | activity = select(FD_SETSIZE, &readfds, NULL, NULL, NULL); | 828 | activity = select(FD_SETSIZE, &readfds, NULL, NULL, NULL); |
716 | if (activity < 0) { | 829 | if (activity < 0) { |
@@ -726,41 +839,40 @@ void poll_for_update() { | |||
726 | dirty = true; | 839 | dirty = true; |
727 | } | 840 | } |
728 | 841 | ||
729 | if (status_command && FD_ISSET(pipefd[0], &readfds)) { | 842 | if (status_command && FD_ISSET(status_read_fd, &readfds)) { |
730 | sway_log(L_DEBUG, "Got update from status command."); | 843 | sway_log(L_DEBUG, "Got update from status command."); |
731 | int linelen; | ||
732 | switch (protocol) { | 844 | switch (protocol) { |
733 | case I3BAR: | 845 | case I3BAR: |
734 | sway_log(L_DEBUG, "Got i3bar protocol."); | 846 | sway_log(L_DEBUG, "Got i3bar protocol."); |
735 | if (i3json_handle(command) > 0) { | 847 | if (i3json_handle_fd(status_read_fd) > 0) { |
736 | dirty = true; | 848 | dirty = true; |
737 | } | 849 | } |
738 | break; | 850 | break; |
739 | case TEXT: | 851 | case TEXT: |
740 | case UNDEF: | ||
741 | sway_log(L_DEBUG, "Got text protocol."); | 852 | sway_log(L_DEBUG, "Got text protocol."); |
742 | fgets(line, sizeof(line), command); | 853 | read_line_tail(status_read_fd, line, sizeof(line), line_rest); |
743 | linelen = strlen(line) - 1; | 854 | dirty = true; |
744 | if (line[linelen] == '\n') { | 855 | break; |
745 | line[linelen] = '\0'; | 856 | case UNDEF: |
857 | sway_log(L_DEBUG, "Detecting protocol..."); | ||
858 | if (read_line_tail(status_read_fd, line, sizeof(line), line_rest) < 0) { | ||
859 | break; | ||
746 | } | 860 | } |
747 | dirty = true; | 861 | dirty = true; |
748 | if (protocol == UNDEF) { | 862 | protocol = TEXT; |
749 | protocol = TEXT; | 863 | if (line[0] == '{') { |
750 | if (line[0] == '{') { | 864 | // detect i3bar json protocol |
751 | // detect i3bar json protocol | 865 | json_object *proto = json_tokener_parse(line); |
752 | json_object *proto = json_tokener_parse(line); | 866 | json_object *version; |
753 | json_object *version; | 867 | if (proto) { |
754 | if (proto) { | 868 | if (json_object_object_get_ex(proto, "version", &version) |
755 | if (json_object_object_get_ex(proto, "version", &version) | 869 | && json_object_get_int(version) == 1 |
756 | && json_object_get_int(version) == 1 | 870 | ) { |
757 | ) { | 871 | sway_log(L_DEBUG, "Switched to i3bar protocol."); |
758 | sway_log(L_DEBUG, "Switched to i3bar protocol."); | 872 | protocol = I3BAR; |
759 | protocol = I3BAR; | 873 | i3json_handle_data(line_rest); |
760 | line[0] = '\0'; | ||
761 | } | ||
762 | json_object_put(proto); | ||
763 | } | 874 | } |
875 | json_object_put(proto); | ||
764 | } | 876 | } |
765 | } | 877 | } |
766 | break; | 878 | break; |
@@ -843,6 +955,7 @@ int main(int argc, char **argv) { | |||
843 | bar_ipc_init(desired_output, bar_id); | 955 | bar_ipc_init(desired_output, bar_id); |
844 | 956 | ||
845 | if (status_command) { | 957 | if (status_command) { |
958 | int pipefd[2]; | ||
846 | pipe(pipefd); | 959 | pipe(pipefd); |
847 | pid = fork(); | 960 | pid = fork(); |
848 | if (pid == 0) { | 961 | if (pid == 0) { |
@@ -860,8 +973,8 @@ int main(int argc, char **argv) { | |||
860 | } | 973 | } |
861 | 974 | ||
862 | close(pipefd[1]); | 975 | close(pipefd[1]); |
863 | command = fdopen(pipefd[0], "r"); | 976 | status_read_fd = pipefd[0]; |
864 | setbuf(command, NULL); | 977 | fcntl(status_read_fd, F_SETFL, O_NONBLOCK); |
865 | line[0] = '\0'; | 978 | line[0] = '\0'; |
866 | } | 979 | } |
867 | 980 | ||