|
| 1 | +#define _POSIX_C_SOURCE 200809L |
| 2 | +#include <json-c/json.h> |
| 3 | +#include <stdlib.h> |
| 4 | +#include <string.h> |
| 5 | +#include <unistd.h> |
| 6 | +#include <wlr/util/log.h> |
| 7 | +#include "swaybar/config.h" |
| 8 | +#include "swaybar/status_line.h" |
| 9 | + |
| 10 | +static void i3bar_block_free(struct i3bar_block *block) { |
| 11 | + if (!block) { |
| 12 | + return; |
| 13 | + } |
| 14 | + wl_list_remove(&block->link); |
| 15 | + free(block->full_text); |
| 16 | + free(block->short_text); |
| 17 | + free(block->align); |
| 18 | + free(block->name); |
| 19 | + free(block->instance); |
| 20 | + free(block->color); |
| 21 | +} |
| 22 | + |
| 23 | +static bool i3bar_parse_json(struct status_line *status, const char *text) { |
| 24 | + struct i3bar_block *block, *tmp; |
| 25 | + wl_list_for_each_safe(block, tmp, &status->blocks, link) { |
| 26 | + i3bar_block_free(block); |
| 27 | + } |
| 28 | + json_object *results = json_tokener_parse(text); |
| 29 | + if (!results) { |
| 30 | + status_error(status, "[failed to parse i3bar json]"); |
| 31 | + return false; |
| 32 | + } |
| 33 | + wlr_log(L_DEBUG, "Got i3bar json: '%s'", text); |
| 34 | + for (size_t i = 0; i < json_object_array_length(results); ++i) { |
| 35 | + json_object *full_text, *short_text, *color, *min_width, *align, *urgent; |
| 36 | + json_object *name, *instance, *separator, *separator_block_width; |
| 37 | + json_object *background, *border, *border_top, *border_bottom; |
| 38 | + json_object *border_left, *border_right, *markup; |
| 39 | + json_object *json = json_object_array_get_idx(results, i); |
| 40 | + if (!json) { |
| 41 | + continue; |
| 42 | + } |
| 43 | + json_object_object_get_ex(json, "full_text", &full_text); |
| 44 | + json_object_object_get_ex(json, "short_text", &short_text); |
| 45 | + json_object_object_get_ex(json, "color", &color); |
| 46 | + json_object_object_get_ex(json, "min_width", &min_width); |
| 47 | + json_object_object_get_ex(json, "align", &align); |
| 48 | + json_object_object_get_ex(json, "urgent", &urgent); |
| 49 | + json_object_object_get_ex(json, "name", &name); |
| 50 | + json_object_object_get_ex(json, "instance", &instance); |
| 51 | + json_object_object_get_ex(json, "markup", &markup); |
| 52 | + json_object_object_get_ex(json, "separator", &separator); |
| 53 | + json_object_object_get_ex(json, "separator_block_width", &separator_block_width); |
| 54 | + json_object_object_get_ex(json, "background", &background); |
| 55 | + json_object_object_get_ex(json, "border", &border); |
| 56 | + json_object_object_get_ex(json, "border_top", &border_top); |
| 57 | + json_object_object_get_ex(json, "border_bottom", &border_bottom); |
| 58 | + json_object_object_get_ex(json, "border_left", &border_left); |
| 59 | + json_object_object_get_ex(json, "border_right", &border_right); |
| 60 | + |
| 61 | + struct i3bar_block *block = calloc(1, sizeof(struct i3bar_block)); |
| 62 | + block->full_text = full_text ? |
| 63 | + strdup(json_object_get_string(full_text)) : NULL; |
| 64 | + block->short_text = short_text ? |
| 65 | + strdup(json_object_get_string(short_text)) : NULL; |
| 66 | + if (color) { |
| 67 | + block->color = malloc(sizeof(uint32_t)); |
| 68 | + *block->color = parse_color(json_object_get_string(color)); |
| 69 | + } |
| 70 | + if (min_width) { |
| 71 | + json_type type = json_object_get_type(min_width); |
| 72 | + if (type == json_type_int) { |
| 73 | + block->min_width = json_object_get_int(min_width); |
| 74 | + } else if (type == json_type_string) { |
| 75 | + /* the width will be calculated when rendering */ |
| 76 | + block->min_width = 0; |
| 77 | + } |
| 78 | + } |
| 79 | + block->align = strdup(align ? json_object_get_string(align) : "left"); |
| 80 | + block->urgent = urgent ? json_object_get_int(urgent) : false; |
| 81 | + block->name = name ? strdup(json_object_get_string(name)) : NULL; |
| 82 | + block->instance = instance ? |
| 83 | + strdup(json_object_get_string(instance)) : NULL; |
| 84 | + if (markup) { |
| 85 | + block->markup = false; |
| 86 | + const char *markup_str = json_object_get_string(markup); |
| 87 | + if (strcmp(markup_str, "pango") == 0) { |
| 88 | + block->markup = true; |
| 89 | + } |
| 90 | + } |
| 91 | + block->separator = separator ? json_object_get_int(separator) : true; |
| 92 | + block->separator_block_width = separator_block_width ? |
| 93 | + json_object_get_int(separator_block_width) : 9; |
| 94 | + // Airblader features |
| 95 | + block->background = background ? |
| 96 | + parse_color(json_object_get_string(background)) : 0; |
| 97 | + block->border = border ? |
| 98 | + parse_color(json_object_get_string(border)) : 0; |
| 99 | + block->border_top = border_top ? json_object_get_int(border_top) : 1; |
| 100 | + block->border_bottom = border_bottom ? |
| 101 | + json_object_get_int(border_bottom) : 1; |
| 102 | + block->border_left = border_left ? json_object_get_int(border_left) : 1; |
| 103 | + block->border_right = border_right ? |
| 104 | + json_object_get_int(border_right) : 1; |
| 105 | + wl_list_insert(&status->blocks, &block->link); |
| 106 | + } |
| 107 | + return true; |
| 108 | +} |
| 109 | + |
| 110 | +bool i3bar_handle_readable(struct status_line *status) { |
| 111 | + struct i3bar_protocol_state *state = &status->i3bar_state; |
| 112 | + |
| 113 | + char *cur = &state->buffer[state->buffer_index]; |
| 114 | + ssize_t n = read(status->read_fd, cur, |
| 115 | + state->buffer_size - state->buffer_index); |
| 116 | + if (n == 0) { |
| 117 | + return 0; |
| 118 | + } |
| 119 | + |
| 120 | + if (n == (ssize_t)(state->buffer_size - state->buffer_index)) { |
| 121 | + state->buffer_size = state->buffer_size * 2; |
| 122 | + char *new_buffer = realloc(state->buffer, state->buffer_size); |
| 123 | + if (!new_buffer) { |
| 124 | + free(state->buffer); |
| 125 | + status_error(status, "[failed to allocate buffer]"); |
| 126 | + return -1; |
| 127 | + } |
| 128 | + state->buffer = new_buffer; |
| 129 | + } |
| 130 | + |
| 131 | + bool redraw = false; |
| 132 | + while (*cur) { |
| 133 | + if (state->nodes[state->depth] == JSON_NODE_STRING) { |
| 134 | + if (!state->escape && *cur == '"') { |
| 135 | + --state->depth; |
| 136 | + } |
| 137 | + state->escape = !state->escape && *cur == '\\'; |
| 138 | + } else { |
| 139 | + switch (*cur) { |
| 140 | + case '[': |
| 141 | + ++state->depth; |
| 142 | + if (state->depth > |
| 143 | + sizeof(state->nodes) / sizeof(state->nodes[0])) { |
| 144 | + status_error(status, "[i3bar json too deep]"); |
| 145 | + return false; |
| 146 | + } |
| 147 | + state->nodes[state->depth] = JSON_NODE_ARRAY; |
| 148 | + if (state->depth == 1) { |
| 149 | + state->current_node = cur; |
| 150 | + } |
| 151 | + break; |
| 152 | + case ']': |
| 153 | + if (state->nodes[state->depth] != JSON_NODE_ARRAY) { |
| 154 | + status_error(status, "[failed to parse i3bar json]"); |
| 155 | + return false; |
| 156 | + } |
| 157 | + --state->depth; |
| 158 | + if (state->depth == 0) { |
| 159 | + // cur[1] is valid since cur[0] != '\0' |
| 160 | + char p = cur[1]; |
| 161 | + cur[1] = '\0'; |
| 162 | + redraw = i3bar_parse_json( |
| 163 | + status, state->current_node) || redraw; |
| 164 | + cur[1] = p; |
| 165 | + memmove(state->buffer, cur, |
| 166 | + state->buffer_size - (cur - state->buffer)); |
| 167 | + cur = state->buffer; |
| 168 | + state->current_node = cur + 1; |
| 169 | + } |
| 170 | + break; |
| 171 | + case '"': |
| 172 | + ++state->depth; |
| 173 | + if (state->depth > |
| 174 | + sizeof(state->nodes) / sizeof(state->nodes[0])) { |
| 175 | + status_error(status, "[i3bar json too deep]"); |
| 176 | + return false; |
| 177 | + } |
| 178 | + state->nodes[state->depth] = JSON_NODE_STRING; |
| 179 | + break; |
| 180 | + } |
| 181 | + } |
| 182 | + ++cur; |
| 183 | + } |
| 184 | + state->buffer_index = cur - state->buffer; |
| 185 | + return redraw; |
| 186 | +} |
| 187 | + |
| 188 | +void i3bar_block_send_click(struct status_line *status, |
| 189 | + struct i3bar_block *block, int x, int y, uint32_t button) { |
| 190 | + wlr_log(L_DEBUG, "block %s clicked", block->name ? block->name : "(nil)"); |
| 191 | + if (!block->name || !status->i3bar_state.click_events) { |
| 192 | + return; |
| 193 | + } |
| 194 | + |
| 195 | + struct json_object *event_json = json_object_new_object(); |
| 196 | + json_object_object_add(event_json, "name", |
| 197 | + json_object_new_string(block->name)); |
| 198 | + if (block->instance) { |
| 199 | + json_object_object_add(event_json, "instance", |
| 200 | + json_object_new_string(block->instance)); |
| 201 | + } |
| 202 | + |
| 203 | + json_object_object_add(event_json, "button", json_object_new_int(button)); |
| 204 | + json_object_object_add(event_json, "x", json_object_new_int(x)); |
| 205 | + json_object_object_add(event_json, "y", json_object_new_int(y)); |
| 206 | + dprintf(status->write_fd, "%s\n", json_object_to_json_string(event_json)); |
| 207 | + json_object_put(event_json); |
| 208 | +} |
0 commit comments