diff options
| -rw-r--r-- | common/pango.c | 9 | ||||
| -rw-r--r-- | include/swaybar/ipc.h | 2 | ||||
| -rw-r--r-- | include/swaybar/status_line.h | 53 | ||||
| -rw-r--r-- | sway/tree/layout.c | 4 | ||||
| -rw-r--r-- | swaybar/bar.c | 4 | ||||
| -rw-r--r-- | swaybar/i3bar.c | 208 | ||||
| -rw-r--r-- | swaybar/ipc.c | 2 | ||||
| -rw-r--r-- | swaybar/meson.build | 1 | ||||
| -rw-r--r-- | swaybar/render.c | 238 | ||||
| -rw-r--r-- | swaybar/status_line.c | 65 | 
10 files changed, 546 insertions, 40 deletions
| diff --git a/common/pango.c b/common/pango.c index 2ae7883c..658d2876 100644 --- a/common/pango.c +++ b/common/pango.c @@ -6,6 +6,7 @@  #include <stdio.h>  #include <stdlib.h>  #include <string.h> +#include "log.h"  PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,  		const char *text, int32_t scale, bool markup) { @@ -13,7 +14,13 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,  	PangoAttrList *attrs;  	if (markup) {  		char *buf; -		pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, NULL); +		GError *error = NULL; +		if (!sway_assert(pango_parse_markup( +					text, -1, 0, &attrs, &buf, NULL, &error), +				"pango_parse_markup '%s' -> error %s", text, +				error ? error->message : NULL)) { +			return NULL; +		}  		pango_layout_set_markup(layout, buf, -1);  		free(buf);  	} else { diff --git a/include/swaybar/ipc.h b/include/swaybar/ipc.h index 6ea7c4d6..a1696bcf 100644 --- a/include/swaybar/ipc.h +++ b/include/swaybar/ipc.h @@ -4,7 +4,7 @@  #include "swaybar/bar.h"  void ipc_initialize(struct swaybar *bar, const char *bar_id); -bool handle_ipc_event(struct swaybar *bar); +bool handle_ipc_readable(struct swaybar *bar);  void ipc_get_workspaces(struct swaybar *bar);  void ipc_send_workspace_command(struct swaybar *bar, const char *ws); diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h index 6c595df0..3538f49c 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,18 @@ 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_error(struct status_line *status, const char *text); +bool status_handle_readable(struct status_line *status);  void status_line_free(struct status_line *status); -bool handle_status_readable(struct status_line *status); +bool i3bar_handle_readable(struct status_line *status); +void i3bar_block_send_click(struct status_line *status, +		struct i3bar_block *block, int x, int y, uint32_t button);  #endif diff --git a/sway/tree/layout.c b/sway/tree/layout.c index a8941a40..de210752 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -274,8 +274,8 @@ void arrange_windows(struct sway_container *container,  			struct wlr_box *area = &output->sway_output->usable_area;  			wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d",  					area->width, area->height, area->x, area->y); -			container->width = area->width; -			container->height = area->height; +			container->width = width = area->width; +			container->height = height = area->height;  			container->x = x = area->x;  			container->y = y = area->y;  			wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", diff --git a/swaybar/bar.c b/swaybar/bar.c index f743236c..fb417095 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -308,14 +308,14 @@ static void display_in(int fd, short mask, void *_bar) {  static void ipc_in(int fd, short mask, void *_bar) {  	struct swaybar *bar = (struct swaybar *)_bar; -	if (handle_ipc_event(bar)) { +	if (handle_ipc_readable(bar)) {  		render_all_frames(bar);  	}  }  static void status_in(int fd, short mask, void *_bar) {  	struct swaybar *bar = (struct swaybar *)_bar; -	if (handle_status_readable(bar->status)) { +	if (status_handle_readable(bar->status)) {  		render_all_frames(bar);  	}  } diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c new file mode 100644 index 00000000..46459e24 --- /dev/null +++ b/swaybar/i3bar.c @@ -0,0 +1,208 @@ +#define _POSIX_C_SOURCE 200809L +#include <json-c/json.h> +#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_block_free(struct i3bar_block *block) { +	if (!block) { +		return; +	} +	wl_list_remove(&block->link); +	free(block->full_text); +	free(block->short_text); +	free(block->align); +	free(block->name); +	free(block->instance); +	free(block->color); +} + +static bool i3bar_parse_json(struct status_line *status, const char *text) { +	struct i3bar_block *block, *tmp; +	wl_list_for_each_safe(block, tmp, &status->blocks, link) { +		i3bar_block_free(block); +	} +	json_object *results = json_tokener_parse(text); +	if (!results) { +		status_error(status, "[failed to parse i3bar json]"); +		return false; +	} +	wlr_log(L_DEBUG, "Got i3bar json: '%s'", text); +	for (size_t i = 0; i < json_object_array_length(results); ++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); +		if (!json) { +			continue; +		} +		json_object_object_get_ex(json, "full_text", &full_text); +		json_object_object_get_ex(json, "short_text", &short_text); +		json_object_object_get_ex(json, "color", &color); +		json_object_object_get_ex(json, "min_width", &min_width); +		json_object_object_get_ex(json, "align", &align); +		json_object_object_get_ex(json, "urgent", &urgent); +		json_object_object_get_ex(json, "name", &name); +		json_object_object_get_ex(json, "instance", &instance); +		json_object_object_get_ex(json, "markup", &markup); +		json_object_object_get_ex(json, "separator", &separator); +		json_object_object_get_ex(json, "separator_block_width", &separator_block_width); +		json_object_object_get_ex(json, "background", &background); +		json_object_object_get_ex(json, "border", &border); +		json_object_object_get_ex(json, "border_top", &border_top); +		json_object_object_get_ex(json, "border_bottom", &border_bottom); +		json_object_object_get_ex(json, "border_left", &border_left); +		json_object_object_get_ex(json, "border_right", &border_right); + +		struct i3bar_block *block = calloc(1, sizeof(struct i3bar_block)); +		block->full_text = full_text ? +			strdup(json_object_get_string(full_text)) : NULL; +		block->short_text = short_text ? +			strdup(json_object_get_string(short_text)) : NULL; +		if (color) { +			block->color = malloc(sizeof(uint32_t)); +			*block->color = parse_color(json_object_get_string(color)); +		} +		if (min_width) { +			json_type type = json_object_get_type(min_width); +			if (type == json_type_int) { +				block->min_width = json_object_get_int(min_width); +			} else if (type == json_type_string) { +				/* the width will be calculated when rendering */ +				block->min_width = 0; +			} +		} +		block->align = strdup(align ? json_object_get_string(align) : "left"); +		block->urgent = urgent ? json_object_get_int(urgent) : false; +		block->name = name ? strdup(json_object_get_string(name)) : NULL; +		block->instance = instance ? +			strdup(json_object_get_string(instance)) : NULL; +		if (markup) { +			block->markup = false; +			const char *markup_str = json_object_get_string(markup); +			if (strcmp(markup_str, "pango") == 0) { +				block->markup = true; +			} +		} +		block->separator = separator ? json_object_get_int(separator) : true; +		block->separator_block_width = separator_block_width ? +			json_object_get_int(separator_block_width) : 9; +		// Airblader features +		block->background = background ? +			parse_color(json_object_get_string(background)) : 0; +		block->border = border ?  +			parse_color(json_object_get_string(border)) : 0; +		block->border_top = border_top ? json_object_get_int(border_top) : 1; +		block->border_bottom = border_bottom ? +			json_object_get_int(border_bottom) : 1; +		block->border_left = border_left ? json_object_get_int(border_left) : 1; +		block->border_right = border_right ? +			json_object_get_int(border_right) : 1; +		wl_list_insert(&status->blocks, &block->link); +	} +	return true; +} + +bool i3bar_handle_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; +	} + +	bool redraw = false; +	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 false; +				} +				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 false; +				} +				--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; +			} +		} +		++cur; +	} +	state->buffer_index = cur - state->buffer; +	return redraw; +} + +void i3bar_block_send_click(struct status_line *status, +		struct i3bar_block *block, int x, int y, uint32_t button) { +	wlr_log(L_DEBUG, "block %s clicked", block->name ? block->name : "(nil)"); +	if (!block->name || !status->i3bar_state.click_events) { +		return; +	} + +	struct json_object *event_json = json_object_new_object(); +	json_object_object_add(event_json, "name", +			json_object_new_string(block->name)); +	if (block->instance) { +		json_object_object_add(event_json, "instance", +				json_object_new_string(block->instance)); +	} + +	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)); +	dprintf(status->write_fd, "%s\n", json_object_to_json_string(event_json)); +	json_object_put(event_json); +} diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 64583df0..ed5d9a31 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -323,7 +323,7 @@ void ipc_initialize(struct swaybar *bar, const char *bar_id) {  			IPC_SUBSCRIBE, subscribe, &len));  } -bool handle_ipc_event(struct swaybar *bar) { +bool handle_ipc_readable(struct swaybar *bar) {  	struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd);  	if (!resp) {  		return false; 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/render.c b/swaybar/render.c index c2358724..6f3b0788 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -18,10 +18,33 @@ static const int ws_horizontal_padding = 5;  static const double ws_vertical_padding = 1.5;  static const double border_width = 1; +static uint32_t render_status_line_error(cairo_t *cairo, +		struct swaybar_config *config, const char *error, +		double *x, uint32_t width, uint32_t height) { +	if (!error) { +		return 0; +	} +	cairo_set_source_u32(cairo, 0xFF0000FF); +	static const int margin = 3; +	int text_width, text_height; +	get_text_size(cairo, config->font, +			&text_width, &text_height, 1, false, "%s", error); +	uint32_t ideal_height = text_height + ws_vertical_padding * 2; +	if (height < ideal_height) { +		return ideal_height; +	} +	*x -= text_width + margin; +	double text_y = height / 2.0 - text_height / 2.0; +	cairo_move_to(cairo, *x, (int)floor(text_y)); +	pango_printf(cairo, config->font, 1, false, "%s", error); +	*x -= margin; +	return ideal_height; +} +  static uint32_t render_status_line_text(cairo_t *cairo, -		struct swaybar_config *config, struct status_line *status, -		bool focused, uint32_t width, uint32_t height) { -	if (!status->text) { +		struct swaybar_config *config, const char *text, +		bool focused, double *x, uint32_t width, uint32_t height) { +	if (!text) {  		return 0;  	}  	cairo_set_source_u32(cairo, focused ? @@ -29,38 +52,211 @@ static uint32_t render_status_line_text(cairo_t *cairo,  	static const int margin = 3;  	int text_width, text_height;  	get_text_size(cairo, config->font, &text_width, &text_height, -			1, config->pango_markup, "%s", status->text); +			1, config->pango_markup, "%s", text);  	uint32_t ideal_height = text_height + ws_vertical_padding * 2;  	if (height < ideal_height) {  		return ideal_height;  	} +	*x -= text_width + margin;  	double text_y = height / 2.0 - text_height / 2.0; -	cairo_move_to(cairo, width - text_width - margin, (int)floor(text_y)); -	pango_printf(cairo, config->font, 1, config->pango_markup, -			"%s", status->text); +	cairo_move_to(cairo, *x, (int)floor(text_y)); +	pango_printf(cairo, config->font, 1, config->pango_markup, "%s", text); +	*x -= margin; +	return ideal_height; +} + +static void render_sharp_line(cairo_t *cairo, uint32_t color, +		double x, double y, double width, double height) { +	cairo_set_source_u32(cairo, color); +	if (width > 1 && height > 1) { +		cairo_rectangle(cairo, x, y, width, height); +		cairo_fill(cairo); +	} else { +		if (width == 1) { +			x += 0.5; +			height += y; +			width = x; +		} +		if (height == 1) { +			y += 0.5; +			width += x; +			height = y; +		} +		cairo_move_to(cairo, x, y); +		cairo_set_line_width(cairo, 1.0); +		cairo_line_to(cairo, width, height); +		cairo_stroke(cairo); +	} +} + +static void block_hotspot_callback(struct swaybar_output *output, +			int x, int y, uint32_t button, void *data) { +	struct i3bar_block *block = data; +	struct status_line *status = output->bar->status; +	i3bar_block_send_click(status, block, x, y, button); +} + +static uint32_t render_status_block(cairo_t *cairo, +		struct swaybar_config *config, struct swaybar_output *output, +		struct i3bar_block *block, double *x, +		uint32_t height, bool focused, bool edge) { +	static const int margin = 3; +	if (!block->full_text || !*block->full_text) { +		return 0; +	} + +	int text_width, text_height; +	get_text_size(cairo, config->font, &text_width, &text_height, +			1, block->markup, "%s", block->full_text); +	int width = text_width; +	if (width < block->min_width) { +		width = block->min_width; +	} +	double block_width = width; +	uint32_t ideal_height = text_height + ws_vertical_padding * 2; +	if (height < ideal_height) { +		return ideal_height; +	} + +	*x -= width; +	if (block->border && block->border_left > 0) { +		*x -= (block->border_left + margin); +		block_width += block->border_left + margin; +	} +	if (block->border && block->border_right > 0) { +		*x -= (block->border_right + margin); +		block_width += block->border_right + margin; +	} + +	int sep_width; +	if (!edge) { +		if (config->sep_symbol) { +			int _height; +			get_text_size(cairo, config->font, &sep_width, &_height, +					1, false, "%s", config->sep_symbol); +			uint32_t _ideal_height = _height + ws_vertical_padding * 2; +			if (height < _ideal_height) { +				return _height; +			} +			if (sep_width > block->separator_block_width) { +				block->separator_block_width = sep_width + margin * 2; +			} +		} +		*x -= block->separator_block_width; +	} else { +		*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 = NULL; +	hotspot->data = block; +	wl_list_insert(&output->hotspots, &hotspot->link); + +	double pos = *x; +	if (block->background) { +		cairo_set_source_u32(cairo, block->background); +		cairo_rectangle(cairo, pos - 0.5, 1, block_width, height); +		cairo_fill(cairo); +	} + +	if (block->border && block->border_top > 0) { +		render_sharp_line(cairo, block->border, +				pos - 0.5, 1, block_width, block->border_top); +	} +	if (block->border && block->border_bottom > 0) { +		render_sharp_line(cairo, block->border, +				pos - 0.5, height - 1 - block->border_bottom, +				block_width, block->border_bottom); +	} +	if (block->border != 0 && block->border_left > 0) { +		render_sharp_line(cairo, block->border, +				pos - 0.5, 1, block->border_left, height); +		pos += block->border_left + margin; +	} + +	double offset = 0; +	if (strncmp(block->align, "left", 5) == 0) { +		offset = pos; +	} else if (strncmp(block->align, "right", 5) == 0) { +		offset = pos + width - text_width; +	} else if (strncmp(block->align, "center", 6) == 0) { +		offset = pos + (width - text_width) / 2; +	} +	cairo_move_to(cairo, offset, height / 2.0 - text_height / 2.0); +	uint32_t color = block->color ?  *block->color : config->colors.statusline; +	cairo_set_source_u32(cairo, color); +	pango_printf(cairo, config->font, 1, block->markup, "%s", block->full_text); +	pos += width; + +	if (block->border && block->border_right > 0) { +		pos += margin; +		render_sharp_line(cairo, block->border, +				pos - 0.5, 1, block->border_right, height); +		pos += block->border_right; +	} + +	if (!edge && block->separator) { +		if (focused) { +			cairo_set_source_u32(cairo, config->colors.focused_separator); +		} else { +			cairo_set_source_u32(cairo, config->colors.separator); +		} +		if (config->sep_symbol) { +			offset = pos + (block->separator_block_width - sep_width) / 2; +			cairo_move_to(cairo, offset, margin); +			pango_printf(cairo, config->font, 1, false, +					"%s", config->sep_symbol); +		} else { +			cairo_set_line_width(cairo, 1); +			cairo_move_to(cairo, +					pos + block->separator_block_width / 2, margin); +			cairo_line_to(cairo, +					pos + block->separator_block_width / 2, height - margin); +			cairo_stroke(cairo); +		} +	}  	return ideal_height;  }  static uint32_t render_status_line_i3bar(cairo_t *cairo, -		struct swaybar_config *config, struct status_line *status, -		bool focused, uint32_t width, uint32_t height) { -	// TODO -	return 0; +		struct swaybar_config *config, struct swaybar_output *output, +		struct status_line *status, bool focused, +		double *x, uint32_t width, uint32_t height) { +	uint32_t max_height = 0; +	bool edge = true; +	struct i3bar_block *block; +	wl_list_for_each(block, &status->blocks, link) { +		uint32_t h = render_status_block(cairo, config, output, +				block, x, height, focused, edge); +		max_height = h > max_height ? h : max_height; +		edge = false; +	} +	return max_height;  }  static uint32_t render_status_line(cairo_t *cairo, -		struct swaybar_config *config, struct status_line *status, -		bool focused, uint32_t width, uint32_t height) { +		struct swaybar_config *config, struct swaybar_output *output, +		struct status_line *status, bool focused, +		double *x, uint32_t width, uint32_t height) {  	switch (status->protocol) { +	case PROTOCOL_ERROR: +		return render_status_line_error(cairo, +				config, status->text, x, width, height);  	case PROTOCOL_TEXT:  		return render_status_line_text(cairo, -				config, status, focused, width, height); +				config, status->text, focused, x, width, height);  	case PROTOCOL_I3BAR: -		return render_status_line_i3bar(cairo, -				config, status, focused, width, height); -	default: +		return render_status_line_i3bar(cairo, config, output, status, +				focused, x, width, height); +	case PROTOCOL_UNDEF:  		return 0;  	} +	return 0;  }  static uint32_t render_binding_mode_indicator(cairo_t *cairo, @@ -166,8 +362,8 @@ static uint32_t render_workspace_button(cairo_t *cairo,  	struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));  	hotspot->x = *x;  	hotspot->y = 0; -	hotspot->height = height;  	hotspot->width = width; +	hotspot->height = height;  	hotspot->callback = workspace_hotspot_callback;  	hotspot->destroy = free;  	hotspot->data = strdup(name); @@ -180,6 +376,7 @@ static uint32_t render_workspace_button(cairo_t *cairo,  static uint32_t render_to_cairo(cairo_t *cairo,  		struct swaybar *bar, struct swaybar_output *output) {  	struct swaybar_config *config = bar->config; +	wlr_log(L_DEBUG, "output %p", output);  	cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);  	if (output->focused) { @@ -211,9 +408,10 @@ static uint32_t render_to_cairo(cairo_t *cairo,  				cairo, config, config->mode, x, output->height);  		max_height = h > max_height ? h : max_height;  	} +	x = output->width;  	if (bar->status) { -		uint32_t h = render_status_line(cairo, config, bar->status, -				output->focused, output->width, output->height); +		uint32_t h = render_status_line(cairo, config, output, bar->status, +				output->focused, &x, output->width, output->height);  		max_height = h > max_height ? h : max_height;  	} diff --git a/swaybar/status_line.c b/swaybar/status_line.c index 3454f207..cc7e217f 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,79 @@  #include "swaybar/status_line.h"  #include "readline.h" -bool handle_status_readable(struct status_line *status) { -	char *line = read_line_buffer(status->read, -			status->buffer, status->buffer_size); +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 status_handle_readable(struct status_line *status) { +	char *line;  	switch (status->protocol) { +	case PROTOCOL_ERROR: +		return false;  	case PROTOCOL_I3BAR: -		// TODO +		if (i3bar_handle_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); +			wl_list_init(&status->blocks); +			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]; | 
