aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar
diff options
context:
space:
mode:
Diffstat (limited to 'swaybar')
-rw-r--r--swaybar/bar.c112
-rw-r--r--swaybar/event_loop.c4
-rw-r--r--swaybar/i3bar.c216
-rw-r--r--swaybar/render.c33
-rw-r--r--swaybar/status_line.c148
5 files changed, 311 insertions, 202 deletions
diff --git a/swaybar/bar.c b/swaybar/bar.c
index 3ae730f7..69069f40 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -48,8 +48,13 @@ static void swaybar_output_free(struct swaybar_output *output) {
48 return; 48 return;
49 } 49 }
50 wlr_log(WLR_DEBUG, "Removing output %s", output->name); 50 wlr_log(WLR_DEBUG, "Removing output %s", output->name);
51 zwlr_layer_surface_v1_destroy(output->layer_surface); 51 if (output->layer_surface != NULL) {
52 wl_surface_destroy(output->surface); 52 zwlr_layer_surface_v1_destroy(output->layer_surface);
53 }
54 if (output->surface != NULL) {
55 wl_surface_destroy(output->surface);
56 }
57 zxdg_output_v1_destroy(output->xdg_output);
53 wl_output_destroy(output->output); 58 wl_output_destroy(output->output);
54 destroy_buffer(&output->buffers[0]); 59 destroy_buffer(&output->buffers[0]);
55 destroy_buffer(&output->buffers[1]); 60 destroy_buffer(&output->buffers[1]);
@@ -283,6 +288,37 @@ const struct wl_seat_listener seat_listener = {
283 .name = seat_handle_name, 288 .name = seat_handle_name,
284}; 289};
285 290
291static void add_layer_surface(struct swaybar_output *output) {
292 if (output->surface != NULL) {
293 return;
294 }
295 struct swaybar *bar = output->bar;
296
297 output->surface = wl_compositor_create_surface(bar->compositor);
298 assert(output->surface);
299 output->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
300 bar->layer_shell, output->surface, output->output,
301 ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel");
302 assert(output->layer_surface);
303 zwlr_layer_surface_v1_add_listener(output->layer_surface,
304 &layer_surface_listener, output);
305 zwlr_layer_surface_v1_set_anchor(output->layer_surface,
306 bar->config->position);
307}
308
309static bool bar_uses_output(struct swaybar *bar, const char *name) {
310 if (bar->config->all_outputs) {
311 return true;
312 }
313 struct config_output *coutput;
314 wl_list_for_each(coutput, &bar->config->outputs, link) {
315 if (strcmp(coutput->name, name) == 0) {
316 return true;
317 }
318 }
319 return false;
320}
321
286static void output_geometry(void *data, struct wl_output *output, int32_t x, 322static void output_geometry(void *data, struct wl_output *output, int32_t x,
287 int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, 323 int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,
288 const char *make, const char *model, int32_t transform) { 324 const char *make, const char *model, int32_t transform) {
@@ -326,7 +362,22 @@ static void xdg_output_handle_logical_size(void *data,
326 362
327static void xdg_output_handle_done(void *data, 363static void xdg_output_handle_done(void *data,
328 struct zxdg_output_v1 *xdg_output) { 364 struct zxdg_output_v1 *xdg_output) {
329 // Who cares 365 struct swaybar_output *output = data;
366 struct swaybar *bar = output->bar;
367
368 assert(output->name != NULL);
369 if (!bar_uses_output(bar, output->name)) {
370 swaybar_output_free(output);
371 return;
372 }
373
374 if (wl_list_empty(&output->link)) {
375 wl_list_remove(&output->link);
376 wl_list_insert(&bar->outputs, &output->link);
377
378 add_layer_surface(output);
379 render_frame(bar, output);
380 }
330} 381}
331 382
332static void xdg_output_handle_name(void *data, 383static void xdg_output_handle_name(void *data,
@@ -349,17 +400,15 @@ struct zxdg_output_v1_listener xdg_output_listener = {
349 .description = xdg_output_handle_description, 400 .description = xdg_output_handle_description,
350}; 401};
351 402
352static bool bar_uses_output(struct swaybar *bar, const char *name) { 403static void add_xdg_output(struct swaybar_output *output) {
353 if (bar->config->all_outputs) { 404 if (output->xdg_output != NULL) {
354 return true; 405 return;
355 }
356 struct config_output *coutput;
357 wl_list_for_each(coutput, &bar->config->outputs, link) {
358 if (strcmp(coutput->name, name) == 0) {
359 return true;
360 }
361 } 406 }
362 return false; 407 assert(output->bar->xdg_output_manager != NULL);
408 output->xdg_output = zxdg_output_manager_v1_get_xdg_output(
409 output->bar->xdg_output_manager, output->output);
410 zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener,
411 output);
363} 412}
364 413
365static void handle_global(void *data, struct wl_registry *registry, 414static void handle_global(void *data, struct wl_registry *registry,
@@ -386,7 +435,10 @@ static void handle_global(void *data, struct wl_registry *registry,
386 output->wl_name = name; 435 output->wl_name = name;
387 wl_list_init(&output->workspaces); 436 wl_list_init(&output->workspaces);
388 wl_list_init(&output->hotspots); 437 wl_list_init(&output->hotspots);
389 wl_list_insert(&bar->outputs, &output->link); 438 wl_list_init(&output->link);
439 if (bar->xdg_output_manager != NULL) {
440 add_xdg_output(output);
441 }
390 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { 442 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
391 bar->layer_shell = wl_registry_bind( 443 bar->layer_shell = wl_registry_bind(
392 registry, name, &zwlr_layer_shell_v1_interface, 1); 444 registry, name, &zwlr_layer_shell_v1_interface, 1);
@@ -416,7 +468,9 @@ static const struct wl_registry_listener registry_listener = {
416static void render_all_frames(struct swaybar *bar) { 468static void render_all_frames(struct swaybar *bar) {
417 struct swaybar_output *output; 469 struct swaybar_output *output;
418 wl_list_for_each(output, &bar->outputs, link) { 470 wl_list_for_each(output, &bar->outputs, link) {
419 render_frame(bar, output); 471 if (output->surface != NULL) {
472 render_frame(bar, output);
473 }
420 } 474 }
421} 475}
422 476
@@ -443,23 +497,10 @@ void bar_setup(struct swaybar *bar,
443 497
444 struct swaybar_output *output; 498 struct swaybar_output *output;
445 wl_list_for_each(output, &bar->outputs, link) { 499 wl_list_for_each(output, &bar->outputs, link) {
446 output->xdg_output = zxdg_output_manager_v1_get_xdg_output( 500 add_xdg_output(output);
447 bar->xdg_output_manager, output->output);
448 zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener,
449 output);
450 } 501 }
451 wl_display_roundtrip(bar->display); 502 wl_display_roundtrip(bar->display);
452 503
453 struct swaybar_output *output_tmp;
454 wl_list_for_each_safe(output, output_tmp, &bar->outputs, link) {
455 if (!bar_uses_output(bar, output->name)) {
456 zxdg_output_v1_destroy(output->xdg_output);
457 wl_output_destroy(output->output);
458 wl_list_remove(&output->link);
459 free(output);
460 }
461 }
462
463 struct swaybar_pointer *pointer = &bar->pointer; 504 struct swaybar_pointer *pointer = &bar->pointer;
464 505
465 int max_scale = 1; 506 int max_scale = 1;
@@ -479,18 +520,6 @@ void bar_setup(struct swaybar *bar,
479 pointer->cursor_surface = wl_compositor_create_surface(bar->compositor); 520 pointer->cursor_surface = wl_compositor_create_surface(bar->compositor);
480 assert(pointer->cursor_surface); 521 assert(pointer->cursor_surface);
481 522
482 wl_list_for_each(output, &bar->outputs, link) {
483 output->surface = wl_compositor_create_surface(bar->compositor);
484 assert(output->surface);
485 output->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
486 bar->layer_shell, output->surface, output->output,
487 ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel");
488 assert(output->layer_surface);
489 zwlr_layer_surface_v1_add_listener(output->layer_surface,
490 &layer_surface_listener, output);
491 zwlr_layer_surface_v1_set_anchor(output->layer_surface,
492 bar->config->position);
493 }
494 ipc_get_workspaces(bar); 523 ipc_get_workspaces(bar);
495 render_all_frames(bar); 524 render_all_frames(bar);
496} 525}
@@ -529,6 +558,7 @@ void bar_run(struct swaybar *bar) {
529 } 558 }
530 while (1) { 559 while (1) {
531 event_loop_poll(); 560 event_loop_poll();
561 wl_display_flush(bar->display);
532 } 562 }
533} 563}
534 564
diff --git a/swaybar/event_loop.c b/swaybar/event_loop.c
index bc4053be..686b9962 100644
--- a/swaybar/event_loop.c
+++ b/swaybar/event_loop.c
@@ -105,7 +105,7 @@ bool remove_timer(timer_t timer) {
105 return false; 105 return false;
106} 106}
107 107
108void event_loop_poll() { 108void event_loop_poll(void) {
109 poll(event_loop.fds.items, event_loop.fds.length, -1); 109 poll(event_loop.fds.items, event_loop.fds.length, -1);
110 110
111 for (int i = 0; i < event_loop.fds.length; ++i) { 111 for (int i = 0; i < event_loop.fds.length; ++i) {
@@ -146,7 +146,7 @@ void event_loop_poll() {
146 } 146 }
147} 147}
148 148
149void init_event_loop() { 149void init_event_loop(void) {
150 event_loop.fds.length = 0; 150 event_loop.fds.length = 0;
151 event_loop.fds.capacity = 10; 151 event_loop.fds.capacity = 10;
152 event_loop.fds.items = malloc( 152 event_loop.fds.items = malloc(
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c
index 0becae5d..325aa61a 100644
--- a/swaybar/i3bar.c
+++ b/swaybar/i3bar.c
@@ -1,6 +1,7 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <json-c/json.h> 2#include <json-c/json.h>
3#include <linux/input-event-codes.h> 3#include <linux/input-event-codes.h>
4#include <ctype.h>
4#include <stdlib.h> 5#include <stdlib.h>
5#include <string.h> 6#include <string.h>
6#include <unistd.h> 7#include <unistd.h>
@@ -24,24 +25,19 @@ void i3bar_block_unref(struct i3bar_block *block) {
24 } 25 }
25} 26}
26 27
27static bool i3bar_parse_json(struct status_line *status, const char *text) { 28static void i3bar_parse_json(struct status_line *status,
29 struct json_object *json_array) {
28 struct i3bar_block *block, *tmp; 30 struct i3bar_block *block, *tmp;
29 wl_list_for_each_safe(block, tmp, &status->blocks, link) { 31 wl_list_for_each_safe(block, tmp, &status->blocks, link) {
30 wl_list_remove(&block->link); 32 wl_list_remove(&block->link);
31 i3bar_block_unref(block); 33 i3bar_block_unref(block);
32 } 34 }
33 json_object *results = json_tokener_parse(text); 35 for (size_t i = 0; i < json_object_array_length(json_array); ++i) {
34 if (!results) {
35 status_error(status, "[failed to parse i3bar json]");
36 return false;
37 }
38 wlr_log(WLR_DEBUG, "Got i3bar json: '%s'", text);
39 for (size_t i = 0; i < json_object_array_length(results); ++i) {
40 json_object *full_text, *short_text, *color, *min_width, *align, *urgent; 36 json_object *full_text, *short_text, *color, *min_width, *align, *urgent;
41 json_object *name, *instance, *separator, *separator_block_width; 37 json_object *name, *instance, *separator, *separator_block_width;
42 json_object *background, *border, *border_top, *border_bottom; 38 json_object *background, *border, *border_top, *border_bottom;
43 json_object *border_left, *border_right, *markup; 39 json_object *border_left, *border_right, *markup;
44 json_object *json = json_object_array_get_idx(results, i); 40 json_object *json = json_object_array_get_idx(json_array, i);
45 if (!json) { 41 if (!json) {
46 continue; 42 continue;
47 } 43 }
@@ -110,96 +106,160 @@ static bool i3bar_parse_json(struct status_line *status, const char *text) {
110 json_object_get_int(border_right) : 1; 106 json_object_get_int(border_right) : 1;
111 wl_list_insert(&status->blocks, &block->link); 107 wl_list_insert(&status->blocks, &block->link);
112 } 108 }
113 json_object_put(results);
114 return true;
115} 109}
116 110
117bool i3bar_handle_readable(struct status_line *status) { 111bool i3bar_handle_readable(struct status_line *status) {
118 struct i3bar_protocol_state *state = &status->i3bar_state; 112 while (!status->started) { // look for opening bracket
119 113 for (size_t c = 0; c < status->buffer_index; ++c) {
120 char *cur = &state->buffer[state->buffer_index]; 114 if (status->buffer[c] == '[') {
121 ssize_t n = read(status->read_fd, cur, 115 status->started = true;
122 state->buffer_size - state->buffer_index); 116 status->buffer_index -= ++c;
123 if (n == -1) { 117 memmove(status->buffer, &status->buffer[c], status->buffer_index);
124 status_error(status, "[failed to read from status command]"); 118 break;
125 return false; 119 } else if (!isspace(status->buffer[c])) {
126 } 120 wlr_log(WLR_DEBUG, "Invalid i3bar json: expected '[' but encountered '%c'",
121 status->buffer[c]);
122 status_error(status, "[invalid i3bar json]");
123 return true;
124 }
125 }
126 if (status->started) {
127 break;
128 }
127 129
128 if (n == (ssize_t)(state->buffer_size - state->buffer_index)) { 130 errno = 0;
129 state->buffer_size = state->buffer_size * 2; 131 ssize_t read_bytes = read(status->read_fd, status->buffer, status->buffer_size);
130 char *new_buffer = realloc(state->buffer, state->buffer_size); 132 if (read_bytes > -1) {
131 if (!new_buffer) { 133 status->buffer_index = read_bytes;
132 free(state->buffer); 134 } else if (errno == EAGAIN) {
133 status_error(status, "[failed to allocate buffer]"); 135 return false;
136 } else {
137 status_error(status, "[error reading from status command]");
134 return true; 138 return true;
135 } 139 }
136 state->current_node += new_buffer - state->buffer;
137 cur += new_buffer - state->buffer;
138 state->buffer = new_buffer;
139 } 140 }
140 141
141 cur[n] = '\0'; 142 struct json_object *last_object = NULL;
142 bool redraw = false; 143 struct json_object *test_object;
143 while (*cur) { 144 size_t buffer_pos = 0;
144 if (state->nodes[state->depth] == JSON_NODE_STRING) { 145 while (true) {
145 if (!state->escape && *cur == '"') { 146 // since the incoming stream is an infinite array
146 --state->depth; 147 // parsing is split into two parts
148 // first, attempt to parse the current object, reading more if the
149 // parser indicates that the current object is incomplete, and failing
150 // if the parser fails
151 // second, look for separating comma, ignoring whitespace, failing if
152 // any other characters are encountered
153 if (status->expecting_comma) {
154 for (; buffer_pos < status->buffer_index; ++buffer_pos) {
155 if (status->buffer[buffer_pos] == ',') {
156 status->expecting_comma = false;
157 ++buffer_pos;
158 break;
159 } else if (!isspace(status->buffer[buffer_pos])) {
160 wlr_log(WLR_DEBUG, "Invalid i3bar json: expected ',' but encountered '%c'",
161 status->buffer[buffer_pos]);
162 status_error(status, "[invalid i3bar json]");
163 return true;
164 }
165 }
166 if (buffer_pos < status->buffer_index) {
167 continue; // look for new object without reading more input
147 } 168 }
148 state->escape = !state->escape && *cur == '\\'; 169 buffer_pos = status->buffer_index = 0;
149 } else { 170 } else {
150 switch (*cur) { 171 test_object = json_tokener_parse_ex(status->tokener,
151 case '[': 172 &status->buffer[buffer_pos], status->buffer_index - buffer_pos);
152 ++state->depth; 173 enum json_tokener_error err = json_tokener_get_error(status->tokener);
153 if (state->depth > 174 if (err == json_tokener_success) {
154 sizeof(state->nodes) / sizeof(state->nodes[0])) { 175 if (json_object_get_type(test_object) == json_type_array) {
155 status_error(status, "[i3bar json too deep]"); 176 if (last_object) {
156 return false; 177 json_object_put(last_object);
157 } 178 }
158 state->nodes[state->depth] = JSON_NODE_ARRAY; 179 last_object = test_object;
159 if (state->depth == 1) { 180 } else {
160 state->current_node = cur; 181 json_object_put(test_object);
161 } 182 }
162 break; 183
163 case ']': 184 // in order to print the json for debugging purposes
164 if (state->nodes[state->depth] != JSON_NODE_ARRAY) { 185 // the last character is temporarily replaced with a null character
165 status_error(status, "[failed to parse i3bar json]"); 186 // (the last character is used in case the buffer is full)
166 return false; 187 char *last_char_pos =
188 &status->buffer[buffer_pos + status->tokener->char_offset - 1];
189 char last_char = *last_char_pos;
190 while (isspace(last_char)) {
191 last_char = *--last_char_pos;
167 } 192 }
168 --state->depth; 193 *last_char_pos = '\0';
169 if (state->depth == 0) { 194 size_t offset = strspn(&status->buffer[buffer_pos], " \f\n\r\t\v");
170 // cur[1] is valid since cur[0] != '\0' 195 wlr_log(WLR_DEBUG, "Received i3bar json: '%s%c'",
171 char p = cur[1]; 196 &status->buffer[buffer_pos + offset], last_char);
172 cur[1] = '\0'; 197 *last_char_pos = last_char;
173 redraw = i3bar_parse_json( 198
174 status, state->current_node) || redraw; 199 buffer_pos += status->tokener->char_offset;
175 cur[1] = p; 200 status->expecting_comma = true;
176 memmove(state->buffer, cur, 201
177 state->buffer_size - (cur - state->buffer)); 202 if (buffer_pos < status->buffer_index) {
178 cur = state->buffer; 203 continue; // look for comma without reading more input
179 state->current_node = cur + 1;
180 } 204 }
181 break; 205 buffer_pos = status->buffer_index = 0;
182 case '"': 206 } else if (err == json_tokener_continue) {
183 ++state->depth; 207 json_tokener_reset(status->tokener);
184 if (state->depth > 208 if (status->buffer_index < status->buffer_size) {
185 sizeof(state->nodes) / sizeof(state->nodes[0])) { 209 // move the object to the start of the buffer
186 status_error(status, "[i3bar json too deep]"); 210 status->buffer_index -= buffer_pos;
187 return false; 211 memmove(status->buffer, &status->buffer[buffer_pos],
212 status->buffer_index);
213 buffer_pos = 0;
214 } else {
215 // expand buffer
216 status->buffer_size *= 2;
217 char *new_buffer = realloc(status->buffer, status->buffer_size);
218 if (new_buffer) {
219 status->buffer = new_buffer;
220 } else {
221 free(status->buffer);
222 status_error(status, "[failed to allocate buffer]");
223 return true;
224 }
188 } 225 }
189 state->nodes[state->depth] = JSON_NODE_STRING; 226 } else {
190 break; 227 char last_char = status->buffer[status->buffer_index - 1];
228 status->buffer[status->buffer_index - 1] = '\0';
229 wlr_log(WLR_DEBUG, "Failed to parse i3bar json - %s: '%s%c'",
230 json_tokener_error_desc(err), &status->buffer[buffer_pos], last_char);
231 status_error(status, "[failed to parse i3bar json]");
232 return true;
191 } 233 }
192 } 234 }
193 ++cur; 235
236 errno = 0;
237 ssize_t read_bytes = read(status->read_fd, &status->buffer[status->buffer_index],
238 status->buffer_size - status->buffer_index);
239 if (read_bytes > -1) {
240 status->buffer_index += read_bytes;
241 } else if (errno == EAGAIN) {
242 break;
243 } else {
244 status_error(status, "[error reading from status command]");
245 return true;
246 }
247 }
248
249 if (last_object) {
250 wlr_log(WLR_DEBUG, "Rendering last received json");
251 i3bar_parse_json(status, last_object);
252 json_object_put(last_object);
253 return true;
254 } else {
255 return false;
194 } 256 }
195 state->buffer_index = cur - state->buffer;
196 return redraw;
197} 257}
198 258
199enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, 259enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
200 struct i3bar_block *block, int x, int y, enum x11_button button) { 260 struct i3bar_block *block, int x, int y, enum x11_button button) {
201 wlr_log(WLR_DEBUG, "block %s clicked", block->name ? block->name : "(nil)"); 261 wlr_log(WLR_DEBUG, "block %s clicked", block->name ? block->name : "(nil)");
202 if (!block->name || !status->i3bar_state.click_events) { 262 if (!block->name || !status->click_events) {
203 return HOTSPOT_PROCESS; 263 return HOTSPOT_PROCESS;
204 } 264 }
205 265
@@ -214,7 +274,7 @@ enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
214 json_object_object_add(event_json, "button", json_object_new_int(button)); 274 json_object_object_add(event_json, "button", json_object_new_int(button));
215 json_object_object_add(event_json, "x", json_object_new_int(x)); 275 json_object_object_add(event_json, "x", json_object_new_int(x));
216 json_object_object_add(event_json, "y", json_object_new_int(y)); 276 json_object_object_add(event_json, "y", json_object_new_int(y));
217 if (dprintf(status->write_fd, "%s\n", 277 if (dprintf(status->write_fd, "%s,\n",
218 json_object_to_json_string(event_json)) < 0) { 278 json_object_to_json_string(event_json)) < 0) {
219 status_error(status, "[failed to write click event]"); 279 status_error(status, "[failed to write click event]");
220 } 280 }
diff --git a/swaybar/render.c b/swaybar/render.c
index 2d848bfa..26db80cb 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -1,4 +1,5 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
2#include <limits.h> 3#include <limits.h>
3#include <stdlib.h> 4#include <stdlib.h>
4#include <stdint.h> 5#include <stdint.h>
@@ -120,14 +121,14 @@ static void i3bar_block_unref_callback(void *data) {
120} 121}
121 122
122static uint32_t render_status_block(cairo_t *cairo, 123static uint32_t render_status_block(cairo_t *cairo,
123 struct swaybar_config *config, struct swaybar_output *output, 124 struct swaybar_output *output, struct i3bar_block *block, double *x,
124 struct i3bar_block *block, double *x,
125 uint32_t surface_height, bool focused, bool edge) { 125 uint32_t surface_height, bool focused, bool edge) {
126 if (!block->full_text || !*block->full_text) { 126 if (!block->full_text || !*block->full_text) {
127 return 0; 127 return 0;
128 } 128 }
129 129
130 uint32_t height = surface_height * output->scale; 130 uint32_t height = surface_height * output->scale;
131 struct swaybar_config *config = output->bar->config;
131 132
132 int text_width, text_height; 133 int text_width, text_height;
133 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 134 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
@@ -177,16 +178,18 @@ static uint32_t render_status_block(cairo_t *cairo,
177 *x -= margin; 178 *x -= margin;
178 } 179 }
179 180
180 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 181 if (output->bar->status->click_events) {
181 hotspot->x = *x; 182 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
182 hotspot->y = 0; 183 hotspot->x = *x;
183 hotspot->width = width; 184 hotspot->y = 0;
184 hotspot->height = height; 185 hotspot->width = width;
185 hotspot->callback = block_hotspot_callback; 186 hotspot->height = height;
186 hotspot->destroy = i3bar_block_unref_callback; 187 hotspot->callback = block_hotspot_callback;
187 hotspot->data = block; 188 hotspot->destroy = i3bar_block_unref_callback;
188 block->ref_count++; 189 hotspot->data = block;
189 wl_list_insert(&output->hotspots, &hotspot->link); 190 block->ref_count++;
191 wl_list_insert(&output->hotspots, &hotspot->link);
192 }
190 193
191 double pos = *x; 194 double pos = *x;
192 if (block->background) { 195 if (block->background) {
@@ -268,7 +271,7 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo,
268 bool edge = true; 271 bool edge = true;
269 struct i3bar_block *block; 272 struct i3bar_block *block;
270 wl_list_for_each(block, &status->blocks, link) { 273 wl_list_for_each(block, &status->blocks, link) {
271 uint32_t h = render_status_block(cairo, config, output, 274 uint32_t h = render_status_block(cairo, output,
272 block, x, surface_height, focused, edge); 275 block, x, surface_height, focused, edge);
273 max_height = h > max_height ? h : max_height; 276 max_height = h > max_height ? h : max_height;
274 edge = false; 277 edge = false;
@@ -478,6 +481,8 @@ static uint32_t render_to_cairo(cairo_t *cairo,
478} 481}
479 482
480void render_frame(struct swaybar *bar, struct swaybar_output *output) { 483void render_frame(struct swaybar *bar, struct swaybar_output *output) {
484 assert(output->surface != NULL);
485
481 struct swaybar_hotspot *hotspot, *tmp; 486 struct swaybar_hotspot *hotspot, *tmp;
482 wl_list_for_each_safe(hotspot, tmp, &output->hotspots, link) { 487 wl_list_for_each_safe(hotspot, tmp, &output->hotspots, link) {
483 if (hotspot->destroy) { 488 if (hotspot->destroy) {
@@ -505,7 +510,6 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {
505 // TODO: this could infinite loop if the compositor assigns us a 510 // TODO: this could infinite loop if the compositor assigns us a
506 // different height than what we asked for 511 // different height than what we asked for
507 wl_surface_commit(output->surface); 512 wl_surface_commit(output->surface);
508 wl_display_roundtrip(bar->display);
509 } else if (height > 0) { 513 } else if (height > 0) {
510 // Replay recording into shm and send it off 514 // Replay recording into shm and send it off
511 output->current_buffer = get_next_buffer(bar->shm, 515 output->current_buffer = get_next_buffer(bar->shm,
@@ -531,7 +535,6 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {
531 wl_surface_damage(output->surface, 0, 0, 535 wl_surface_damage(output->surface, 0, 0,
532 output->width, output->height); 536 output->width, output->height);
533 wl_surface_commit(output->surface); 537 wl_surface_commit(output->surface);
534 wl_display_roundtrip(bar->display);
535 } 538 }
536 cairo_surface_destroy(recorder); 539 cairo_surface_destroy(recorder);
537 cairo_destroy(cairo); 540 cairo_destroy(cairo);
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index 3ba990bd..48b43248 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -7,86 +7,106 @@
7#include <unistd.h> 7#include <unistd.h>
8#include <wlr/util/log.h> 8#include <wlr/util/log.h>
9#include "swaybar/config.h" 9#include "swaybar/config.h"
10#include "swaybar/event_loop.h"
10#include "swaybar/status_line.h" 11#include "swaybar/status_line.h"
11#include "readline.h" 12#include "readline.h"
12 13
14static void status_line_close_fds(struct status_line *status) {
15 if (status->read_fd != -1) {
16 remove_event(status->read_fd);
17 close(status->read_fd);
18 status->read_fd = -1;
19 }
20 if (status->write_fd != -1) {
21 close(status->write_fd);
22 status->write_fd = -1;
23 }
24}
25
13void status_error(struct status_line *status, const char *text) { 26void status_error(struct status_line *status, const char *text) {
14 close(status->read_fd); 27 status_line_close_fds(status);
15 close(status->write_fd);
16 status->protocol = PROTOCOL_ERROR; 28 status->protocol = PROTOCOL_ERROR;
17 status->text = text; 29 status->text = text;
18} 30}
19 31
20bool status_handle_readable(struct status_line *status) { 32bool status_handle_readable(struct status_line *status) {
21 char *line; 33 ssize_t read_bytes = 1;
22 switch (status->protocol) { 34 switch (status->protocol) {
23 case PROTOCOL_ERROR:
24 return false;
25 case PROTOCOL_I3BAR:
26 if (i3bar_handle_readable(status) > 0) {
27 return true;
28 }
29 break;
30 case PROTOCOL_TEXT:
31 line = read_line_buffer(status->read,
32 status->text_state.buffer, status->text_state.buffer_size);
33 if (!line) {
34 status_error(status, "[error reading from status command]");
35 } else {
36 status->text = line;
37 }
38 return true;
39 case PROTOCOL_UNDEF: 35 case PROTOCOL_UNDEF:
40 line = read_line_buffer(status->read, 36 errno = 0;
41 status->text_state.buffer, status->text_state.buffer_size); 37 read_bytes = getline(&status->buffer,
42 if (!line) { 38 &status->buffer_size, status->read);
39 if (errno == EAGAIN) {
40 clearerr(status->read);
41 } else if (errno) {
43 status_error(status, "[error reading from status command]"); 42 status_error(status, "[error reading from status command]");
44 return false; 43 return true;
45 } 44 }
46 if (line[0] == '{') { 45
47 json_object *proto = json_tokener_parse(line); 46 // the header must be sent completely the first time round
48 if (proto) { 47 json_object *header, *version;
49 json_object *version; 48 if (status->buffer[read_bytes - 1] == '\n'
50 if (json_object_object_get_ex(proto, "version", &version) 49 && (header = json_tokener_parse(status->buffer))
51 && json_object_get_int(version) == 1) { 50 && json_object_object_get_ex(header, "version", &version)
52 wlr_log(WLR_DEBUG, "Switched to i3bar protocol."); 51 && json_object_get_int(version) == 1) {
53 status->protocol = PROTOCOL_I3BAR; 52 wlr_log(WLR_DEBUG, "Using i3bar protocol.");
54 } 53 status->protocol = PROTOCOL_I3BAR;
55 json_object *click_events; 54
56 if (json_object_object_get_ex( 55 json_object *click_events;
57 proto, "click_events", &click_events) 56 if (json_object_object_get_ex(header, "click_events", &click_events)
58 && json_object_get_boolean(click_events)) { 57 && json_object_get_boolean(click_events)) {
59 wlr_log(WLR_DEBUG, "Enabled click events."); 58 wlr_log(WLR_DEBUG, "Enabling click events.");
60 status->i3bar_state.click_events = true; 59 status->click_events = true;
61 const char *events_array = "[\n"; 60 if (write(status->write_fd, "[\n", 2) != 2) {
62 ssize_t len = strlen(events_array); 61 status_error(status, "[failed to write to status command]");
63 if (write(status->write_fd, events_array, len) != len) { 62 json_object_put(header);
64 status_error(status, 63 return true;
65 "[failed to write to status command]");
66 }
67 } 64 }
68 json_object_put(proto);
69 } 65 }
66 json_object_put(header);
70 67
71 status->protocol = PROTOCOL_I3BAR;
72 free(status->text_state.buffer);
73 wl_list_init(&status->blocks); 68 wl_list_init(&status->blocks);
74 status->i3bar_state.buffer_size = 4096; 69 status->tokener = json_tokener_new();
75 status->i3bar_state.buffer = 70 read_bytes = getdelim(&status->buffer, &status->buffer_size, EOF, status->read);
76 malloc(status->i3bar_state.buffer_size); 71 if (read_bytes > 0) {
77 } else { 72 status->buffer_index = read_bytes;
78 status->protocol = PROTOCOL_TEXT; 73 return i3bar_handle_readable(status);
79 status->text = line; 74 } else {
75 return false;
76 }
77 }
78
79 wlr_log(WLR_DEBUG, "Using text protocol.");
80 status->protocol = PROTOCOL_TEXT;
81 status->text = status->buffer;
82 // intentional fall-through
83 case PROTOCOL_TEXT:
84 errno = 0;
85 while (true) {
86 if (status->buffer[read_bytes - 1] == '\n') {
87 status->buffer[read_bytes - 1] = '\0';
88 }
89 read_bytes = getline(&status->buffer,
90 &status->buffer_size, status->read);
91 if (errno == EAGAIN) {
92 clearerr(status->read);
93 return true;
94 } else if (errno) {
95 status_error(status, "[error reading from status command]");
96 return true;
97 }
80 } 98 }
81 return true; 99 case PROTOCOL_I3BAR:
100 return i3bar_handle_readable(status);
101 default:
102 return false;
82 } 103 }
83 return false;
84} 104}
85 105
86struct status_line *status_line_init(char *cmd) { 106struct status_line *status_line_init(char *cmd) {
87 struct status_line *status = calloc(1, sizeof(struct status_line)); 107 struct status_line *status = calloc(1, sizeof(struct status_line));
88 status->text_state.buffer_size = 8192; 108 status->buffer_size = 8192;
89 status->text_state.buffer = malloc(status->text_state.buffer_size); 109 status->buffer = malloc(status->buffer_size);
90 110
91 int pipe_read_fd[2]; 111 int pipe_read_fd[2];
92 int pipe_write_fd[2]; 112 int pipe_write_fd[2];
@@ -123,20 +143,16 @@ struct status_line *status_line_init(char *cmd) {
123} 143}
124 144
125void status_line_free(struct status_line *status) { 145void status_line_free(struct status_line *status) {
126 close(status->read_fd); 146 status_line_close_fds(status);
127 close(status->write_fd);
128 kill(status->pid, SIGTERM); 147 kill(status->pid, SIGTERM);
129 switch (status->protocol) { 148 if (status->protocol == PROTOCOL_I3BAR) {
130 case PROTOCOL_I3BAR:;
131 struct i3bar_block *block, *tmp; 149 struct i3bar_block *block, *tmp;
132 wl_list_for_each_safe(block, tmp, &status->blocks, link) { 150 wl_list_for_each_safe(block, tmp, &status->blocks, link) {
151 wl_list_remove(&block->link);
133 i3bar_block_unref(block); 152 i3bar_block_unref(block);
134 } 153 }
135 free(status->i3bar_state.buffer); 154 json_tokener_free(status->tokener);
136 break;
137 default:
138 free(status->text_state.buffer);
139 break;
140 } 155 }
156 free(status->buffer);
141 free(status); 157 free(status);
142} 158}