summaryrefslogtreecommitdiffstats
path: root/swaybar/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'swaybar/main.c')
-rw-r--r--swaybar/main.c1224
1 files changed, 11 insertions, 1213 deletions
diff --git a/swaybar/main.c b/swaybar/main.c
index a3a3b56a..fc5acdae 100644
--- a/swaybar/main.c
+++ b/swaybar/main.c
@@ -1,1191 +1,26 @@
1#include <fcntl.h>
2#include <stdio.h> 1#include <stdio.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
5#include <stdint.h>
6#include <stdbool.h> 4#include <stdbool.h>
7#include <unistd.h>
8#include <sys/select.h>
9#include <sys/wait.h>
10#include <errno.h>
11#include <json-c/json.h>
12#include <sys/un.h>
13#include <sys/socket.h>
14#include <sys/ioctl.h>
15#include <getopt.h> 5#include <getopt.h>
16#include "ipc-client.h" 6#include "ipc-client.h"
17#include "client/registry.h"
18#include "client/window.h"
19#include "client/pango.h"
20#include "stringop.h"
21#include "log.h" 7#include "log.h"
8#include "bar.h"
22 9
23struct box_colors { 10/* global bar state */
24 uint32_t border; 11struct bar swaybar;
25 uint32_t background;
26 uint32_t text;
27};
28
29struct colors {
30 uint32_t background;
31 uint32_t statusline;
32 uint32_t separator;
33
34 struct box_colors focused_workspace;
35 struct box_colors active_workspace;
36 struct box_colors inactive_workspace;
37 struct box_colors urgent_workspace;
38 struct box_colors binding_mode;
39};
40
41struct workspace {
42 int num;
43 char *name;
44 bool focused;
45 bool visible;
46 bool urgent;
47};
48
49struct status_block {
50 char *full_text, *short_text, *align;
51 bool urgent;
52 uint32_t color;
53 int min_width;
54 char *name, *instance;
55 bool separator;
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;
64};
65
66list_t *status_line = NULL;
67
68list_t *workspaces = NULL;
69int ipc_socketfd, ipc_event_socketfd;
70pid_t pid;
71int status_read_fd;
72char line[1024];
73char line_rest[1024];
74char *output, *status_command;
75struct registry *registry;
76struct window *window;
77bool dirty = true;
78char *separator_symbol = NULL;
79char *mode = NULL;
80bool binding_mode_indicator = true;
81bool strip_workspace_numbers = false;
82bool workspace_buttons = true;
83typedef enum {UNDEF, TEXT, I3BAR} command_protocol;
84command_protocol protocol = UNDEF;
85
86struct colors colors = {
87 .background = 0x000000FF,
88 .statusline = 0xFFFFFFFF,
89 .separator = 0x666666FF,
90
91 .focused_workspace = {
92 .border = 0x4C7899FF,
93 .background = 0x285577FF,
94 .text = 0xFFFFFFFF
95 },
96 .active_workspace = {
97 .border = 0x333333FF,
98 .background = 0x5F676AFF,
99 .text = 0xFFFFFFFF
100 },
101 .inactive_workspace = {
102 .border = 0x333333FF,
103 .background = 0x222222FF,
104 .text = 0x888888FF
105 },
106 .urgent_workspace = {
107 .border = 0x2F343AFF,
108 .background = 0x900000FF,
109 .text = 0xFFFFFFFF
110 },
111 .binding_mode = {
112 .border = 0x2F343AFF,
113 .background = 0x900000FF,
114 .text = 0xFFFFFFFF
115 },
116};
117
118#define I3JSON_MAXDEPTH 4
119#define I3JSON_UNKNOWN 0
120#define I3JSON_ARRAY 1
121#define I3JSON_STRING 2
122struct {
123 int bufsize;
124 char *buffer;
125 char *line_start;
126 char *parserpos;
127 bool escape;
128 int depth;
129 int state[I3JSON_MAXDEPTH+1];
130} i3json_state = { 0, NULL, NULL, NULL, false, 0, { I3JSON_UNKNOWN } };
131
132
133void swaybar_teardown() {
134 window_teardown(window);
135 if (registry) {
136 registry_teardown(registry);
137 }
138
139 if (status_read_fd) {
140 close(status_read_fd);
141 }
142
143 if (pid) {
144 // terminate status_command process
145 int ret = kill(pid, SIGTERM);
146 if (ret != 0) {
147 sway_log(L_ERROR, "Unable to terminate status_command [pid: %d]", pid);
148 } else {
149 int status;
150 waitpid(pid, &status, 0);
151 }
152 }
153
154 if (status_read_fd) {
155 close(status_read_fd);
156 }
157
158 if (ipc_socketfd) {
159 close(ipc_socketfd);
160 }
161
162 if (ipc_event_socketfd) {
163 close(ipc_event_socketfd);
164 }
165}
166 12
167void sway_terminate(void) { 13void sway_terminate(void) {
168 swaybar_teardown(); 14 bar_teardown(&swaybar);
169 exit(EXIT_FAILURE); 15 exit(EXIT_FAILURE);
170} 16}
171 17
172void sig_handler(int signal) { 18void sig_handler(int signal) {
173 swaybar_teardown(); 19 bar_teardown(&swaybar);
174 exit(0); 20 exit(0);
175} 21}
176 22
177void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
178 cairo_set_source_rgba(cairo,
179 ((color & 0xFF000000) >> 24) / 256.0,
180 ((color & 0xFF0000) >> 16) / 256.0,
181 ((color & 0xFF00) >> 8) / 256.0,
182 (color & 0xFF) / 256.0);
183}
184
185void free_workspace(void *item) {
186 if (!item) {
187 return;
188 }
189 struct workspace *ws = (struct workspace *)item;
190 if (ws->name) {
191 free(ws->name);
192 }
193 free(ws);
194}
195
196void ipc_update_workspaces() {
197 if (workspaces) {
198 list_foreach(workspaces, free_workspace);
199 list_free(workspaces);
200 }
201 workspaces = create_list();
202
203 uint32_t len = 0;
204 char *res = ipc_single_command(ipc_socketfd, IPC_GET_WORKSPACES, NULL, &len);
205 json_object *results = json_tokener_parse(res);
206 if (!results) {
207 free(res);
208 return;
209 }
210
211 int i;
212 int length = json_object_array_length(results);
213 json_object *ws_json;
214 json_object *num, *name, *visible, *focused, *out, *urgent;
215 for (i = 0; i < length; ++i) {
216 ws_json = json_object_array_get_idx(results, i);
217
218 json_object_object_get_ex(ws_json, "num", &num);
219 json_object_object_get_ex(ws_json, "name", &name);
220 json_object_object_get_ex(ws_json, "visible", &visible);
221 json_object_object_get_ex(ws_json, "focused", &focused);
222 json_object_object_get_ex(ws_json, "output", &out);
223 json_object_object_get_ex(ws_json, "urgent", &urgent);
224
225 if (strcmp(json_object_get_string(out), output) == 0) {
226 struct workspace *ws = malloc(sizeof(struct workspace));
227 ws->num = json_object_get_int(num);
228 ws->name = strdup(json_object_get_string(name));
229 ws->visible = json_object_get_boolean(visible);
230 ws->focused = json_object_get_boolean(focused);
231 ws->urgent = json_object_get_boolean(urgent);
232 list_add(workspaces, ws);
233 }
234 }
235
236 json_object_put(results);
237 free(res);
238}
239
240uint32_t parse_color(const char *color) {
241 if (color[0] != '#') {
242 sway_abort("Invalid color %s", color);
243 }
244 char *end;
245 uint32_t res = (uint32_t)strtol(color + 1, &end, 16);
246 if (strlen(color) == 7) {
247 res = (res << 8) | 0xFF;
248 }
249 return res;
250}
251
252uint32_t parse_position(const char *position) {
253 if (strcmp("top", position) == 0) {
254 return DESKTOP_SHELL_PANEL_POSITION_TOP;
255 } else if (strcmp("bottom", position) == 0) {
256 return DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
257 } else if (strcmp("left", position) == 0) {
258 return DESKTOP_SHELL_PANEL_POSITION_LEFT;
259 } else if (strcmp("right", position) == 0) {
260 return DESKTOP_SHELL_PANEL_POSITION_RIGHT;
261 } else {
262 return DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
263 }
264}
265
266char *parse_font(const char *font) {
267 char *new_font = NULL;
268 if (strncmp("pango:", font, 6) == 0) {
269 new_font = strdup(font + 6);
270 }
271
272 return new_font;
273}
274
275static int margin = 3;
276static const int ws_hor_padding = 5;
277static double ws_ver_padding = 1.5;
278static const int ws_spacing = 1;
279
280void bar_ipc_init(int outputi, const char *bar_id) {
281 uint32_t len = 0;
282 char *res = ipc_single_command(ipc_socketfd, IPC_GET_OUTPUTS, NULL, &len);
283 json_object *outputs = json_tokener_parse(res);
284 json_object *info = json_object_array_get_idx(outputs, outputi);
285 json_object *name;
286 json_object_object_get_ex(info, "name", &name);
287 output = strdup(json_object_get_string(name));
288 free(res);
289 json_object_put(outputs);
290
291 len = strlen(bar_id);
292 res = ipc_single_command(ipc_socketfd, IPC_GET_BAR_CONFIG, bar_id, &len);
293
294 json_object *bar_config = json_tokener_parse(res);
295 json_object *tray_output, *mode, *hidden_state, *position, *_status_command;
296 json_object *font, *bar_height, *_workspace_buttons, *_strip_workspace_numbers;
297 json_object *_binding_mode_indicator, *verbose, *_colors, *sep_symbol;
298 json_object_object_get_ex(bar_config, "tray_output", &tray_output);
299 json_object_object_get_ex(bar_config, "mode", &mode);
300 json_object_object_get_ex(bar_config, "hidden_state", &hidden_state);
301 json_object_object_get_ex(bar_config, "position", &position);
302 json_object_object_get_ex(bar_config, "status_command", &_status_command);
303 json_object_object_get_ex(bar_config, "font", &font);
304 json_object_object_get_ex(bar_config, "bar_height", &bar_height);
305 json_object_object_get_ex(bar_config, "workspace_buttons", &_workspace_buttons);
306 json_object_object_get_ex(bar_config, "strip_workspace_numbers", &_strip_workspace_numbers);
307 json_object_object_get_ex(bar_config, "binding_mode_indicator", &_binding_mode_indicator);
308 json_object_object_get_ex(bar_config, "verbose", &verbose);
309 json_object_object_get_ex(bar_config, "separator_symbol", &sep_symbol);
310 json_object_object_get_ex(bar_config, "colors", &_colors);
311
312 // TODO: More of these options
313 // TODO: Refactor swaybar into several files, create a bar config struct (shared with compositor?)
314 if (_status_command) {
315 status_command = strdup(json_object_get_string(_status_command));
316 }
317
318 if (position) {
319 desktop_shell_set_panel_position(registry->desktop_shell, parse_position(json_object_get_string(position)));
320 }
321
322 if (font) {
323 window->font = parse_font(json_object_get_string(font));
324 }
325
326 if (sep_symbol) {
327 separator_symbol = strdup(json_object_get_string(sep_symbol));
328 }
329
330 if (_strip_workspace_numbers) {
331 strip_workspace_numbers = json_object_get_boolean(_strip_workspace_numbers);
332 }
333
334 if (_binding_mode_indicator) {
335 binding_mode_indicator = json_object_get_boolean(_binding_mode_indicator);
336 }
337
338 if (_workspace_buttons) {
339 workspace_buttons = json_object_get_boolean(_workspace_buttons);
340 }
341
342 if (bar_height) {
343 int width, height;
344 get_text_size(window, &width, &height, "Test string for measuring purposes");
345 int bar_height_value = json_object_get_int(bar_height);
346 if (bar_height_value > 0) {
347 margin = (bar_height_value - height) / 2;
348 ws_ver_padding = margin - 1.5;
349 }
350 window->height = height + margin * 2;
351 }
352
353 if (_colors) {
354 json_object *background, *statusline, *separator;
355 json_object *focused_workspace_border, *focused_workspace_bg, *focused_workspace_text;
356 json_object *inactive_workspace_border, *inactive_workspace_bg, *inactive_workspace_text;
357 json_object *active_workspace_border, *active_workspace_bg, *active_workspace_text;
358 json_object *urgent_workspace_border, *urgent_workspace_bg, *urgent_workspace_text;
359 json_object *binding_mode_border, *binding_mode_bg, *binding_mode_text;
360 json_object_object_get_ex(_colors, "background", &background);
361 json_object_object_get_ex(_colors, "statusline", &statusline);
362 json_object_object_get_ex(_colors, "separator", &separator);
363 json_object_object_get_ex(_colors, "focused_workspace_border", &focused_workspace_border);
364 json_object_object_get_ex(_colors, "focused_workspace_bg", &focused_workspace_bg);
365 json_object_object_get_ex(_colors, "focused_workspace_text", &focused_workspace_text);
366 json_object_object_get_ex(_colors, "active_workspace_border", &active_workspace_border);
367 json_object_object_get_ex(_colors, "active_workspace_bg", &active_workspace_bg);
368 json_object_object_get_ex(_colors, "active_workspace_text", &active_workspace_text);
369 json_object_object_get_ex(_colors, "inactive_workspace_border", &inactive_workspace_border);
370 json_object_object_get_ex(_colors, "inactive_workspace_bg", &inactive_workspace_bg);
371 json_object_object_get_ex(_colors, "inactive_workspace_text", &inactive_workspace_text);
372 json_object_object_get_ex(_colors, "urgent_workspace_border", &urgent_workspace_border);
373 json_object_object_get_ex(_colors, "urgent_workspace_bg", &urgent_workspace_bg);
374 json_object_object_get_ex(_colors, "urgent_workspace_text", &urgent_workspace_text);
375 json_object_object_get_ex(_colors, "binding_mode_border", &binding_mode_border);
376 json_object_object_get_ex(_colors, "binding_mode_bg", &binding_mode_bg);
377 json_object_object_get_ex(_colors, "binding_mode_text", &binding_mode_text);
378 if (background) colors.background = parse_color(json_object_get_string(background));
379 if (statusline) colors.statusline = parse_color(json_object_get_string(statusline));
380 if (separator) colors.separator = parse_color(json_object_get_string(separator));
381 if (focused_workspace_border) {
382 colors.focused_workspace.border = parse_color(json_object_get_string(focused_workspace_border));
383 }
384 if (focused_workspace_bg) {
385 colors.focused_workspace.background = parse_color(json_object_get_string(focused_workspace_bg));
386 }
387 if (focused_workspace_text) {
388 colors.focused_workspace.text = parse_color(json_object_get_string(focused_workspace_text));
389 }
390 if (active_workspace_border) {
391 colors.active_workspace.border = parse_color(json_object_get_string(active_workspace_border));
392 }
393 if (active_workspace_bg) {
394 colors.active_workspace.background = parse_color(json_object_get_string(active_workspace_bg));
395 }
396 if (active_workspace_text) {
397 colors.active_workspace.text = parse_color(json_object_get_string(active_workspace_text));
398 }
399 if (inactive_workspace_border) {
400 colors.inactive_workspace.border = parse_color(json_object_get_string(inactive_workspace_border));
401 }
402 if (inactive_workspace_bg) {
403 colors.inactive_workspace.background = parse_color(json_object_get_string(inactive_workspace_bg));
404 }
405 if (inactive_workspace_text) {
406 colors.inactive_workspace.text = parse_color(json_object_get_string(inactive_workspace_text));
407 }
408 if (binding_mode_border) {
409 colors.binding_mode.border = parse_color(json_object_get_string(binding_mode_border));
410 }
411 if (binding_mode_bg) {
412 colors.binding_mode.background = parse_color(json_object_get_string(binding_mode_bg));
413 }
414 if (binding_mode_text) {
415 colors.binding_mode.text = parse_color(json_object_get_string(binding_mode_text));
416 }
417 }
418
419 json_object_put(bar_config);
420 free(res);
421
422 const char *subscribe_json = "[ \"workspace\", \"mode\" ]";
423 len = strlen(subscribe_json);
424 res = ipc_single_command(ipc_event_socketfd, IPC_SUBSCRIBE, subscribe_json, &len);
425 free(res);
426
427 ipc_update_workspaces();
428}
429
430/**
431 * Renders a sharp line of any width and height.
432 *
433 * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0
434 * if the line has a width/height of one pixel, respectively.
435 */
436void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) {
437 cairo_set_source_u32(cairo, color);
438
439 if (width > 1 && height > 1) {
440 cairo_rectangle(cairo, x, y, width, height);
441 cairo_fill(cairo);
442 } else {
443 if (width == 1) {
444 x += 0.5;
445 height += y;
446 width = x;
447 }
448
449 if (height == 1) {
450 y += 0.5;
451 width += x;
452 height = y;
453 }
454
455 cairo_move_to(cairo, x, y);
456 cairo_set_line_width(cairo, 1.0);
457 cairo_line_to(cairo, width, height);
458 cairo_stroke(cairo);
459 }
460}
461
462void render_block(struct status_block *block, double *x, bool edge) {
463 int width, height, sep_width;
464 get_text_size(window, &width, &height, "%s", block->full_text);
465
466 int textwidth = width;
467 double block_width = width;
468
469 if (width < block->min_width) {
470 width = block->min_width;
471 }
472
473 *x -= width;
474
475 if (block->border != 0 && block->border_left > 0) {
476 *x -= (block->border_left + margin);
477 block_width += block->border_left + margin;
478 }
479
480 if (block->border != 0 && block->border_right > 0) {
481 *x -= (block->border_right + margin);
482 block_width += block->border_right + margin;
483 }
484
485 // Add separator
486 if (!edge) {
487 if (separator_symbol) {
488 get_text_size(window, &sep_width, &height, "%s", separator_symbol);
489 if (sep_width > block->separator_block_width) {
490 block->separator_block_width = sep_width + margin * 2;
491 }
492 }
493
494 *x -= block->separator_block_width;
495 } else {
496 *x -= margin;
497 }
498
499 double pos = *x;
500
501 // render background
502 if (block->background != 0x0) {
503 cairo_set_source_u32(window->cairo, block->background);
504 cairo_rectangle(window->cairo, pos - 0.5, 1, block_width, window->height - 2);
505 cairo_fill(window->cairo);
506 }
507
508 // render top border
509 if (block->border != 0 && block->border_top > 0) {
510 render_sharp_line(window->cairo, block->border,
511 pos - 0.5,
512 1,
513 block_width,
514 block->border_top);
515 }
516
517 // render bottom border
518 if (block->border != 0 && block->border_bottom > 0) {
519 render_sharp_line(window->cairo, block->border,
520 pos - 0.5,
521 window->height - 1 - block->border_bottom,
522 block_width,
523 block->border_bottom);
524 }
525
526 // render left border
527 if (block->border != 0 && block->border_left > 0) {
528 render_sharp_line(window->cairo, block->border,
529 pos - 0.5,
530 1,
531 block->border_left,
532 window->height - 2);
533
534 pos += block->border_left + margin;
535 }
536
537 // render text
538 double offset = 0;
539
540 if (strncmp(block->align, "left", 5) == 0) {
541 offset = pos;
542 } else if (strncmp(block->align, "right", 5) == 0) {
543 offset = pos + width - textwidth;
544 } else if (strncmp(block->align, "center", 6) == 0) {
545 offset = pos + (width - textwidth) / 2;
546 }
547
548 cairo_move_to(window->cairo, offset, margin);
549 cairo_set_source_u32(window->cairo, block->color);
550 pango_printf(window, "%s", block->full_text);
551
552 pos += width;
553
554 // render right border
555 if (block->border != 0 && block->border_right > 0) {
556 pos += margin;
557
558 render_sharp_line(window->cairo, block->border,
559 pos - 0.5,
560 1,
561 block->border_right,
562 window->height - 2);
563
564 pos += block->border_right;
565 }
566
567 // render separator
568 if (!edge && block->separator) {
569 cairo_set_source_u32(window->cairo, colors.separator);
570 if (separator_symbol) {
571 offset = pos + (block->separator_block_width - sep_width) / 2;
572 cairo_move_to(window->cairo, offset, margin);
573 pango_printf(window, "%s", separator_symbol);
574 } else {
575 cairo_set_line_width(window->cairo, 1);
576 cairo_move_to(window->cairo, pos + block->separator_block_width/2,
577 margin);
578 cairo_line_to(window->cairo, pos + block->separator_block_width/2,
579 window->height - margin);
580 cairo_stroke(window->cairo);
581 }
582 }
583
584}
585
586char *handle_workspace_number(bool strip_num, const char *ws_name) {
587 bool strip = false;
588 int i;
589
590 if (strip_num) {
591 int len = strlen(ws_name);
592 for (i = 0; i < len; ++i) {
593 if (!('0' <= ws_name[i] && ws_name[i] <= '9')) {
594 if (':' == ws_name[i] && i < len-1 && i > 0) {
595 strip = true;
596 ++i;
597 }
598 break;
599 }
600 }
601 }
602
603 if (strip) {
604 return strdup(ws_name + i);
605 }
606
607 return strdup(ws_name);
608}
609
610void render_workspace_button(struct workspace *ws, double *x) {
611 // strip workspace numbers if required
612 char *name = handle_workspace_number(strip_workspace_numbers, ws->name);
613
614 int width, height;
615 get_text_size(window, &width, &height, "%s", name);
616 struct box_colors box_colors;
617 if (ws->urgent) {
618 box_colors = colors.urgent_workspace;
619 } else if (ws->focused) {
620 box_colors = colors.focused_workspace;
621 } else if (ws->visible) {
622 box_colors = colors.active_workspace;
623 } else {
624 box_colors = colors.inactive_workspace;
625 }
626
627 // background
628 cairo_set_source_u32(window->cairo, box_colors.background);
629 cairo_rectangle(window->cairo, *x, 1.5, width + ws_hor_padding * 2 - 1,
630 height + ws_ver_padding * 2);
631 cairo_fill(window->cairo);
632
633 // border
634 cairo_set_source_u32(window->cairo, box_colors.border);
635 cairo_rectangle(window->cairo, *x, 1.5, width + ws_hor_padding * 2 - 1,
636 height + ws_ver_padding * 2);
637 cairo_stroke(window->cairo);
638
639 // text
640 cairo_set_source_u32(window->cairo, box_colors.text);
641 cairo_move_to(window->cairo, (int)*x + ws_hor_padding, margin);
642 pango_printf(window, "%s", name);
643
644 *x += width + ws_hor_padding * 2 + ws_spacing;
645
646 free(name);
647}
648
649void render_binding_mode_indicator(double pos) {
650 int width, height;
651 get_text_size(window, &width, &height, "%s", mode);
652
653 // background
654 cairo_set_source_u32(window->cairo, colors.binding_mode.background);
655 cairo_rectangle(window->cairo, pos, 1.5, width + ws_hor_padding * 2 - 1,
656 height + ws_ver_padding * 2);
657 cairo_fill(window->cairo);
658
659 // border
660 cairo_set_source_u32(window->cairo, colors.binding_mode.border);
661 cairo_rectangle(window->cairo, pos, 1.5, width + ws_hor_padding * 2 - 1,
662 height + ws_ver_padding * 2);
663 cairo_stroke(window->cairo);
664
665 // text
666 cairo_set_source_u32(window->cairo, colors.binding_mode.text);
667 cairo_move_to(window->cairo, (int)pos + ws_hor_padding, margin);
668 pango_printf(window, "%s", mode);
669}
670
671void render() {
672 int i;
673
674 // Clear
675 cairo_save(window->cairo);
676 cairo_set_operator(window->cairo, CAIRO_OPERATOR_CLEAR);
677 cairo_paint(window->cairo);
678 cairo_restore(window->cairo);
679
680 // Background
681 cairo_set_source_u32(window->cairo, colors.background);
682 cairo_paint(window->cairo);
683
684 // Command output
685 cairo_set_source_u32(window->cairo, colors.statusline);
686 int width, height;
687
688 if (protocol == TEXT) {
689 get_text_size(window, &width, &height, "%s", line);
690 cairo_move_to(window->cairo, window->width - margin - width, margin);
691 pango_printf(window, "%s", line);
692 } else if (protocol == I3BAR && status_line) {
693 double pos = window->width - 0.5;
694 bool edge = true;
695 for (i = status_line->length - 1; i >= 0; --i) {
696 struct status_block *block = status_line->items[i];
697 if (block->full_text && block->full_text[0]) {
698 render_block(block, &pos, edge);
699 edge = false;
700 }
701 }
702 }
703
704 cairo_set_line_width(window->cairo, 1.0);
705 double x = 0.5;
706
707 // Workspaces
708 if (workspace_buttons) {
709 for (i = 0; i < workspaces->length; ++i) {
710 struct workspace *ws = workspaces->items[i];
711 render_workspace_button(ws, &x);
712 }
713 }
714
715 // binding mode indicator
716 if (mode && binding_mode_indicator) {
717 render_binding_mode_indicator(x);
718 }
719}
720
721void free_status_block(void *item) {
722 if (!item) {
723 return;
724 }
725 struct status_block *sb = (struct status_block*)item;
726 if (sb->full_text) {
727 free(sb->full_text);
728 }
729 if (sb->short_text) {
730 free(sb->short_text);
731 }
732 if (sb->align) {
733 free(sb->align);
734 }
735 if (sb->name) {
736 free(sb->name);
737 }
738 if (sb->instance) {
739 free(sb->instance);
740 }
741 free(sb);
742}
743
744void parse_json(const char *text) {
745 json_object *results = json_tokener_parse(text);
746 if (!results) {
747 sway_log(L_DEBUG, "Failed to parse json");
748 return;
749 }
750
751 if (json_object_array_length(results) < 1) {
752 return;
753 }
754
755 if (status_line) {
756 list_foreach(status_line, free_status_block);
757 list_free(status_line);
758 }
759
760 status_line = create_list();
761
762 int i;
763 for (i = 0; i < json_object_array_length(results); ++i) {
764 json_object *full_text, *short_text, *color, *min_width, *align, *urgent;
765 json_object *name, *instance, *separator, *separator_block_width;
766 json_object *background, *border, *border_top, *border_bottom;
767 json_object *border_left, *border_right;
768
769 json_object *json = json_object_array_get_idx(results, i);
770 if (!json) {
771 continue;
772 }
773
774 json_object_object_get_ex(json, "full_text", &full_text);
775 json_object_object_get_ex(json, "short_text", &short_text);
776 json_object_object_get_ex(json, "color", &color);
777 json_object_object_get_ex(json, "min_width", &min_width);
778 json_object_object_get_ex(json, "align", &align);
779 json_object_object_get_ex(json, "urgent", &urgent);
780 json_object_object_get_ex(json, "name", &name);
781 json_object_object_get_ex(json, "instance", &instance);
782 json_object_object_get_ex(json, "separator", &separator);
783 json_object_object_get_ex(json, "separator_block_width", &separator_block_width);
784 json_object_object_get_ex(json, "background", &background);
785 json_object_object_get_ex(json, "border", &border);
786 json_object_object_get_ex(json, "border_top", &border_top);
787 json_object_object_get_ex(json, "border_bottom", &border_bottom);
788 json_object_object_get_ex(json, "border_left", &border_left);
789 json_object_object_get_ex(json, "border_right", &border_right);
790
791 struct status_block *new = malloc(sizeof(struct status_block));
792 memset(new, 0, sizeof(struct status_block));
793
794 if (full_text) {
795 new->full_text = strdup(json_object_get_string(full_text));
796 }
797
798 if (short_text) {
799 new->short_text = strdup(json_object_get_string(short_text));
800 }
801
802 if (color) {
803 new->color = parse_color(json_object_get_string(color));
804 } else {
805 new->color = colors.statusline;
806 }
807
808 if (min_width) {
809 json_type type = json_object_get_type(min_width);
810 if (type == json_type_int) {
811 new->min_width = json_object_get_int(min_width);
812 } else if (type == json_type_string) {
813 int width, height;
814 get_text_size(window, &width, &height, "%s", json_object_get_string(min_width));
815 new->min_width = width;
816 }
817 }
818
819 if (align) {
820 new->align = strdup(json_object_get_string(align));
821 } else {
822 new->align = strdup("left");
823 }
824
825 if (urgent) {
826 new->urgent = json_object_get_int(urgent);
827 }
828
829 if (name) {
830 new->name = strdup(json_object_get_string(name));
831 }
832
833 if (instance) {
834 new->instance = strdup(json_object_get_string(instance));
835 }
836
837 if (separator) {
838 new->separator = json_object_get_int(separator);
839 } else {
840 new->separator = true; // i3bar spec
841 }
842
843 if (separator_block_width) {
844 new->separator_block_width = json_object_get_int(separator_block_width);
845 } else {
846 new->separator_block_width = 9; // i3bar spec
847 }
848
849 // Airblader features
850 if (background) {
851 new->background = parse_color(json_object_get_string(background));
852 } else {
853 new->background = 0x0; // transparent
854 }
855
856 if (border) {
857 new->border = parse_color(json_object_get_string(border));
858 } else {
859 new->border = 0x0; // transparent
860 }
861
862 if (border_top) {
863 new->border_top = json_object_get_int(border_top);
864 } else {
865 new->border_top = 1;
866 }
867
868 if (border_bottom) {
869 new->border_bottom = json_object_get_int(border_bottom);
870 } else {
871 new->border_bottom = 1;
872 }
873
874 if (border_left) {
875 new->border_left = json_object_get_int(border_left);
876 } else {
877 new->border_left = 1;
878 }
879
880 if (border_right) {
881 new->border_right = json_object_get_int(border_right);
882 } else {
883 new->border_right = 1;
884 }
885
886 list_add(status_line, new);
887 }
888
889 json_object_put(results);
890}
891
892// Read line from file descriptor, only show the line tail if it is too long.
893// In non-blocking mode treat "no more data" as a linebreak.
894// If data after a line break has been read, return it in rest.
895// If rest is non-empty, then use that as the start of the next line.
896int read_line_tail(int fd, char *buf, int nbyte, char *rest) {
897 if (fd < 0 || !buf || !nbyte) {
898 return -1;
899 }
900 int l;
901 char *buffer = malloc(nbyte*2+1);
902 char *readpos = buffer;
903 char *lf;
904 // prepend old data to new line if necessary
905 if (rest) {
906 l = strlen(rest);
907 if (l > nbyte) {
908 strcpy(buffer, rest + l - nbyte);
909 readpos += nbyte;
910 } else if (l) {
911 strcpy(buffer, rest);
912 readpos += l;
913 }
914 }
915 // read until a linefeed is found or no more data is available
916 while ((l = read(fd, readpos, nbyte)) > 0) {
917 readpos[l] = '\0';
918 lf = strchr(readpos, '\n');
919 if (lf) {
920 // linefeed found, replace with \0
921 *lf = '\0';
922 // give data from the end of the line, try to fill the buffer
923 if (lf-buffer > nbyte) {
924 strcpy(buf, lf - nbyte + 1);
925 } else {
926 strcpy(buf, buffer);
927 }
928 // we may have read data from the next line, save it to rest
929 if (rest) {
930 rest[0] = '\0';
931 strcpy(rest, lf + 1);
932 }
933 free(buffer);
934 return strlen(buf);
935 } else {
936 // no linefeed found, slide data back.
937 int overflow = readpos - buffer + l - nbyte;
938 if (overflow > 0) {
939 memmove(buffer, buffer + overflow , nbyte + 1);
940 }
941 }
942 }
943 if (l < 0) {
944 free(buffer);
945 return l;
946 }
947 readpos[l]='\0';
948 if (rest) {
949 rest[0] = '\0';
950 }
951 if (nbyte < readpos - buffer + l - 1) {
952 memcpy(buf, readpos - nbyte + l + 1, nbyte);
953 } else {
954 strncpy(buf, buffer, nbyte);
955 }
956 buf[nbyte-1] = '\0';
957 free(buffer);
958 return strlen(buf);
959}
960
961// make sure that enough buffer space is available starting from parserpos
962void i3json_ensure_free(int min_free) {
963 int _step = 10240;
964 int r = min_free % _step;
965 if (r) {
966 min_free += _step - r;
967 }
968 if (!i3json_state.buffer) {
969 i3json_state.buffer = malloc(min_free);
970 i3json_state.bufsize = min_free;
971 i3json_state.parserpos = i3json_state.buffer;
972 } else {
973 int len = 0;
974 int pos = 0;
975 if (i3json_state.line_start) {
976 len = strlen(i3json_state.line_start);
977 pos = i3json_state.parserpos - i3json_state.line_start;
978 if (i3json_state.line_start != i3json_state.buffer) {
979 memmove(i3json_state.buffer, i3json_state.line_start, len+1);
980 }
981 } else {
982 len = strlen(i3json_state.buffer);
983 }
984 if (i3json_state.bufsize < len+min_free) {
985 i3json_state.bufsize += min_free;
986 if (i3json_state.bufsize > 1024000) {
987 sway_abort("Status line json too long or malformed.");
988 }
989 i3json_state.buffer = realloc(i3json_state.buffer, i3json_state.bufsize);
990 if (!i3json_state.buffer) {
991 sway_abort("Could not allocate json buffer");
992 }
993 }
994 if (i3json_state.line_start) {
995 i3json_state.line_start = i3json_state.buffer;
996 i3json_state.parserpos = i3json_state.buffer + pos;
997 } else {
998 i3json_state.parserpos = i3json_state.buffer;
999 }
1000 }
1001 if (!i3json_state.buffer) {
1002 sway_abort("Could not allocate buffer.");
1003 }
1004}
1005
1006// continue parsing from last parserpos
1007int i3json_parse() {
1008 char *c = i3json_state.parserpos;
1009 int handled = 0;
1010 while (*c) {
1011 if (i3json_state.state[i3json_state.depth] == I3JSON_STRING) {
1012 if (!i3json_state.escape && *c == '"') {
1013 --i3json_state.depth;
1014 }
1015 i3json_state.escape = !i3json_state.escape && *c == '\\';
1016 } else {
1017 switch (*c) {
1018 case '[':
1019 ++i3json_state.depth;
1020 if (i3json_state.depth > I3JSON_MAXDEPTH) {
1021 sway_abort("JSON too deep");
1022 }
1023 i3json_state.state[i3json_state.depth] = I3JSON_ARRAY;
1024 if (i3json_state.depth == 2) {
1025 i3json_state.line_start = c;
1026 }
1027 break;
1028 case ']':
1029 if (i3json_state.state[i3json_state.depth] != I3JSON_ARRAY) {
1030 sway_abort("JSON malformed");
1031 }
1032 --i3json_state.depth;
1033 if (i3json_state.depth == 1) {
1034 // c[1] is valid since c[0] != '\0'
1035 char p = c[1];
1036 c[1] = '\0';
1037 parse_json(i3json_state.line_start);
1038 c[1] = p;
1039 ++handled;
1040 i3json_state.line_start = c+1;
1041 }
1042 break;
1043 case '"':
1044 ++i3json_state.depth;
1045 if (i3json_state.depth > I3JSON_MAXDEPTH) {
1046 sway_abort("JSON too deep");
1047 }
1048 i3json_state.state[i3json_state.depth] = I3JSON_STRING;
1049 break;
1050 }
1051 }
1052 ++c;
1053 }
1054 i3json_state.parserpos = c;
1055 return handled;
1056}
1057
1058// append data and parse it.
1059int i3json_handle_data(char *data) {
1060 int len = strlen(data);
1061 i3json_ensure_free(len);
1062 strcpy(i3json_state.parserpos, data);
1063 return i3json_parse();
1064}
1065
1066// read data from fd and parse it.
1067int i3json_handle_fd(int fd) {
1068 i3json_ensure_free(10240);
1069 // get fresh data at the end of the buffer
1070 int readlen = read(fd, i3json_state.parserpos, 10239);
1071 if (readlen < 0) {
1072 return readlen;
1073 }
1074 i3json_state.parserpos[readlen] = '\0';
1075 return i3json_parse();
1076}
1077
1078bool handle_ipc_event() {
1079 struct ipc_response *resp = ipc_recv_response(ipc_event_socketfd);
1080 switch (resp->type) {
1081 case IPC_EVENT_WORKSPACE:
1082 ipc_update_workspaces();
1083 break;
1084 case IPC_EVENT_MODE: {
1085 json_object *result = json_tokener_parse(resp->payload);
1086 if (!result) {
1087 free_ipc_response(resp);
1088 sway_log(L_ERROR, "failed to parse payload as json");
1089 return false;
1090 }
1091 json_object *json_change;
1092 if (json_object_object_get_ex(result, "change", &json_change)) {
1093 const char *change = json_object_get_string(json_change);
1094
1095 free(mode);
1096 if (strcmp(change, "default") == 0) {
1097 mode = NULL;
1098 } else {
1099 mode = strdup(change);
1100 }
1101 } else {
1102 sway_log(L_ERROR, "failed to parse response");
1103 }
1104
1105 json_object_put(result);
1106 break;
1107 }
1108 default:
1109 free_ipc_response(resp);
1110 return false;
1111 }
1112
1113 free_ipc_response(resp);
1114 return true;
1115}
1116
1117void poll_for_update() {
1118 fd_set readfds;
1119 int activity;
1120
1121 while (1) {
1122 if (dirty && window_prerender(window) && window->cairo) {
1123 render();
1124 window_render(window);
1125 if (wl_display_dispatch(registry->display) == -1) {
1126 break;
1127 }
1128 }
1129
1130 dirty = false;
1131 FD_ZERO(&readfds);
1132 FD_SET(ipc_event_socketfd, &readfds);
1133 FD_SET(status_read_fd, &readfds);
1134
1135 activity = select(FD_SETSIZE, &readfds, NULL, NULL, NULL);
1136 if (activity < 0) {
1137 sway_log(L_ERROR, "polling failed: %d", errno);
1138 }
1139
1140 if (FD_ISSET(ipc_event_socketfd, &readfds)) {
1141 sway_log(L_DEBUG, "Got IPC event.");
1142 dirty = handle_ipc_event();
1143 }
1144
1145 if (status_command && FD_ISSET(status_read_fd, &readfds)) {
1146 sway_log(L_DEBUG, "Got update from status command.");
1147 switch (protocol) {
1148 case I3BAR:
1149 sway_log(L_DEBUG, "Got i3bar protocol.");
1150 if (i3json_handle_fd(status_read_fd) > 0) {
1151 dirty = true;
1152 }
1153 break;
1154 case TEXT:
1155 sway_log(L_DEBUG, "Got text protocol.");
1156 read_line_tail(status_read_fd, line, sizeof(line), line_rest);
1157 dirty = true;
1158 break;
1159 case UNDEF:
1160 sway_log(L_DEBUG, "Detecting protocol...");
1161 if (read_line_tail(status_read_fd, line, sizeof(line), line_rest) < 0) {
1162 break;
1163 }
1164 dirty = true;
1165 protocol = TEXT;
1166 if (line[0] == '{') {
1167 // detect i3bar json protocol
1168 json_object *proto = json_tokener_parse(line);
1169 json_object *version;
1170 if (proto) {
1171 if (json_object_object_get_ex(proto, "version", &version)
1172 && json_object_get_int(version) == 1
1173 ) {
1174 sway_log(L_DEBUG, "Switched to i3bar protocol.");
1175 protocol = I3BAR;
1176 i3json_handle_data(line_rest);
1177 }
1178 json_object_put(proto);
1179 }
1180 }
1181 break;
1182 }
1183 }
1184 }
1185}
1186
1187int main(int argc, char **argv) { 23int main(int argc, char **argv) {
1188
1189 char *socket_path = NULL; 24 char *socket_path = NULL;
1190 char *bar_id = NULL; 25 char *bar_id = NULL;
1191 bool debug = false; 26 bool debug = false;
@@ -1253,67 +88,30 @@ int main(int argc, char **argv) {
1253 init_log(L_ERROR); 88 init_log(L_ERROR);
1254 } 89 }
1255 90
1256 registry = registry_poll();
1257
1258 if (!registry->desktop_shell) {
1259 sway_abort("swaybar requires the compositor to support the desktop-shell extension.");
1260 }
1261
1262 if (!socket_path) { 91 if (!socket_path) {
1263 socket_path = get_socketpath(); 92 socket_path = get_socketpath();
1264 if (!socket_path) { 93 if (!socket_path) {
1265 sway_abort("Unable to retrieve socket path"); 94 sway_abort("Unable to retrieve socket path");
1266 } 95 }
1267 } 96 }
1268 ipc_socketfd = ipc_open_socket(socket_path);
1269 ipc_event_socketfd = ipc_open_socket(socket_path);
1270
1271 97
1272 if (argc == optind) { 98 if (argc == optind) {
1273 sway_abort("No output index provided"); 99 sway_abort("No output index provided");
1274 } 100 }
1275 101
1276 int desired_output = atoi(argv[optind]); 102 int desired_output = atoi(argv[optind]);
1277 struct output_state *output = registry->outputs->items[desired_output];
1278
1279 window = window_setup(registry, output->width, 30, false);
1280 if (!window) {
1281 sway_abort("Failed to create window.");
1282 }
1283 desktop_shell_set_panel(registry->desktop_shell, output->output, window->surface);
1284
1285 bar_ipc_init(desired_output, bar_id);
1286 103
1287 if (status_command) { 104 signal(SIGTERM, sig_handler);
1288 int pipefd[2];
1289 pipe(pipefd);
1290 pid = fork();
1291 if (pid == 0) {
1292 close(pipefd[0]);
1293 dup2(pipefd[1], STDOUT_FILENO);
1294 close(pipefd[1]);
1295 char *const cmd[] = {
1296 "sh",
1297 "-c",
1298 status_command,
1299 NULL,
1300 };
1301 execvp(cmd[0], cmd);
1302 return 0;
1303 }
1304 105
1305 close(pipefd[1]); 106 bar_setup(&swaybar, socket_path, bar_id, desired_output);
1306 status_read_fd = pipefd[0];
1307 fcntl(status_read_fd, F_SETFL, O_NONBLOCK);
1308 line[0] = '\0';
1309 }
1310 107
1311 signal(SIGTERM, sig_handler); 108 free(socket_path);
109 free(bar_id);
1312 110
1313 poll_for_update(); 111 bar_run(&swaybar);
1314 112
1315 // gracefully shutdown swaybar and status_command 113 // gracefully shutdown swaybar and status_command
1316 swaybar_teardown(); 114 bar_teardown(&swaybar);
1317 115
1318 return 0; 116 return 0;
1319} 117}