From ee85c918317ec6a685a999db46f692c7d13cdf2a Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 31 Mar 2018 13:07:22 -0400 Subject: Demarcate i3bar JSON into individual updates --- swaybar/i3bar.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++ swaybar/meson.build | 1 + swaybar/status_line.c | 62 ++++++++++++++++++++++++++++++------ 3 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 swaybar/i3bar.c (limited to 'swaybar') diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c new file mode 100644 index 00000000..5be348b2 --- /dev/null +++ b/swaybar/i3bar.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include "swaybar/config.h" +#include "swaybar/status_line.h" + +static void i3bar_parse_json(struct status_line *status, const char *text) { + wlr_log(L_DEBUG, "got json: %s", text); +} + +int i3bar_readable(struct status_line *status) { + struct i3bar_protocol_state *state = &status->i3bar_state; + + char *cur = &state->buffer[state->buffer_index]; + ssize_t n = read(status->read_fd, cur, + state->buffer_size - state->buffer_index); + if (n == 0) { + return 0; + } + + 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]"); + return -1; + } + state->buffer = new_buffer; + } + + int handled = 0; + while (*cur) { + if (state->nodes[state->depth] == JSON_NODE_STRING) { + if (!state->escape && *cur == '"') { + --state->depth; + } + state->escape = !state->escape && *cur == '\\'; + } else { + switch (*cur) { + case '[': + ++state->depth; + if (state->depth > + sizeof(state->nodes) / sizeof(state->nodes[0])) { + status_error(status, "[i3bar json too deep]"); + return -1; + } + state->nodes[state->depth] = JSON_NODE_ARRAY; + if (state->depth == 1) { + state->current_node = cur; + } + break; + case ']': + if (state->nodes[state->depth] != JSON_NODE_ARRAY) { + status_error(status, "[failed to parse i3bar json]"); + return -1; + } + --state->depth; + if (state->depth == 0) { + // cur[1] is valid since cur[0] != '\0' + char p = cur[1]; + cur[1] = '\0'; + i3bar_parse_json(status, state->current_node); + cur[1] = p; + memmove(state->buffer, cur, + state->buffer_size - (cur - state->buffer)); + ++handled; + 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 -1; + } + state->nodes[state->depth] = JSON_NODE_STRING; + break; + } + } + ++cur; + } + state->buffer_index = cur - state->buffer; + return handled; +} diff --git a/swaybar/meson.build b/swaybar/meson.build index bf6f6d7a..d65edb11 100644 --- a/swaybar/meson.build +++ b/swaybar/meson.build @@ -3,6 +3,7 @@ executable( 'bar.c', 'config.c', 'event_loop.c', + 'i3bar.c', 'ipc.c', 'main.c', 'render.c', diff --git a/swaybar/status_line.c b/swaybar/status_line.c index 3454f207..c94ac6d4 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE #include +#include #include #include #include @@ -9,35 +10,78 @@ #include "swaybar/status_line.h" #include "readline.h" +void status_error(struct status_line *status, const char *text) { + close(status->read_fd); + close(status->write_fd); + status->protocol = PROTOCOL_ERROR; + status->text = text; +} + bool handle_status_readable(struct status_line *status) { - char *line = read_line_buffer(status->read, - status->buffer, status->buffer_size); + char *line; switch (status->protocol) { + case PROTOCOL_ERROR: + return false; case PROTOCOL_I3BAR: - // TODO + if (i3bar_readable(status) > 0) { + return true; + } break; case PROTOCOL_TEXT: - status->text = line; + 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); if (!line) { + status_error(status, "[error reading from status command]"); return false; } if (line[0] == '{') { - // TODO: JSON + 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(L_DEBUG, "Switched to i3bar protocol."); + status->protocol = PROTOCOL_I3BAR; + } + json_object *click_events; + if (json_object_object_get_ex( + proto, "click_events", &click_events) + && json_object_get_boolean(click_events)) { + wlr_log(L_DEBUG, "Enabled click events."); + status->i3bar_state.click_events = true; + const char *events_array = "[\n"; + write(status->write_fd, events_array, strlen(events_array)); + } + json_object_put(proto); + } + + status->protocol = PROTOCOL_I3BAR; + free(status->text_state.buffer); + status->i3bar_state.buffer_size = 4096; + status->i3bar_state.buffer = + malloc(status->i3bar_state.buffer_size); } else { - status->text = line; status->protocol = PROTOCOL_TEXT; + status->text = line; } - return false; + return true; } return false; } struct status_line *status_line_init(char *cmd) { struct status_line *status = calloc(1, sizeof(struct status_line)); - status->buffer_size = 4096; - status->buffer = malloc(status->buffer_size); + status->text_state.buffer_size = 8192; + status->text_state.buffer = malloc(status->text_state.buffer_size); int pipe_read_fd[2]; int pipe_write_fd[2]; -- cgit v1.2.3-54-g00ecf