commit
						9fa70ce426
					
				| @ -0,0 +1,311 @@ | ||||
| #define _XOPEN_SOURCE 500 | ||||
| #include <ctype.h> | ||||
| #include <libgen.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <strings.h> | ||||
| #include <unistd.h> | ||||
| #include <wordexp.h> | ||||
| #include "sway/commands.h" | ||||
| #include "sway/config.h" | ||||
| #include "list.h" | ||||
| #include "log.h" | ||||
| #include "stringop.h" | ||||
| 
 | ||||
| static char *bg_options[] = { | ||||
| 	"stretch", | ||||
| 	"center", | ||||
| 	"fill", | ||||
| 	"fit", | ||||
| 	"tile", | ||||
| }; | ||||
| 
 | ||||
| struct cmd_results *cmd_output(int argc, char **argv) { | ||||
| 	struct cmd_results *error = NULL; | ||||
| 	if ((error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1))) { | ||||
| 		return error; | ||||
| 	} | ||||
| 	const char *name = argv[0]; | ||||
| 
 | ||||
| 	struct output_config *output = new_output_config(); | ||||
| 	if (!output) { | ||||
| 		sway_log(L_ERROR, "Failed to allocate output config"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	output->name = strdup(name); | ||||
| 
 | ||||
| 	int i; | ||||
| 	for (i = 1; i < argc; ++i) { | ||||
| 		const char *command = argv[i]; | ||||
| 
 | ||||
| 		if (strcasecmp(command, "disable") == 0) { | ||||
| 			output->enabled = 0; | ||||
| 		} else if (strcasecmp(command, "mode") == 0 || | ||||
| 				strcasecmp(command, "resolution") == 0 || | ||||
| 				strcasecmp(command, "res") == 0) { | ||||
| 			if (++i >= argc) { | ||||
| 				error = cmd_results_new(CMD_INVALID, "output", | ||||
| 					"Missing mode argument."); | ||||
| 				goto fail; | ||||
| 			} | ||||
| 
 | ||||
| 			int width = -1, height = -1; | ||||
| 			float refresh_rate = -1; | ||||
| 
 | ||||
| 			char *end; | ||||
| 			width = strtol(argv[i], &end, 10); | ||||
| 			if (*end) { | ||||
| 				// Format is 1234x4321
 | ||||
| 				if (*end != 'x') { | ||||
| 					error = cmd_results_new(CMD_INVALID, "output", | ||||
| 						"Invalid mode width."); | ||||
| 					goto fail; | ||||
| 				} | ||||
| 				++end; | ||||
| 				height = strtol(end, &end, 10); | ||||
| 				if (*end) { | ||||
| 					if (*end != '@') { | ||||
| 						error = cmd_results_new(CMD_INVALID, "output", | ||||
| 							"Invalid mode height."); | ||||
| 						goto fail; | ||||
| 					} | ||||
| 					++end; | ||||
| 					refresh_rate = strtof(end, &end); | ||||
| 					if (strcasecmp("Hz", end) != 0) { | ||||
| 						error = cmd_results_new(CMD_INVALID, "output", | ||||
| 							"Invalid mode refresh rate."); | ||||
| 						goto fail; | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Format is 1234 4321
 | ||||
| 				if (++i >= argc) { | ||||
| 					error = cmd_results_new(CMD_INVALID, "output", | ||||
| 						"Missing mode argument (height)."); | ||||
| 					goto fail; | ||||
| 				} | ||||
| 				height = strtol(argv[i], &end, 10); | ||||
| 				if (*end) { | ||||
| 					error = cmd_results_new(CMD_INVALID, "output", | ||||
| 						"Invalid mode height."); | ||||
| 					goto fail; | ||||
| 				} | ||||
| 			} | ||||
| 			output->width = width; | ||||
| 			output->height = height; | ||||
| 			output->refresh_rate = refresh_rate; | ||||
| 		} else if (strcasecmp(command, "position") == 0 || | ||||
| 				strcasecmp(command, "pos") == 0) { | ||||
| 			if (++i >= argc) { | ||||
| 				error = cmd_results_new(CMD_INVALID, "output", | ||||
| 					"Missing position argument."); | ||||
| 				goto fail; | ||||
| 			} | ||||
| 
 | ||||
| 			int x = -1, y = -1; | ||||
| 
 | ||||
| 			char *end; | ||||
| 			x = strtol(argv[i], &end, 10); | ||||
| 			if (*end) { | ||||
| 				// Format is 1234,4321
 | ||||
| 				if (*end != ',') { | ||||
| 					error = cmd_results_new(CMD_INVALID, "output", | ||||
| 						"Invalid position x."); | ||||
| 					goto fail; | ||||
| 				} | ||||
| 				++end; | ||||
| 				y = strtol(end, &end, 10); | ||||
| 				if (*end) { | ||||
| 					error = cmd_results_new(CMD_INVALID, "output", | ||||
| 						"Invalid position y."); | ||||
| 					goto fail; | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Format is 1234 4321 (legacy)
 | ||||
| 				if (++i >= argc) { | ||||
| 					error = cmd_results_new(CMD_INVALID, "output", | ||||
| 						"Missing position argument (y)."); | ||||
| 					goto fail; | ||||
| 				} | ||||
| 				y = strtol(argv[i], &end, 10); | ||||
| 				if (*end) { | ||||
| 					error = cmd_results_new(CMD_INVALID, "output", | ||||
| 						"Invalid position y."); | ||||
| 					goto fail; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			output->x = x; | ||||
| 			output->y = y; | ||||
| 		} else if (strcasecmp(command, "scale") == 0) { | ||||
| 			if (++i >= argc) { | ||||
| 				error = cmd_results_new(CMD_INVALID, "output", | ||||
| 					"Missing scale parameter."); | ||||
| 				goto fail; | ||||
| 			} | ||||
| 			char *end; | ||||
| 			output->scale = strtol(argv[i], &end, 10); | ||||
| 			if (*end) { | ||||
| 				error = cmd_results_new(CMD_INVALID, "output", | ||||
| 					"Invalid scale."); | ||||
| 				goto fail; | ||||
| 			} | ||||
| 		} else if (strcasecmp(command, "transform") == 0) { | ||||
| 			if (++i >= argc) { | ||||
| 				error = cmd_results_new(CMD_INVALID, "output", | ||||
| 					"Missing transform parameter."); | ||||
| 				goto fail; | ||||
| 			} | ||||
| 			char *value = argv[i]; | ||||
| 			if (strcmp(value, "normal") == 0) { | ||||
| 				output->transform = WL_OUTPUT_TRANSFORM_NORMAL; | ||||
| 			} else if (strcmp(value, "90") == 0) { | ||||
| 				output->transform = WL_OUTPUT_TRANSFORM_90; | ||||
| 			} else if (strcmp(value, "180") == 0) { | ||||
| 				output->transform = WL_OUTPUT_TRANSFORM_180; | ||||
| 			} else if (strcmp(value, "270") == 0) { | ||||
| 				output->transform = WL_OUTPUT_TRANSFORM_270; | ||||
| 			} else if (strcmp(value, "flipped") == 0) { | ||||
| 				output->transform = WL_OUTPUT_TRANSFORM_FLIPPED; | ||||
| 			} else if (strcmp(value, "flipped-90") == 0) { | ||||
| 				output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90; | ||||
| 			} else if (strcmp(value, "flipped-180") == 0) { | ||||
| 				output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180; | ||||
| 			} else if (strcmp(value, "flipped-270") == 0) { | ||||
| 				output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270; | ||||
| 			} else { | ||||
| 				error = cmd_results_new(CMD_INVALID, "output", | ||||
| 					"Invalid output transform."); | ||||
| 				goto fail; | ||||
| 			} | ||||
| 		} else if (strcasecmp(command, "background") == 0 || | ||||
| 				strcasecmp(command, "bg") == 0) { | ||||
| 			wordexp_t p; | ||||
| 			if (++i >= argc) { | ||||
| 				error = cmd_results_new(CMD_INVALID, "output", | ||||
| 					"Missing background file or color specification."); | ||||
| 				goto fail; | ||||
| 			} | ||||
| 			if (i + 1 >= argc) { | ||||
| 				error = cmd_results_new(CMD_INVALID, "output", | ||||
| 					"Missing background scaling mode or `solid_color`."); | ||||
| 				goto fail; | ||||
| 			} | ||||
| 			if (strcasecmp(argv[i + 1], "solid_color") == 0) { | ||||
| 				output->background = strdup(argv[argc - 2]); | ||||
| 				output->background_option = strdup("solid_color"); | ||||
| 			} else { | ||||
| 				// argv[i+j]=bg_option
 | ||||
| 				bool valid = false; | ||||
| 				char *mode; | ||||
| 				size_t j; | ||||
| 				for (j = 0; j < (size_t) (argc - i); ++j) { | ||||
| 					mode = argv[i + j]; | ||||
| 					size_t n = sizeof(bg_options) / sizeof(char *); | ||||
| 					for (size_t k = 0; k < n; ++k) { | ||||
| 						if (strcasecmp(mode, bg_options[k]) == 0) { | ||||
| 							valid = true; | ||||
| 							break; | ||||
| 						} | ||||
| 					} | ||||
| 					if (valid) { | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 				if (!valid) { | ||||
| 					error = cmd_results_new(CMD_INVALID, "output", | ||||
| 						"Missing background scaling mode."); | ||||
| 					goto fail; | ||||
| 				} | ||||
| 
 | ||||
| 				char *src = join_args(argv + i, j); | ||||
| 				if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) { | ||||
| 					error = cmd_results_new(CMD_INVALID, "output", | ||||
| 						"Invalid syntax (%s).", src); | ||||
| 					goto fail; | ||||
| 				} | ||||
| 				free(src); | ||||
| 				src = p.we_wordv[0]; | ||||
| 				if (config->reading && *src != '/') { | ||||
| 					char *conf = strdup(config->current_config); | ||||
| 					if (conf) { | ||||
| 						char *conf_path = dirname(conf); | ||||
| 						src = malloc(strlen(conf_path) + strlen(src) + 2); | ||||
| 						if (src) { | ||||
| 							sprintf(src, "%s/%s", conf_path, p.we_wordv[0]); | ||||
| 						} else { | ||||
| 							sway_log(L_ERROR, | ||||
| 								"Unable to allocate background source"); | ||||
| 						} | ||||
| 						free(conf); | ||||
| 					} else { | ||||
| 						sway_log(L_ERROR, | ||||
| 							"Unable to allocate background source"); | ||||
| 					} | ||||
| 				} | ||||
| 				if (!src || access(src, F_OK) == -1) { | ||||
| 					error = cmd_results_new(CMD_INVALID, "output", | ||||
| 						"Background file unreadable (%s).", src); | ||||
| 					wordfree(&p); | ||||
| 					goto fail; | ||||
| 				} | ||||
| 
 | ||||
| 				output->background = strdup(src); | ||||
| 				output->background_option = strdup(mode); | ||||
| 				if (src != p.we_wordv[0]) { | ||||
| 					free(src); | ||||
| 				} | ||||
| 				wordfree(&p); | ||||
| 
 | ||||
| 				i += j; | ||||
| 			} | ||||
| 		} else { | ||||
| 			error = cmd_results_new(CMD_INVALID, "output", | ||||
| 				"Invalid output subcommand: %s.", command); | ||||
| 			goto fail; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	i = list_seq_find(config->output_configs, output_name_cmp, name); | ||||
| 	if (i >= 0) { | ||||
| 		// merge existing config
 | ||||
| 		struct output_config *oc = config->output_configs->items[i]; | ||||
| 		merge_output_config(oc, output); | ||||
| 		free_output_config(output); | ||||
| 		output = oc; | ||||
| 	} else { | ||||
| 		list_add(config->output_configs, output); | ||||
| 	} | ||||
| 
 | ||||
| 	sway_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " | ||||
| 		"position %d,%d scale %d transform %d) (bg %s %s)", | ||||
| 		output->name, output->enabled, output->width, output->height, | ||||
| 		output->refresh_rate, output->x, output->y, output->scale, | ||||
| 		output->transform, output->background, output->background_option); | ||||
| 
 | ||||
| 	if (output->name) { | ||||
| 		// Try to find the output container and apply configuration now. If
 | ||||
| 		// this is during startup then there will be no container and config
 | ||||
| 		// will be applied during normal "new output" event from wlroots.
 | ||||
| 		swayc_t *cont = NULL; | ||||
| 		for (int i = 0; i < root_container.children->length; ++i) { | ||||
| 			cont = root_container.children->items[i]; | ||||
| 			if (cont->name && ((strcmp(cont->name, output->name) == 0) || | ||||
| 					(strcmp(output->name, "*") == 0))) { | ||||
| 				apply_output_config(output, cont); | ||||
| 
 | ||||
| 				if (strcmp(output->name, "*") != 0) { | ||||
| 					// Stop looking if the output config isn't applicable to all
 | ||||
| 					// outputs
 | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||||
| 
 | ||||
| fail: | ||||
| 	free_output_config(output); | ||||
| 	return error; | ||||
| } | ||||
| @ -0,0 +1,185 @@ | ||||
| #define _XOPEN_SOURCE 700 | ||||
| #include <string.h> | ||||
| #include <assert.h> | ||||
| #include <wlr/types/wlr_output.h> | ||||
| #include <wlr/types/wlr_output_layout.h> | ||||
| #include "sway/config.h" | ||||
| #include "sway/output.h" | ||||
| #include "log.h" | ||||
| 
 | ||||
| int output_name_cmp(const void *item, const void *data) { | ||||
| 	const struct output_config *output = item; | ||||
| 	const char *name = data; | ||||
| 
 | ||||
| 	return strcmp(output->name, name); | ||||
| } | ||||
| 
 | ||||
| struct output_config *new_output_config() { | ||||
| 	struct output_config *oc = calloc(1, sizeof(struct output_config)); | ||||
| 	if (oc == NULL) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	oc->enabled = -1; | ||||
| 	oc->width = oc->height = -1; | ||||
| 	oc->refresh_rate = -1; | ||||
| 	oc->x = oc->y = -1; | ||||
| 	oc->scale = -1; | ||||
| 	oc->transform = -1; | ||||
| 	return oc; | ||||
| } | ||||
| 
 | ||||
| void merge_output_config(struct output_config *dst, struct output_config *src) { | ||||
| 	if (src->name) { | ||||
| 		free(dst->name); | ||||
| 		dst->name = strdup(src->name); | ||||
| 	} | ||||
| 	if (src->enabled != -1) { | ||||
| 		dst->enabled = src->enabled; | ||||
| 	} | ||||
| 	if (src->width != -1) { | ||||
| 		dst->width = src->width; | ||||
| 	} | ||||
| 	if (src->height != -1) { | ||||
| 		dst->height = src->height; | ||||
| 	} | ||||
| 	if (src->x != -1) { | ||||
| 		dst->x = src->x; | ||||
| 	} | ||||
| 	if (src->y != -1) { | ||||
| 		dst->y = src->y; | ||||
| 	} | ||||
| 	if (src->scale != -1) { | ||||
| 		dst->scale = src->scale; | ||||
| 	} | ||||
| 	if (src->refresh_rate != -1) { | ||||
| 		dst->refresh_rate = src->refresh_rate; | ||||
| 	} | ||||
| 	if (src->transform != -1) { | ||||
| 		dst->transform = src->transform; | ||||
| 	} | ||||
| 	if (src->background) { | ||||
| 		free(dst->background); | ||||
| 		dst->background = strdup(src->background); | ||||
| 	} | ||||
| 	if (src->background_option) { | ||||
| 		free(dst->background_option); | ||||
| 		dst->background_option = strdup(src->background_option); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void set_mode(struct wlr_output *output, int width, int height, | ||||
| 		float refresh_rate) { | ||||
| 	int mhz = (int)(refresh_rate * 1000); | ||||
| 	if (wl_list_empty(&output->modes)) { | ||||
| 		sway_log(L_DEBUG, "Assigning custom mode to %s", output->name); | ||||
| 		wlr_output_set_custom_mode(output, width, height, mhz); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_output_mode *mode, *best = NULL; | ||||
| 	wl_list_for_each(mode, &output->modes, link) { | ||||
| 		if (mode->width == width && mode->height == height) { | ||||
| 			if (mode->refresh == mhz) { | ||||
| 				best = mode; | ||||
| 				break; | ||||
| 			} | ||||
| 			best = mode; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!best) { | ||||
| 		sway_log(L_ERROR, "Configured mode for %s not available", output->name); | ||||
| 	} else { | ||||
| 		sway_log(L_DEBUG, "Assigning configured mode to %s", output->name); | ||||
| 		wlr_output_set_mode(output, best); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void apply_output_config(struct output_config *oc, swayc_t *output) { | ||||
| 	assert(output->type == C_OUTPUT); | ||||
| 
 | ||||
| 	struct wlr_output *wlr_output = output->sway_output->wlr_output; | ||||
| 	if (oc && oc->enabled == 0) { | ||||
| 		wlr_output_layout_remove(root_container.sway_root->output_layout, | ||||
| 			wlr_output); | ||||
| 		destroy_output(output); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (oc && oc->width > 0 && oc->height > 0) { | ||||
| 		sway_log(L_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width, | ||||
| 			oc->height, oc->refresh_rate); | ||||
| 		set_mode(wlr_output, oc->width, oc->height, oc->refresh_rate); | ||||
| 	} | ||||
| 	if (oc && oc->scale > 0) { | ||||
| 		sway_log(L_DEBUG, "Set %s scale to %d", oc->name, oc->scale); | ||||
| 		wlr_output_set_scale(wlr_output, oc->scale); | ||||
| 	} | ||||
| 	if (oc && oc->transform >= 0) { | ||||
| 		sway_log(L_DEBUG, "Set %s transform to %d", oc->name, oc->transform); | ||||
| 		wlr_output_set_transform(wlr_output, oc->transform); | ||||
| 	} | ||||
| 
 | ||||
| 	// Find position for it
 | ||||
| 	if (oc && (oc->x != -1 || oc->y != -1)) { | ||||
| 		sway_log(L_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); | ||||
| 		wlr_output_layout_add(root_container.sway_root->output_layout, | ||||
| 			wlr_output, oc->x, oc->y); | ||||
| 	} else { | ||||
| 		wlr_output_layout_add_auto(root_container.sway_root->output_layout, | ||||
| 			wlr_output); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!oc || !oc->background) { | ||||
| 		// Look for a * config for background
 | ||||
| 		int i = list_seq_find(config->output_configs, output_name_cmp, "*"); | ||||
| 		if (i >= 0) { | ||||
| 			oc = config->output_configs->items[i]; | ||||
| 		} else { | ||||
| 			oc = NULL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	int output_i; | ||||
| 	for (output_i = 0; output_i < root_container.children->length; ++output_i) { | ||||
| 		if (root_container.children->items[output_i] == output) { | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (oc && oc->background) { | ||||
| 		// TODO swaybg
 | ||||
| 		/*if (output->bg_pid != 0) {
 | ||||
| 			terminate_swaybg(output->bg_pid); | ||||
| 		} | ||||
| 
 | ||||
| 		sway_log(L_DEBUG, "Setting background for output %d to %s", output_i, oc->background); | ||||
| 
 | ||||
| 		size_t bufsize = 12; | ||||
| 		char output_id[bufsize]; | ||||
| 		snprintf(output_id, bufsize, "%d", output_i); | ||||
| 		output_id[bufsize-1] = 0; | ||||
| 
 | ||||
| 		char *const cmd[] = { | ||||
| 			"swaybg", | ||||
| 			output_id, | ||||
| 			oc->background, | ||||
| 			oc->background_option, | ||||
| 			NULL, | ||||
| 		}; | ||||
| 
 | ||||
| 		output->bg_pid = fork(); | ||||
| 		if (output->bg_pid == 0) { | ||||
| 			execvp(cmd[0], cmd); | ||||
| 		}*/ | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void free_output_config(struct output_config *oc) { | ||||
| 	if (!oc) { | ||||
| 		return; | ||||
| 	} | ||||
| 	free(oc->name); | ||||
| 	free(oc->background); | ||||
| 	free(oc->background_option); | ||||
| 	free(oc); | ||||
| } | ||||
					Loading…
					
					
				
		Reference in new issue