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