aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'swaybar/main.c')
-rw-r--r--swaybar/main.c335
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
48struct 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
58list_t *status_line = NULL;
59
48list_t *workspaces = NULL; 60list_t *workspaces = NULL;
49int socketfd; 61int socketfd;
50pid_t pid; 62pid_t pid;
@@ -55,6 +67,8 @@ char *output, *status_command;
55struct registry *registry; 67struct registry *registry;
56struct window *window; 68struct window *window;
57bool dirty = true; 69bool dirty = true;
70typedef enum {UNDEF, TEXT, I3BAR} command_protocol;
71command_protocol protocol = UNDEF;
58 72
59struct colors colors = { 73struct 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
109struct {
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
91void swaybar_teardown() { 119void 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
118void sway_terminate(void) { 145void 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
163void 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
136void ipc_update_workspaces() { 174void 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
471void 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
494void 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
610int 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
404void poll_for_update() { 685void 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
449int main(int argc, char **argv) { 761int 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