summaryrefslogtreecommitdiffstats
path: root/sway/commands/output.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/commands/output.c')
-rw-r--r--sway/commands/output.c429
1 files changed, 266 insertions, 163 deletions
diff --git a/sway/commands/output.c b/sway/commands/output.c
index 911391d2..f7e3372c 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -17,196 +17,299 @@ static char *bg_options[] = {
17 "center", 17 "center",
18 "fill", 18 "fill",
19 "fit", 19 "fit",
20 "tile" 20 "tile",
21}; 21};
22 22
23struct cmd_results *cmd_output(int argc, char **argv) { 23static struct cmd_results *cmd_output_mode(struct output_config *output,
24 struct cmd_results *error = NULL; 24 int *i, int argc, char **argv) {
25 if ((error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1))) { 25 if (++*i >= argc) {
26 return error; 26 return cmd_results_new(CMD_INVALID, "output", "Missing mode argument.");
27 } 27 }
28 const char *name = argv[0];
29 28
30 struct output_config *output = calloc(1, sizeof(struct output_config)); 29 char *end;
31 if (!output) { 30 output->width = strtol(argv[*i], &end, 10);
32 return cmd_results_new(CMD_FAILURE, "output", "Unable to allocate output config"); 31 if (*end) {
32 // Format is 1234x4321
33 if (*end != 'x') {
34 return cmd_results_new(CMD_INVALID, "output",
35 "Invalid mode width.");
36 }
37 ++end;
38 output->height = strtol(end, &end, 10);
39 if (*end) {
40 if (*end != '@') {
41 return cmd_results_new(CMD_INVALID, "output",
42 "Invalid mode height.");
43 }
44 ++end;
45 output->refresh_rate = strtof(end, &end);
46 if (strcasecmp("Hz", end) != 0) {
47 return cmd_results_new(CMD_INVALID, "output",
48 "Invalid mode refresh rate.");
49 }
50 }
51 } else {
52 // Format is 1234 4321
53 if (++*i >= argc) {
54 return cmd_results_new(CMD_INVALID, "output",
55 "Missing mode argument (height).");
56 }
57 output->height = strtol(argv[*i], &end, 10);
58 if (*end) {
59 return cmd_results_new(CMD_INVALID, "output",
60 "Invalid mode height.");
61 }
33 } 62 }
34 output->x = output->y = output->width = output->height = -1;
35 output->name = strdup(name);
36 output->enabled = -1;
37 output->scale = 1;
38 63
39 // TODO: atoi doesn't handle invalid numbers 64 return NULL;
65}
40 66
41 int i; 67static struct cmd_results *cmd_output_position(struct output_config *output,
42 for (i = 1; i < argc; ++i) { 68 int *i, int argc, char **argv) {
43 const char *command = argv[i]; 69 if (++*i >= argc) {
70 return cmd_results_new(CMD_INVALID, "output",
71 "Missing position argument.");
72 }
44 73
45 if (strcasecmp(command, "disable") == 0) { 74 char *end;
46 output->enabled = 0; 75 output->x = strtol(argv[*i], &end, 10);
47 } else if (strcasecmp(command, "resolution") == 0 || strcasecmp(command, "res") == 0) { 76 if (*end) {
48 if (++i >= argc) { 77 // Format is 1234,4321
49 error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument."); 78 if (*end != ',') {
50 goto fail; 79 return cmd_results_new(CMD_INVALID, "output",
51 } 80 "Invalid position x.");
52 char *res = argv[i]; 81 }
53 char *x = strchr(res, 'x'); 82 ++end;
54 int width = -1, height = -1; 83 output->y = strtol(end, &end, 10);
55 if (x != NULL) { 84 if (*end) {
56 // Format is 1234x4321 85 return cmd_results_new(CMD_INVALID, "output",
57 *x = '\0'; 86 "Invalid position y.");
58 width = atoi(res); 87 }
59 height = atoi(x + 1); 88 } else {
60 *x = 'x'; 89 // Format is 1234 4321 (legacy)
61 } else { 90 if (++*i >= argc) {
62 // Format is 1234 4321 91 return cmd_results_new(CMD_INVALID, "output",
63 width = atoi(res); 92 "Missing position argument (y).");
64 if (++i >= argc) { 93 }
65 error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument (height)."); 94 output->y = strtol(argv[*i], &end, 10);
66 goto fail; 95 if (*end) {
96 return cmd_results_new(CMD_INVALID, "output",
97 "Invalid position y.");
98 }
99 }
100
101 return NULL;
102}
103
104static struct cmd_results *cmd_output_scale(struct output_config *output,
105 int *i, int argc, char **argv) {
106 if (++*i >= argc) {
107 return cmd_results_new(CMD_INVALID, "output",
108 "Missing scale argument.");
109 }
110
111 char *end;
112 output->scale = strtof(argv[*i], &end);
113 if (*end) {
114 return cmd_results_new(CMD_INVALID, "output", "Invalid scale.");
115 }
116
117 return NULL;
118}
119
120static struct cmd_results *cmd_output_transform(struct output_config *output,
121 int *i, int argc, char **argv) {
122 if (++*i >= argc) {
123 return cmd_results_new(CMD_INVALID, "output",
124 "Missing transform argument.");
125 }
126
127 char *value = argv[*i];
128 if (strcmp(value, "normal") == 0) {
129 output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
130 } else if (strcmp(value, "90") == 0) {
131 output->transform = WL_OUTPUT_TRANSFORM_90;
132 } else if (strcmp(value, "180") == 0) {
133 output->transform = WL_OUTPUT_TRANSFORM_180;
134 } else if (strcmp(value, "270") == 0) {
135 output->transform = WL_OUTPUT_TRANSFORM_270;
136 } else if (strcmp(value, "flipped") == 0) {
137 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED;
138 } else if (strcmp(value, "flipped-90") == 0) {
139 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90;
140 } else if (strcmp(value, "flipped-180") == 0) {
141 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180;
142 } else if (strcmp(value, "flipped-270") == 0) {
143 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270;
144 } else {
145 return cmd_results_new(CMD_INVALID, "output",
146 "Invalid output transform.");
147 }
148
149 return NULL;
150}
151
152static struct cmd_results *cmd_output_background(struct output_config *output,
153 int *i, int argc, char **argv) {
154 if (++*i >= argc) {
155 return cmd_results_new(CMD_INVALID, "output",
156 "Missing background file or color specification.");
157 }
158 const char *background = argv[*i];
159 if (*i + 1 >= argc) {
160 return cmd_results_new(CMD_INVALID, "output",
161 "Missing background scaling mode or `solid_color`.");
162 }
163 const char *background_option = argv[*i];
164
165 if (strcasecmp(background_option, "solid_color") == 0) {
166 output->background = strdup(background);
167 output->background_option = strdup("solid_color");
168 } else {
169 bool valid = false;
170 char *mode;
171 size_t j;
172 for (j = 0; j < (size_t)(argc - *i); ++j) {
173 mode = argv[*i + j];
174 size_t n = sizeof(bg_options) / sizeof(char *);
175 for (size_t k = 0; k < n; ++k) {
176 if (strcasecmp(mode, bg_options[k]) == 0) {
177 valid = true;
178 break;
67 } 179 }
68 res = argv[i];
69 height = atoi(res);
70 } 180 }
71 output->width = width; 181 if (valid) {
72 output->height = height; 182 break;
73 } else if (strcasecmp(command, "position") == 0 || strcasecmp(command, "pos") == 0) {
74 if (++i >= argc) {
75 error = cmd_results_new(CMD_INVALID, "output", "Missing position argument.");
76 goto fail;
77 } 183 }
78 char *res = argv[i]; 184 }
79 char *c = strchr(res, ','); 185 if (!valid) {
80 int x = -1, y = -1; 186 return cmd_results_new(CMD_INVALID, "output",
81 if (c != NULL) { 187 "Missing background scaling mode.");
82 // Format is 1234,4321 188 }
83 *c = '\0'; 189
84 x = atoi(res); 190 wordexp_t p;
85 y = atoi(c + 1); 191 char *src = join_args(argv + *i, j);
86 *c = ','; 192 if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) {
87 } else { 193 return cmd_results_new(CMD_INVALID, "output",
88 // Format is 1234 4321 194 "Invalid syntax (%s).", src);
89 x = atoi(res); 195 }
90 if (++i >= argc) { 196 free(src);
91 error = cmd_results_new(CMD_INVALID, "output", "Missing position argument (y)."); 197 src = p.we_wordv[0];
92 goto fail; 198 if (config->reading && *src != '/') {
199 char *conf = strdup(config->current_config);
200 if (conf) {
201 char *conf_path = dirname(conf);
202 src = malloc(strlen(conf_path) + strlen(src) + 2);
203 if (src) {
204 sprintf(src, "%s/%s", conf_path, p.we_wordv[0]);
205 } else {
206 wlr_log(L_ERROR,
207 "Unable to allocate background source");
93 } 208 }
94 res = argv[i]; 209 free(conf);
95 y = atoi(res);
96 }
97 output->x = x;
98 output->y = y;
99 } else if (strcasecmp(command, "scale") == 0) {
100 if (++i >= argc) {
101 error = cmd_results_new(CMD_INVALID, "output", "Missing scale parameter.");
102 goto fail;
103 }
104 output->scale = atoi(argv[i]);
105 } else if (strcasecmp(command, "background") == 0 || strcasecmp(command, "bg") == 0) {
106 wordexp_t p;
107 if (++i >= argc) {
108 error = cmd_results_new(CMD_INVALID, "output", "Missing background file or color specification.");
109 goto fail;
110 }
111 if (i + 1 >= argc) {
112 error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode or `solid_color`.");
113 goto fail;
114 }
115 if (strcasecmp(argv[i + 1], "solid_color") == 0) {
116 output->background = strdup(argv[argc - 2]);
117 output->background_option = strdup("solid_color");
118 } else { 210 } else {
119 // argv[i+j]=bg_option 211 wlr_log(L_ERROR, "Unable to allocate background source");
120 bool valid = false; 212 }
121 char *mode; 213 }
122 size_t j; 214 if (!src || access(src, F_OK) == -1) {
123 for (j = 0; j < (size_t) (argc - i); ++j) { 215 wordfree(&p);
124 mode = argv[i + j]; 216 return cmd_results_new(CMD_INVALID, "output",
125 for (size_t k = 0; k < sizeof(bg_options) / sizeof(char *); ++k) { 217 "Background file unreadable (%s).", src);
126 if (strcasecmp(mode, bg_options[k]) == 0) { 218 }
127 valid = true;
128 break;
129 }
130 }
131 if (valid) {
132 break;
133 }
134 }
135 if (!valid) {
136 error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode.");
137 goto fail;
138 }
139 219
140 char *src = join_args(argv + i, j); 220 output->background = strdup(src);
141 if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) { 221 output->background_option = strdup(mode);
142 error = cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s)", src); 222 if (src != p.we_wordv[0]) {
143 goto fail; 223 free(src);
144 } 224 }
145 free(src); 225 wordfree(&p);
146 src = p.we_wordv[0];
147 if (config->reading && *src != '/') {
148 char *conf = strdup(config->current_config);
149 if (conf) {
150 char *conf_path = dirname(conf);
151 src = malloc(strlen(conf_path) + strlen(src) + 2);
152 if (src) {
153 sprintf(src, "%s/%s", conf_path, p.we_wordv[0]);
154 } else {
155 sway_log(L_ERROR, "Unable to allocate background source");
156 }
157 free(conf);
158 } else {
159 sway_log(L_ERROR, "Unable to allocate background source");
160 }
161 }
162 if (!src || access(src, F_OK) == -1) {
163 error = cmd_results_new(CMD_INVALID, "output", "Background file unreadable (%s)", src);
164 wordfree(&p);
165 goto fail;
166 }
167 226
168 output->background = strdup(src); 227 *i += j;
169 output->background_option = strdup(mode); 228 }
170 if (src != p.we_wordv[0]) {
171 free(src);
172 }
173 wordfree(&p);
174 229
175 i += j; 230 return NULL;
176 } 231}
232
233struct cmd_results *cmd_output(int argc, char **argv) {
234 struct cmd_results *error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1);
235 if (error != NULL) {
236 return error;
237 }
238
239 struct output_config *output = new_output_config(argv[0]);
240 if (!output) {
241 wlr_log(L_ERROR, "Failed to allocate output config");
242 return NULL;
243 }
244
245 for (int i = 1; i < argc; ++i) {
246 const char *command = argv[i];
247
248 if (strcasecmp(command, "enable") == 0) {
249 output->enabled = 1;
250 } else if (strcasecmp(command, "disable") == 0) {
251 output->enabled = 0;
252 } else if (strcasecmp(command, "mode") == 0 ||
253 strcasecmp(command, "resolution") == 0 ||
254 strcasecmp(command, "res") == 0) {
255 error = cmd_output_mode(output, &i, argc, argv);
256 } else if (strcasecmp(command, "position") == 0 ||
257 strcasecmp(command, "pos") == 0) {
258 error = cmd_output_position(output, &i, argc, argv);
259 } else if (strcasecmp(command, "scale") == 0) {
260 error = cmd_output_scale(output, &i, argc, argv);
261 } else if (strcasecmp(command, "transform") == 0) {
262 error = cmd_output_transform(output, &i, argc, argv);
263 } else if (strcasecmp(command, "background") == 0 ||
264 strcasecmp(command, "bg") == 0) {
265 error = cmd_output_background(output, &i, argc, argv);
266 } else {
267 error = cmd_results_new(CMD_INVALID, "output",
268 "Invalid output subcommand: %s.", command);
269 }
270
271 if (error != NULL) {
272 goto fail;
177 } 273 }
178 } 274 }
179 275
180 i = list_seq_find(config->output_configs, output_name_cmp, name); 276 int i = list_seq_find(config->output_configs, output_name_cmp, output->name);
181 if (i >= 0) { 277 if (i >= 0) {
182 // merge existing config 278 // Merge existing config
183 struct output_config *oc = config->output_configs->items[i]; 279 struct output_config *current = config->output_configs->items[i];
184 merge_output_config(oc, output); 280 merge_output_config(current, output);
185 free_output_config(output); 281 free_output_config(output);
186 output = oc; 282 output = current;
187 } else { 283 } else {
188 list_add(config->output_configs, output); 284 list_add(config->output_configs, output);
189 } 285 }
190 286
191 sway_log(L_DEBUG, "Config stored for output %s (enabled:%d) (%d x %d @ %d, %d scale %d) (bg %s %s)", 287 wlr_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
192 output->name, output->enabled, output->width, 288 "position %d,%d scale %f transform %d) (bg %s %s)",
193 output->height, output->x, output->y, output->scale, 289 output->name, output->enabled, output->width, output->height,
194 output->background, output->background_option); 290 output->refresh_rate, output->x, output->y, output->scale,
291 output->transform, output->background, output->background_option);
195 292
196 if (output->name) { 293 // Try to find the output container and apply configuration now. If
197 // Try to find the output container and apply configuration now. If 294 // this is during startup then there will be no container and config
198 // this is during startup then there will be no container and config 295 // will be applied during normal "new output" event from wlroots.
199 // will be applied during normal "new output" event from wlc. 296 char identifier[128];
200 swayc_t *cont = NULL; 297 bool all = strcmp(output->name, "*") == 0;
201 for (int i = 0; i < root_container.children->length; ++i) { 298 for (int i = 0; i < root_container.children->length; ++i) {
202 cont = root_container.children->items[i]; 299 struct sway_container *cont = root_container.children->items[i];
203 if (cont->name && ((strcmp(cont->name, output->name) == 0) || (strcmp(output->name, "*") == 0))) { 300 if (cont->type != C_OUTPUT) {
204 apply_output_config(output, cont); 301 continue;
302 }
205 303
206 if (strcmp(output->name, "*") != 0) { 304 output_get_identifier(identifier, sizeof(identifier), cont->sway_output);
207 // stop looking if the output config isn't applicable to all outputs 305 if (all || strcmp(cont->name, output->name) == 0 ||
208 break; 306 strcmp(identifier, output->name) == 0) {
209 } 307 apply_output_config(output, cont);
308
309 if (!all) {
310 // Stop looking if the output config isn't applicable to all
311 // outputs
312 break;
210 } 313 }
211 } 314 }
212 } 315 }