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