mirror of
				https://github.com/swaywm/sway.git
				synced 2025-10-29 05:40:18 -04:00 
			
		
		
		
	Make single bar handle multiple outputs
This commit is contained in:
		
							parent
							
								
									e19e32cf81
								
							
						
					
					
						commit
						6d57f03028
					
				
					 7 changed files with 160 additions and 71 deletions
				
			
		|  | @ -8,8 +8,7 @@ | |||
| struct bar { | ||||
| 	struct config *config; | ||||
| 	struct status_line *status; | ||||
| 	struct output *output; | ||||
| 	/* list_t *outputs; */ | ||||
| 	list_t *outputs; | ||||
| 
 | ||||
| 	int ipc_event_socketfd; | ||||
| 	int ipc_socketfd; | ||||
|  | @ -22,6 +21,7 @@ struct output { | |||
| 	struct registry *registry; | ||||
| 	list_t *workspaces; | ||||
| 	char *name; | ||||
| 	int idx; | ||||
| }; | ||||
| 
 | ||||
| struct workspace { | ||||
|  | @ -35,7 +35,12 @@ struct workspace { | |||
| /**
 | ||||
|  * Setup bar. | ||||
|  */ | ||||
| void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id, int desired_output); | ||||
| void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id); | ||||
| 
 | ||||
| /**
 | ||||
|  * Create new output struct from name. | ||||
|  */ | ||||
| struct output *new_output(const char *name); | ||||
| 
 | ||||
| /**
 | ||||
|  * Bar mainloop. | ||||
|  |  | |||
|  | @ -4,6 +4,8 @@ | |||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| #include "list.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * Colors for a box with background, border and text colors. | ||||
|  */ | ||||
|  | @ -25,6 +27,7 @@ struct config { | |||
| 	bool strip_workspace_numbers; | ||||
| 	bool binding_mode_indicator; | ||||
| 	bool workspace_buttons; | ||||
| 	list_t *outputs; | ||||
| 
 | ||||
| 	int height; | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| /**
 | ||||
|  * Initialize ipc connection to sway and get sway state, outputs, bar_config. | ||||
|  */ | ||||
| void ipc_bar_init(struct bar *bar, int outputi, const char *bar_id); | ||||
| void ipc_bar_init(struct bar *bar, const char *bar_id); | ||||
| 
 | ||||
| /**
 | ||||
|  * Handle ipc event from sway. | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <string.h> | ||||
| #include <fcntl.h> | ||||
| #include <errno.h> | ||||
| #include <sys/types.h> | ||||
|  | @ -17,11 +18,7 @@ | |||
| static void bar_init(struct bar *bar) { | ||||
| 	bar->config = init_config(); | ||||
| 	bar->status = init_status_line(); | ||||
| 	bar->output = malloc(sizeof(struct output)); | ||||
| 	bar->output->window = NULL; | ||||
| 	bar->output->registry = NULL; | ||||
| 	bar->output->workspaces = create_list(); | ||||
| 	bar->output->name = NULL; | ||||
| 	bar->outputs = create_list(); | ||||
| } | ||||
| 
 | ||||
| static void spawn_status_cmd_proc(struct bar *bar) { | ||||
|  | @ -49,38 +46,50 @@ static void spawn_status_cmd_proc(struct bar *bar) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct output *new_output(const char *name) { | ||||
| 	struct output *output = malloc(sizeof(struct output)); | ||||
| 	output->name = strdup(name); | ||||
| 	output->window = NULL; | ||||
| 	output->registry = NULL; | ||||
| 	output->workspaces = create_list(); | ||||
| 	return output; | ||||
| } | ||||
| 
 | ||||
| void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id, int desired_output) { | ||||
| void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id) { | ||||
| 	/* initialize bar with default values */ | ||||
| 	bar_init(bar); | ||||
| 
 | ||||
| 	bar->output->registry = registry_poll(); | ||||
| 
 | ||||
| 	if (!bar->output->registry->desktop_shell) { | ||||
| 		sway_abort("swaybar requires the compositor to support the desktop-shell extension."); | ||||
| 	} | ||||
| 
 | ||||
| 	/* connect to sway ipc */ | ||||
| 	bar->ipc_socketfd = ipc_open_socket(socket_path); | ||||
| 	bar->ipc_event_socketfd = ipc_open_socket(socket_path); | ||||
| 
 | ||||
| 	ipc_bar_init(bar, desired_output, bar_id); | ||||
| 	ipc_bar_init(bar, bar_id); | ||||
| 
 | ||||
| 	struct output_state *output = bar->output->registry->outputs->items[desired_output]; | ||||
| 	int i; | ||||
| 	for (i = 0; i < bar->outputs->length; ++i) { | ||||
| 		struct output *bar_output = bar->outputs->items[i]; | ||||
| 
 | ||||
| 	bar->output->window = window_setup(bar->output->registry, output->width, 30, false); | ||||
| 	if (!bar->output->window) { | ||||
| 		bar_output->registry = registry_poll(); | ||||
| 
 | ||||
| 		if (!bar_output->registry->desktop_shell) { | ||||
| 			sway_abort("swaybar requires the compositor to support the desktop-shell extension."); | ||||
| 		} | ||||
| 
 | ||||
| 		struct output_state *output = bar_output->registry->outputs->items[bar_output->idx]; | ||||
| 
 | ||||
| 		bar_output->window = window_setup(bar_output->registry, output->width, 30, false); | ||||
| 		if (!bar_output->window) { | ||||
| 			sway_abort("Failed to create window."); | ||||
| 		} | ||||
| 	desktop_shell_set_panel(bar->output->registry->desktop_shell, output->output, bar->output->window->surface); | ||||
| 	desktop_shell_set_panel_position(bar->output->registry->desktop_shell, bar->config->position); | ||||
| 		desktop_shell_set_panel(bar_output->registry->desktop_shell, output->output, bar_output->window->surface); | ||||
| 		desktop_shell_set_panel_position(bar_output->registry->desktop_shell, bar->config->position); | ||||
| 
 | ||||
| 		/* set font */ | ||||
| 	bar->output->window->font = bar->config->font; | ||||
| 		bar_output->window->font = bar->config->font; | ||||
| 
 | ||||
| 		/* set window height */ | ||||
| 	set_window_height(bar->output->window, bar->config->height); | ||||
| 
 | ||||
| 		set_window_height(bar_output->window, bar->config->height); | ||||
| 	} | ||||
| 	/* spawn status command */ | ||||
| 	spawn_status_cmd_proc(bar); | ||||
| } | ||||
|  | @ -92,7 +101,9 @@ void bar_run(struct bar *bar) { | |||
| 
 | ||||
| 	while (1) { | ||||
| 		if (dirty) { | ||||
| 			struct output *output = bar->output; | ||||
| 			int i; | ||||
| 			for (i = 0; i < bar->outputs->length; ++i) { | ||||
| 				struct output *output = bar->outputs->items[i]; | ||||
| 				if (window_prerender(output->window) && output->window->cairo) { | ||||
| 					render(output, bar->config, bar->status); | ||||
| 					window_render(output->window); | ||||
|  | @ -101,6 +112,7 @@ void bar_run(struct bar *bar) { | |||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		dirty = false; | ||||
| 		FD_ZERO(&readfds); | ||||
|  | @ -149,6 +161,14 @@ static void free_output(struct output *output) { | |||
| 	free(output); | ||||
| } | ||||
| 
 | ||||
| static void free_outputs(list_t *outputs) { | ||||
| 	int i; | ||||
| 	for (i = 0; i < outputs->length; ++i) { | ||||
| 		free_output(outputs->items[i]); | ||||
| 	} | ||||
| 	list_free(outputs); | ||||
| } | ||||
| 
 | ||||
| static void terminate_status_command(pid_t pid) { | ||||
| 	if (pid) { | ||||
| 		// terminate status_command process
 | ||||
|  | @ -164,7 +184,7 @@ static void terminate_status_command(pid_t pid) { | |||
| 
 | ||||
| void bar_teardown(struct bar *bar) { | ||||
| 	free_config(bar->config); | ||||
| 	free_output(bar->output); | ||||
| 	free_outputs(bar->outputs); | ||||
| 	free_status_line(bar->status); | ||||
| 
 | ||||
| 	/* close sockets/pipes */ | ||||
|  |  | |||
|  | @ -51,6 +51,7 @@ struct config *init_config() { | |||
| 	config->strip_workspace_numbers = false; | ||||
| 	config->binding_mode_indicator = true; | ||||
| 	config->workspace_buttons = true; | ||||
| 	config->outputs = create_list(); | ||||
| 
 | ||||
| 	/* height */ | ||||
| 	config->height = 0; | ||||
|  |  | |||
							
								
								
									
										106
									
								
								swaybar/ipc.c
									
										
									
									
									
								
							
							
						
						
									
										106
									
								
								swaybar/ipc.c
									
										
									
									
									
								
							|  | @ -11,7 +11,7 @@ static void ipc_parse_config(struct config *config, const char *payload) { | |||
| 	json_object *bar_config = json_tokener_parse(payload); | ||||
| 	json_object *tray_output, *mode, *hidden_bar, *position, *status_command; | ||||
| 	json_object *font, *bar_height, *workspace_buttons, *strip_workspace_numbers; | ||||
| 	json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol; | ||||
| 	json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs; | ||||
| 	json_object_object_get_ex(bar_config, "tray_output", &tray_output); | ||||
| 	json_object_object_get_ex(bar_config, "mode", &mode); | ||||
| 	json_object_object_get_ex(bar_config, "hidden_bar", &hidden_bar); | ||||
|  | @ -25,6 +25,7 @@ static void ipc_parse_config(struct config *config, const char *payload) { | |||
| 	json_object_object_get_ex(bar_config, "verbose", &verbose); | ||||
| 	json_object_object_get_ex(bar_config, "separator_symbol", &sep_symbol); | ||||
| 	json_object_object_get_ex(bar_config, "colors", &colors); | ||||
| 	json_object_object_get_ex(bar_config, "outputs", &outputs); | ||||
| 
 | ||||
| 	if (status_command) { | ||||
| 		free(config->status_command); | ||||
|  | @ -61,6 +62,28 @@ static void ipc_parse_config(struct config *config, const char *payload) { | |||
| 		config->height = json_object_get_int(bar_height); | ||||
| 	} | ||||
| 
 | ||||
| 	// free previous outputs list
 | ||||
| 	int i; | ||||
| 	for (i = 0; i < config->outputs->length; ++i) { | ||||
| 		free(config->outputs->items[i]); | ||||
| 	} | ||||
| 	list_free(config->outputs); | ||||
| 	config->outputs = create_list(); | ||||
| 
 | ||||
| 	if (outputs) { | ||||
| 		int length = json_object_array_length(outputs); | ||||
| 		json_object *output; | ||||
| 		const char *output_str; | ||||
| 		for (i = 0; i < length; ++i) { | ||||
| 			output = json_object_array_get_idx(outputs, i); | ||||
| 			output_str = json_object_get_string(output); | ||||
| 			if (strcmp("*", output_str) == 0) { | ||||
| 				break; | ||||
| 			} | ||||
| 			list_add(config->outputs, strdup(output_str)); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (colors) { | ||||
| 		json_object *background, *statusline, *separator; | ||||
| 		json_object *focused_workspace_border, *focused_workspace_bg, *focused_workspace_text; | ||||
|  | @ -151,10 +174,14 @@ static void ipc_parse_config(struct config *config, const char *payload) { | |||
| } | ||||
| 
 | ||||
| static void ipc_update_workspaces(struct bar *bar) { | ||||
| 	if (bar->output->workspaces) { | ||||
| 		free_workspaces(bar->output->workspaces); | ||||
| 	int i; | ||||
| 	for (i = 0; i < bar->outputs->length; ++i) { | ||||
| 		struct output *output = bar->outputs->items[i]; | ||||
| 		if (output->workspaces) { | ||||
| 			free_workspaces(output->workspaces); | ||||
| 		} | ||||
| 		output->workspaces = create_list(); | ||||
| 	} | ||||
| 	bar->output->workspaces = create_list(); | ||||
| 
 | ||||
| 	uint32_t len = 0; | ||||
| 	char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_WORKSPACES, NULL, &len); | ||||
|  | @ -164,7 +191,6 @@ static void ipc_update_workspaces(struct bar *bar) { | |||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	int i; | ||||
| 	int length = json_object_array_length(results); | ||||
| 	json_object *ws_json; | ||||
| 	json_object *num, *name, *visible, *focused, *out, *urgent; | ||||
|  | @ -178,14 +204,18 @@ static void ipc_update_workspaces(struct bar *bar) { | |||
| 		json_object_object_get_ex(ws_json, "output", &out); | ||||
| 		json_object_object_get_ex(ws_json, "urgent", &urgent); | ||||
| 
 | ||||
| 		if (strcmp(json_object_get_string(out), bar->output->name) == 0) { | ||||
| 		int j; | ||||
| 		for (j = 0; j < bar->outputs->length; ++j) { | ||||
| 			struct output *output = bar->outputs->items[j]; | ||||
| 			if (strcmp(json_object_get_string(out), output->name) == 0) { | ||||
| 				struct workspace *ws = malloc(sizeof(struct workspace)); | ||||
| 				ws->num = json_object_get_int(num); | ||||
| 				ws->name = strdup(json_object_get_string(name)); | ||||
| 				ws->visible = json_object_get_boolean(visible); | ||||
| 				ws->focused = json_object_get_boolean(focused); | ||||
| 				ws->urgent = json_object_get_boolean(urgent); | ||||
| 			list_add(bar->output->workspaces, ws); | ||||
| 				list_add(output->workspaces, ws); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -193,23 +223,59 @@ static void ipc_update_workspaces(struct bar *bar) { | |||
| 	free(res); | ||||
| } | ||||
| 
 | ||||
| void ipc_bar_init(struct bar *bar, int outputi, const char *bar_id) { | ||||
| 	uint32_t len = 0; | ||||
| 	char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_OUTPUTS, NULL, &len); | ||||
| 	json_object *outputs = json_tokener_parse(res); | ||||
| 	json_object *info = json_object_array_get_idx(outputs, outputi); | ||||
| 	json_object *name; | ||||
| 	json_object_object_get_ex(info, "name", &name); | ||||
| 	bar->output->name = strdup(json_object_get_string(name)); | ||||
| 	free(res); | ||||
| 	json_object_put(outputs); | ||||
| 
 | ||||
| 	len = strlen(bar_id); | ||||
| 	res = ipc_single_command(bar->ipc_socketfd, IPC_GET_BAR_CONFIG, bar_id, &len); | ||||
| void ipc_bar_init(struct bar *bar, const char *bar_id) { | ||||
| 	// Get bar config
 | ||||
| 	uint32_t len = strlen(bar_id); | ||||
| 	char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_BAR_CONFIG, bar_id, &len); | ||||
| 
 | ||||
| 	ipc_parse_config(bar->config, res); | ||||
| 	free(res); | ||||
| 
 | ||||
| 	// Get outputs
 | ||||
| 	len = 0; | ||||
| 	res = ipc_single_command(bar->ipc_socketfd, IPC_GET_OUTPUTS, NULL, &len); | ||||
| 	json_object *outputs = json_tokener_parse(res); | ||||
| 	int i; | ||||
| 	int length = json_object_array_length(outputs); | ||||
| 	json_object *output, *output_name, *output_active; | ||||
| 	const char *name; | ||||
| 	bool active; | ||||
| 	for (i = 0; i < length; ++i) { | ||||
| 		output = json_object_array_get_idx(outputs, i); | ||||
| 		json_object_object_get_ex(output, "name", &output_name); | ||||
| 		json_object_object_get_ex(output, "active", &output_active); | ||||
| 		name = json_object_get_string(output_name); | ||||
| 		active = json_object_get_boolean(output_active); | ||||
| 		if (!active) { | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		bool use_output = false; | ||||
| 		if (bar->config->outputs->length == 0) { | ||||
| 			use_output = true; | ||||
| 		} else { | ||||
| 			int j = 0; | ||||
| 			for (j = 0; j < bar->config->outputs->length; ++j) { | ||||
| 				const char *conf_name = bar->config->outputs->items[i]; | ||||
| 				if (strcasecmp(name, conf_name) == 0) { | ||||
| 					use_output = true; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (!use_output) { | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		// add bar to the output
 | ||||
| 		struct output *bar_output = new_output(name); | ||||
| 		bar_output->idx = i; | ||||
| 		list_add(bar->outputs, bar_output); | ||||
| 	} | ||||
| 	free(res); | ||||
| 	json_object_put(outputs); | ||||
| 
 | ||||
| 	const char *subscribe_json = "[ \"workspace\", \"mode\" ]"; | ||||
| 	len = strlen(subscribe_json); | ||||
| 	res = ipc_single_command(bar->ipc_event_socketfd, IPC_SUBSCRIBE, subscribe_json, &len); | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ int main(int argc, char **argv) { | |||
| 	}; | ||||
| 
 | ||||
| 	const char *usage = | ||||
| 		"Usage: swaybar [options...] <output>\n" | ||||
| 		"Usage: swaybar [options...]\n" | ||||
| 		"\n" | ||||
| 		"  -h, --help             Show help message and quit.\n" | ||||
| 		"  -v, --version          Show the version number and quit.\n" | ||||
|  | @ -95,15 +95,9 @@ int main(int argc, char **argv) { | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (argc == optind) { | ||||
| 		sway_abort("No output index provided"); | ||||
| 	} | ||||
| 
 | ||||
| 	int desired_output = atoi(argv[optind]); | ||||
| 
 | ||||
| 	signal(SIGTERM, sig_handler); | ||||
| 
 | ||||
| 	bar_setup(&swaybar, socket_path, bar_id, desired_output); | ||||
| 	bar_setup(&swaybar, socket_path, bar_id); | ||||
| 
 | ||||
| 	free(socket_path); | ||||
| 	free(bar_id); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Mikkel Oscar Lyderik
						Mikkel Oscar Lyderik