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