From 4cb28de89fa50c2c992702c238d54d16a0269553 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Sat, 15 Sep 2018 10:14:21 +0100 Subject: [PATCH 01/14] swaybar: remove block links upon exit --- swaybar/status_line.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/swaybar/status_line.c b/swaybar/status_line.c index 3ba990bd..688e5947 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -127,13 +127,15 @@ void status_line_free(struct status_line *status) { close(status->write_fd); kill(status->pid, SIGTERM); switch (status->protocol) { - case PROTOCOL_I3BAR:; + case PROTOCOL_I3BAR: { struct i3bar_block *block, *tmp; wl_list_for_each_safe(block, tmp, &status->blocks, link) { + wl_list_remove(&block->link); i3bar_block_unref(block); } free(status->i3bar_state.buffer); break; + } default: free(status->text_state.buffer); break; From 952453480f6764eb583b2507c7af83e242e0630a Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Sat, 15 Sep 2018 10:15:30 +0100 Subject: [PATCH 02/14] swaybar: invalidate file descriptors upon closing --- swaybar/status_line.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/swaybar/status_line.c b/swaybar/status_line.c index 688e5947..09ed2375 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -7,12 +7,24 @@ #include #include #include "swaybar/config.h" +#include "swaybar/event_loop.h" #include "swaybar/status_line.h" #include "readline.h" +static void status_line_close_fds(struct status_line *status) { + if (status->read_fd != -1) { + remove_event(status->read_fd); + close(status->read_fd); + status->read_fd = -1; + } + if (status->write_fd != -1) { + close(status->write_fd); + status->write_fd = -1; + } +} + void status_error(struct status_line *status, const char *text) { - close(status->read_fd); - close(status->write_fd); + status_line_close_fds(status); status->protocol = PROTOCOL_ERROR; status->text = text; } @@ -123,8 +135,7 @@ struct status_line *status_line_init(char *cmd) { } void status_line_free(struct status_line *status) { - close(status->read_fd); - close(status->write_fd); + status_line_close_fds(status); kill(status->pid, SIGTERM); switch (status->protocol) { case PROTOCOL_I3BAR: { From 9932c6a1f1f295531989d9e76494b0ca43509cd4 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Sun, 16 Sep 2018 11:24:24 +0100 Subject: [PATCH 03/14] swaybar: fix empty function prototypes --- include/swaybar/event_loop.h | 4 ++-- swaybar/event_loop.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/swaybar/event_loop.h b/include/swaybar/event_loop.h index 99f6ed36..47be5b79 100644 --- a/include/swaybar/event_loop.h +++ b/include/swaybar/event_loop.h @@ -19,8 +19,8 @@ bool remove_event(int fd); bool remove_timer(timer_t timer); // Blocks and returns after sending callbacks -void event_loop_poll(); +void event_loop_poll(void); -void init_event_loop(); +void init_event_loop(void); #endif 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) { return false; } -void event_loop_poll() { +void event_loop_poll(void) { poll(event_loop.fds.items, event_loop.fds.length, -1); for (int i = 0; i < event_loop.fds.length; ++i) { @@ -146,7 +146,7 @@ void event_loop_poll() { } } -void init_event_loop() { +void init_event_loop(void) { event_loop.fds.length = 0; event_loop.fds.capacity = 10; event_loop.fds.items = malloc( From 87c93d6ad986faa384f1279e21c10e35c798686a Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Mon, 17 Sep 2018 13:31:00 +0100 Subject: [PATCH 04/14] swaybar: send trailing comma with click event json --- swaybar/i3bar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index 0becae5d..f4ca5504 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c @@ -214,7 +214,7 @@ enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, json_object_object_add(event_json, "button", json_object_new_int(button)); json_object_object_add(event_json, "x", json_object_new_int(x)); json_object_object_add(event_json, "y", json_object_new_int(y)); - if (dprintf(status->write_fd, "%s\n", + if (dprintf(status->write_fd, "%s,\n", json_object_to_json_string(event_json)) < 0) { status_error(status, "[failed to write click event]"); } From babd9618b93987665c11ff268463b344da698744 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Mon, 17 Sep 2018 13:31:24 +0100 Subject: [PATCH 05/14] swaybar: only create i3bar block hotspot if click events are enabled --- swaybar/render.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/swaybar/render.c b/swaybar/render.c index 2d848bfa..b2c1c710 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -120,14 +120,14 @@ static void i3bar_block_unref_callback(void *data) { } static uint32_t render_status_block(cairo_t *cairo, - struct swaybar_config *config, struct swaybar_output *output, - struct i3bar_block *block, double *x, + struct swaybar_output *output, struct i3bar_block *block, double *x, uint32_t surface_height, bool focused, bool edge) { if (!block->full_text || !*block->full_text) { return 0; } uint32_t height = surface_height * output->scale; + struct swaybar_config *config = output->bar->config; int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, NULL, @@ -177,16 +177,18 @@ static uint32_t render_status_block(cairo_t *cairo, *x -= margin; } - struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); - hotspot->x = *x; - hotspot->y = 0; - hotspot->width = width; - hotspot->height = height; - hotspot->callback = block_hotspot_callback; - hotspot->destroy = i3bar_block_unref_callback; - hotspot->data = block; - block->ref_count++; - wl_list_insert(&output->hotspots, &hotspot->link); + if (output->bar->status->i3bar_state.click_events) { + struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); + hotspot->x = *x; + hotspot->y = 0; + hotspot->width = width; + hotspot->height = height; + hotspot->callback = block_hotspot_callback; + hotspot->destroy = i3bar_block_unref_callback; + hotspot->data = block; + block->ref_count++; + wl_list_insert(&output->hotspots, &hotspot->link); + } double pos = *x; if (block->background) { @@ -268,7 +270,7 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo, bool edge = true; struct i3bar_block *block; wl_list_for_each(block, &status->blocks, link) { - uint32_t h = render_status_block(cairo, config, output, + uint32_t h = render_status_block(cairo, output, block, x, surface_height, focused, edge); max_height = h > max_height ? h : max_height; edge = false; From 70245c2cd5c34586fa91eb784e58471869ba66bd Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Mon, 17 Sep 2018 13:43:27 +0100 Subject: [PATCH 06/14] swaybar: rewrite text protocol handling This now uses getline to correctly handle multiple or long statuses. It also removes the struct text_protocol_state and moves its members into the status_line struct. --- include/swaybar/status_line.h | 8 ++----- swaybar/status_line.c | 40 +++++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h index 150267cd..857948a5 100644 --- a/include/swaybar/status_line.h +++ b/include/swaybar/status_line.h @@ -12,11 +12,6 @@ enum status_protocol { PROTOCOL_I3BAR, }; -struct text_protocol_state { - char *buffer; - size_t buffer_size; -}; - enum json_node_type { JSON_NODE_UNKNOWN, JSON_NODE_ARRAY, @@ -63,7 +58,8 @@ struct status_line { const char *text; struct wl_list blocks; // i3bar_block::link - struct text_protocol_state text_state; + char *buffer; + size_t buffer_size; struct i3bar_protocol_state i3bar_state; }; diff --git a/swaybar/status_line.c b/swaybar/status_line.c index 09ed2375..54a68b40 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -30,27 +30,17 @@ void status_error(struct status_line *status, const char *text) { } bool status_handle_readable(struct status_line *status) { + ssize_t read_bytes = 1; char *line; switch (status->protocol) { - case PROTOCOL_ERROR: - return false; case PROTOCOL_I3BAR: if (i3bar_handle_readable(status) > 0) { return true; } break; - case PROTOCOL_TEXT: - line = read_line_buffer(status->read, - status->text_state.buffer, status->text_state.buffer_size); - if (!line) { - status_error(status, "[error reading from status command]"); - } else { - status->text = line; - } - return true; case PROTOCOL_UNDEF: line = read_line_buffer(status->read, - status->text_state.buffer, status->text_state.buffer_size); + status->buffer, status->buffer_size); if (!line) { status_error(status, "[error reading from status command]"); return false; @@ -81,7 +71,7 @@ bool status_handle_readable(struct status_line *status) { } status->protocol = PROTOCOL_I3BAR; - free(status->text_state.buffer); + free(status->buffer); wl_list_init(&status->blocks); status->i3bar_state.buffer_size = 4096; status->i3bar_state.buffer = @@ -91,14 +81,32 @@ bool status_handle_readable(struct status_line *status) { status->text = line; } return true; + case PROTOCOL_TEXT: + errno = 0; + while (true) { + if (status->buffer[read_bytes - 1] == '\n') { + status->buffer[read_bytes - 1] = '\0'; + } + read_bytes = getline(&status->buffer, + &status->buffer_size, status->read); + if (errno == EAGAIN) { + clearerr(status->read); + return true; + } else if (errno) { + status_error(status, "[error reading from status command]"); + return true; + } + } + default: + return false; } return false; } struct status_line *status_line_init(char *cmd) { struct status_line *status = calloc(1, sizeof(struct status_line)); - status->text_state.buffer_size = 8192; - status->text_state.buffer = malloc(status->text_state.buffer_size); + status->buffer_size = 8192; + status->buffer = malloc(status->buffer_size); int pipe_read_fd[2]; int pipe_write_fd[2]; @@ -148,7 +156,7 @@ void status_line_free(struct status_line *status) { break; } default: - free(status->text_state.buffer); + free(status->buffer); break; } free(status); From 8cbce77e1deb811754a6175388e5ef89f274ff05 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Mon, 17 Sep 2018 14:00:44 +0100 Subject: [PATCH 07/14] swaybar: rewrite protocol determination This now uses the getline function to receive the header, replacing read_line_buffer, which has been deleted since it is otherwise unused. Furthermore, once the protocol has been determined, the current status is handled immediately to be shown (though this has not been added for the i3bar protocol since it has not yet been rewritten to handle this). --- common/readline.c | 25 ----------------- swaybar/status_line.c | 64 ++++++++++++++++++++++--------------------- 2 files changed, 33 insertions(+), 56 deletions(-) diff --git a/common/readline.c b/common/readline.c index a2c69018..58652429 100644 --- a/common/readline.c +++ b/common/readline.c @@ -70,28 +70,3 @@ char *peek_line(FILE *file, int line_offset, long *position) { fseek(file, pos, SEEK_SET); return line; } - -char *read_line_buffer(FILE *file, char *string, size_t string_len) { - size_t length = 0; - if (!string) { - return NULL; - } - while (1) { - int c = getc(file); - if (c == EOF || c == '\n' || c == '\0') { - break; - } - if (c == '\r') { - continue; - } - string[length++] = c; - if (string_len <= length) { - return NULL; - } - } - if (length + 1 == string_len) { - return NULL; - } - string[length] = '\0'; - return string; -} diff --git a/swaybar/status_line.c b/swaybar/status_line.c index 54a68b40..fcc0cb93 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -31,7 +31,6 @@ void status_error(struct status_line *status, const char *text) { bool status_handle_readable(struct status_line *status) { ssize_t read_bytes = 1; - char *line; switch (status->protocol) { case PROTOCOL_I3BAR: if (i3bar_handle_readable(status) > 0) { @@ -39,36 +38,37 @@ bool status_handle_readable(struct status_line *status) { } break; case PROTOCOL_UNDEF: - line = read_line_buffer(status->read, - status->buffer, status->buffer_size); - if (!line) { + errno = 0; + read_bytes = getline(&status->buffer, + &status->buffer_size, status->read); + if (errno == EAGAIN) { + clearerr(status->read); + } else if (errno) { status_error(status, "[error reading from status command]"); - return false; + return true; } - if (line[0] == '{') { - json_object *proto = json_tokener_parse(line); - if (proto) { - json_object *version; - if (json_object_object_get_ex(proto, "version", &version) - && json_object_get_int(version) == 1) { - wlr_log(WLR_DEBUG, "Switched to i3bar protocol."); - status->protocol = PROTOCOL_I3BAR; + + // the header must be sent completely the first time round + json_object *header, *version; + if (status->buffer[read_bytes - 1] == '\n' + && (header = json_tokener_parse(status->buffer)) + && json_object_object_get_ex(header, "version", &version) + && json_object_get_int(version) == 1) { + wlr_log(WLR_DEBUG, "Using i3bar protocol."); + status->protocol = PROTOCOL_I3BAR; + + json_object *click_events; + if (json_object_object_get_ex(header, "click_events", &click_events) + && json_object_get_boolean(click_events)) { + wlr_log(WLR_DEBUG, "Enabling click events."); + status->i3bar_state.click_events = true; + if (write(status->write_fd, "[\n", 2) != 2) { + status_error(status, "[failed to write to status command]"); + json_object_put(header); + return true; } - json_object *click_events; - if (json_object_object_get_ex( - proto, "click_events", &click_events) - && json_object_get_boolean(click_events)) { - wlr_log(WLR_DEBUG, "Enabled click events."); - status->i3bar_state.click_events = true; - const char *events_array = "[\n"; - ssize_t len = strlen(events_array); - if (write(status->write_fd, events_array, len) != len) { - status_error(status, - "[failed to write to status command]"); - } - } - json_object_put(proto); } + json_object_put(header); status->protocol = PROTOCOL_I3BAR; free(status->buffer); @@ -76,11 +76,13 @@ bool status_handle_readable(struct status_line *status) { status->i3bar_state.buffer_size = 4096; status->i3bar_state.buffer = malloc(status->i3bar_state.buffer_size); - } else { - status->protocol = PROTOCOL_TEXT; - status->text = line; + return false; } - return true; + + wlr_log(WLR_DEBUG, "Using text protocol."); + status->protocol = PROTOCOL_TEXT; + status->text = status->buffer; + // intentional fall-through case PROTOCOL_TEXT: errno = 0; while (true) { From 7882ac66ef4308922045fd100e6a9e12942a240b Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Mon, 17 Sep 2018 14:10:57 +0100 Subject: [PATCH 08/14] swaybar: rewrite i3bar protocol handling This now correctly handles an incoming json infinite array by shifting most of the heavy listing to the json-c parser, as well as sending multiple statuses at once. It also removes the struct i3bar_protocol_state and moves its members into the status_line struct, allowing the same buffer to be used for both protocols. --- include/swaybar/status_line.h | 24 ++--- swaybar/i3bar.c | 189 ++++++++++++++++++++-------------- swaybar/render.c | 2 +- swaybar/status_line.c | 31 ++---- 4 files changed, 128 insertions(+), 118 deletions(-) diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h index 857948a5..d3eabdf6 100644 --- a/include/swaybar/status_line.h +++ b/include/swaybar/status_line.h @@ -1,5 +1,6 @@ #ifndef _SWAYBAR_STATUS_LINE_H #define _SWAYBAR_STATUS_LINE_H +#include #include #include #include @@ -12,23 +13,6 @@ enum status_protocol { PROTOCOL_I3BAR, }; -enum json_node_type { - JSON_NODE_UNKNOWN, - JSON_NODE_ARRAY, - JSON_NODE_STRING, -}; - -struct i3bar_protocol_state { - bool click_events; - char *buffer; - size_t buffer_size; - size_t buffer_index; - const char *current_node; - bool escape; - size_t depth; - enum json_node_type nodes[16]; -}; - struct i3bar_block { struct wl_list link; int ref_count; @@ -58,9 +42,13 @@ struct status_line { const char *text; struct wl_list blocks; // i3bar_block::link + bool click_events; char *buffer; size_t buffer_size; - struct i3bar_protocol_state i3bar_state; + size_t buffer_index; + bool started; + bool expecting_comma; + json_tokener *tokener; }; struct status_line *status_line_init(char *cmd); diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index f4ca5504..78cb5bd4 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c @@ -1,6 +1,7 @@ #define _POSIX_C_SOURCE 200809L #include #include +#include #include #include #include @@ -24,24 +25,19 @@ void i3bar_block_unref(struct i3bar_block *block) { } } -static bool i3bar_parse_json(struct status_line *status, const char *text) { +static void i3bar_parse_json(struct status_line *status, + struct json_object *json_array) { struct i3bar_block *block, *tmp; wl_list_for_each_safe(block, tmp, &status->blocks, link) { wl_list_remove(&block->link); i3bar_block_unref(block); } - json_object *results = json_tokener_parse(text); - if (!results) { - status_error(status, "[failed to parse i3bar json]"); - return false; - } - wlr_log(WLR_DEBUG, "Got i3bar json: '%s'", text); - for (size_t i = 0; i < json_object_array_length(results); ++i) { + for (size_t i = 0; i < json_object_array_length(json_array); ++i) { json_object *full_text, *short_text, *color, *min_width, *align, *urgent; json_object *name, *instance, *separator, *separator_block_width; json_object *background, *border, *border_top, *border_bottom; json_object *border_left, *border_right, *markup; - json_object *json = json_object_array_get_idx(results, i); + json_object *json = json_object_array_get_idx(json_array, i); if (!json) { continue; } @@ -110,96 +106,133 @@ static bool i3bar_parse_json(struct status_line *status, const char *text) { json_object_get_int(border_right) : 1; wl_list_insert(&status->blocks, &block->link); } - json_object_put(results); - return true; } bool i3bar_handle_readable(struct status_line *status) { - struct i3bar_protocol_state *state = &status->i3bar_state; + while (!status->started) { // look for opening bracket + for (size_t c = 0; c < status->buffer_index; ++c) { + if (status->buffer[c] == '[') { + status->started = true; + status->buffer_index -= ++c; + memmove(status->buffer, &status->buffer[c], status->buffer_index); + break; + } else if (!isspace(status->buffer[c])) { + status_error(status, "[invalid json]"); + return true; + } + } + if (status->started) { + break; + } - char *cur = &state->buffer[state->buffer_index]; - ssize_t n = read(status->read_fd, cur, - state->buffer_size - state->buffer_index); - if (n == -1) { - status_error(status, "[failed to read from status command]"); - return false; - } - - if (n == (ssize_t)(state->buffer_size - state->buffer_index)) { - state->buffer_size = state->buffer_size * 2; - char *new_buffer = realloc(state->buffer, state->buffer_size); - if (!new_buffer) { - free(state->buffer); - status_error(status, "[failed to allocate buffer]"); + errno = 0; + ssize_t read_bytes = read(status->read_fd, status->buffer, status->buffer_size); + if (read_bytes > -1) { + status->buffer_index = read_bytes; + } else if (errno == EAGAIN) { + return false; + } else { + status_error(status, "[error reading from status command]"); return true; } - state->current_node += new_buffer - state->buffer; - cur += new_buffer - state->buffer; - state->buffer = new_buffer; } - cur[n] = '\0'; - bool redraw = false; - while (*cur) { - if (state->nodes[state->depth] == JSON_NODE_STRING) { - if (!state->escape && *cur == '"') { - --state->depth; + struct json_object *last_object = NULL; + struct json_object *test_object; + size_t buffer_pos = 0; + while (true) { + // since the incoming stream is an infinite array + // parsing is split into two parts + // first, attempt to parse the current object, reading more if the + // parser indicates that the current object is incomplete, and failing + // if the parser fails + // second, look for separating comma, ignoring whitespace, failing if + // any other characters are encountered + if (status->expecting_comma) { + for (; buffer_pos < status->buffer_index; ++buffer_pos) { + if (status->buffer[buffer_pos] == ',') { + status->expecting_comma = false; + ++buffer_pos; + break; + } else if (!isspace(status->buffer[buffer_pos])) { + status_error(status, "[invalid i3bar json]"); + return true; + } } - state->escape = !state->escape && *cur == '\\'; + if (buffer_pos < status->buffer_index) { + continue; // look for new object without reading more input + } + buffer_pos = status->buffer_index = 0; } else { - switch (*cur) { - case '[': - ++state->depth; - if (state->depth > - sizeof(state->nodes) / sizeof(state->nodes[0])) { - status_error(status, "[i3bar json too deep]"); - return false; + test_object = json_tokener_parse_ex(status->tokener, + &status->buffer[buffer_pos], status->buffer_index - buffer_pos); + if (json_tokener_get_error(status->tokener) == json_tokener_success) { + if (json_object_get_type(test_object) == json_type_array) { + if (last_object) { + json_object_put(last_object); + } + last_object = test_object; + } else { + json_object_put(test_object); } - state->nodes[state->depth] = JSON_NODE_ARRAY; - if (state->depth == 1) { - state->current_node = cur; + + buffer_pos += status->tokener->char_offset; + status->expecting_comma = true; + + if (buffer_pos < status->buffer_index) { + continue; // look for comma without reading more input } - break; - case ']': - if (state->nodes[state->depth] != JSON_NODE_ARRAY) { - status_error(status, "[failed to parse i3bar json]"); - return false; + buffer_pos = status->buffer_index = 0; + } else if (json_tokener_get_error(status->tokener) == json_tokener_continue) { + if (status->buffer_index < status->buffer_size) { + // move the object to the start of the buffer + status->buffer_index -= buffer_pos; + memmove(status->buffer, &status->buffer[buffer_pos], + status->buffer_index); + } else { + // expand buffer + status->buffer_size *= 2; + char *new_buffer = realloc(status->buffer, status->buffer_size); + if (new_buffer) { + status->buffer = new_buffer; + } else { + free(status->buffer); + status_error(status, "[failed to allocate buffer]"); + return true; + } } - --state->depth; - if (state->depth == 0) { - // cur[1] is valid since cur[0] != '\0' - char p = cur[1]; - cur[1] = '\0'; - redraw = i3bar_parse_json( - status, state->current_node) || redraw; - cur[1] = p; - memmove(state->buffer, cur, - state->buffer_size - (cur - state->buffer)); - cur = state->buffer; - state->current_node = cur + 1; - } - break; - case '"': - ++state->depth; - if (state->depth > - sizeof(state->nodes) / sizeof(state->nodes[0])) { - status_error(status, "[i3bar json too deep]"); - return false; - } - state->nodes[state->depth] = JSON_NODE_STRING; - break; + } else { + status_error(status, "[failed to parse i3bar json]"); + return true; } } - ++cur; + + errno = 0; + ssize_t read_bytes = read(status->read_fd, &status->buffer[status->buffer_index], + status->buffer_size - status->buffer_index); + if (read_bytes > -1) { + status->buffer_index += read_bytes; + } else if (errno == EAGAIN) { + break; + } else { + status_error(status, "[error reading from status command]"); + return true; + } + } + + if (last_object) { + i3bar_parse_json(status, last_object); + json_object_put(last_object); + return true; + } else { + return false; } - state->buffer_index = cur - state->buffer; - return redraw; } enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, struct i3bar_block *block, int x, int y, enum x11_button button) { wlr_log(WLR_DEBUG, "block %s clicked", block->name ? block->name : "(nil)"); - if (!block->name || !status->i3bar_state.click_events) { + if (!block->name || !status->click_events) { return HOTSPOT_PROCESS; } diff --git a/swaybar/render.c b/swaybar/render.c index b2c1c710..97690338 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -177,7 +177,7 @@ static uint32_t render_status_block(cairo_t *cairo, *x -= margin; } - if (output->bar->status->i3bar_state.click_events) { + if (output->bar->status->click_events) { struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); hotspot->x = *x; hotspot->y = 0; diff --git a/swaybar/status_line.c b/swaybar/status_line.c index fcc0cb93..01ed70d9 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -32,11 +32,6 @@ void status_error(struct status_line *status, const char *text) { bool status_handle_readable(struct status_line *status) { ssize_t read_bytes = 1; switch (status->protocol) { - case PROTOCOL_I3BAR: - if (i3bar_handle_readable(status) > 0) { - return true; - } - break; case PROTOCOL_UNDEF: errno = 0; read_bytes = getline(&status->buffer, @@ -61,7 +56,7 @@ bool status_handle_readable(struct status_line *status) { if (json_object_object_get_ex(header, "click_events", &click_events) && json_object_get_boolean(click_events)) { wlr_log(WLR_DEBUG, "Enabling click events."); - status->i3bar_state.click_events = true; + status->click_events = true; if (write(status->write_fd, "[\n", 2) != 2) { status_error(status, "[failed to write to status command]"); json_object_put(header); @@ -70,13 +65,11 @@ bool status_handle_readable(struct status_line *status) { } json_object_put(header); - status->protocol = PROTOCOL_I3BAR; - free(status->buffer); wl_list_init(&status->blocks); - status->i3bar_state.buffer_size = 4096; - status->i3bar_state.buffer = - malloc(status->i3bar_state.buffer_size); - return false; + status->tokener = json_tokener_new(); + status->buffer_index = getdelim(&status->buffer, + &status->buffer_size, EOF, status->read); + return i3bar_handle_readable(status); } wlr_log(WLR_DEBUG, "Using text protocol."); @@ -99,10 +92,11 @@ bool status_handle_readable(struct status_line *status) { return true; } } + case PROTOCOL_I3BAR: + return i3bar_handle_readable(status); default: return false; } - return false; } struct status_line *status_line_init(char *cmd) { @@ -147,19 +141,14 @@ struct status_line *status_line_init(char *cmd) { void status_line_free(struct status_line *status) { status_line_close_fds(status); kill(status->pid, SIGTERM); - switch (status->protocol) { - case PROTOCOL_I3BAR: { + if (status->protocol == PROTOCOL_I3BAR) { struct i3bar_block *block, *tmp; wl_list_for_each_safe(block, tmp, &status->blocks, link) { wl_list_remove(&block->link); i3bar_block_unref(block); } - free(status->i3bar_state.buffer); - break; - } - default: - free(status->buffer); - break; } + json_tokener_free(status->tokener); + free(status->buffer); free(status); } From 5912325d5ca63a552bb1f8e5de0b088e9bfa83e4 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Mon, 17 Sep 2018 14:21:14 +0100 Subject: [PATCH 09/14] swaybar: add debugging statements for handling i3bar json --- swaybar/i3bar.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index 78cb5bd4..88404703 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c @@ -176,6 +176,21 @@ bool i3bar_handle_readable(struct status_line *status) { json_object_put(test_object); } + // in order to print the json for debugging purposes + // the last character is temporarily replaced with a null character + // (the last character is used in case the buffer is full) + char *last_char_pos = + &status->buffer[buffer_pos + status->tokener->char_offset - 1]; + char last_char = *last_char_pos; + while (isspace(last_char)) { + last_char = *--last_char_pos; + } + *last_char_pos = '\0'; + size_t offset = strspn(&status->buffer[buffer_pos], " \f\n\r\t\v"); + wlr_log(WLR_DEBUG, "Received i3bar json: '%s%c'", + &status->buffer[buffer_pos + offset], last_char); + *last_char_pos = last_char; + buffer_pos += status->tokener->char_offset; status->expecting_comma = true; @@ -221,6 +236,7 @@ bool i3bar_handle_readable(struct status_line *status) { } if (last_object) { + wlr_log(WLR_DEBUG, "Rendering last received json"); i3bar_parse_json(status, last_object); json_object_put(last_object); return true; From 47a66da5de37faccac1207615ce0914b4ab32220 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Tue, 18 Sep 2018 18:06:19 +0100 Subject: [PATCH 10/14] swaybar: only free tokener when using i3bar protocol --- swaybar/status_line.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swaybar/status_line.c b/swaybar/status_line.c index 01ed70d9..401bf6f6 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -147,8 +147,8 @@ void status_line_free(struct status_line *status) { wl_list_remove(&block->link); i3bar_block_unref(block); } + json_tokener_free(status->tokener); } - json_tokener_free(status->tokener); free(status->buffer); free(status); } From db2845963408dd0d66a3587469c6f26bfa4d9e1f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 19 Sep 2018 21:54:27 +1000 Subject: [PATCH 11/14] Introduce create_output command (for developer use) Should help with testing hotplugging. --- include/sway/commands.h | 1 + sway/commands.c | 1 + sway/commands/create_output.c | 39 +++++++++++++++++++++++++++++++++++ sway/meson.build | 1 + 4 files changed, 42 insertions(+) create mode 100644 sway/commands/create_output.c diff --git a/include/sway/commands.h b/include/sway/commands.h index e51b12fd..226cf932 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -103,6 +103,7 @@ sway_cmd cmd_client_urgent; sway_cmd cmd_client_placeholder; sway_cmd cmd_client_background; sway_cmd cmd_commands; +sway_cmd cmd_create_output; sway_cmd cmd_debuglog; sway_cmd cmd_default_border; sway_cmd cmd_default_floating_border; diff --git a/sway/commands.c b/sway/commands.c index 41e1c653..27a88319 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -143,6 +143,7 @@ static struct cmd_handler config_handlers[] = { /* Runtime-only commands. Keep alphabetized */ static struct cmd_handler command_handlers[] = { { "border", cmd_border }, + { "create_output", cmd_create_output }, { "exit", cmd_exit }, { "floating", cmd_floating }, { "fullscreen", cmd_fullscreen }, diff --git a/sway/commands/create_output.c b/sway/commands/create_output.c new file mode 100644 index 00000000..a852c2a0 --- /dev/null +++ b/sway/commands/create_output.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include "sway/commands.h" +#include "sway/server.h" +#include "log.h" + +static void create_output(struct wlr_backend *backend, void *data) { + bool *done = data; + if (*done) { + return; + } + + if (wlr_backend_is_wl(backend)) { + wlr_wl_output_create(backend); + *done = true; + } else if (wlr_backend_is_x11(backend)) { + wlr_x11_output_create(backend); + *done = true; + } +} + +/** + * This command is intended for developer use only. + */ +struct cmd_results *cmd_create_output(int argc, char **argv) { + sway_assert(wlr_backend_is_multi(server.backend), + "Expected a multi backend"); + + bool done = false; + wlr_multi_for_each_backend(server.backend, create_output, &done); + + if (!done) { + return cmd_results_new(CMD_INVALID, "create_output", + "Can only create outputs for Wayland or X11 backends"); + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/meson.build b/sway/meson.build index 01c83a33..d67a4c64 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -35,6 +35,7 @@ sway_sources = files( 'commands/bind.c', 'commands/border.c', 'commands/client.c', + 'commands/create_output.c', 'commands/default_border.c', 'commands/default_floating_border.c', 'commands/default_orientation.c', From 8e8a5ca217871e3b97c3aaa6779c2111142c9de8 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Wed, 19 Sep 2018 12:50:19 +0100 Subject: [PATCH 12/14] config: free strings fields when freeing input config --- sway/config/input.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sway/config/input.c b/sway/config/input.c index 9885e85c..ad5b96c8 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -140,6 +140,13 @@ void free_input_config(struct input_config *ic) { return; } free(ic->identifier); + free(ic->xkb_layout); + free(ic->xkb_model); + free(ic->xkb_options); + free(ic->xkb_rules); + free(ic->xkb_variant); + free(ic->mapped_from_region); + free(ic->mapped_to_output); free(ic); } From efc08ec8880b8078d35f687c7913f787efc85f72 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 19 Sep 2018 15:21:04 +0200 Subject: [PATCH 13/14] Fix segfault when executing command without focus --- sway/commands.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sway/commands.c b/sway/commands.c index 41e1c653..32079492 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -215,18 +215,23 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers, static void set_config_node(struct sway_node *node) { config->handler_context.node = node; + config->handler_context.container = NULL; + config->handler_context.workspace = NULL; + + if (node == NULL) { + return; + } + switch (node->type) { case N_CONTAINER: config->handler_context.container = node->sway_container; config->handler_context.workspace = node->sway_container->workspace; break; case N_WORKSPACE: - config->handler_context.container = NULL; config->handler_context.workspace = node->sway_workspace; break; - default: - config->handler_context.container = NULL; - config->handler_context.workspace = NULL; + case N_ROOT: + case N_OUTPUT: break; } } From 81f3fda6fa9b0789c8b40582a4b85b2e9501c185 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Wed, 19 Sep 2018 22:21:09 +0100 Subject: [PATCH 14/14] ipc: add pid information for views in layout tree --- include/sway/tree/view.h | 2 ++ sway/ipc-json.c | 2 ++ sway/tree/view.c | 1 + 3 files changed, 5 insertions(+) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 439dc1bf..d10251dd 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -61,6 +61,8 @@ struct sway_view { struct sway_container *container; // NULL if unmapped and transactions finished struct wlr_surface *surface; // NULL for unmapped views + pid_t pid; + // Geometry of the view itself (excludes borders) in layout coordinates double x, y; int width, height; diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 52278be2..f054ac9f 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -221,6 +221,8 @@ static const char *describe_container_border(enum sway_container_border border) } static void ipc_json_describe_view(struct sway_container *c, json_object *object) { + json_object_object_add(object, "pid", json_object_new_int(c->view->pid)); + const char *app_id = view_get_app_id(c->view); json_object_object_add(object, "app_id", app_id ? json_object_new_string(app_id) : NULL); diff --git a/sway/tree/view.c b/sway/tree/view.c index e4e1c161..4398f518 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -470,6 +470,7 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { wl_resource_get_client(view->surface->resource); wl_client_get_credentials(client, &pid, NULL, NULL); #endif + view->pid = pid; ws = root_workspace_for_pid(pid); if (ws) { return ws;