aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/swaybar/status_line.h49
-rw-r--r--swaybar/i3bar.c88
-rw-r--r--swaybar/meson.build1
-rw-r--r--swaybar/status_line.c62
4 files changed, 189 insertions, 11 deletions
diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h
index 6c595df0..3b93e28f 100644
--- a/include/swaybar/status_line.h
+++ b/include/swaybar/status_line.h
@@ -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
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 <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;
+}
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 <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];