diff options
author | Drew DeVault <sir@cmpwn.com> | 2015-12-24 07:04:35 -0700 |
---|---|---|
committer | Drew DeVault <sir@cmpwn.com> | 2015-12-24 07:04:35 -0700 |
commit | deb1546be355fa571da15569599bec0141d67883 (patch) | |
tree | c39311faf2cd8325adbf333819a674a561ff28e7 /swaybar/main.c | |
parent | Merge pull request #404 from StephenBrown2/readme_feature_support (diff) | |
parent | swaybar: Replace fgets with read and own buffer (diff) | |
download | sway-deb1546be355fa571da15569599bec0141d67883.tar.gz sway-deb1546be355fa571da15569599bec0141d67883.tar.zst sway-deb1546be355fa571da15569599bec0141d67883.zip |
Merge pull request #402 from progandy/without-fgets
swaybar: read status line without fgets
Diffstat (limited to 'swaybar/main.c')
-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 74930075..86ccfbb9 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 | ||
@@ -425,9 +427,9 @@ void render() { | |||
425 | cairo_set_source_u32(window->cairo, colors.separator); | 427 | cairo_set_source_u32(window->cairo, colors.separator); |
426 | cairo_set_line_width(window->cairo, 1); | 428 | cairo_set_line_width(window->cairo, 1); |
427 | cairo_move_to(window->cairo, blockpos + width | 429 | cairo_move_to(window->cairo, blockpos + width |
428 | + block->separator_block_width/2, margin); | 430 | + block->separator_block_width/2, margin); |
429 | cairo_line_to(window->cairo, blockpos + width | 431 | cairo_line_to(window->cairo, blockpos + width |
430 | + block->separator_block_width/2, window->height - margin); | 432 | + block->separator_block_width/2, window->height - margin); |
431 | cairo_stroke(window->cairo); | 433 | cairo_stroke(window->cairo); |
432 | } | 434 | } |
433 | } | 435 | } |
@@ -607,34 +609,124 @@ void parse_json(const char *text) { | |||
607 | json_object_put(results); | 609 | json_object_put(results); |
608 | } | 610 | } |
609 | 611 | ||
610 | int i3json_handle(FILE *file) { | 612 | // Read line from file descriptor, only show the line tail if it is too long. |
611 | char *c; | 613 | // In non-blocking mode treat "no more data" as a linebreak. |
612 | int handled = 0; | 614 | // If data after a line break has been read, return it in rest. |
613 | // handle partially buffered data | 615 | // If rest is non-empty, then use that as the start of the next line. |
614 | // make sure 1023+1 bytes at the end are free | 616 | int read_line_tail(int fd, char *buf, int nbyte, char *rest) { |
615 | if (i3json_state.line_start) { | 617 | if (fd < 0 || !buf || !nbyte) { |
616 | int len = strlen(i3json_state.line_start); | 618 | return -1; |
617 | memmove(i3json_state.buffer, i3json_state.line_start, len+1); | 619 | } |
618 | if (i3json_state.bufsize < len+1024) { | 620 | int l; |
619 | i3json_state.bufsize += 1024; | 621 | char *buffer = malloc(nbyte*2+1); |
620 | i3json_state.buffer = realloc(i3json_state.buffer, i3json_state.bufsize); | 622 | char *readpos = buffer; |
623 | char *lf; | ||
624 | // prepend old data to new line if necessary | ||
625 | if (rest) { | ||
626 | l = strlen(rest); | ||
627 | if (l > nbyte) { | ||
628 | strcpy(buffer, rest + l - nbyte); | ||
629 | readpos += nbyte; | ||
630 | } else if (l) { | ||
631 | strcpy(buffer, rest); | ||
632 | readpos += l; | ||
633 | } | ||
634 | } | ||
635 | // read until a linefeed is found or no more data is available | ||
636 | while ((l = read(fd, readpos, nbyte)) > 0) { | ||
637 | readpos[l] = '\0'; | ||
638 | lf = strchr(readpos, '\n'); | ||
639 | if (lf) { | ||
640 | // linefeed found, replace with \0 | ||
641 | *lf = '\0'; | ||
642 | // give data from the end of the line, try to fill the buffer | ||
643 | if (lf-buffer > nbyte) { | ||
644 | strcpy(buf, lf - nbyte + 1); | ||
645 | } else { | ||
646 | strcpy(buf, buffer); | ||
647 | } | ||
648 | // we may have read data from the next line, save it to rest | ||
649 | if (rest) { | ||
650 | rest[0] = '\0'; | ||
651 | strcpy(rest, lf + 1); | ||
652 | } | ||
653 | free(buffer); | ||
654 | return strlen(buf); | ||
655 | } else { | ||
656 | // no linefeed found, slide data back. | ||
657 | int overflow = readpos - buffer + l - nbyte; | ||
658 | if (overflow > 0) { | ||
659 | memmove(buffer, buffer + overflow , nbyte + 1); | ||
660 | } | ||
621 | } | 661 | } |
622 | c = i3json_state.buffer+len; | 662 | } |
623 | i3json_state.line_start = i3json_state.buffer; | 663 | if (l < 0) { |
624 | } else if (!i3json_state.buffer) { | 664 | free(buffer); |
625 | i3json_state.buffer = malloc(1024); | 665 | return l; |
626 | i3json_state.bufsize = 1024; | 666 | } |
627 | c = i3json_state.buffer; | 667 | readpos[l]='\0'; |
668 | if (rest) { | ||
669 | rest[0] = '\0'; | ||
670 | } | ||
671 | if (nbyte < readpos - buffer + l - 1) { | ||
672 | memcpy(buf, readpos - nbyte + l + 1, nbyte); | ||
628 | } else { | 673 | } else { |
629 | c = i3json_state.buffer; | 674 | strncpy(buf, buffer, nbyte); |
675 | } | ||
676 | buf[nbyte-1] = '\0'; | ||
677 | free(buffer); | ||
678 | return strlen(buf); | ||
679 | } | ||
680 | |||
681 | // make sure that enough buffer space is available starting from parserpos | ||
682 | void i3json_ensure_free(int min_free) { | ||
683 | int _step = 10240; | ||
684 | int r = min_free % _step; | ||
685 | if (r) { | ||
686 | min_free += _step - r; | ||
687 | } | ||
688 | if (!i3json_state.buffer) { | ||
689 | i3json_state.buffer = malloc(min_free); | ||
690 | i3json_state.bufsize = min_free; | ||
691 | i3json_state.parserpos = i3json_state.buffer; | ||
692 | } else { | ||
693 | int len = 0; | ||
694 | int pos = 0; | ||
695 | if (i3json_state.line_start) { | ||
696 | len = strlen(i3json_state.line_start); | ||
697 | pos = i3json_state.parserpos - i3json_state.line_start; | ||
698 | if (i3json_state.line_start != i3json_state.buffer) { | ||
699 | memmove(i3json_state.buffer, i3json_state.line_start, len+1); | ||
700 | } | ||
701 | } else { | ||
702 | len = strlen(i3json_state.buffer); | ||
703 | } | ||
704 | if (i3json_state.bufsize < len+min_free) { | ||
705 | i3json_state.bufsize += min_free; | ||
706 | if (i3json_state.bufsize > 1024000) { | ||
707 | sway_abort("Status line json too long or malformed."); | ||
708 | } | ||
709 | i3json_state.buffer = realloc(i3json_state.buffer, i3json_state.bufsize); | ||
710 | if (!i3json_state.buffer) { | ||
711 | sway_abort("Could not allocate json buffer"); | ||
712 | } | ||
713 | } | ||
714 | if (i3json_state.line_start) { | ||
715 | i3json_state.line_start = i3json_state.buffer; | ||
716 | i3json_state.parserpos = i3json_state.buffer + pos; | ||
717 | } else { | ||
718 | i3json_state.parserpos = i3json_state.buffer; | ||
719 | } | ||
630 | } | 720 | } |
631 | if (!i3json_state.buffer) { | 721 | if (!i3json_state.buffer) { |
632 | sway_abort("Could not allocate buffer."); | 722 | sway_abort("Could not allocate buffer."); |
633 | } | 723 | } |
634 | // get fresh data at the end of the buffer | 724 | } |
635 | if (!fgets(c, 1023, file)) return -1; | ||
636 | c[1023] = '\0'; | ||
637 | 725 | ||
726 | // continue parsing from last parserpos | ||
727 | int i3json_parse() { | ||
728 | char *c = i3json_state.parserpos; | ||
729 | int handled = 0; | ||
638 | while (*c) { | 730 | while (*c) { |
639 | if (i3json_state.state[i3json_state.depth] == I3JSON_STRING) { | 731 | if (i3json_state.state[i3json_state.depth] == I3JSON_STRING) { |
640 | if (!i3json_state.escape && *c == '"') { | 732 | if (!i3json_state.escape && *c == '"') { |
@@ -659,11 +751,11 @@ int i3json_handle(FILE *file) { | |||
659 | } | 751 | } |
660 | --i3json_state.depth; | 752 | --i3json_state.depth; |
661 | if (i3json_state.depth == 1) { | 753 | if (i3json_state.depth == 1) { |
662 | ssize_t len = c-i3json_state.line_start+1; | 754 | // c[1] is valid since c[0] != '\0' |
663 | char p = c[len]; | 755 | char p = c[1]; |
664 | c[len] = '\0'; | 756 | c[1] = '\0'; |
665 | parse_json(i3json_state.line_start); | 757 | parse_json(i3json_state.line_start); |
666 | c[len] = p; | 758 | c[1] = p; |
667 | ++handled; | 759 | ++handled; |
668 | i3json_state.line_start = c+1; | 760 | i3json_state.line_start = c+1; |
669 | } | 761 | } |
@@ -679,9 +771,30 @@ int i3json_handle(FILE *file) { | |||
679 | } | 771 | } |
680 | ++c; | 772 | ++c; |
681 | } | 773 | } |
774 | i3json_state.parserpos = c; | ||
682 | return handled; | 775 | return handled; |
683 | } | 776 | } |
684 | 777 | ||
778 | // append data and parse it. | ||
779 | int i3json_handle_data(char *data) { | ||
780 | int len = strlen(data); | ||
781 | i3json_ensure_free(len); | ||
782 | strcpy(i3json_state.parserpos, data); | ||
783 | return i3json_parse(); | ||
784 | } | ||
785 | |||
786 | // read data from fd and parse it. | ||
787 | int i3json_handle_fd(int fd) { | ||
788 | i3json_ensure_free(10240); | ||
789 | // get fresh data at the end of the buffer | ||
790 | int readlen = read(fd, i3json_state.parserpos, 10239); | ||
791 | if (readlen < 0) { | ||
792 | return readlen; | ||
793 | } | ||
794 | i3json_state.parserpos[readlen] = '\0'; | ||
795 | return i3json_parse(); | ||
796 | } | ||
797 | |||
685 | void poll_for_update() { | 798 | void poll_for_update() { |
686 | fd_set readfds; | 799 | fd_set readfds; |
687 | int activity; | 800 | int activity; |
@@ -698,7 +811,7 @@ void poll_for_update() { | |||
698 | dirty = false; | 811 | dirty = false; |
699 | FD_ZERO(&readfds); | 812 | FD_ZERO(&readfds); |
700 | FD_SET(socketfd, &readfds); | 813 | FD_SET(socketfd, &readfds); |
701 | FD_SET(pipefd[0], &readfds); | 814 | FD_SET(status_read_fd, &readfds); |
702 | 815 | ||
703 | activity = select(FD_SETSIZE, &readfds, NULL, NULL, NULL); | 816 | activity = select(FD_SETSIZE, &readfds, NULL, NULL, NULL); |
704 | if (activity < 0) { | 817 | if (activity < 0) { |
@@ -714,41 +827,40 @@ void poll_for_update() { | |||
714 | dirty = true; | 827 | dirty = true; |
715 | } | 828 | } |
716 | 829 | ||
717 | if (status_command && FD_ISSET(pipefd[0], &readfds)) { | 830 | if (status_command && FD_ISSET(status_read_fd, &readfds)) { |
718 | sway_log(L_DEBUG, "Got update from status command."); | 831 | sway_log(L_DEBUG, "Got update from status command."); |
719 | int linelen; | ||
720 | switch (protocol) { | 832 | switch (protocol) { |
721 | case I3BAR: | 833 | case I3BAR: |
722 | sway_log(L_DEBUG, "Got i3bar protocol."); | 834 | sway_log(L_DEBUG, "Got i3bar protocol."); |
723 | if (i3json_handle(command) > 0) { | 835 | if (i3json_handle_fd(status_read_fd) > 0) { |
724 | dirty = true; | 836 | dirty = true; |
725 | } | 837 | } |
726 | break; | 838 | break; |
727 | case TEXT: | 839 | case TEXT: |
728 | case UNDEF: | ||
729 | sway_log(L_DEBUG, "Got text protocol."); | 840 | sway_log(L_DEBUG, "Got text protocol."); |
730 | fgets(line, sizeof(line), command); | 841 | read_line_tail(status_read_fd, line, sizeof(line), line_rest); |
731 | linelen = strlen(line) - 1; | 842 | dirty = true; |
732 | if (line[linelen] == '\n') { | 843 | break; |
733 | line[linelen] = '\0'; | 844 | case UNDEF: |
845 | sway_log(L_DEBUG, "Detecting protocol..."); | ||
846 | if (read_line_tail(status_read_fd, line, sizeof(line), line_rest) < 0) { | ||
847 | break; | ||
734 | } | 848 | } |
735 | dirty = true; | 849 | dirty = true; |
736 | if (protocol == UNDEF) { | 850 | protocol = TEXT; |
737 | protocol = TEXT; | 851 | if (line[0] == '{') { |
738 | if (line[0] == '{') { | 852 | // detect i3bar json protocol |
739 | // detect i3bar json protocol | 853 | json_object *proto = json_tokener_parse(line); |
740 | json_object *proto = json_tokener_parse(line); | 854 | json_object *version; |
741 | json_object *version; | 855 | if (proto) { |
742 | if (proto) { | 856 | if (json_object_object_get_ex(proto, "version", &version) |
743 | if (json_object_object_get_ex(proto, "version", &version) | 857 | && json_object_get_int(version) == 1 |
744 | && json_object_get_int(version) == 1 | 858 | ) { |
745 | ) { | 859 | sway_log(L_DEBUG, "Switched to i3bar protocol."); |
746 | sway_log(L_DEBUG, "Switched to i3bar protocol."); | 860 | protocol = I3BAR; |
747 | protocol = I3BAR; | 861 | i3json_handle_data(line_rest); |
748 | line[0] = '\0'; | ||
749 | } | ||
750 | json_object_put(proto); | ||
751 | } | 862 | } |
863 | json_object_put(proto); | ||
752 | } | 864 | } |
753 | } | 865 | } |
754 | break; | 866 | break; |
@@ -831,6 +943,7 @@ int main(int argc, char **argv) { | |||
831 | bar_ipc_init(desired_output, bar_id); | 943 | bar_ipc_init(desired_output, bar_id); |
832 | 944 | ||
833 | if (status_command) { | 945 | if (status_command) { |
946 | int pipefd[2]; | ||
834 | pipe(pipefd); | 947 | pipe(pipefd); |
835 | pid = fork(); | 948 | pid = fork(); |
836 | if (pid == 0) { | 949 | if (pid == 0) { |
@@ -848,8 +961,8 @@ int main(int argc, char **argv) { | |||
848 | } | 961 | } |
849 | 962 | ||
850 | close(pipefd[1]); | 963 | close(pipefd[1]); |
851 | command = fdopen(pipefd[0], "r"); | 964 | status_read_fd = pipefd[0]; |
852 | setbuf(command, NULL); | 965 | fcntl(status_read_fd, F_SETFL, O_NONBLOCK); |
853 | line[0] = '\0'; | 966 | line[0] = '\0'; |
854 | } | 967 | } |
855 | 968 | ||