Demarcate i3bar JSON into individual updates

This commit is contained in:
Drew DeVault 2018-03-31 13:07:22 -04:00
parent 122b96abed
commit ee85c91831
4 changed files with 189 additions and 11 deletions

View file

@ -7,10 +7,52 @@
enum status_protocol {
PROTOCOL_UNDEF,
PROTOCOL_ERROR,
PROTOCOL_TEXT,
PROTOCOL_I3BAR,
};
struct text_protocol_state {
char *buffer;
size_t buffer_size;
};
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;
char *full_text, *short_text, *align;
bool urgent;
uint32_t color;
int min_width;
char *name, *instance;
bool separator;
int separator_block_width;
bool markup;
// Airblader features
uint32_t background;
uint32_t border;
int border_top;
int border_bottom;
int border_left;
int border_right;
};
struct status_line {
pid_t pid;
int read_fd, write_fd;
@ -18,13 +60,16 @@ struct status_line {
enum status_protocol protocol;
const char *text;
struct wl_list blocks; // i3bar_block::link
char *buffer;
size_t buffer_size;
struct text_protocol_state text_state;
struct i3bar_protocol_state i3bar_state;
};
struct status_line *status_line_init(char *cmd);
void status_line_free(struct status_line *status);
bool handle_status_readable(struct status_line *status);
int i3bar_readable(struct status_line *status);
void status_error(struct status_line *status, const char *text);
#endif

88
swaybar/i3bar.c Normal file
View file

@ -0,0 +1,88 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wlr/util/log.h>
#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;
}

View file

@ -3,6 +3,7 @@ executable(
'bar.c',
'config.c',
'event_loop.c',
'i3bar.c',
'ipc.c',
'main.c',
'render.c',

View file

@ -1,5 +1,6 @@
#define _POSIX_C_SOURCE
#include <fcntl.h>
#include <json-c/json.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@ -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];