mirror of
				https://github.com/swaywm/sway.git
				synced 2025-11-03 09:01:43 -05:00 
			
		
		
		
	Add output config
This commit is contained in:
		
							parent
							
								
									3fe64482bb
								
							
						
					
					
						commit
						aaae59026f
					
				
					 8 changed files with 512 additions and 2 deletions
				
			
		| 
						 | 
					@ -6,6 +6,7 @@
 | 
				
			||||||
#include <libinput.h>
 | 
					#include <libinput.h>
 | 
				
			||||||
#include <stdint.h>
 | 
					#include <stdint.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <wayland-server.h>
 | 
				
			||||||
#include <wlr/types/wlr_box.h>
 | 
					#include <wlr/types/wlr_box.h>
 | 
				
			||||||
#include <xkbcommon/xkbcommon.h>
 | 
					#include <xkbcommon/xkbcommon.h>
 | 
				
			||||||
#include <time.h>
 | 
					#include <time.h>
 | 
				
			||||||
| 
						 | 
					@ -80,8 +81,11 @@ struct output_config {
 | 
				
			||||||
	char *name;
 | 
						char *name;
 | 
				
			||||||
	int enabled;
 | 
						int enabled;
 | 
				
			||||||
	int width, height;
 | 
						int width, height;
 | 
				
			||||||
 | 
						float refresh_rate;
 | 
				
			||||||
	int x, y;
 | 
						int x, y;
 | 
				
			||||||
	int scale;
 | 
						int scale;
 | 
				
			||||||
 | 
						enum wl_output_transform transform;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	char *background;
 | 
						char *background;
 | 
				
			||||||
	char *background_option;
 | 
						char *background_option;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -132,6 +132,7 @@ swayc_t *new_output(struct sway_output *sway_output);
 | 
				
			||||||
swayc_t *new_workspace(swayc_t *output, const char *name);
 | 
					swayc_t *new_workspace(swayc_t *output, const char *name);
 | 
				
			||||||
swayc_t *new_view(swayc_t *sibling, struct sway_view *sway_view);
 | 
					swayc_t *new_view(swayc_t *sibling, struct sway_view *sway_view);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					swayc_t *destroy_output(swayc_t *output);
 | 
				
			||||||
swayc_t *destroy_view(swayc_t *view);
 | 
					swayc_t *destroy_view(swayc_t *view);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type);
 | 
					swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -95,6 +95,7 @@ static struct cmd_handler handlers[] = {
 | 
				
			||||||
	{ "exec", cmd_exec },
 | 
						{ "exec", cmd_exec },
 | 
				
			||||||
	{ "exec_always", cmd_exec_always },
 | 
						{ "exec_always", cmd_exec_always },
 | 
				
			||||||
	{ "exit", cmd_exit },
 | 
						{ "exit", cmd_exit },
 | 
				
			||||||
 | 
						{ "output", cmd_output },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int handler_compare(const void *_a, const void *_b) {
 | 
					static int handler_compare(const void *_a, const void *_b) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										253
									
								
								sway/commands/output.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								sway/commands/output.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,253 @@
 | 
				
			||||||
 | 
					#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 = calloc(1, sizeof(struct output_config));
 | 
				
			||||||
 | 
						if (!output) {
 | 
				
			||||||
 | 
							return cmd_results_new(CMD_FAILURE, "output", "Unable to allocate output config");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						output->x = output->y = output->width = output->height = -1;
 | 
				
			||||||
 | 
						output->name = strdup(name);
 | 
				
			||||||
 | 
						output->enabled = -1;
 | 
				
			||||||
 | 
						output->scale = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO: atoi doesn't handle invalid numbers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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, "resolution") == 0 || strcasecmp(command, "res") == 0) {
 | 
				
			||||||
 | 
								if (++i >= argc) {
 | 
				
			||||||
 | 
									error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument.");
 | 
				
			||||||
 | 
									goto fail;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								char *res = argv[i];
 | 
				
			||||||
 | 
								char *x = strchr(res, 'x');
 | 
				
			||||||
 | 
								int width = -1, height = -1;
 | 
				
			||||||
 | 
								if (x != NULL) {
 | 
				
			||||||
 | 
									// Format is 1234x4321
 | 
				
			||||||
 | 
									*x = '\0';
 | 
				
			||||||
 | 
									width = atoi(res);
 | 
				
			||||||
 | 
									height = atoi(x + 1);
 | 
				
			||||||
 | 
									*x = 'x';
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// Format is 1234 4321
 | 
				
			||||||
 | 
									width = atoi(res);
 | 
				
			||||||
 | 
									if (++i >= argc) {
 | 
				
			||||||
 | 
										error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument (height).");
 | 
				
			||||||
 | 
										goto fail;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									res = argv[i];
 | 
				
			||||||
 | 
									height = atoi(res);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								output->width = width;
 | 
				
			||||||
 | 
								output->height = height;
 | 
				
			||||||
 | 
							} else if (strcasecmp(command, "refresh_rate") == 0) {
 | 
				
			||||||
 | 
								if (++i >= argc) {
 | 
				
			||||||
 | 
									error = cmd_results_new(CMD_INVALID, "output", "Missing refresh_rate argument.");
 | 
				
			||||||
 | 
									goto fail;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								output->refresh_rate = atof(argv[i]);
 | 
				
			||||||
 | 
							} 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;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								char *res = argv[i];
 | 
				
			||||||
 | 
								char *c = strchr(res, ',');
 | 
				
			||||||
 | 
								int x = -1, y = -1;
 | 
				
			||||||
 | 
								if (c != NULL) {
 | 
				
			||||||
 | 
									// Format is 1234,4321
 | 
				
			||||||
 | 
									*c = '\0';
 | 
				
			||||||
 | 
									x = atoi(res);
 | 
				
			||||||
 | 
									y = atoi(c + 1);
 | 
				
			||||||
 | 
									*c = ',';
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// Format is 1234 4321
 | 
				
			||||||
 | 
									x = atoi(res);
 | 
				
			||||||
 | 
									if (++i >= argc) {
 | 
				
			||||||
 | 
										error = cmd_results_new(CMD_INVALID, "output", "Missing position argument (y).");
 | 
				
			||||||
 | 
										goto fail;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									res = argv[i];
 | 
				
			||||||
 | 
									y = atoi(res);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								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;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								output->scale = atoi(argv[i]);
 | 
				
			||||||
 | 
							} 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];
 | 
				
			||||||
 | 
										for (size_t k = 0; k < sizeof(bg_options) / sizeof(char *); ++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;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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) (%d x %d @ "
 | 
				
			||||||
 | 
								"%d, %d scale %d transform %d refresh_rate %f) (bg %s %s)",
 | 
				
			||||||
 | 
								output->name, output->enabled, output->width,
 | 
				
			||||||
 | 
								output->height, output->x, output->y, output->scale,
 | 
				
			||||||
 | 
								output->transform, output->refresh_rate,
 | 
				
			||||||
 | 
								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 wlc.
 | 
				
			||||||
 | 
							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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										166
									
								
								sway/config/output.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								sway/config/output.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,166 @@
 | 
				
			||||||
 | 
					#define _XOPEN_SOURCE 700
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					#include <wlr/types/wlr_output.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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void merge_output_config(struct output_config *dst, struct output_config *src) {
 | 
				
			||||||
 | 
						if (src->name) {
 | 
				
			||||||
 | 
							if (dst->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->background) {
 | 
				
			||||||
 | 
							if (dst->background) {
 | 
				
			||||||
 | 
								free(dst->background);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							dst->background = strdup(src->background);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (src->background_option) {
 | 
				
			||||||
 | 
							if (dst->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) {
 | 
				
			||||||
 | 
						struct wlr_output_mode *mode, *best = NULL;
 | 
				
			||||||
 | 
						int mhz = (int)(refresh_rate * 1000);
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (oc && oc->enabled == 0) {
 | 
				
			||||||
 | 
							destroy_output(output);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct wlr_output *wlr_output = output->sway_output->wlr_output;
 | 
				
			||||||
 | 
						if (oc && oc->width > 0 && oc->height > 0) {
 | 
				
			||||||
 | 
							set_mode(wlr_output, oc->width, oc->height, oc->refresh_rate);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (oc && oc->scale > 0) {
 | 
				
			||||||
 | 
							wlr_output->scale = oc->scale;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (oc && oc->transform != WL_OUTPUT_TRANSFORM_NORMAL) {
 | 
				
			||||||
 | 
							wlr_output_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);
 | 
				
			||||||
 | 
							output->x = oc->x;
 | 
				
			||||||
 | 
							output->y = oc->y;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							int x = 0;
 | 
				
			||||||
 | 
							for (int i = 0; i < root_container.children->length; ++i) {
 | 
				
			||||||
 | 
								swayc_t *c = root_container.children->items[i];
 | 
				
			||||||
 | 
								if (c->type == C_OUTPUT) {
 | 
				
			||||||
 | 
									if (c->width + c->x > x) {
 | 
				
			||||||
 | 
										x = c->width + c->x;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							output->x = x;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -404,6 +404,10 @@ int main(int argc, char **argv) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	security_sanity_check();
 | 
						security_sanity_check();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO: wait for server to be ready
 | 
				
			||||||
 | 
						// TODO: consume config->cmd_queue
 | 
				
			||||||
 | 
						config->active = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!terminate_request) {
 | 
						if (!terminate_request) {
 | 
				
			||||||
		server_run(&server);
 | 
							server_run(&server);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,9 @@ sway_sources = files(
 | 
				
			||||||
	'commands/exit.c',
 | 
						'commands/exit.c',
 | 
				
			||||||
	'commands/exec.c',
 | 
						'commands/exec.c',
 | 
				
			||||||
	'commands/exec_always.c',
 | 
						'commands/exec_always.c',
 | 
				
			||||||
 | 
						'commands/output.c',
 | 
				
			||||||
	'config.c',
 | 
						'config.c',
 | 
				
			||||||
 | 
						'config/output.c',
 | 
				
			||||||
	'ipc-json.c',
 | 
						'ipc-json.c',
 | 
				
			||||||
	'ipc-server.c',
 | 
						'ipc-server.c',
 | 
				
			||||||
	'desktop/output.c',
 | 
						'desktop/output.c',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,9 @@
 | 
				
			||||||
#include <stdint.h>
 | 
					#include <stdint.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <strings.h>
 | 
				
			||||||
#include <wlr/types/wlr_output_layout.h>
 | 
					#include <wlr/types/wlr_output_layout.h>
 | 
				
			||||||
 | 
					#include "sway/config.h"
 | 
				
			||||||
#include "sway/container.h"
 | 
					#include "sway/container.h"
 | 
				
			||||||
#include "sway/layout.h"
 | 
					#include "sway/layout.h"
 | 
				
			||||||
#include "sway/output.h"
 | 
					#include "sway/output.h"
 | 
				
			||||||
| 
						 | 
					@ -23,6 +25,30 @@ void swayc_descendants_of_type(swayc_t *root, enum swayc_types type,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void update_root_geometry() {
 | 
				
			||||||
 | 
						int width = 0;
 | 
				
			||||||
 | 
						int height = 0;
 | 
				
			||||||
 | 
						swayc_t *child;
 | 
				
			||||||
 | 
						int child_width;
 | 
				
			||||||
 | 
						int child_height;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (int i = 0; i < root_container.children->length; ++i) {
 | 
				
			||||||
 | 
							child = root_container.children->items[i];
 | 
				
			||||||
 | 
							child_width = child->width + child->x;
 | 
				
			||||||
 | 
							child_height = child->height + child->y;
 | 
				
			||||||
 | 
							if (child_width > width) {
 | 
				
			||||||
 | 
								width = child_width;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (child_height > height) {
 | 
				
			||||||
 | 
								height = child_height;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						root_container.width = width;
 | 
				
			||||||
 | 
						root_container.height = height;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static swayc_t *new_swayc(enum swayc_types type) {
 | 
					static swayc_t *new_swayc(enum swayc_types type) {
 | 
				
			||||||
	// next id starts at 1 because 0 is assigned to root_container in layout.c
 | 
						// next id starts at 1 because 0 is assigned to root_container in layout.c
 | 
				
			||||||
	static size_t next_id = 1;
 | 
						static size_t next_id = 1;
 | 
				
			||||||
| 
						 | 
					@ -44,10 +70,33 @@ static swayc_t *new_swayc(enum swayc_types type) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
swayc_t *new_output(struct sway_output *sway_output) {
 | 
					swayc_t *new_output(struct sway_output *sway_output) {
 | 
				
			||||||
	struct wlr_box size;
 | 
						struct wlr_box size;
 | 
				
			||||||
	wlr_output_effective_resolution(
 | 
						wlr_output_effective_resolution(sway_output->wlr_output, &size.width,
 | 
				
			||||||
			sway_output->wlr_output, &size.width, &size.height);
 | 
							&size.height);
 | 
				
			||||||
	const char *name = sway_output->wlr_output->name;
 | 
						const char *name = sway_output->wlr_output->name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct output_config *oc = NULL, *all = NULL;
 | 
				
			||||||
 | 
						for (int i = 0; i < config->output_configs->length; ++i) {
 | 
				
			||||||
 | 
							struct output_config *cur = config->output_configs->items[i];
 | 
				
			||||||
 | 
							if (strcasecmp(name, cur->name) == 0) {
 | 
				
			||||||
 | 
								sway_log(L_DEBUG, "Matched output config for %s", name);
 | 
				
			||||||
 | 
								oc = cur;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (strcasecmp("*", cur->name) == 0) {
 | 
				
			||||||
 | 
								sway_log(L_DEBUG, "Matched wildcard output config for %s", name);
 | 
				
			||||||
 | 
								all = cur;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (oc && all) {
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!oc) {
 | 
				
			||||||
 | 
							oc = all;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (oc && !oc->enabled) {
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	swayc_t *output = new_swayc(C_OUTPUT);
 | 
						swayc_t *output = new_swayc(C_OUTPUT);
 | 
				
			||||||
	output->sway_output = sway_output;
 | 
						output->sway_output = sway_output;
 | 
				
			||||||
	output->name = name ? strdup(name) : NULL;
 | 
						output->name = name ? strdup(name) : NULL;
 | 
				
			||||||
| 
						 | 
					@ -58,6 +107,8 @@ swayc_t *new_output(struct sway_output *sway_output) {
 | 
				
			||||||
	wlr_output_layout_add_auto(root_container.output_layout,
 | 
						wlr_output_layout_add_auto(root_container.output_layout,
 | 
				
			||||||
		sway_output->wlr_output);
 | 
							sway_output->wlr_output);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						apply_output_config(oc, output);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	add_child(&root_container, output);
 | 
						add_child(&root_container, output);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Create workspace
 | 
						// Create workspace
 | 
				
			||||||
| 
						 | 
					@ -139,6 +190,34 @@ static void free_swayc(swayc_t *cont) {
 | 
				
			||||||
	free(cont);
 | 
						free(cont);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					swayc_t *destroy_output(swayc_t *output) {
 | 
				
			||||||
 | 
						if (!sway_assert(output, "null output passed to destroy_output")) {
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (output->children->length > 0) {
 | 
				
			||||||
 | 
							// TODO save workspaces when there are no outputs.
 | 
				
			||||||
 | 
							// TODO also check if there will ever be no outputs except for exiting
 | 
				
			||||||
 | 
							// program
 | 
				
			||||||
 | 
							if (root_container.children->length > 1) {
 | 
				
			||||||
 | 
								int p = root_container.children->items[0] == output;
 | 
				
			||||||
 | 
								// Move workspace from this output to another output
 | 
				
			||||||
 | 
								while (output->children->length) {
 | 
				
			||||||
 | 
									swayc_t *child = output->children->items[0];
 | 
				
			||||||
 | 
									remove_child(child);
 | 
				
			||||||
 | 
									add_child(root_container.children->items[p], child);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								sort_workspaces(root_container.children->items[p]);
 | 
				
			||||||
 | 
								// TODO WLR: is this needed anymore?
 | 
				
			||||||
 | 
								//update_visibility(root_container.children->items[p]);
 | 
				
			||||||
 | 
								arrange_windows(root_container.children->items[p], -1, -1);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sway_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
 | 
				
			||||||
 | 
						free_swayc(output);
 | 
				
			||||||
 | 
						update_root_geometry();
 | 
				
			||||||
 | 
						return &root_container;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
swayc_t *destroy_view(swayc_t *view) {
 | 
					swayc_t *destroy_view(swayc_t *view) {
 | 
				
			||||||
	if (!sway_assert(view, "null view passed to destroy_view")) {
 | 
						if (!sway_assert(view, "null view passed to destroy_view")) {
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue