aboutsummaryrefslogtreecommitdiffstats
path: root/sway/config/output.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/config/output.c')
-rw-r--r--sway/config/output.c788
1 files changed, 511 insertions, 277 deletions
diff --git a/sway/config/output.c b/sway/config/output.c
index 1a5215fe..fb1956df 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <drm_fourcc.h> 2#include <drm_fourcc.h>
4#include <stdbool.h> 3#include <stdbool.h>
@@ -10,6 +9,7 @@
10#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
11#include <wlr/types/wlr_output_layout.h> 10#include <wlr/types/wlr_output_layout.h>
12#include <wlr/types/wlr_output.h> 11#include <wlr/types/wlr_output.h>
12#include <wlr/types/wlr_output_swapchain_manager.h>
13#include "sway/config.h" 13#include "sway/config.h"
14#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
15#include "sway/output.h" 15#include "sway/output.h"
@@ -79,7 +79,72 @@ struct output_config *new_output_config(const char *name) {
79 return oc; 79 return oc;
80} 80}
81 81
82void merge_output_config(struct output_config *dst, struct output_config *src) { 82// supersede_output_config clears all fields in dst that were set in src
83static void supersede_output_config(struct output_config *dst, struct output_config *src) {
84 if (src->enabled != -1) {
85 dst->enabled = -1;
86 }
87 if (src->width != -1) {
88 dst->width = -1;
89 }
90 if (src->height != -1) {
91 dst->height = -1;
92 }
93 if (src->x != -1) {
94 dst->x = -1;
95 }
96 if (src->y != -1) {
97 dst->y = -1;
98 }
99 if (src->scale != -1) {
100 dst->scale = -1;
101 }
102 if (src->scale_filter != SCALE_FILTER_DEFAULT) {
103 dst->scale_filter = SCALE_FILTER_DEFAULT;
104 }
105 if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) {
106 dst->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
107 }
108 if (src->refresh_rate != -1) {
109 dst->refresh_rate = -1;
110 }
111 if (src->custom_mode != -1) {
112 dst->custom_mode = -1;
113 }
114 if (src->drm_mode.type != (uint32_t) -1) {
115 dst->drm_mode.type = -1;
116 }
117 if (src->transform != -1) {
118 dst->transform = -1;
119 }
120 if (src->max_render_time != -1) {
121 dst->max_render_time = -1;
122 }
123 if (src->adaptive_sync != -1) {
124 dst->adaptive_sync = -1;
125 }
126 if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
127 dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
128 }
129 if (src->background) {
130 free(dst->background);
131 dst->background = NULL;
132 }
133 if (src->background_option) {
134 free(dst->background_option);
135 dst->background_option = NULL;
136 }
137 if (src->background_fallback) {
138 free(dst->background_fallback);
139 dst->background_fallback = NULL;
140 }
141 if (src->power != -1) {
142 dst->power = -1;
143 }
144}
145
146// merge_output_config sets all fields in dst that were set in src
147static void merge_output_config(struct output_config *dst, struct output_config *src) {
83 if (src->enabled != -1) { 148 if (src->enabled != -1) {
84 dst->enabled = src->enabled; 149 dst->enabled = src->enabled;
85 } 150 }
@@ -142,94 +207,42 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
142 } 207 }
143} 208}
144 209
145static void merge_wildcard_on_all(struct output_config *wildcard) { 210void store_output_config(struct output_config *oc) {
146 for (int i = 0; i < config->output_configs->length; i++) { 211 bool merged = false;
147 struct output_config *oc = config->output_configs->items[i]; 212 bool wildcard = strcmp(oc->name, "*") == 0;
148 if (strcmp(wildcard->name, oc->name) != 0) { 213 struct sway_output *output = wildcard ? NULL : all_output_by_name_or_id(oc->name);
149 sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name);
150 merge_output_config(oc, wildcard);
151 }
152 }
153}
154
155static void merge_id_on_name(struct output_config *oc) {
156 struct sway_output *output = all_output_by_name_or_id(oc->name);
157 if (output == NULL) {
158 return;
159 }
160 214
161 const char *name = output->wlr_output->name;
162 char id[128]; 215 char id[128];
163 output_get_identifier(id, sizeof(id), output); 216 if (output) {
164 217 output_get_identifier(id, sizeof(id), output);
165 char *id_on_name = format_str("%s on %s", id, name);
166 if (!id_on_name) {
167 return;
168 } 218 }
169 219
170 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 220 for (int i = 0; i < config->output_configs->length; i++) {
171 if (i >= 0) { 221 struct output_config *old = config->output_configs->items[i];
172 sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); 222
173 merge_output_config(config->output_configs->items[i], oc); 223 // If the old config matches the new config's name, regardless of
174 } else { 224 // whether it was name or identifier, merge on top of the existing
175 // If both a name and identifier config, exist generate an id on name 225 // config. If the new config is a wildcard, this also merges on top of
176 int ni = list_seq_find(config->output_configs, output_name_cmp, name); 226 // old wildcard configs.
177 int ii = list_seq_find(config->output_configs, output_name_cmp, id); 227 if (strcmp(old->name, oc->name) == 0) {
178 if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) 228 merge_output_config(old, oc);
179 || (ii >= 0 && strcmp(oc->name, name) == 0)) { 229 merged = true;
180 struct output_config *ion_oc = new_output_config(id_on_name); 230 continue;
181 if (ni >= 0) {
182 merge_output_config(ion_oc, config->output_configs->items[ni]);
183 }
184 if (ii >= 0) {
185 merge_output_config(ion_oc, config->output_configs->items[ii]);
186 }
187 merge_output_config(ion_oc, oc);
188 list_add(config->output_configs, ion_oc);
189 sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\""
190 " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f "
191 "transform %d) (bg %s %s) (power %d) (max render time: %d)",
192 ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height,
193 ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale,
194 ion_oc->transform, ion_oc->background,
195 ion_oc->background_option, ion_oc->power,
196 ion_oc->max_render_time);
197 } 231 }
198 }
199 free(id_on_name);
200}
201 232
202struct output_config *store_output_config(struct output_config *oc) { 233 // If the new config is a wildcard config we supersede all non-wildcard
203 bool wildcard = strcmp(oc->name, "*") == 0; 234 // configs. Old wildcard configs have already been handled above.
204 if (wildcard) { 235 if (wildcard) {
205 merge_wildcard_on_all(oc); 236 supersede_output_config(old, oc);
206 } else { 237 continue;
207 merge_id_on_name(oc); 238 }
208 }
209 239
210 int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); 240 // If the new config matches an output's name, and the old config
211 if (i >= 0) { 241 // matches on that output's identifier, supersede it.
212 sway_log(SWAY_DEBUG, "Merging on top of existing output config"); 242 if (output && strcmp(old->name, id) == 0 &&
213 struct output_config *current = config->output_configs->items[i]; 243 strcmp(oc->name, output->wlr_output->name) == 0) {
214 merge_output_config(current, oc); 244 supersede_output_config(old, oc);
215 free_output_config(oc);
216 oc = current;
217 } else if (!wildcard) {
218 sway_log(SWAY_DEBUG, "Adding non-wildcard output config");
219 i = list_seq_find(config->output_configs, output_name_cmp, "*");
220 if (i >= 0) {
221 sway_log(SWAY_DEBUG, "Merging on top of output * config");
222 struct output_config *current = new_output_config(oc->name);
223 merge_output_config(current, config->output_configs->items[i]);
224 merge_output_config(current, oc);
225 free_output_config(oc);
226 oc = current;
227 } 245 }
228 list_add(config->output_configs, oc);
229 } else {
230 // New wildcard config. Just add it
231 sway_log(SWAY_DEBUG, "Adding output * config");
232 list_add(config->output_configs, oc);
233 } 246 }
234 247
235 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " 248 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
@@ -240,7 +253,13 @@ struct output_config *store_output_config(struct output_config *oc) {
240 oc->transform, oc->background, oc->background_option, oc->power, 253 oc->transform, oc->background, oc->background_option, oc->power,
241 oc->max_render_time); 254 oc->max_render_time);
242 255
243 return oc; 256 // If the configuration was not merged into an existing configuration, add
257 // it to the list. Otherwise we're done with it and can free it.
258 if (!merged) {
259 list_add(config->output_configs, oc);
260 } else {
261 free_output_config(oc);
262 }
244} 263}
245 264
246static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, 265static void set_mode(struct wlr_output *output, struct wlr_output_state *pending,
@@ -367,22 +386,18 @@ static int compute_default_scale(struct wlr_output *output,
367 return 2; 386 return 2;
368} 387}
369 388
370/* Lists of formats to try, in order, when a specific render bit depth has 389static bool render_format_is_10bit(uint32_t render_format) {
371 * been asked for. The second to last format in each list should always 390 return render_format == DRM_FORMAT_XRGB2101010 ||
372 * be XRGB8888, as a reliable backup in case the others are not available; 391 render_format == DRM_FORMAT_XBGR2101010;
373 * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ 392}
374static const uint32_t *bit_depth_preferences[] = { 393
375 [RENDER_BIT_DEPTH_8] = (const uint32_t []){ 394static bool render_format_is_bgr(uint32_t fmt) {
376 DRM_FORMAT_XRGB8888, 395 return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888;
377 DRM_FORMAT_INVALID, 396}
378 }, 397
379 [RENDER_BIT_DEPTH_10] = (const uint32_t []){ 398static bool output_config_is_disabling(struct output_config *oc) {
380 DRM_FORMAT_XRGB2101010, 399 return oc && (!oc->enabled || oc->power == 0);
381 DRM_FORMAT_XBGR2101010, 400}
382 DRM_FORMAT_XRGB8888,
383 DRM_FORMAT_INVALID,
384 },
385};
386 401
387static void queue_output_config(struct output_config *oc, 402static void queue_output_config(struct output_config *oc,
388 struct sway_output *output, struct wlr_output_state *pending) { 403 struct sway_output *output, struct wlr_output_state *pending) {
@@ -392,7 +407,7 @@ static void queue_output_config(struct output_config *oc,
392 407
393 struct wlr_output *wlr_output = output->wlr_output; 408 struct wlr_output *wlr_output = output->wlr_output;
394 409
395 if (oc && (!oc->enabled || oc->power == 0)) { 410 if (output_config_is_disabling(oc)) {
396 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); 411 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
397 wlr_output_state_set_enabled(pending, false); 412 wlr_output_state_set_enabled(pending, false);
398 return; 413 return;
@@ -415,22 +430,6 @@ static void queue_output_config(struct output_config *oc,
415 struct wlr_output_mode *preferred_mode = 430 struct wlr_output_mode *preferred_mode =
416 wlr_output_preferred_mode(wlr_output); 431 wlr_output_preferred_mode(wlr_output);
417 wlr_output_state_set_mode(pending, preferred_mode); 432 wlr_output_state_set_mode(pending, preferred_mode);
418
419 if (!wlr_output_test_state(wlr_output, pending)) {
420 sway_log(SWAY_DEBUG, "Preferred mode rejected, "
421 "falling back to another mode");
422 struct wlr_output_mode *mode;
423 wl_list_for_each(mode, &wlr_output->modes, link) {
424 if (mode == preferred_mode) {
425 continue;
426 }
427
428 wlr_output_state_set_mode(pending, mode);
429 if (wlr_output_test_state(wlr_output, pending)) {
430 break;
431 }
432 }
433 }
434 } 433 }
435 434
436 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 435 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
@@ -481,54 +480,27 @@ static void queue_output_config(struct output_config *oc,
481 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, 480 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name,
482 oc->adaptive_sync); 481 oc->adaptive_sync);
483 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); 482 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1);
484 if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) {
485 sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring");
486 wlr_output_state_set_adaptive_sync_enabled(pending, false);
487 }
488 } 483 }
489 484
490 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { 485 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
491 const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; 486 if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 &&
492 assert(fmts); 487 render_format_is_10bit(output->wlr_output->render_format)) {
493 488 // 10-bit was set successfully before, try to save some tests by reusing the format
494 for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { 489 wlr_output_state_set_render_format(pending, output->wlr_output->render_format);
495 wlr_output_state_set_render_format(pending, fmts[i]); 490 } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) {
496 if (wlr_output_test_state(wlr_output, pending)) { 491 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010);
497 break; 492 } else {
498 } 493 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888);
499
500 sway_log(SWAY_DEBUG, "Preferred output format 0x%08x "
501 "failed to work, falling back to next in "
502 "list, 0x%08x", fmts[i], fmts[i + 1]);
503 } 494 }
504 } 495 }
505} 496}
506 497
507bool apply_output_config(struct output_config *oc, struct sway_output *output) { 498static bool finalize_output_config(struct output_config *oc, struct sway_output *output) {
508 if (output == root->fallback_output) { 499 if (output == root->fallback_output) {
509 return false; 500 return false;
510 } 501 }
511 502
512 struct wlr_output *wlr_output = output->wlr_output; 503 struct wlr_output *wlr_output = output->wlr_output;
513
514 // Flag to prevent the output mode event handler from calling us
515 output->enabling = (!oc || oc->enabled);
516
517 struct wlr_output_state pending = {0};
518 queue_output_config(oc, output, &pending);
519
520 sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name);
521 if (!wlr_output_commit_state(wlr_output, &pending)) {
522 // Failed to commit output changes, maybe the output is missing a CRTC.
523 // Leave the output disabled for now and try again when the output gets
524 // the mode we asked for.
525 sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name);
526 output->enabling = false;
527 return false;
528 }
529
530 output->enabling = false;
531
532 if (oc && !oc->enabled) { 504 if (oc && !oc->enabled) {
533 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); 505 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name);
534 if (output->enabled) { 506 if (output->enabled) {
@@ -584,25 +556,9 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
584 output->max_render_time = oc->max_render_time; 556 output->max_render_time = oc->max_render_time;
585 } 557 }
586 558
587 // Reconfigure all devices, since input config may have been applied before
588 // this output came online, and some config items (like map_to_output) are
589 // dependent on an output being present.
590 input_manager_configure_all_input_mappings();
591 // Reconfigure the cursor images, since the scale may have changed.
592 input_manager_configure_xcursor();
593 return true; 559 return true;
594} 560}
595 561
596bool test_output_config(struct output_config *oc, struct sway_output *output) {
597 if (output == root->fallback_output) {
598 return false;
599 }
600
601 struct wlr_output_state pending = {0};
602 queue_output_config(oc, output, &pending);
603 return wlr_output_test_state(output->wlr_output, &pending);
604}
605
606static void default_output_config(struct output_config *oc, 562static void default_output_config(struct output_config *oc,
607 struct wlr_output *wlr_output) { 563 struct wlr_output *wlr_output) {
608 oc->enabled = 1; 564 oc->enabled = 1;
@@ -622,140 +578,415 @@ static void default_output_config(struct output_config *oc,
622 oc->max_render_time = 0; 578 oc->max_render_time = 0;
623} 579}
624 580
625static struct output_config *get_output_config(char *identifier, 581// find_output_config returns a merged output_config containing all stored
626 struct sway_output *sway_output) { 582// configuration that applies to the specified output.
583struct output_config *find_output_config(struct sway_output *sway_output) {
627 const char *name = sway_output->wlr_output->name; 584 const char *name = sway_output->wlr_output->name;
585 struct output_config *oc = NULL;
628 586
629 struct output_config *oc_id_on_name = NULL; 587 struct output_config *result = new_output_config(name);
630 struct output_config *oc_name = NULL; 588 if (config->reloading) {
631 struct output_config *oc_id = NULL; 589 default_output_config(result, sway_output->wlr_output);
590 }
632 591
633 char *id_on_name = format_str("%s on %s", identifier, name); 592 char id[128];
634 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 593 output_get_identifier(id, sizeof(id), sway_output);
635 if (i >= 0) { 594
636 oc_id_on_name = config->output_configs->items[i]; 595 int i;
637 } else { 596 bool match = false;
638 i = list_seq_find(config->output_configs, output_name_cmp, name); 597 if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) {
639 if (i >= 0) { 598 match = true;
640 oc_name = config->output_configs->items[i]; 599 oc = config->output_configs->items[i];
600 merge_output_config(result, oc);
601 }
602 if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) {
603 match = true;
604 oc = config->output_configs->items[i];
605 merge_output_config(result, oc);
606 }
607 if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) {
608 match = true;
609 oc = config->output_configs->items[i];
610 merge_output_config(result, oc);
611 }
612
613 if (!match && !config->reloading) {
614 // No name, identifier, or wildcard config. Since we are not
615 // reloading with defaults, the output config will be empty, so
616 // just return NULL
617 free_output_config(result);
618 return NULL;
619 }
620
621 return result;
622}
623
624static bool config_has_auto_mode(struct output_config *oc) {
625 if (!oc) {
626 return true;
627 }
628 if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) {
629 return true;
630 } else if (oc->width > 0 && oc->height > 0) {
631 return true;
632 }
633 return false;
634}
635
636struct search_context {
637 struct wlr_output_swapchain_manager *swapchain_mgr;
638 struct wlr_backend_output_state *states;
639 struct matched_output_config *configs;
640 size_t configs_len;
641 bool degrade_to_off;
642};
643
644static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_state *state) {
645 sway_log(SWAY_DEBUG, "Output state for %s", wlr_output->name);
646 if (state->committed & WLR_OUTPUT_STATE_ENABLED) {
647 sway_log(SWAY_DEBUG, " enabled: %s", state->enabled ? "yes" : "no");
648 }
649 if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) {
650 sway_log(SWAY_DEBUG, " render_format: %d", state->render_format);
651 }
652 if (state->committed & WLR_OUTPUT_STATE_MODE) {
653 if (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) {
654 sway_log(SWAY_DEBUG, " custom mode: %dx%d@%dmHz",
655 state->custom_mode.width, state->custom_mode.height, state->custom_mode.refresh);
656 } else {
657 sway_log(SWAY_DEBUG, " mode: %dx%d@%dmHz%s",
658 state->mode->width, state->mode->height, state->mode->refresh,
659 state->mode->preferred ? " (preferred)" : "");
660 }
661 }
662 if (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) {
663 sway_log(SWAY_DEBUG, " adaptive_sync: %s",
664 state->adaptive_sync_enabled ? "enabled": "disabled");
665 }
666}
667
668static bool search_valid_config(struct search_context *ctx, size_t output_idx);
669
670static void reset_output_state(struct wlr_output_state *state) {
671 wlr_output_state_finish(state);
672 wlr_output_state_init(state);
673 state->committed = 0;
674}
675
676static void clear_later_output_states(struct wlr_backend_output_state *states,
677 size_t configs_len, size_t output_idx) {
678
679 // Clear and disable all output states after this one to avoid conflict
680 // with previous tests.
681 for (size_t idx = output_idx+1; idx < configs_len; idx++) {
682 struct wlr_backend_output_state *backend_state = &states[idx];
683 struct wlr_output_state *state = &backend_state->base;
684
685 reset_output_state(state);
686 wlr_output_state_set_enabled(state, false);
687 }
688}
689
690static bool search_finish(struct search_context *ctx, size_t output_idx) {
691 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
692 struct wlr_output_state *state = &backend_state->base;
693 struct wlr_output *wlr_output = backend_state->output;
694
695 clear_later_output_states(ctx->states, ctx->configs_len, output_idx);
696 dump_output_state(wlr_output, state);
697 return wlr_output_swapchain_manager_prepare(ctx->swapchain_mgr, ctx->states, ctx->configs_len) &&
698 search_valid_config(ctx, output_idx+1);
699}
700
701static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) {
702 struct matched_output_config *cfg = &ctx->configs[output_idx];
703 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
704 struct wlr_output_state *state = &backend_state->base;
705
706 if (cfg->config && cfg->config->adaptive_sync == 1) {
707 wlr_output_state_set_adaptive_sync_enabled(state, true);
708 if (search_finish(ctx, output_idx)) {
709 return true;
710 }
711 }
712 if (!cfg->config || cfg->config->adaptive_sync != -1) {
713 wlr_output_state_set_adaptive_sync_enabled(state, false);
714 if (search_finish(ctx, output_idx)) {
715 return true;
641 } 716 }
717 }
718 // If adaptive sync has not been set, or fallback in case we are on a
719 // backend that cannot disable adaptive sync such as the wayland backend.
720 state->committed &= ~WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED;
721 return search_finish(ctx, output_idx);
722}
723
724static bool search_mode(struct search_context *ctx, size_t output_idx) {
725 struct matched_output_config *cfg = &ctx->configs[output_idx];
726 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
727 struct wlr_output_state *state = &backend_state->base;
728 struct wlr_output *wlr_output = backend_state->output;
729
730 if (!config_has_auto_mode(cfg->config)) {
731 return search_adaptive_sync(ctx, output_idx);
732 }
642 733
643 i = list_seq_find(config->output_configs, output_name_cmp, identifier); 734 struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
644 if (i >= 0) { 735 if (preferred_mode) {
645 oc_id = config->output_configs->items[i]; 736 wlr_output_state_set_mode(state, preferred_mode);
737 if (search_adaptive_sync(ctx, output_idx)) {
738 return true;
646 } 739 }
647 } 740 }
648 741
649 struct output_config *result = new_output_config("temp"); 742 if (wl_list_empty(&wlr_output->modes)) {
650 if (config->reloading) { 743 state->committed &= ~WLR_OUTPUT_STATE_MODE;
651 default_output_config(result, sway_output->wlr_output); 744 return search_adaptive_sync(ctx, output_idx);
652 } 745 }
653 if (oc_id_on_name) { 746
654 // Already have an identifier on name config, use that 747 struct wlr_output_mode *mode;
655 free(result->name); 748 wl_list_for_each(mode, &backend_state->output->modes, link) {
656 result->name = strdup(id_on_name); 749 if (mode == preferred_mode) {
657 merge_output_config(result, oc_id_on_name); 750 continue;
658 } else if (oc_name && oc_id) { 751 }
659 // Generate a config named `<identifier> on <name>` which contains a 752 wlr_output_state_set_mode(state, mode);
660 // merged copy of the identifier on name. This will make sure that both 753 if (search_adaptive_sync(ctx, output_idx)) {
661 // identifier and name configs are respected, with identifier getting 754 return true;
662 // priority
663 struct output_config *temp = new_output_config(id_on_name);
664 merge_output_config(temp, oc_name);
665 merge_output_config(temp, oc_id);
666 list_add(config->output_configs, temp);
667
668 free(result->name);
669 result->name = strdup(id_on_name);
670 merge_output_config(result, temp);
671
672 sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)"
673 " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
674 " (power %d) (max render time: %d)", result->name, result->enabled,
675 result->width, result->height, result->refresh_rate,
676 result->x, result->y, result->scale, result->transform,
677 result->background, result->background_option, result->power,
678 result->max_render_time);
679 } else if (oc_name) {
680 // No identifier config, just return a copy of the name config
681 free(result->name);
682 result->name = strdup(name);
683 merge_output_config(result, oc_name);
684 } else if (oc_id) {
685 // No name config, just return a copy of the identifier config
686 free(result->name);
687 result->name = strdup(identifier);
688 merge_output_config(result, oc_id);
689 } else {
690 i = list_seq_find(config->output_configs, output_name_cmp, "*");
691 if (i >= 0) {
692 // No name or identifier config, but there is a wildcard config
693 free(result->name);
694 result->name = strdup("*");
695 merge_output_config(result, config->output_configs->items[i]);
696 } else if (!config->reloading) {
697 // No name, identifier, or wildcard config. Since we are not
698 // reloading with defaults, the output config will be empty, so
699 // just return NULL
700 free_output_config(result);
701 result = NULL;
702 } 755 }
703 } 756 }
704 757
705 free(id_on_name); 758 return false;
706 return result;
707} 759}
708 760
709struct output_config *find_output_config(struct sway_output *output) { 761static bool search_render_format(struct search_context *ctx, size_t output_idx) {
710 char id[128]; 762 struct matched_output_config *cfg = &ctx->configs[output_idx];
711 output_get_identifier(id, sizeof(id), output); 763 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
712 return get_output_config(id, output); 764 struct wlr_output_state *state = &backend_state->base;
765 struct wlr_output *wlr_output = backend_state->output;
766
767 uint32_t fmts[] = {
768 DRM_FORMAT_XRGB2101010,
769 DRM_FORMAT_XBGR2101010,
770 DRM_FORMAT_XRGB8888,
771 DRM_FORMAT_INVALID,
772 };
773 if (render_format_is_bgr(wlr_output->render_format)) {
774 // Start with BGR in the unlikely event that we previously required it.
775 fmts[0] = DRM_FORMAT_XBGR2101010;
776 fmts[1] = DRM_FORMAT_XRGB2101010;
777 }
778
779 const struct wlr_drm_format_set *primary_formats =
780 wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF);
781 bool need_10bit = cfg->config && cfg->config->render_bit_depth == RENDER_BIT_DEPTH_10;
782 for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) {
783 if (!need_10bit && render_format_is_10bit(fmts[idx])) {
784 continue;
785 }
786 if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) {
787 // This is not a supported format for this output
788 continue;
789 }
790 wlr_output_state_set_render_format(state, fmts[idx]);
791 if (search_mode(ctx, output_idx)) {
792 return true;
793 }
794 }
795 return false;
713} 796}
714 797
715void apply_output_config_to_outputs(struct output_config *oc) { 798static bool search_valid_config(struct search_context *ctx, size_t output_idx) {
716 // Try to find the output container and apply configuration now. If 799 if (output_idx >= ctx->configs_len) {
717 // this is during startup then there will be no container and config 800 // We reached the end of the search, all good!
718 // will be applied during normal "new output" event from wlroots. 801 return true;
719 bool wildcard = strcmp(oc->name, "*") == 0; 802 }
720 struct sway_output *sway_output, *tmp;
721 wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) {
722 if (output_match_name_or_id(sway_output, oc->name)) {
723 char id[128];
724 output_get_identifier(id, sizeof(id), sway_output);
725 struct output_config *current = get_output_config(id, sway_output);
726 if (!current) {
727 // No stored output config matched, apply oc directly
728 sway_log(SWAY_DEBUG, "Applying oc directly");
729 current = new_output_config(oc->name);
730 merge_output_config(current, oc);
731 }
732 apply_output_config(current, sway_output);
733 free_output_config(current);
734 803
735 if (!wildcard) { 804 struct matched_output_config *cfg = &ctx->configs[output_idx];
736 // Stop looking if the output config isn't applicable to all 805 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
737 // outputs 806 struct wlr_output_state *state = &backend_state->base;
738 break; 807 struct wlr_output *wlr_output = backend_state->output;
739 } 808
809 if (!output_config_is_disabling(cfg->config)) {
810 // Search through our possible configurations, doing a depth-first
811 // through render_format, modes, adaptive_sync and the next output's
812 // config.
813 queue_output_config(cfg->config, cfg->output, &backend_state->base);
814 if (search_render_format(ctx, output_idx)) {
815 return true;
816 } else if (!ctx->degrade_to_off) {
817 return false;
818 }
819 // We could not get anything to work, try to disable this output to see
820 // if we can at least make the outputs before us work.
821 sway_log(SWAY_DEBUG, "Unable to find valid config with output %s, disabling",
822 wlr_output->name);
823 reset_output_state(state);
824 }
825
826 wlr_output_state_set_enabled(state, false);
827 return search_finish(ctx, output_idx);
828}
829
830static int compare_matched_output_config_priority(const void *a, const void *b) {
831
832 const struct matched_output_config *amc = a;
833 const struct matched_output_config *bmc = b;
834 bool a_disabling = output_config_is_disabling(amc->config);
835 bool b_disabling = output_config_is_disabling(bmc->config);
836 bool a_enabled = amc->output->enabled;
837 bool b_enabled = bmc->output->enabled;
838
839 // We want to give priority to existing enabled outputs. To do so, we want
840 // the configuration order to be:
841 // 1. Existing, enabled outputs
842 // 2. Outputs that need to be enabled
843 // 3. Disabled or disabling outputs
844 if (a_enabled && !a_disabling) {
845 return -1;
846 } else if (b_enabled && !b_disabling) {
847 return 1;
848 } else if (b_disabling && !a_disabling) {
849 return -1;
850 } else if (a_disabling && !b_disabling) {
851 return 1;
852 }
853 return 0;
854}
855
856void sort_output_configs_by_priority(struct matched_output_config *configs,
857 size_t configs_len) {
858 qsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority);
859}
860
861bool apply_output_configs(struct matched_output_config *configs,
862 size_t configs_len, bool test_only, bool degrade_to_off) {
863 struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states));
864 if (!states) {
865 return false;
866 }
867
868 sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len);
869 for (size_t idx = 0; idx < configs_len; idx++) {
870 struct matched_output_config *cfg = &configs[idx];
871 struct wlr_backend_output_state *backend_state = &states[idx];
872
873 backend_state->output = cfg->output->wlr_output;
874 wlr_output_state_init(&backend_state->base);
875
876 sway_log(SWAY_DEBUG, "Preparing config for %s",
877 cfg->output->wlr_output->name);
878 queue_output_config(cfg->config, cfg->output, &backend_state->base);
879 }
880
881 struct wlr_output_swapchain_manager swapchain_mgr;
882 wlr_output_swapchain_manager_init(&swapchain_mgr, server.backend);
883
884 bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len);
885 if (!ok) {
886 sway_log(SWAY_ERROR, "Requested backend configuration failed, searching for valid fallbacks");
887 struct search_context ctx = {
888 .swapchain_mgr = &swapchain_mgr,
889 .states = states,
890 .configs = configs,
891 .configs_len = configs_len,
892 .degrade_to_off = degrade_to_off,
893 };
894 if (!search_valid_config(&ctx, 0)) {
895 sway_log(SWAY_ERROR, "Search for valid config failed");
896 goto out;
897 }
898 }
899
900 if (test_only) {
901 // The swapchain manager already did a test for us
902 goto out;
903 }
904
905 for (size_t idx = 0; idx < configs_len; idx++) {
906 struct matched_output_config *cfg = &configs[idx];
907 struct wlr_backend_output_state *backend_state = &states[idx];
908
909 struct wlr_scene_output_state_options opts = {
910 .swapchain = wlr_output_swapchain_manager_get_swapchain(
911 &swapchain_mgr, backend_state->output),
912 };
913 struct wlr_scene_output *scene_output = cfg->output->scene_output;
914 struct wlr_output_state *state = &backend_state->base;
915 if (!wlr_scene_output_build_state(scene_output, state, &opts)) {
916 sway_log(SWAY_ERROR, "Building output state for '%s' failed",
917 backend_state->output->name);
918 goto out;
740 } 919 }
741 } 920 }
742 921
922 ok = wlr_backend_commit(server.backend, states, configs_len);
923 if (!ok) {
924 sway_log(SWAY_ERROR, "Backend commit failed");
925 goto out;
926 }
927
928 sway_log(SWAY_DEBUG, "Commit of %zd outputs succeeded", configs_len);
929
930 wlr_output_swapchain_manager_apply(&swapchain_mgr);
931
932 for (size_t idx = 0; idx < configs_len; idx++) {
933 struct matched_output_config *cfg = &configs[idx];
934 sway_log(SWAY_DEBUG, "Finalizing config for %s",
935 cfg->output->wlr_output->name);
936 finalize_output_config(cfg->config, cfg->output);
937 }
938
939out:
940 wlr_output_swapchain_manager_finish(&swapchain_mgr);
941 for (size_t idx = 0; idx < configs_len; idx++) {
942 struct wlr_backend_output_state *backend_state = &states[idx];
943 wlr_output_state_finish(&backend_state->base);
944 }
945 free(states);
946
947 // Reconfigure all devices, since input config may have been applied before
948 // this output came online, and some config items (like map_to_output) are
949 // dependent on an output being present.
950 input_manager_configure_all_input_mappings();
951 // Reconfigure the cursor images, since the scale may have changed.
952 input_manager_configure_xcursor();
953
743 struct sway_seat *seat; 954 struct sway_seat *seat;
744 wl_list_for_each(seat, &server.input->seats, link) { 955 wl_list_for_each(seat, &server.input->seats, link) {
745 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 956 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
746 cursor_rebase(seat->cursor); 957 cursor_rebase(seat->cursor);
747 } 958 }
959
960 return ok;
748} 961}
749 962
750void reset_outputs(void) { 963void apply_all_output_configs(void) {
751 struct output_config *oc = NULL; 964 size_t configs_len = wl_list_length(&root->all_outputs);
752 int i = list_seq_find(config->output_configs, output_name_cmp, "*"); 965 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
753 if (i >= 0) { 966 if (!configs) {
754 oc = config->output_configs->items[i]; 967 return;
755 } else {
756 oc = store_output_config(new_output_config("*"));
757 } 968 }
758 apply_output_config_to_outputs(oc); 969
970 int config_idx = 0;
971 struct sway_output *sway_output;
972 wl_list_for_each(sway_output, &root->all_outputs, link) {
973 if (sway_output == root->fallback_output) {
974 configs_len--;
975 continue;
976 }
977
978 struct matched_output_config *config = &configs[config_idx++];
979 config->output = sway_output;
980 config->config = find_output_config(sway_output);
981 }
982
983 sort_output_configs_by_priority(configs, configs_len);
984 apply_output_configs(configs, configs_len, false, true);
985 for (size_t idx = 0; idx < configs_len; idx++) {
986 struct matched_output_config *cfg = &configs[idx];
987 free_output_config(cfg->config);
988 }
989 free(configs);
759} 990}
760 991
761void free_output_config(struct output_config *oc) { 992void free_output_config(struct output_config *oc) {
@@ -822,7 +1053,9 @@ static bool _spawn_swaybg(char **command) {
822 setenv("WAYLAND_SOCKET", wayland_socket_str, true); 1053 setenv("WAYLAND_SOCKET", wayland_socket_str, true);
823 1054
824 execvp(command[0], command); 1055 execvp(command[0], command);
825 sway_log_errno(SWAY_ERROR, "execvp failed"); 1056 sway_log_errno(SWAY_ERROR, "failed to execute '%s' "
1057 "(background configuration probably not applied)",
1058 command[0]);
826 _exit(EXIT_FAILURE); 1059 _exit(EXIT_FAILURE);
827 } 1060 }
828 _exit(EXIT_SUCCESS); 1061 _exit(EXIT_SUCCESS);
@@ -832,12 +1065,13 @@ static bool _spawn_swaybg(char **command) {
832 sway_log_errno(SWAY_ERROR, "close failed"); 1065 sway_log_errno(SWAY_ERROR, "close failed");
833 return false; 1066 return false;
834 } 1067 }
835 if (waitpid(pid, NULL, 0) < 0) { 1068 int fork_status = 0;
1069 if (waitpid(pid, &fork_status, 0) < 0) {
836 sway_log_errno(SWAY_ERROR, "waitpid failed"); 1070 sway_log_errno(SWAY_ERROR, "waitpid failed");
837 return false; 1071 return false;
838 } 1072 }
839 1073
840 return true; 1074 return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS;
841} 1075}
842 1076
843bool spawn_swaybg(void) { 1077bool spawn_swaybg(void) {