mirror of
				https://github.com/swaywm/sway.git
				synced 2025-10-29 05:40:18 -04:00 
			
		
		
		
	swaybar: feactor render, statusline
This commit is contained in:
		
							parent
							
								
									bb986cb336
								
							
						
					
					
						commit
						a6349a2444
					
				
					 8 changed files with 968 additions and 840 deletions
				
			
		|  | @ -9,6 +9,9 @@ include_directories( | |||
| add_executable(swaybar | ||||
| 	main.c | ||||
| 	config.c | ||||
| 	render.c | ||||
| 	state.c | ||||
| 	status_line.c | ||||
| ) | ||||
| 
 | ||||
| target_link_libraries(swaybar | ||||
|  |  | |||
							
								
								
									
										930
									
								
								swaybar/main.c
									
										
									
									
									
								
							
							
						
						
									
										930
									
								
								swaybar/main.c
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										311
									
								
								swaybar/render.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										311
									
								
								swaybar/render.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,311 @@ | |||
| #include <stdlib.h> | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include "client/pango.h" | ||||
| #include "client/window.h" | ||||
| #include "config.h" | ||||
| #include "status_line.h" | ||||
| #include "render.h" | ||||
| 
 | ||||
| static void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { | ||||
| 	cairo_set_source_rgba(cairo, | ||||
| 			((color & 0xFF000000) >> 24) / 256.0, | ||||
| 			((color & 0xFF0000) >> 16) / 256.0, | ||||
| 			((color & 0xFF00) >> 8) / 256.0, | ||||
| 			(color & 0xFF) / 256.0); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Renders a sharp line of any width and height. | ||||
|  * | ||||
|  * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0 | ||||
|  * if the line has a width/height of one pixel, respectively. | ||||
|  */ | ||||
| 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 render_block(struct window *window, struct swaybar_config *config, struct status_block *block, double *x, bool edge) { | ||||
| 	int width, height, sep_width; | ||||
| 	get_text_size(window, &width, &height, "%s", block->full_text); | ||||
| 
 | ||||
| 	int textwidth = width; | ||||
| 	double block_width = width; | ||||
| 
 | ||||
| 	if (width < block->min_width) { | ||||
| 		width = block->min_width; | ||||
| 	} | ||||
| 
 | ||||
| 	*x -= width; | ||||
| 
 | ||||
| 	if (block->border != 0 && block->border_left > 0) { | ||||
| 		*x -= (block->border_left + config->margin); | ||||
| 		block_width += block->border_left + config->margin; | ||||
| 	} | ||||
| 
 | ||||
| 	if (block->border != 0 && block->border_right > 0) { | ||||
| 		*x -= (block->border_right + config->margin); | ||||
| 		block_width += block->border_right + config->margin; | ||||
| 	} | ||||
| 
 | ||||
| 	// Add separator
 | ||||
| 	if (!edge) { | ||||
| 		if (config->sep_symbol) { | ||||
| 			get_text_size(window, &sep_width, &height, "%s", config->sep_symbol); | ||||
| 			if (sep_width > block->separator_block_width) { | ||||
| 				block->separator_block_width = sep_width + config->margin * 2; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		*x -= block->separator_block_width; | ||||
| 	} else { | ||||
| 		*x -= config->margin; | ||||
| 	} | ||||
| 
 | ||||
| 	double pos = *x; | ||||
| 
 | ||||
| 	// render background
 | ||||
| 	if (block->background != 0x0) { | ||||
| 		cairo_set_source_u32(window->cairo, block->background); | ||||
| 		cairo_rectangle(window->cairo, pos - 0.5, 1, block_width, window->height - 2); | ||||
| 		cairo_fill(window->cairo); | ||||
| 	} | ||||
| 
 | ||||
| 	// render top border
 | ||||
| 	if (block->border != 0 && block->border_top > 0) { | ||||
| 		render_sharp_line(window->cairo, block->border, | ||||
| 				pos - 0.5, | ||||
| 				1, | ||||
| 				block_width, | ||||
| 				block->border_top); | ||||
| 	} | ||||
| 
 | ||||
| 	// render bottom border
 | ||||
| 	if (block->border != 0 && block->border_bottom > 0) { | ||||
| 		render_sharp_line(window->cairo, block->border, | ||||
| 				pos - 0.5, | ||||
| 				window->height - 1 - block->border_bottom, | ||||
| 				block_width, | ||||
| 				block->border_bottom); | ||||
| 	} | ||||
| 
 | ||||
| 	// render left border
 | ||||
| 	if (block->border != 0 && block->border_left > 0) { | ||||
| 		render_sharp_line(window->cairo, block->border, | ||||
| 				pos - 0.5, | ||||
| 				1, | ||||
| 				block->border_left, | ||||
| 				window->height - 2); | ||||
| 
 | ||||
| 		pos += block->border_left + config->margin; | ||||
| 	} | ||||
| 
 | ||||
| 	// render text
 | ||||
| 	double offset = 0; | ||||
| 
 | ||||
| 	if (strncmp(block->align, "left", 5) == 0) { | ||||
| 		offset = pos; | ||||
| 	} else if (strncmp(block->align, "right", 5) == 0) { | ||||
| 		offset = pos + width - textwidth; | ||||
| 	} else if (strncmp(block->align, "center", 6) == 0) { | ||||
| 		offset = pos + (width - textwidth) / 2; | ||||
| 	} | ||||
| 
 | ||||
| 	cairo_move_to(window->cairo, offset, config->margin); | ||||
| 	cairo_set_source_u32(window->cairo, block->color); | ||||
| 	pango_printf(window, "%s", block->full_text); | ||||
| 
 | ||||
| 	pos += width; | ||||
| 
 | ||||
| 	// render right border
 | ||||
| 	if (block->border != 0 && block->border_right > 0) { | ||||
| 		pos += config->margin; | ||||
| 
 | ||||
| 		render_sharp_line(window->cairo, block->border, | ||||
| 				pos - 0.5, | ||||
| 				1, | ||||
| 				block->border_right, | ||||
| 				window->height - 2); | ||||
| 
 | ||||
| 		pos += block->border_right; | ||||
| 	} | ||||
| 
 | ||||
| 	// render separator
 | ||||
| 	if (!edge && block->separator) { | ||||
| 		cairo_set_source_u32(window->cairo, config->colors.separator); | ||||
| 		if (config->sep_symbol) { | ||||
| 			offset = pos + (block->separator_block_width - sep_width) / 2; | ||||
| 			cairo_move_to(window->cairo, offset, config->margin); | ||||
| 			pango_printf(window, "%s", config->sep_symbol); | ||||
| 		} else { | ||||
| 			cairo_set_line_width(window->cairo, 1); | ||||
| 			cairo_move_to(window->cairo, pos + block->separator_block_width/2, | ||||
| 					config->margin); | ||||
| 			cairo_line_to(window->cairo, pos + block->separator_block_width/2, | ||||
| 					window->height - config->margin); | ||||
| 			cairo_stroke(window->cairo); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static char *handle_workspace_number(bool strip_num, const char *ws_name) { | ||||
| 	bool strip = false; | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (strip_num) { | ||||
| 		int len = strlen(ws_name); | ||||
| 		for (i = 0; i < len; ++i) { | ||||
| 			if (!('0' <= ws_name[i] && ws_name[i] <= '9')) { | ||||
| 				if (':' == ws_name[i] && i < len-1 && i > 0) { | ||||
| 					strip = true; | ||||
| 					++i; | ||||
| 				} | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (strip) { | ||||
| 		return strdup(ws_name + i); | ||||
| 	} | ||||
| 
 | ||||
| 	return strdup(ws_name); | ||||
| } | ||||
| 
 | ||||
| static void render_workspace_button(struct window *window, struct swaybar_config *config, struct workspace *ws, double *x) { | ||||
| 	// strip workspace numbers if required
 | ||||
| 	char *name = handle_workspace_number(config->strip_workspace_numbers, ws->name); | ||||
| 
 | ||||
| 	int width, height; | ||||
| 	get_text_size(window, &width, &height, "%s", name); | ||||
| 	struct box_colors box_colors; | ||||
| 	if (ws->urgent) { | ||||
| 		box_colors = config->colors.urgent_workspace; | ||||
| 	} else if (ws->focused) { | ||||
| 		box_colors = config->colors.focused_workspace; | ||||
| 	} else if (ws->visible) { | ||||
| 		box_colors = config->colors.active_workspace; | ||||
| 	} else { | ||||
| 		box_colors = config->colors.inactive_workspace; | ||||
| 	} | ||||
| 
 | ||||
| 	// background
 | ||||
| 	cairo_set_source_u32(window->cairo, box_colors.background); | ||||
| 	cairo_rectangle(window->cairo, *x, 1.5, width + config->ws_horizontal_padding * 2 - 1, | ||||
| 			height + config->ws_vertical_padding * 2); | ||||
| 	cairo_fill(window->cairo); | ||||
| 
 | ||||
| 	// border
 | ||||
| 	cairo_set_source_u32(window->cairo, box_colors.border); | ||||
| 	cairo_rectangle(window->cairo, *x, 1.5, width + config->ws_horizontal_padding * 2 - 1, | ||||
| 			height + config->ws_vertical_padding * 2); | ||||
| 	cairo_stroke(window->cairo); | ||||
| 
 | ||||
| 	// text
 | ||||
| 	cairo_set_source_u32(window->cairo, box_colors.text); | ||||
| 	cairo_move_to(window->cairo, (int)*x + config->ws_horizontal_padding, config->margin); | ||||
| 	pango_printf(window, "%s", name); | ||||
| 
 | ||||
| 	*x += width + config->ws_horizontal_padding * 2 + config->ws_spacing; | ||||
| 
 | ||||
| 	free(name); | ||||
| } | ||||
| 
 | ||||
| static void render_binding_mode_indicator(struct window *window, struct swaybar_config *config, double pos) { | ||||
| 	int width, height; | ||||
| 	get_text_size(window, &width, &height, "%s", config->mode); | ||||
| 
 | ||||
| 	// background
 | ||||
| 	cairo_set_source_u32(window->cairo, config->colors.binding_mode.background); | ||||
| 	cairo_rectangle(window->cairo, pos, 1.5, width + config->ws_horizontal_padding * 2 - 1, | ||||
| 			height + config->ws_vertical_padding * 2); | ||||
| 	cairo_fill(window->cairo); | ||||
| 
 | ||||
| 	// border
 | ||||
| 	cairo_set_source_u32(window->cairo, config->colors.binding_mode.border); | ||||
| 	cairo_rectangle(window->cairo, pos, 1.5, width + config->ws_horizontal_padding * 2 - 1, | ||||
| 			height + config->ws_vertical_padding * 2); | ||||
| 	cairo_stroke(window->cairo); | ||||
| 
 | ||||
| 	// text
 | ||||
| 	cairo_set_source_u32(window->cairo, config->colors.binding_mode.text); | ||||
| 	cairo_move_to(window->cairo, (int)pos + config->ws_horizontal_padding, config->margin); | ||||
| 	pango_printf(window, "%s", config->mode); | ||||
| } | ||||
| 
 | ||||
| void render(struct output *output, struct swaybar_config *config, struct status_line *line) { | ||||
| 	int i; | ||||
| 
 | ||||
| 	struct window *window = output->window; | ||||
| 	cairo_t *cairo = window->cairo; | ||||
| 
 | ||||
| 	// Clear
 | ||||
| 	cairo_save(cairo); | ||||
| 	cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); | ||||
| 	cairo_paint(cairo); | ||||
| 	cairo_restore(cairo); | ||||
| 
 | ||||
| 	// Background
 | ||||
| 	cairo_set_source_u32(cairo, config->colors.background); | ||||
| 	cairo_paint(cairo); | ||||
| 
 | ||||
| 	// Command output
 | ||||
| 	cairo_set_source_u32(cairo, config->colors.statusline); | ||||
| 	int width, height; | ||||
| 
 | ||||
| 	if (line->protocol == TEXT) { | ||||
| 		get_text_size(window, &width, &height, "%s", line->text_line); | ||||
| 		cairo_move_to(cairo, window->width - config->margin - width, config->margin); | ||||
| 		pango_printf(window, "%s", line); | ||||
| 	} else if (line->protocol == I3BAR && line->block_line) { | ||||
| 		double pos = window->width - 0.5; | ||||
| 		bool edge = true; | ||||
| 		for (i = line->block_line->length - 1; i >= 0; --i) { | ||||
| 			struct status_block *block = line->block_line->items[i]; | ||||
| 			if (block->full_text && block->full_text[0]) { | ||||
| 				render_block(window, config, block, &pos, edge); | ||||
| 				edge = false; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	cairo_set_line_width(cairo, 1.0); | ||||
| 	double x = 0.5; | ||||
| 
 | ||||
| 	// Workspaces
 | ||||
| 	if (config->workspace_buttons) { | ||||
| 		for (i = 0; i < output->workspaces->length; ++i) { | ||||
| 			struct workspace *ws = output->workspaces->items[i]; | ||||
| 			render_workspace_button(window, config, ws, &x); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// binding mode indicator
 | ||||
| 	if (config->mode && config->binding_mode_indicator) { | ||||
| 		render_binding_mode_indicator(window, config, x); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										12
									
								
								swaybar/render.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								swaybar/render.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| #ifndef _SWAYBAR_RENDER_H | ||||
| #define _SWAYBAR_RENDER_H | ||||
| 
 | ||||
| #include "config.h" | ||||
| #include "state.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * Render swaybar. | ||||
|  */ | ||||
| void render(struct output *output, struct swaybar_config *config, struct status_line *line); | ||||
| 
 | ||||
| #endif /* _SWAYBAR_RENDER_H */ | ||||
							
								
								
									
										29
									
								
								swaybar/state.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								swaybar/state.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | |||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include "list.h" | ||||
| #include "config.h" | ||||
| #include "status_line.h" | ||||
| #include "state.h" | ||||
| 
 | ||||
| struct swaybar_state *init_state() { | ||||
| 	struct swaybar_state *state = calloc(1, sizeof(struct swaybar_state)); | ||||
| 	state->config = init_config(); | ||||
| 	state->status = init_status_line(); | ||||
| 	state->output = malloc(sizeof(struct output)); | ||||
| 	state->output->window = NULL; | ||||
| 	state->output->registry = NULL; | ||||
| 	state->output->workspaces = create_list(); | ||||
| 
 | ||||
| 	return state; | ||||
| } | ||||
| 
 | ||||
| void free_workspace(void *item) { | ||||
| 	if (!item) { | ||||
| 		return; | ||||
| 	} | ||||
| 	struct workspace *ws = (struct workspace *)item; | ||||
| 	if (ws->name) { | ||||
| 		free(ws->name); | ||||
| 	} | ||||
| 	free(ws); | ||||
| } | ||||
							
								
								
									
										43
									
								
								swaybar/state.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								swaybar/state.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | |||
| #ifndef _SWAYBAR_STATE_H | ||||
| #define _SWAYBAR_STATE_H | ||||
| 
 | ||||
| #include "client/registry.h" | ||||
| #include "client/window.h" | ||||
| 
 | ||||
| struct swaybar_state { | ||||
| 	struct swaybar_config *config; | ||||
| 	struct status_line *status; | ||||
| 	struct output *output; | ||||
| 	/* list_t *outputs; */ | ||||
| 
 | ||||
| 	int ipc_event_socketfd; | ||||
| 	int ipc_socketfd; | ||||
| 	int status_read_fd; | ||||
| 	pid_t status_command_pid; | ||||
| }; | ||||
| 
 | ||||
| struct output { | ||||
| 	struct window *window; | ||||
| 	struct registry *registry; | ||||
| 	list_t *workspaces; | ||||
| }; | ||||
| 
 | ||||
| struct workspace { | ||||
| 	int num; | ||||
| 	char *name; | ||||
| 	bool focused; | ||||
| 	bool visible; | ||||
| 	bool urgent; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Initialize state. | ||||
|  */ | ||||
| struct swaybar_state *init_state(); | ||||
| 
 | ||||
| /**
 | ||||
|  * free workspace struct. | ||||
|  */ | ||||
| void free_workspace(void *item); | ||||
| 
 | ||||
| #endif /* _SWAYBAR_STATE_H */ | ||||
							
								
								
									
										435
									
								
								swaybar/status_line.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										435
									
								
								swaybar/status_line.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,435 @@ | |||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
| #include <json-c/json.h> | ||||
| 
 | ||||
| #include "log.h" | ||||
| #include "config.h" | ||||
| #include "status_line.h" | ||||
| 
 | ||||
| #define I3JSON_MAXDEPTH 4 | ||||
| #define I3JSON_UNKNOWN 0 | ||||
| #define I3JSON_ARRAY 1 | ||||
| #define I3JSON_STRING 2 | ||||
| 
 | ||||
| struct { | ||||
| 	int bufsize; | ||||
| 	char *buffer; | ||||
| 	char *line_start; | ||||
| 	char *parserpos; | ||||
| 	bool escape; | ||||
| 	int depth; | ||||
| 	int state[I3JSON_MAXDEPTH+1]; | ||||
| } i3json_state = { 0, NULL, NULL, NULL, false, 0, { I3JSON_UNKNOWN } }; | ||||
| 
 | ||||
| static char line[1024]; | ||||
| static char line_rest[1024]; | ||||
| 
 | ||||
| static void free_status_block(void *item) { | ||||
| 	if (!item) { | ||||
| 		return; | ||||
| 	} | ||||
| 	struct status_block *sb = (struct status_block*)item; | ||||
| 	if (sb->full_text) { | ||||
| 		free(sb->full_text); | ||||
| 	} | ||||
| 	if (sb->short_text) { | ||||
| 		free(sb->short_text); | ||||
| 	} | ||||
| 	if (sb->align) { | ||||
| 		free(sb->align); | ||||
| 	} | ||||
| 	if (sb->name) { | ||||
| 		free(sb->name); | ||||
| 	} | ||||
| 	if (sb->instance) { | ||||
| 		free(sb->instance); | ||||
| 	} | ||||
| 	free(sb); | ||||
| } | ||||
| 
 | ||||
| static void parse_json(struct swaybar_state *st, const char *text) { | ||||
| 	json_object *results = json_tokener_parse(text); | ||||
| 	if (!results) { | ||||
| 		sway_log(L_DEBUG, "Failed to parse json"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (json_object_array_length(results) < 1) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (st->status->block_line) { | ||||
| 		list_foreach(st->status->block_line, free_status_block); | ||||
| 		list_free(st->status->block_line); | ||||
| 	} | ||||
| 
 | ||||
| 	st->status->block_line = create_list(); | ||||
| 
 | ||||
| 	int i; | ||||
| 	for (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; | ||||
| 
 | ||||
| 		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, "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 status_block *new = calloc(1, sizeof(struct status_block)); | ||||
| 
 | ||||
| 		if (full_text) { | ||||
| 			new->full_text = strdup(json_object_get_string(full_text)); | ||||
| 		} | ||||
| 
 | ||||
| 		if (short_text) { | ||||
| 			new->short_text = strdup(json_object_get_string(short_text)); | ||||
| 		} | ||||
| 
 | ||||
| 		if (color) { | ||||
| 			new->color = parse_color(json_object_get_string(color)); | ||||
| 		} else { | ||||
| 			new->color = st->config->colors.statusline; | ||||
| 		} | ||||
| 
 | ||||
| 		if (min_width) { | ||||
| 			json_type type = json_object_get_type(min_width); | ||||
| 			if (type == json_type_int) { | ||||
| 				new->min_width = json_object_get_int(min_width); | ||||
| 			} else if (type == json_type_string) { | ||||
| 				/* the width will be calculated when rendering */ | ||||
| 				new->min_width = 0; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (align) { | ||||
| 			new->align = strdup(json_object_get_string(align)); | ||||
| 		} else { | ||||
| 			new->align = strdup("left"); | ||||
| 		} | ||||
| 
 | ||||
| 		if (urgent) { | ||||
| 			new->urgent = json_object_get_int(urgent); | ||||
| 		} | ||||
| 
 | ||||
| 		if (name) { | ||||
| 			new->name = strdup(json_object_get_string(name)); | ||||
| 		} | ||||
| 
 | ||||
| 		if (instance) { | ||||
| 			new->instance = strdup(json_object_get_string(instance)); | ||||
| 		} | ||||
| 
 | ||||
| 		if (separator) { | ||||
| 			new->separator = json_object_get_int(separator); | ||||
| 		} else { | ||||
| 			new->separator = true; // i3bar spec
 | ||||
| 		} | ||||
| 
 | ||||
| 		if (separator_block_width) { | ||||
| 			new->separator_block_width = json_object_get_int(separator_block_width); | ||||
| 		} else { | ||||
| 			new->separator_block_width = 9; // i3bar spec
 | ||||
| 		} | ||||
| 
 | ||||
| 		// Airblader features
 | ||||
| 		if (background) { | ||||
| 			new->background = parse_color(json_object_get_string(background)); | ||||
| 		} else { | ||||
| 			new->background = 0x0; // transparent
 | ||||
| 		} | ||||
| 
 | ||||
| 		if (border) { | ||||
| 			new->border = parse_color(json_object_get_string(border)); | ||||
| 		} else { | ||||
| 			new->border = 0x0; // transparent
 | ||||
| 		} | ||||
| 
 | ||||
| 		if (border_top) { | ||||
| 			new->border_top = json_object_get_int(border_top); | ||||
| 		} else { | ||||
| 			new->border_top = 1; | ||||
| 		} | ||||
| 
 | ||||
| 		if (border_bottom) { | ||||
| 			new->border_bottom = json_object_get_int(border_bottom); | ||||
| 		} else { | ||||
| 			new->border_bottom = 1; | ||||
| 		} | ||||
| 
 | ||||
| 		if (border_left) { | ||||
| 			new->border_left = json_object_get_int(border_left); | ||||
| 		} else { | ||||
| 			new->border_left = 1; | ||||
| 		} | ||||
| 
 | ||||
| 		if (border_right) { | ||||
| 			new->border_right = json_object_get_int(border_right); | ||||
| 		} else { | ||||
| 			new->border_right = 1; | ||||
| 		} | ||||
| 
 | ||||
| 		list_add(st->status->block_line, new); | ||||
| 	} | ||||
| 
 | ||||
| 	json_object_put(results); | ||||
| } | ||||
| 
 | ||||
| // continue parsing from last parserpos
 | ||||
| static int i3json_parse(struct swaybar_state *st) { | ||||
| 	char *c = i3json_state.parserpos; | ||||
| 	int handled = 0; | ||||
| 	while (*c) { | ||||
| 		if (i3json_state.state[i3json_state.depth] == I3JSON_STRING) { | ||||
| 			if (!i3json_state.escape && *c == '"') { | ||||
| 				--i3json_state.depth; | ||||
| 			} | ||||
| 			i3json_state.escape = !i3json_state.escape && *c == '\\'; | ||||
| 		} else { | ||||
| 			switch (*c) { | ||||
| 			case '[': | ||||
| 				++i3json_state.depth; | ||||
| 				if (i3json_state.depth > I3JSON_MAXDEPTH) { | ||||
| 					sway_abort("JSON too deep"); | ||||
| 				} | ||||
| 				i3json_state.state[i3json_state.depth] = I3JSON_ARRAY; | ||||
| 				if (i3json_state.depth == 2) { | ||||
| 					i3json_state.line_start = c; | ||||
| 				} | ||||
| 				break; | ||||
| 			case ']': | ||||
| 				if (i3json_state.state[i3json_state.depth] != I3JSON_ARRAY) { | ||||
| 					sway_abort("JSON malformed"); | ||||
| 				} | ||||
| 				--i3json_state.depth; | ||||
| 				if (i3json_state.depth == 1) { | ||||
| 					// c[1] is valid since c[0] != '\0'
 | ||||
| 					char p = c[1]; | ||||
| 					c[1] = '\0'; | ||||
| 					parse_json(st, i3json_state.line_start); | ||||
| 					c[1] = p; | ||||
| 					++handled; | ||||
| 					i3json_state.line_start = c+1; | ||||
| 				} | ||||
| 				break; | ||||
| 			case '"': | ||||
| 				++i3json_state.depth; | ||||
| 				if (i3json_state.depth > I3JSON_MAXDEPTH) { | ||||
| 					sway_abort("JSON too deep"); | ||||
| 				} | ||||
| 				i3json_state.state[i3json_state.depth] = I3JSON_STRING; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		++c; | ||||
| 	} | ||||
| 	i3json_state.parserpos = c; | ||||
| 	return handled; | ||||
| } | ||||
| 
 | ||||
| // Read line from file descriptor, only show the line tail if it is too long.
 | ||||
| // In non-blocking mode treat "no more data" as a linebreak.
 | ||||
| // If data after a line break has been read, return it in rest.
 | ||||
| // If rest is non-empty, then use that as the start of the next line.
 | ||||
| static int read_line_tail(int fd, char *buf, int nbyte, char *rest) { | ||||
| 	if (fd < 0 || !buf || !nbyte) { | ||||
| 		return -1; | ||||
| 	} | ||||
| 	int l; | ||||
| 	char *buffer = malloc(nbyte*2+1); | ||||
| 	char *readpos = buffer; | ||||
| 	char *lf; | ||||
| 	// prepend old data to new line if necessary
 | ||||
| 	if (rest) { | ||||
| 		l = strlen(rest); | ||||
| 		if (l > nbyte) { | ||||
| 			strcpy(buffer, rest + l - nbyte); | ||||
| 			readpos += nbyte; | ||||
| 		} else if (l) { | ||||
| 			strcpy(buffer, rest); | ||||
| 			readpos += l; | ||||
| 		} | ||||
| 	} | ||||
| 	// read until a linefeed is found or no more data is available
 | ||||
| 	while ((l = read(fd, readpos, nbyte)) > 0) { | ||||
| 		readpos[l] = '\0'; | ||||
| 		lf = strchr(readpos, '\n'); | ||||
| 		if (lf) { | ||||
| 			// linefeed found, replace with \0
 | ||||
| 			*lf = '\0'; | ||||
| 			// give data from the end of the line, try to fill the buffer
 | ||||
| 			if (lf-buffer > nbyte) { | ||||
| 				strcpy(buf, lf - nbyte + 1); | ||||
| 			} else { | ||||
| 				strcpy(buf, buffer); | ||||
| 			} | ||||
| 			// we may have read data from the next line, save it to rest
 | ||||
| 			if (rest) { | ||||
| 				rest[0] = '\0'; | ||||
| 				strcpy(rest, lf + 1); | ||||
| 			} | ||||
| 			free(buffer); | ||||
| 			return strlen(buf); | ||||
| 		} else { | ||||
| 			// no linefeed found, slide data back.
 | ||||
| 			int overflow = readpos - buffer + l - nbyte; | ||||
| 			if (overflow > 0) { | ||||
| 				memmove(buffer, buffer + overflow , nbyte + 1); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if (l < 0) { | ||||
| 		free(buffer); | ||||
| 		return l; | ||||
| 	} | ||||
| 	readpos[l]='\0'; | ||||
| 	if (rest) { | ||||
| 		rest[0] = '\0'; | ||||
| 	} | ||||
| 	if (nbyte < readpos - buffer + l - 1) { | ||||
| 		memcpy(buf, readpos - nbyte + l + 1, nbyte); | ||||
| 	} else { | ||||
| 		strncpy(buf, buffer, nbyte); | ||||
| 	} | ||||
| 	buf[nbyte-1] = '\0'; | ||||
| 	free(buffer); | ||||
| 	return strlen(buf); | ||||
| } | ||||
| 
 | ||||
| // make sure that enough buffer space is available starting from parserpos
 | ||||
| static void i3json_ensure_free(int min_free) { | ||||
| 	int _step = 10240; | ||||
| 	int r = min_free % _step; | ||||
| 	if (r) { | ||||
| 		min_free += _step - r; | ||||
| 	} | ||||
| 	if (!i3json_state.buffer) { | ||||
| 		i3json_state.buffer = malloc(min_free); | ||||
| 		i3json_state.bufsize = min_free; | ||||
| 		i3json_state.parserpos = i3json_state.buffer; | ||||
| 	} else { | ||||
| 		int len = 0; | ||||
| 		int pos = 0; | ||||
| 		if (i3json_state.line_start) { | ||||
| 			len = strlen(i3json_state.line_start); | ||||
| 			pos = i3json_state.parserpos - i3json_state.line_start; | ||||
| 			if (i3json_state.line_start != i3json_state.buffer) { | ||||
| 				memmove(i3json_state.buffer, i3json_state.line_start, len+1); | ||||
| 			} | ||||
| 		} else { | ||||
| 			len = strlen(i3json_state.buffer); | ||||
| 		} | ||||
| 		if (i3json_state.bufsize < len+min_free) { | ||||
| 			i3json_state.bufsize += min_free; | ||||
| 			if (i3json_state.bufsize > 1024000) { | ||||
| 				sway_abort("Status line json too long or malformed."); | ||||
| 			} | ||||
| 			i3json_state.buffer = realloc(i3json_state.buffer, i3json_state.bufsize); | ||||
| 			if (!i3json_state.buffer) { | ||||
| 				sway_abort("Could not allocate json buffer"); | ||||
| 			} | ||||
| 		} | ||||
| 		if (i3json_state.line_start) { | ||||
| 			i3json_state.line_start = i3json_state.buffer; | ||||
| 			i3json_state.parserpos = i3json_state.buffer + pos; | ||||
| 		} else { | ||||
| 			i3json_state.parserpos = i3json_state.buffer; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!i3json_state.buffer) { | ||||
| 		sway_abort("Could not allocate buffer."); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // append data and parse it.
 | ||||
| static int i3json_handle_data(struct swaybar_state *st, char *data) { | ||||
| 	int len = strlen(data); | ||||
| 	i3json_ensure_free(len); | ||||
| 	strcpy(i3json_state.parserpos, data); | ||||
| 	return i3json_parse(st); | ||||
| } | ||||
| 
 | ||||
| // read data from fd and parse it.
 | ||||
| static int i3json_handle_fd(struct swaybar_state *st) { | ||||
| 	i3json_ensure_free(10240); | ||||
| 	// get fresh data at the end of the buffer
 | ||||
| 	int readlen = read(st->status_read_fd, i3json_state.parserpos, 10239); | ||||
| 	if (readlen < 0) { | ||||
| 		return readlen; | ||||
| 	} | ||||
| 	i3json_state.parserpos[readlen] = '\0'; | ||||
| 	return i3json_parse(st); | ||||
| } | ||||
| 
 | ||||
| bool handle_status_line(struct swaybar_state *st) { | ||||
| 	bool dirty = false; | ||||
| 
 | ||||
| 	switch (st->status->protocol) { | ||||
| 	case I3BAR: | ||||
| 		sway_log(L_DEBUG, "Got i3bar protocol."); | ||||
| 		if (i3json_handle_fd(st) > 0) { | ||||
| 			dirty = true; | ||||
| 		} | ||||
| 		break; | ||||
| 	case TEXT: | ||||
| 		sway_log(L_DEBUG, "Got text protocol."); | ||||
| 		read_line_tail(st->status_read_fd, line, sizeof(line), line_rest); | ||||
| 		dirty = true; | ||||
| 		st->status->text_line = line; | ||||
| 		break; | ||||
| 	case UNDEF: | ||||
| 		sway_log(L_DEBUG, "Detecting protocol..."); | ||||
| 		if (read_line_tail(st->status_read_fd, line, sizeof(line), line_rest) < 0) { | ||||
| 			break; | ||||
| 		} | ||||
| 		dirty = true; | ||||
| 		st->status->text_line = line; | ||||
| 		st->status->protocol = TEXT; | ||||
| 		if (line[0] == '{') { | ||||
| 			// detect i3bar json protocol
 | ||||
| 			json_object *proto = json_tokener_parse(line); | ||||
| 			json_object *version; | ||||
| 			if (proto) { | ||||
| 				if (json_object_object_get_ex(proto, "version", &version) | ||||
| 							&& json_object_get_int(version) == 1 | ||||
| 				) { | ||||
| 					sway_log(L_DEBUG, "Switched to i3bar protocol."); | ||||
| 					st->status->protocol = I3BAR; | ||||
| 					i3json_handle_data(st, line_rest); | ||||
| 				} | ||||
| 				json_object_put(proto); | ||||
| 			} | ||||
| 		} | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return dirty; | ||||
| } | ||||
| 
 | ||||
| struct status_line *init_status_line() { | ||||
| 	struct status_line *line = malloc(sizeof(struct status_line)); | ||||
| 	line->block_line = create_list(); | ||||
| 	line->text_line = NULL; | ||||
| 	line->protocol = UNDEF; | ||||
| 
 | ||||
| 	return line; | ||||
| } | ||||
							
								
								
									
										45
									
								
								swaybar/status_line.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								swaybar/status_line.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | |||
| #ifndef _SWAYBAR_STATUS_LINE_H | ||||
| #define _SWAYBAR_STATUS_LINE_H | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| #include "list.h" | ||||
| #include "state.h" | ||||
| 
 | ||||
| typedef enum {UNDEF, TEXT, I3BAR} command_protocol; | ||||
| 
 | ||||
| struct status_line { | ||||
| 	list_t *block_line; | ||||
| 	char *text_line; | ||||
| 	command_protocol protocol; | ||||
| }; | ||||
| 
 | ||||
| struct status_block { | ||||
| 	char *full_text, *short_text, *align; | ||||
| 	bool urgent; | ||||
| 	uint32_t color; | ||||
| 	int min_width; | ||||
| 	char *name, *instance; | ||||
| 	bool separator; | ||||
| 	int separator_block_width; | ||||
| 	// Airblader features
 | ||||
| 	uint32_t background; | ||||
| 	uint32_t border; | ||||
| 	int border_top; | ||||
| 	int border_bottom; | ||||
| 	int border_left; | ||||
| 	int border_right; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Initialize status line struct. | ||||
|  */ | ||||
| struct status_line *init_status_line(); | ||||
| 
 | ||||
| /**
 | ||||
|  * handle status line activity. | ||||
|  */ | ||||
| bool handle_status_line(struct swaybar_state *st); | ||||
| 
 | ||||
| #endif /* _SWAYBAR_STATUS_LINE_H */ | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Mikkel Oscar Lyderik
						Mikkel Oscar Lyderik