1 #define _POSIX_C_SOURCE 200809L
2 #include <assert.h>
3 #include <stdbool.h>
4 #include <string.h>
5 #include <sys/socket.h>
6 #include <sys/wait.h>
7 #include <unistd.h>
8 #include <wlr/types/wlr_cursor.h>
9 #include <wlr/types/wlr_output_layout.h>
10 #include <wlr/types/wlr_output.h>
11 #include "sway/config.h"
12 #include "sway/input/cursor.h"
13 #include "sway/output.h"
14 #include "sway/tree/root.h"
15 #include "log.h"
16 #include "util.h"
17
output_name_cmp(const void * item,const void * data)18 int output_name_cmp(const void *item, const void *data) {
19 const struct output_config *output = item;
20 const char *name = data;
21
22 return strcmp(output->name, name);
23 }
24
output_get_identifier(char * identifier,size_t len,struct sway_output * output)25 void output_get_identifier(char *identifier, size_t len,
26 struct sway_output *output) {
27 struct wlr_output *wlr_output = output->wlr_output;
28 snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model,
29 wlr_output->serial);
30 }
31
sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter)32 const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) {
33 switch (scale_filter) {
34 case SCALE_FILTER_DEFAULT:
35 return "smart";
36 case SCALE_FILTER_LINEAR:
37 return "linear";
38 case SCALE_FILTER_NEAREST:
39 return "nearest";
40 case SCALE_FILTER_SMART:
41 return "smart";
42 }
43 sway_assert(false, "Unknown value for scale_filter.");
44 return NULL;
45 }
46
new_output_config(const char * name)47 struct output_config *new_output_config(const char *name) {
48 struct output_config *oc = calloc(1, sizeof(struct output_config));
49 if (oc == NULL) {
50 return NULL;
51 }
52 oc->name = strdup(name);
53 if (oc->name == NULL) {
54 free(oc);
55 return NULL;
56 }
57 oc->enabled = -1;
58 oc->width = oc->height = -1;
59 oc->refresh_rate = -1;
60 oc->custom_mode = -1;
61 oc->x = oc->y = -1;
62 oc->scale = -1;
63 oc->scale_filter = SCALE_FILTER_DEFAULT;
64 oc->transform = -1;
65 oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
66 oc->max_render_time = -1;
67 oc->adaptive_sync = -1;
68 return oc;
69 }
70
merge_output_config(struct output_config * dst,struct output_config * src)71 void merge_output_config(struct output_config *dst, struct output_config *src) {
72 if (src->enabled != -1) {
73 dst->enabled = src->enabled;
74 }
75 if (src->width != -1) {
76 dst->width = src->width;
77 }
78 if (src->height != -1) {
79 dst->height = src->height;
80 }
81 if (src->x != -1) {
82 dst->x = src->x;
83 }
84 if (src->y != -1) {
85 dst->y = src->y;
86 }
87 if (src->scale != -1) {
88 dst->scale = src->scale;
89 }
90 if (src->scale_filter != SCALE_FILTER_DEFAULT) {
91 dst->scale_filter = src->scale_filter;
92 }
93 if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) {
94 dst->subpixel = src->subpixel;
95 }
96 if (src->refresh_rate != -1) {
97 dst->refresh_rate = src->refresh_rate;
98 }
99 if (src->custom_mode != -1) {
100 dst->custom_mode = src->custom_mode;
101 }
102 if (src->transform != -1) {
103 dst->transform = src->transform;
104 }
105 if (src->max_render_time != -1) {
106 dst->max_render_time = src->max_render_time;
107 }
108 if (src->adaptive_sync != -1) {
109 dst->adaptive_sync = src->adaptive_sync;
110 }
111 if (src->background) {
112 free(dst->background);
113 dst->background = strdup(src->background);
114 }
115 if (src->background_option) {
116 free(dst->background_option);
117 dst->background_option = strdup(src->background_option);
118 }
119 if (src->background_fallback) {
120 free(dst->background_fallback);
121 dst->background_fallback = strdup(src->background_fallback);
122 }
123 if (src->dpms_state != 0) {
124 dst->dpms_state = src->dpms_state;
125 }
126 }
127
merge_wildcard_on_all(struct output_config * wildcard)128 static void merge_wildcard_on_all(struct output_config *wildcard) {
129 for (int i = 0; i < config->output_configs->length; i++) {
130 struct output_config *oc = config->output_configs->items[i];
131 if (strcmp(wildcard->name, oc->name) != 0) {
132 sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name);
133 merge_output_config(oc, wildcard);
134 }
135 }
136 }
137
merge_id_on_name(struct output_config * oc)138 static void merge_id_on_name(struct output_config *oc) {
139 char *id_on_name = NULL;
140 char id[128];
141 char *name = NULL;
142 struct sway_output *output;
143 wl_list_for_each(output, &root->all_outputs, link) {
144 name = output->wlr_output->name;
145 output_get_identifier(id, sizeof(id), output);
146 if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) {
147 size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1;
148 id_on_name = malloc(length);
149 if (!id_on_name) {
150 sway_log(SWAY_ERROR, "Failed to allocate id on name string");
151 return;
152 }
153 snprintf(id_on_name, length, "%s on %s", id, name);
154 break;
155 }
156 }
157
158 if (!id_on_name) {
159 return;
160 }
161
162 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name);
163 if (i >= 0) {
164 sway_log(SWAY_DEBUG, "Merging on top of existing id on name config");
165 merge_output_config(config->output_configs->items[i], oc);
166 } else {
167 // If both a name and identifier config, exist generate an id on name
168 int ni = list_seq_find(config->output_configs, output_name_cmp, name);
169 int ii = list_seq_find(config->output_configs, output_name_cmp, id);
170 if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0)
171 || (ii >= 0 && strcmp(oc->name, name) == 0)) {
172 struct output_config *ion_oc = new_output_config(id_on_name);
173 if (ni >= 0) {
174 merge_output_config(ion_oc, config->output_configs->items[ni]);
175 }
176 if (ii >= 0) {
177 merge_output_config(ion_oc, config->output_configs->items[ii]);
178 }
179 merge_output_config(ion_oc, oc);
180 list_add(config->output_configs, ion_oc);
181 sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\""
182 " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f "
183 "transform %d) (bg %s %s) (dpms %d) (max render time: %d)",
184 ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height,
185 ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale,
186 ion_oc->transform, ion_oc->background,
187 ion_oc->background_option, ion_oc->dpms_state,
188 ion_oc->max_render_time);
189 }
190 }
191 free(id_on_name);
192 }
193
store_output_config(struct output_config * oc)194 struct output_config *store_output_config(struct output_config *oc) {
195 bool wildcard = strcmp(oc->name, "*") == 0;
196 if (wildcard) {
197 merge_wildcard_on_all(oc);
198 } else {
199 merge_id_on_name(oc);
200 }
201
202 int i = list_seq_find(config->output_configs, output_name_cmp, oc->name);
203 if (i >= 0) {
204 sway_log(SWAY_DEBUG, "Merging on top of existing output config");
205 struct output_config *current = config->output_configs->items[i];
206 merge_output_config(current, oc);
207 free_output_config(oc);
208 oc = current;
209 } else if (!wildcard) {
210 sway_log(SWAY_DEBUG, "Adding non-wildcard output config");
211 i = list_seq_find(config->output_configs, output_name_cmp, "*");
212 if (i >= 0) {
213 sway_log(SWAY_DEBUG, "Merging on top of output * config");
214 struct output_config *current = new_output_config(oc->name);
215 merge_output_config(current, config->output_configs->items[i]);
216 merge_output_config(current, oc);
217 free_output_config(oc);
218 oc = current;
219 }
220 list_add(config->output_configs, oc);
221 } else {
222 // New wildcard config. Just add it
223 sway_log(SWAY_DEBUG, "Adding output * config");
224 list_add(config->output_configs, oc);
225 }
226
227 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
228 "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d) "
229 "(max render time: %d)",
230 oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate,
231 oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel),
232 oc->transform, oc->background, oc->background_option, oc->dpms_state,
233 oc->max_render_time);
234
235 return oc;
236 }
237
set_mode(struct wlr_output * output,int width,int height,float refresh_rate,bool custom)238 static void set_mode(struct wlr_output *output, int width, int height,
239 float refresh_rate, bool custom) {
240 int mhz = (int)(refresh_rate * 1000);
241
242 if (wl_list_empty(&output->modes) || custom) {
243 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name);
244 wlr_output_set_custom_mode(output, width, height,
245 refresh_rate > 0 ? mhz : 0);
246 return;
247 }
248
249 struct wlr_output_mode *mode, *best = NULL;
250 wl_list_for_each(mode, &output->modes, link) {
251 if (mode->width == width && mode->height == height) {
252 if (mode->refresh == mhz) {
253 best = mode;
254 break;
255 }
256 if (best == NULL || mode->refresh > best->refresh) {
257 best = mode;
258 }
259 }
260 }
261 if (!best) {
262 sway_log(SWAY_ERROR, "Configured mode for %s not available", output->name);
263 sway_log(SWAY_INFO, "Picking preferred mode instead");
264 best = wlr_output_preferred_mode(output);
265 } else {
266 sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name);
267 }
268 wlr_output_set_mode(output, best);
269 }
270
271 /* Some manufacturers hardcode the aspect-ratio of the output in the physical
272 * size field. */
phys_size_is_aspect_ratio(struct wlr_output * output)273 static bool phys_size_is_aspect_ratio(struct wlr_output *output) {
274 return (output->phys_width == 1600 && output->phys_height == 900) ||
275 (output->phys_width == 1600 && output->phys_height == 1000) ||
276 (output->phys_width == 160 && output->phys_height == 90) ||
277 (output->phys_width == 160 && output->phys_height == 100) ||
278 (output->phys_width == 16 && output->phys_height == 9) ||
279 (output->phys_width == 16 && output->phys_height == 10);
280 }
281
282 // The minimum DPI at which we turn on a scale of 2
283 #define HIDPI_DPI_LIMIT (2 * 96)
284 // The minimum screen height at which we turn on a scale of 2
285 #define HIDPI_MIN_HEIGHT 1200
286 // 1 inch = 25.4 mm
287 #define MM_PER_INCH 25.4
288
compute_default_scale(struct wlr_output * output)289 static int compute_default_scale(struct wlr_output *output) {
290 struct wlr_box box = { .width = output->width, .height = output->height };
291 if (output->pending.committed & WLR_OUTPUT_STATE_MODE) {
292 switch (output->pending.mode_type) {
293 case WLR_OUTPUT_STATE_MODE_FIXED:
294 box.width = output->pending.mode->width;
295 box.height = output->pending.mode->height;
296 break;
297 case WLR_OUTPUT_STATE_MODE_CUSTOM:
298 box.width = output->pending.custom_mode.width;
299 box.height = output->pending.custom_mode.height;
300 break;
301 }
302 }
303 enum wl_output_transform transform = output->transform;
304 if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) {
305 transform = output->pending.transform;
306 }
307 wlr_box_transform(&box, &box, transform, box.width, box.height);
308
309 int width = box.width;
310 int height = box.height;
311
312 if (height < HIDPI_MIN_HEIGHT) {
313 return 1;
314 }
315
316 if (output->phys_width == 0 || output->phys_height == 0) {
317 return 1;
318 }
319
320 if (phys_size_is_aspect_ratio(output)) {
321 return 1;
322 }
323
324 double dpi_x = (double) width / (output->phys_width / MM_PER_INCH);
325 double dpi_y = (double) height / (output->phys_height / MM_PER_INCH);
326 sway_log(SWAY_DEBUG, "Output DPI: %fx%f", dpi_x, dpi_y);
327 if (dpi_x <= HIDPI_DPI_LIMIT || dpi_y <= HIDPI_DPI_LIMIT) {
328 return 1;
329 }
330
331 return 2;
332 }
333
queue_output_config(struct output_config * oc,struct sway_output * output)334 static void queue_output_config(struct output_config *oc,
335 struct sway_output *output) {
336 if (output == root->noop_output) {
337 return;
338 }
339
340 struct wlr_output *wlr_output = output->wlr_output;
341
342 if (oc && (!oc->enabled || oc->dpms_state == DPMS_OFF)) {
343 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
344 wlr_output_enable(wlr_output, false);
345 return;
346 }
347
348 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name);
349 wlr_output_enable(wlr_output, true);
350
351 if (oc && oc->width > 0 && oc->height > 0) {
352 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)",
353 wlr_output->name, oc->width, oc->height, oc->refresh_rate);
354 set_mode(wlr_output, oc->width, oc->height,
355 oc->refresh_rate, oc->custom_mode == 1);
356 } else if (!wl_list_empty(&wlr_output->modes)) {
357 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
358 wlr_output_set_mode(wlr_output, mode);
359 }
360
361 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
362 sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name,
363 sway_wl_output_subpixel_to_string(oc->subpixel));
364 wlr_output_set_subpixel(wlr_output, oc->subpixel);
365 }
366
367 if (oc && oc->transform >= 0) {
368 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform);
369 wlr_output_set_transform(wlr_output, oc->transform);
370 }
371
372 // Apply the scale last before the commit, because the scale auto-detection
373 // reads the pending output size
374 float scale;
375 if (oc && oc->scale > 0) {
376 scale = oc->scale;
377 } else {
378 scale = compute_default_scale(wlr_output);
379 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale);
380 }
381 if (scale != wlr_output->scale) {
382 sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale);
383 wlr_output_set_scale(wlr_output, scale);
384 }
385
386 if (oc && oc->adaptive_sync != -1) {
387 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name,
388 oc->adaptive_sync);
389 wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1);
390 }
391 }
392
apply_output_config(struct output_config * oc,struct sway_output * output)393 bool apply_output_config(struct output_config *oc, struct sway_output *output) {
394 if (output == root->noop_output) {
395 return false;
396 }
397
398 struct wlr_output *wlr_output = output->wlr_output;
399
400 // Flag to prevent the output mode event handler from calling us
401 output->enabling = (!oc || oc->enabled);
402
403 queue_output_config(oc, output);
404
405 if (!oc || oc->dpms_state != DPMS_OFF) {
406 output->current_mode = wlr_output->pending.mode;
407 }
408
409 sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name);
410 if (!wlr_output_commit(wlr_output)) {
411 // Failed to commit output changes, maybe the output is missing a CRTC.
412 // Leave the output disabled for now and try again when the output gets
413 // the mode we asked for.
414 sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name);
415 output->enabling = false;
416 return false;
417 }
418
419 output->enabling = false;
420
421 if (oc && !oc->enabled) {
422 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name);
423 if (output->enabled) {
424 output_disable(output);
425 wlr_output_layout_remove(root->output_layout, wlr_output);
426 }
427 return true;
428 }
429
430 if (config->reloading) {
431 output_damage_whole(output);
432 }
433
434 if (oc) {
435 enum scale_filter_mode scale_filter_old = output->scale_filter;
436 switch (oc->scale_filter) {
437 case SCALE_FILTER_DEFAULT:
438 case SCALE_FILTER_SMART:
439 output->scale_filter = ceilf(wlr_output->scale) == wlr_output->scale ?
440 SCALE_FILTER_NEAREST : SCALE_FILTER_LINEAR;
441 break;
442 case SCALE_FILTER_LINEAR:
443 case SCALE_FILTER_NEAREST:
444 output->scale_filter = oc->scale_filter;
445 break;
446 }
447 if (scale_filter_old != output->scale_filter) {
448 sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name,
449 sway_output_scale_filter_to_string(output->scale_filter));
450 }
451 }
452
453 // Find position for it
454 if (oc && (oc->x != -1 || oc->y != -1)) {
455 sway_log(SWAY_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y);
456 wlr_output_layout_add(root->output_layout, wlr_output, oc->x, oc->y);
457 } else {
458 wlr_output_layout_add_auto(root->output_layout, wlr_output);
459 }
460
461 // Update output->{lx, ly, width, height}
462 struct wlr_box *output_box =
463 wlr_output_layout_get_box(root->output_layout, wlr_output);
464 output->lx = output_box->x;
465 output->ly = output_box->y;
466 output->width = output_box->width;
467 output->height = output_box->height;
468
469 if (!output->enabled) {
470 output_enable(output);
471 }
472
473 if (oc && oc->max_render_time >= 0) {
474 sway_log(SWAY_DEBUG, "Set %s max render time to %d",
475 oc->name, oc->max_render_time);
476 output->max_render_time = oc->max_render_time;
477 }
478
479 // Reconfigure all devices, since input config may have been applied before
480 // this output came online, and some config items (like map_to_output) are
481 // dependent on an output being present.
482 input_manager_configure_all_inputs();
483 return true;
484 }
485
test_output_config(struct output_config * oc,struct sway_output * output)486 bool test_output_config(struct output_config *oc, struct sway_output *output) {
487 if (output == root->noop_output) {
488 return false;
489 }
490
491 queue_output_config(oc, output);
492 bool ok = wlr_output_test(output->wlr_output);
493 wlr_output_rollback(output->wlr_output);
494 return ok;
495 }
496
default_output_config(struct output_config * oc,struct wlr_output * wlr_output)497 static void default_output_config(struct output_config *oc,
498 struct wlr_output *wlr_output) {
499 oc->enabled = 1;
500 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
501 if (mode != NULL) {
502 oc->width = mode->width;
503 oc->height = mode->height;
504 oc->refresh_rate = mode->refresh / 1000.f;
505 }
506 oc->x = oc->y = -1;
507 oc->scale = 0; // auto
508 oc->scale_filter = SCALE_FILTER_DEFAULT;
509 struct sway_output *output = wlr_output->data;
510 oc->subpixel = output->detected_subpixel;
511 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
512 oc->dpms_state = DPMS_ON;
513 oc->max_render_time = 0;
514 }
515
get_output_config(char * identifier,struct sway_output * sway_output)516 static struct output_config *get_output_config(char *identifier,
517 struct sway_output *sway_output) {
518 const char *name = sway_output->wlr_output->name;
519
520 struct output_config *oc_id_on_name = NULL;
521 struct output_config *oc_name = NULL;
522 struct output_config *oc_id = NULL;
523
524 size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1;
525 char *id_on_name = malloc(length);
526 snprintf(id_on_name, length, "%s on %s", identifier, name);
527 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name);
528 if (i >= 0) {
529 oc_id_on_name = config->output_configs->items[i];
530 } else {
531 i = list_seq_find(config->output_configs, output_name_cmp, name);
532 if (i >= 0) {
533 oc_name = config->output_configs->items[i];
534 }
535
536 i = list_seq_find(config->output_configs, output_name_cmp, identifier);
537 if (i >= 0) {
538 oc_id = config->output_configs->items[i];
539 }
540 }
541
542 struct output_config *result = new_output_config("temp");
543 if (config->reloading) {
544 default_output_config(result, sway_output->wlr_output);
545 }
546 if (oc_id_on_name) {
547 // Already have an identifier on name config, use that
548 free(result->name);
549 result->name = strdup(id_on_name);
550 merge_output_config(result, oc_id_on_name);
551 } else if (oc_name && oc_id) {
552 // Generate a config named `<identifier> on <name>` which contains a
553 // merged copy of the identifier on name. This will make sure that both
554 // identifier and name configs are respected, with identifier getting
555 // priority
556 struct output_config *temp = new_output_config(id_on_name);
557 merge_output_config(temp, oc_name);
558 merge_output_config(temp, oc_id);
559 list_add(config->output_configs, temp);
560
561 free(result->name);
562 result->name = strdup(id_on_name);
563 merge_output_config(result, temp);
564
565 sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)"
566 " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
567 " (dpms %d) (max render time: %d)", result->name, result->enabled,
568 result->width, result->height, result->refresh_rate,
569 result->x, result->y, result->scale, result->transform,
570 result->background, result->background_option, result->dpms_state,
571 result->max_render_time);
572 } else if (oc_name) {
573 // No identifier config, just return a copy of the name config
574 free(result->name);
575 result->name = strdup(name);
576 merge_output_config(result, oc_name);
577 } else if (oc_id) {
578 // No name config, just return a copy of the identifier config
579 free(result->name);
580 result->name = strdup(identifier);
581 merge_output_config(result, oc_id);
582 } else {
583 i = list_seq_find(config->output_configs, output_name_cmp, "*");
584 if (i >= 0) {
585 // No name or identifier config, but there is a wildcard config
586 free(result->name);
587 result->name = strdup("*");
588 merge_output_config(result, config->output_configs->items[i]);
589 } else if (!config->reloading) {
590 // No name, identifier, or wildcard config. Since we are not
591 // reloading with defaults, the output config will be empty, so
592 // just return NULL
593 free_output_config(result);
594 result = NULL;
595 }
596 }
597
598 free(id_on_name);
599 return result;
600 }
601
find_output_config(struct sway_output * output)602 struct output_config *find_output_config(struct sway_output *output) {
603 char id[128];
604 output_get_identifier(id, sizeof(id), output);
605 return get_output_config(id, output);
606 }
607
apply_output_config_to_outputs(struct output_config * oc)608 void apply_output_config_to_outputs(struct output_config *oc) {
609 // Try to find the output container and apply configuration now. If
610 // this is during startup then there will be no container and config
611 // will be applied during normal "new output" event from wlroots.
612 bool wildcard = strcmp(oc->name, "*") == 0;
613 char id[128];
614 struct sway_output *sway_output, *tmp;
615 wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) {
616 char *name = sway_output->wlr_output->name;
617 output_get_identifier(id, sizeof(id), sway_output);
618 if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) {
619 struct output_config *current = get_output_config(id, sway_output);
620 if (!current) {
621 // No stored output config matched, apply oc directly
622 sway_log(SWAY_DEBUG, "Applying oc directly");
623 current = new_output_config(oc->name);
624 merge_output_config(current, oc);
625 }
626 apply_output_config(current, sway_output);
627 free_output_config(current);
628
629 if (!wildcard) {
630 // Stop looking if the output config isn't applicable to all
631 // outputs
632 break;
633 }
634 }
635 }
636
637 struct sway_seat *seat;
638 wl_list_for_each(seat, &server.input->seats, link) {
639 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
640 cursor_rebase(seat->cursor);
641 }
642 }
643
reset_outputs(void)644 void reset_outputs(void) {
645 struct output_config *oc = NULL;
646 int i = list_seq_find(config->output_configs, output_name_cmp, "*");
647 if (i >= 0) {
648 oc = config->output_configs->items[i];
649 } else {
650 oc = store_output_config(new_output_config("*"));
651 }
652 apply_output_config_to_outputs(oc);
653 }
654
free_output_config(struct output_config * oc)655 void free_output_config(struct output_config *oc) {
656 if (!oc) {
657 return;
658 }
659 free(oc->name);
660 free(oc->background);
661 free(oc->background_option);
662 free(oc);
663 }
664
handle_swaybg_client_destroy(struct wl_listener * listener,void * data)665 static void handle_swaybg_client_destroy(struct wl_listener *listener,
666 void *data) {
667 struct sway_config *sway_config =
668 wl_container_of(listener, sway_config, swaybg_client_destroy);
669 wl_list_remove(&sway_config->swaybg_client_destroy.link);
670 wl_list_init(&sway_config->swaybg_client_destroy.link);
671 sway_config->swaybg_client = NULL;
672 }
673
_spawn_swaybg(char ** command)674 static bool _spawn_swaybg(char **command) {
675 if (config->swaybg_client != NULL) {
676 wl_client_destroy(config->swaybg_client);
677 }
678 int sockets[2];
679 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) {
680 sway_log_errno(SWAY_ERROR, "socketpair failed");
681 return false;
682 }
683 if (!sway_set_cloexec(sockets[0], true) || !sway_set_cloexec(sockets[1], true)) {
684 return false;
685 }
686
687 config->swaybg_client = wl_client_create(server.wl_display, sockets[0]);
688 if (config->swaybg_client == NULL) {
689 sway_log_errno(SWAY_ERROR, "wl_client_create failed");
690 return false;
691 }
692
693 config->swaybg_client_destroy.notify = handle_swaybg_client_destroy;
694 wl_client_add_destroy_listener(config->swaybg_client,
695 &config->swaybg_client_destroy);
696
697 pid_t pid = fork();
698 if (pid < 0) {
699 sway_log_errno(SWAY_ERROR, "fork failed");
700 return false;
701 } else if (pid == 0) {
702 pid = fork();
703 if (pid < 0) {
704 sway_log_errno(SWAY_ERROR, "fork failed");
705 _exit(EXIT_FAILURE);
706 } else if (pid == 0) {
707 if (!sway_set_cloexec(sockets[1], false)) {
708 _exit(EXIT_FAILURE);
709 }
710
711 char wayland_socket_str[16];
712 snprintf(wayland_socket_str, sizeof(wayland_socket_str),
713 "%d", sockets[1]);
714 setenv("WAYLAND_SOCKET", wayland_socket_str, true);
715
716 execvp(command[0], command);
717 sway_log_errno(SWAY_ERROR, "execvp failed");
718 _exit(EXIT_FAILURE);
719 }
720 _exit(EXIT_SUCCESS);
721 }
722
723 if (close(sockets[1]) != 0) {
724 sway_log_errno(SWAY_ERROR, "close failed");
725 return false;
726 }
727 if (waitpid(pid, NULL, 0) < 0) {
728 sway_log_errno(SWAY_ERROR, "waitpid failed");
729 return false;
730 }
731
732 return true;
733 }
734
spawn_swaybg(void)735 bool spawn_swaybg(void) {
736 if (!config->swaybg_command) {
737 return true;
738 }
739
740 size_t length = 2;
741 for (int i = 0; i < config->output_configs->length; i++) {
742 struct output_config *oc = config->output_configs->items[i];
743 if (!oc->background) {
744 continue;
745 }
746 if (strcmp(oc->background_option, "solid_color") == 0) {
747 length += 4;
748 } else if (oc->background_fallback) {
749 length += 8;
750 } else {
751 length += 6;
752 }
753 }
754
755 char **cmd = calloc(length, sizeof(char *));
756 if (!cmd) {
757 sway_log(SWAY_ERROR, "Failed to allocate spawn_swaybg command");
758 return false;
759 }
760
761 size_t i = 0;
762 cmd[i++] = config->swaybg_command;
763 for (int j = 0; j < config->output_configs->length; j++) {
764 struct output_config *oc = config->output_configs->items[j];
765 if (!oc->background) {
766 continue;
767 }
768 if (strcmp(oc->background_option, "solid_color") == 0) {
769 cmd[i++] = "-o";
770 cmd[i++] = oc->name;
771 cmd[i++] = "-c";
772 cmd[i++] = oc->background;
773 } else {
774 cmd[i++] = "-o";
775 cmd[i++] = oc->name;
776 cmd[i++] = "-i";
777 cmd[i++] = oc->background;
778 cmd[i++] = "-m";
779 cmd[i++] = oc->background_option;
780 if (oc->background_fallback) {
781 cmd[i++] = "-c";
782 cmd[i++] = oc->background_fallback;
783 }
784 }
785 assert(i <= length);
786 }
787
788 for (size_t k = 0; k < i; k++) {
789 sway_log(SWAY_DEBUG, "spawn_swaybg cmd[%zd] = %s", k, cmd[k]);
790 }
791
792 bool result = _spawn_swaybg(cmd);
793 free(cmd);
794 return result;
795 }
796