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.c854
1 files changed, 550 insertions, 304 deletions
diff --git a/sway/config/output.c b/sway/config/output.c
index b3e8371e..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>
@@ -6,10 +5,11 @@
6#include <sys/socket.h> 5#include <sys/socket.h>
7#include <sys/wait.h> 6#include <sys/wait.h>
8#include <unistd.h> 7#include <unistd.h>
8#include <wlr/config.h>
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/backend/drm.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"
@@ -17,6 +17,10 @@
17#include "log.h" 17#include "log.h"
18#include "util.h" 18#include "util.h"
19 19
20#if WLR_HAS_DRM_BACKEND
21#include <wlr/backend/drm.h>
22#endif
23
20int output_name_cmp(const void *item, const void *data) { 24int output_name_cmp(const void *item, const void *data) {
21 const struct output_config *output = item; 25 const struct output_config *output = item;
22 const char *name = data; 26 const char *name = data;
@@ -75,7 +79,72 @@ struct output_config *new_output_config(const char *name) {
75 return oc; 79 return oc;
76} 80}
77 81
78void 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) {
79 if (src->enabled != -1) { 148 if (src->enabled != -1) {
80 dst->enabled = src->enabled; 149 dst->enabled = src->enabled;
81 } 150 }
@@ -138,103 +207,42 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
138 } 207 }
139} 208}
140 209
141static void merge_wildcard_on_all(struct output_config *wildcard) { 210void store_output_config(struct output_config *oc) {
142 for (int i = 0; i < config->output_configs->length; i++) { 211 bool merged = false;
143 struct output_config *oc = config->output_configs->items[i]; 212 bool wildcard = strcmp(oc->name, "*") == 0;
144 if (strcmp(wildcard->name, oc->name) != 0) { 213 struct sway_output *output = wildcard ? NULL : all_output_by_name_or_id(oc->name);
145 sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name);
146 merge_output_config(oc, wildcard);
147 }
148 }
149}
150 214
151static void merge_id_on_name(struct output_config *oc) {
152 char *id_on_name = NULL;
153 char id[128]; 215 char id[128];
154 char *name = NULL; 216 if (output) {
155 struct sway_output *output;
156 wl_list_for_each(output, &root->all_outputs, link) {
157 name = output->wlr_output->name;
158 output_get_identifier(id, sizeof(id), output); 217 output_get_identifier(id, sizeof(id), output);
159 if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) {
160 size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1;
161 id_on_name = malloc(length);
162 if (!id_on_name) {
163 sway_log(SWAY_ERROR, "Failed to allocate id on name string");
164 return;
165 }
166 snprintf(id_on_name, length, "%s on %s", id, name);
167 break;
168 }
169 } 218 }
170 219
171 if (!id_on_name) { 220 for (int i = 0; i < config->output_configs->length; i++) {
172 return; 221 struct output_config *old = config->output_configs->items[i];
173 } 222
174 223 // If the old config matches the new config's name, regardless of
175 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 224 // whether it was name or identifier, merge on top of the existing
176 if (i >= 0) { 225 // config. If the new config is a wildcard, this also merges on top of
177 sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); 226 // old wildcard configs.
178 merge_output_config(config->output_configs->items[i], oc); 227 if (strcmp(old->name, oc->name) == 0) {
179 } else { 228 merge_output_config(old, oc);
180 // If both a name and identifier config, exist generate an id on name 229 merged = true;
181 int ni = list_seq_find(config->output_configs, output_name_cmp, name); 230 continue;
182 int ii = list_seq_find(config->output_configs, output_name_cmp, id);
183 if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0)
184 || (ii >= 0 && strcmp(oc->name, name) == 0)) {
185 struct output_config *ion_oc = new_output_config(id_on_name);
186 if (ni >= 0) {
187 merge_output_config(ion_oc, config->output_configs->items[ni]);
188 }
189 if (ii >= 0) {
190 merge_output_config(ion_oc, config->output_configs->items[ii]);
191 }
192 merge_output_config(ion_oc, oc);
193 list_add(config->output_configs, ion_oc);
194 sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\""
195 " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f "
196 "transform %d) (bg %s %s) (power %d) (max render time: %d)",
197 ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height,
198 ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale,
199 ion_oc->transform, ion_oc->background,
200 ion_oc->background_option, ion_oc->power,
201 ion_oc->max_render_time);
202 } 231 }
203 }
204 free(id_on_name);
205}
206 232
207struct output_config *store_output_config(struct output_config *oc) { 233 // If the new config is a wildcard config we supersede all non-wildcard
208 bool wildcard = strcmp(oc->name, "*") == 0; 234 // configs. Old wildcard configs have already been handled above.
209 if (wildcard) { 235 if (wildcard) {
210 merge_wildcard_on_all(oc); 236 supersede_output_config(old, oc);
211 } else { 237 continue;
212 merge_id_on_name(oc); 238 }
213 }
214 239
215 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
216 if (i >= 0) { 241 // matches on that output's identifier, supersede it.
217 sway_log(SWAY_DEBUG, "Merging on top of existing output config"); 242 if (output && strcmp(old->name, id) == 0 &&
218 struct output_config *current = config->output_configs->items[i]; 243 strcmp(oc->name, output->wlr_output->name) == 0) {
219 merge_output_config(current, oc); 244 supersede_output_config(old, oc);
220 free_output_config(oc);
221 oc = current;
222 } else if (!wildcard) {
223 sway_log(SWAY_DEBUG, "Adding non-wildcard output config");
224 i = list_seq_find(config->output_configs, output_name_cmp, "*");
225 if (i >= 0) {
226 sway_log(SWAY_DEBUG, "Merging on top of output * config");
227 struct output_config *current = new_output_config(oc->name);
228 merge_output_config(current, config->output_configs->items[i]);
229 merge_output_config(current, oc);
230 free_output_config(oc);
231 oc = current;
232 } 245 }
233 list_add(config->output_configs, oc);
234 } else {
235 // New wildcard config. Just add it
236 sway_log(SWAY_DEBUG, "Adding output * config");
237 list_add(config->output_configs, oc);
238 } 246 }
239 247
240 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 "
@@ -245,7 +253,13 @@ struct output_config *store_output_config(struct output_config *oc) {
245 oc->transform, oc->background, oc->background_option, oc->power, 253 oc->transform, oc->background, oc->background_option, oc->power,
246 oc->max_render_time); 254 oc->max_render_time);
247 255
248 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 }
249} 263}
250 264
251static 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,
@@ -253,7 +267,9 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending
253 // Not all floating point integers can be represented exactly 267 // Not all floating point integers can be represented exactly
254 // as (int)(1000 * mHz / 1000.f) 268 // as (int)(1000 * mHz / 1000.f)
255 // round() the result to avoid any error 269 // round() the result to avoid any error
256 int mhz = (int)round(refresh_rate * 1000); 270 int mhz = (int)roundf(refresh_rate * 1000);
271 // If no target refresh rate is given, match highest available
272 mhz = mhz <= 0 ? INT_MAX : mhz;
257 273
258 if (wl_list_empty(&output->modes) || custom) { 274 if (wl_list_empty(&output->modes) || custom) {
259 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); 275 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name);
@@ -263,29 +279,35 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending
263 } 279 }
264 280
265 struct wlr_output_mode *mode, *best = NULL; 281 struct wlr_output_mode *mode, *best = NULL;
282 int best_diff_mhz = INT_MAX;
266 wl_list_for_each(mode, &output->modes, link) { 283 wl_list_for_each(mode, &output->modes, link) {
267 if (mode->width == width && mode->height == height) { 284 if (mode->width == width && mode->height == height) {
268 if (mode->refresh == mhz) { 285 int diff_mhz = abs(mode->refresh - mhz);
269 best = mode; 286 if (diff_mhz < best_diff_mhz) {
270 break; 287 best_diff_mhz = diff_mhz;
271 }
272 if (best == NULL || mode->refresh > best->refresh) {
273 best = mode; 288 best = mode;
289 if (best_diff_mhz == 0) {
290 break;
291 }
274 } 292 }
275 } 293 }
276 } 294 }
277 if (!best) { 295 if (best) {
278 sway_log(SWAY_ERROR, "Configured mode for %s not available", output->name); 296 sway_log(SWAY_INFO, "Assigning configured mode (%dx%d@%.3fHz) to %s",
279 sway_log(SWAY_INFO, "Picking preferred mode instead"); 297 best->width, best->height, best->refresh / 1000.f, output->name);
280 best = wlr_output_preferred_mode(output);
281 } else { 298 } else {
282 sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); 299 best = wlr_output_preferred_mode(output);
300 sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, "
301 "applying preferred mode (%dx%d@%.3fHz)",
302 width, height, refresh_rate,
303 best->width, best->height, best->refresh / 1000.f);
283 } 304 }
284 wlr_output_state_set_mode(pending, best); 305 wlr_output_state_set_mode(pending, best);
285} 306}
286 307
287static void set_modeline(struct wlr_output *output, 308static void set_modeline(struct wlr_output *output,
288 struct wlr_output_state *pending, drmModeModeInfo *drm_mode) { 309 struct wlr_output_state *pending, drmModeModeInfo *drm_mode) {
310#if WLR_HAS_DRM_BACKEND
289 if (!wlr_output_is_drm(output)) { 311 if (!wlr_output_is_drm(output)) {
290 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); 312 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
291 return; 313 return;
@@ -295,6 +317,9 @@ static void set_modeline(struct wlr_output *output,
295 if (mode) { 317 if (mode) {
296 wlr_output_state_set_mode(pending, mode); 318 wlr_output_state_set_mode(pending, mode);
297 } 319 }
320#else
321 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
322#endif
298} 323}
299 324
300/* Some manufacturers hardcode the aspect-ratio of the output in the physical 325/* Some manufacturers hardcode the aspect-ratio of the output in the physical
@@ -361,22 +386,18 @@ static int compute_default_scale(struct wlr_output *output,
361 return 2; 386 return 2;
362} 387}
363 388
364/* Lists of formats to try, in order, when a specific render bit depth has 389static bool render_format_is_10bit(uint32_t render_format) {
365 * been asked for. The second to last format in each list should always 390 return render_format == DRM_FORMAT_XRGB2101010 ||
366 * be XRGB8888, as a reliable backup in case the others are not available; 391 render_format == DRM_FORMAT_XBGR2101010;
367 * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ 392}
368static const uint32_t *bit_depth_preferences[] = { 393
369 [RENDER_BIT_DEPTH_8] = (const uint32_t []){ 394static bool render_format_is_bgr(uint32_t fmt) {
370 DRM_FORMAT_XRGB8888, 395 return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888;
371 DRM_FORMAT_INVALID, 396}
372 }, 397
373 [RENDER_BIT_DEPTH_10] = (const uint32_t []){ 398static bool output_config_is_disabling(struct output_config *oc) {
374 DRM_FORMAT_XRGB2101010, 399 return oc && (!oc->enabled || oc->power == 0);
375 DRM_FORMAT_XBGR2101010, 400}
376 DRM_FORMAT_XRGB8888,
377 DRM_FORMAT_INVALID,
378 },
379};
380 401
381static void queue_output_config(struct output_config *oc, 402static void queue_output_config(struct output_config *oc,
382 struct sway_output *output, struct wlr_output_state *pending) { 403 struct sway_output *output, struct wlr_output_state *pending) {
@@ -386,7 +407,7 @@ static void queue_output_config(struct output_config *oc,
386 407
387 struct wlr_output *wlr_output = output->wlr_output; 408 struct wlr_output *wlr_output = output->wlr_output;
388 409
389 if (oc && (!oc->enabled || oc->power == 0)) { 410 if (output_config_is_disabling(oc)) {
390 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); 411 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
391 wlr_output_state_set_enabled(pending, false); 412 wlr_output_state_set_enabled(pending, false);
392 return; 413 return;
@@ -409,22 +430,6 @@ static void queue_output_config(struct output_config *oc,
409 struct wlr_output_mode *preferred_mode = 430 struct wlr_output_mode *preferred_mode =
410 wlr_output_preferred_mode(wlr_output); 431 wlr_output_preferred_mode(wlr_output);
411 wlr_output_state_set_mode(pending, preferred_mode); 432 wlr_output_state_set_mode(pending, preferred_mode);
412
413 if (!wlr_output_test_state(wlr_output, pending)) {
414 sway_log(SWAY_DEBUG, "Preferred mode rejected, "
415 "falling back to another mode");
416 struct wlr_output_mode *mode;
417 wl_list_for_each(mode, &wlr_output->modes, link) {
418 if (mode == preferred_mode) {
419 continue;
420 }
421
422 wlr_output_state_set_mode(pending, mode);
423 if (wlr_output_test_state(wlr_output, pending)) {
424 break;
425 }
426 }
427 }
428 } 433 }
429 434
430 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 435 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
@@ -436,9 +441,11 @@ static void queue_output_config(struct output_config *oc,
436 enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; 441 enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL;
437 if (oc && oc->transform >= 0) { 442 if (oc && oc->transform >= 0) {
438 tr = oc->transform; 443 tr = oc->transform;
444#if WLR_HAS_DRM_BACKEND
439 } else if (wlr_output_is_drm(wlr_output)) { 445 } else if (wlr_output_is_drm(wlr_output)) {
440 tr = wlr_drm_connector_get_panel_orientation(wlr_output); 446 tr = wlr_drm_connector_get_panel_orientation(wlr_output);
441 sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); 447 sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr);
448#endif
442 } 449 }
443 if (wlr_output->transform != tr) { 450 if (wlr_output->transform != tr) {
444 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); 451 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr);
@@ -450,6 +457,16 @@ static void queue_output_config(struct output_config *oc,
450 float scale; 457 float scale;
451 if (oc && oc->scale > 0) { 458 if (oc && oc->scale > 0) {
452 scale = oc->scale; 459 scale = oc->scale;
460
461 // The factional-scale-v1 protocol uses increments of 120ths to send
462 // the scale factor to the client. Adjust the scale so that we use the
463 // same value as the clients'.
464 float adjusted_scale = round(scale * 120) / 120;
465 if (scale != adjusted_scale) {
466 sway_log(SWAY_INFO, "Adjusting output scale from %f to %f",
467 scale, adjusted_scale);
468 scale = adjusted_scale;
469 }
453 } else { 470 } else {
454 scale = compute_default_scale(wlr_output, pending); 471 scale = compute_default_scale(wlr_output, pending);
455 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); 472 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale);
@@ -466,51 +483,24 @@ static void queue_output_config(struct output_config *oc,
466 } 483 }
467 484
468 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { 485 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
469 const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; 486 if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 &&
470 assert(fmts); 487 render_format_is_10bit(output->wlr_output->render_format)) {
471 488 // 10-bit was set successfully before, try to save some tests by reusing the format
472 for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { 489 wlr_output_state_set_render_format(pending, output->wlr_output->render_format);
473 wlr_output_state_set_render_format(pending, fmts[i]); 490 } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) {
474 if (wlr_output_test_state(wlr_output, pending)) { 491 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010);
475 break; 492 } else {
476 } 493 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888);
477
478 sway_log(SWAY_DEBUG, "Preferred output format 0x%08x "
479 "failed to work, falling back to next in "
480 "list, 0x%08x", fmts[i], fmts[i + 1]);
481 } 494 }
482 } 495 }
483} 496}
484 497
485bool apply_output_config(struct output_config *oc, struct sway_output *output) { 498static bool finalize_output_config(struct output_config *oc, struct sway_output *output) {
486 if (output == root->fallback_output) { 499 if (output == root->fallback_output) {
487 return false; 500 return false;
488 } 501 }
489 502
490 struct wlr_output *wlr_output = output->wlr_output; 503 struct wlr_output *wlr_output = output->wlr_output;
491
492 // Flag to prevent the output mode event handler from calling us
493 output->enabling = (!oc || oc->enabled);
494
495 struct wlr_output_state pending = {0};
496 queue_output_config(oc, output, &pending);
497
498 if (!oc || oc->power != 0) {
499 output->current_mode = pending.mode;
500 }
501
502 sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name);
503 if (!wlr_output_commit_state(wlr_output, &pending)) {
504 // Failed to commit output changes, maybe the output is missing a CRTC.
505 // Leave the output disabled for now and try again when the output gets
506 // the mode we asked for.
507 sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name);
508 output->enabling = false;
509 return false;
510 }
511
512 output->enabling = false;
513
514 if (oc && !oc->enabled) { 504 if (oc && !oc->enabled) {
515 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); 505 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name);
516 if (output->enabled) { 506 if (output->enabled) {
@@ -520,10 +510,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
520 return true; 510 return true;
521 } 511 }
522 512
523 if (config->reloading) {
524 output_damage_whole(output);
525 }
526
527 if (oc) { 513 if (oc) {
528 enum scale_filter_mode scale_filter_old = output->scale_filter; 514 enum scale_filter_mode scale_filter_old = output->scale_filter;
529 switch (oc->scale_filter) { 515 switch (oc->scale_filter) {
@@ -540,6 +526,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
540 if (scale_filter_old != output->scale_filter) { 526 if (scale_filter_old != output->scale_filter) {
541 sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, 527 sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name,
542 sway_output_scale_filter_to_string(output->scale_filter)); 528 sway_output_scale_filter_to_string(output->scale_filter));
529 wlr_damage_ring_add_whole(&output->scene_output->damage_ring);
543 } 530 }
544 } 531 }
545 532
@@ -569,25 +556,9 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
569 output->max_render_time = oc->max_render_time; 556 output->max_render_time = oc->max_render_time;
570 } 557 }
571 558
572 // Reconfigure all devices, since input config may have been applied before
573 // this output came online, and some config items (like map_to_output) are
574 // dependent on an output being present.
575 input_manager_configure_all_inputs();
576 // Reconfigure the cursor images, since the scale may have changed.
577 input_manager_configure_xcursor();
578 return true; 559 return true;
579} 560}
580 561
581bool test_output_config(struct output_config *oc, struct sway_output *output) {
582 if (output == root->fallback_output) {
583 return false;
584 }
585
586 struct wlr_output_state pending = {0};
587 queue_output_config(oc, output, &pending);
588 return wlr_output_test_state(output->wlr_output, &pending);
589}
590
591static void default_output_config(struct output_config *oc, 562static void default_output_config(struct output_config *oc,
592 struct wlr_output *wlr_output) { 563 struct wlr_output *wlr_output) {
593 oc->enabled = 1; 564 oc->enabled = 1;
@@ -607,143 +578,415 @@ static void default_output_config(struct output_config *oc,
607 oc->max_render_time = 0; 578 oc->max_render_time = 0;
608} 579}
609 580
610static struct output_config *get_output_config(char *identifier, 581// find_output_config returns a merged output_config containing all stored
611 struct sway_output *sway_output) { 582// configuration that applies to the specified output.
583struct output_config *find_output_config(struct sway_output *sway_output) {
612 const char *name = sway_output->wlr_output->name; 584 const char *name = sway_output->wlr_output->name;
585 struct output_config *oc = NULL;
613 586
614 struct output_config *oc_id_on_name = NULL; 587 struct output_config *result = new_output_config(name);
615 struct output_config *oc_name = NULL; 588 if (config->reloading) {
616 struct output_config *oc_id = NULL; 589 default_output_config(result, sway_output->wlr_output);
590 }
617 591
618 size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; 592 char id[128];
619 char *id_on_name = malloc(length); 593 output_get_identifier(id, sizeof(id), sway_output);
620 snprintf(id_on_name, length, "%s on %s", identifier, name); 594
621 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 595 int i;
622 if (i >= 0) { 596 bool match = false;
623 oc_id_on_name = config->output_configs->items[i]; 597 if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) {
624 } else { 598 match = true;
625 i = list_seq_find(config->output_configs, output_name_cmp, name); 599 oc = config->output_configs->items[i];
626 if (i >= 0) { 600 merge_output_config(result, oc);
627 oc_name = config->output_configs->items[i]; 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;
628 } 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 }
629 733
630 i = list_seq_find(config->output_configs, output_name_cmp, identifier); 734 struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
631 if (i >= 0) { 735 if (preferred_mode) {
632 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;
633 } 739 }
634 } 740 }
635 741
636 struct output_config *result = new_output_config("temp"); 742 if (wl_list_empty(&wlr_output->modes)) {
637 if (config->reloading) { 743 state->committed &= ~WLR_OUTPUT_STATE_MODE;
638 default_output_config(result, sway_output->wlr_output); 744 return search_adaptive_sync(ctx, output_idx);
639 } 745 }
640 if (oc_id_on_name) { 746
641 // Already have an identifier on name config, use that 747 struct wlr_output_mode *mode;
642 free(result->name); 748 wl_list_for_each(mode, &backend_state->output->modes, link) {
643 result->name = strdup(id_on_name); 749 if (mode == preferred_mode) {
644 merge_output_config(result, oc_id_on_name); 750 continue;
645 } else if (oc_name && oc_id) { 751 }
646 // Generate a config named `<identifier> on <name>` which contains a 752 wlr_output_state_set_mode(state, mode);
647 // merged copy of the identifier on name. This will make sure that both 753 if (search_adaptive_sync(ctx, output_idx)) {
648 // identifier and name configs are respected, with identifier getting 754 return true;
649 // priority
650 struct output_config *temp = new_output_config(id_on_name);
651 merge_output_config(temp, oc_name);
652 merge_output_config(temp, oc_id);
653 list_add(config->output_configs, temp);
654
655 free(result->name);
656 result->name = strdup(id_on_name);
657 merge_output_config(result, temp);
658
659 sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)"
660 " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
661 " (power %d) (max render time: %d)", result->name, result->enabled,
662 result->width, result->height, result->refresh_rate,
663 result->x, result->y, result->scale, result->transform,
664 result->background, result->background_option, result->power,
665 result->max_render_time);
666 } else if (oc_name) {
667 // No identifier config, just return a copy of the name config
668 free(result->name);
669 result->name = strdup(name);
670 merge_output_config(result, oc_name);
671 } else if (oc_id) {
672 // No name config, just return a copy of the identifier config
673 free(result->name);
674 result->name = strdup(identifier);
675 merge_output_config(result, oc_id);
676 } else {
677 i = list_seq_find(config->output_configs, output_name_cmp, "*");
678 if (i >= 0) {
679 // No name or identifier config, but there is a wildcard config
680 free(result->name);
681 result->name = strdup("*");
682 merge_output_config(result, config->output_configs->items[i]);
683 } else if (!config->reloading) {
684 // No name, identifier, or wildcard config. Since we are not
685 // reloading with defaults, the output config will be empty, so
686 // just return NULL
687 free_output_config(result);
688 result = NULL;
689 } 755 }
690 } 756 }
691 757
692 free(id_on_name); 758 return false;
693 return result;
694} 759}
695 760
696struct output_config *find_output_config(struct sway_output *output) { 761static bool search_render_format(struct search_context *ctx, size_t output_idx) {
697 char id[128]; 762 struct matched_output_config *cfg = &ctx->configs[output_idx];
698 output_get_identifier(id, sizeof(id), output); 763 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
699 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;
700} 796}
701 797
702void apply_output_config_to_outputs(struct output_config *oc) { 798static bool search_valid_config(struct search_context *ctx, size_t output_idx) {
703 // Try to find the output container and apply configuration now. If 799 if (output_idx >= ctx->configs_len) {
704 // this is during startup then there will be no container and config 800 // We reached the end of the search, all good!
705 // will be applied during normal "new output" event from wlroots. 801 return true;
706 bool wildcard = strcmp(oc->name, "*") == 0; 802 }
707 char id[128];
708 struct sway_output *sway_output, *tmp;
709 wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) {
710 char *name = sway_output->wlr_output->name;
711 output_get_identifier(id, sizeof(id), sway_output);
712 if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) {
713 struct output_config *current = get_output_config(id, sway_output);
714 if (!current) {
715 // No stored output config matched, apply oc directly
716 sway_log(SWAY_DEBUG, "Applying oc directly");
717 current = new_output_config(oc->name);
718 merge_output_config(current, oc);
719 }
720 apply_output_config(current, sway_output);
721 free_output_config(current);
722 803
723 if (!wildcard) { 804 struct matched_output_config *cfg = &ctx->configs[output_idx];
724 // Stop looking if the output config isn't applicable to all 805 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
725 // outputs 806 struct wlr_output_state *state = &backend_state->base;
726 break; 807 struct wlr_output *wlr_output = backend_state->output;
727 } 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;
728 } 897 }
729 } 898 }
730 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;
919 }
920 }
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
731 struct sway_seat *seat; 954 struct sway_seat *seat;
732 wl_list_for_each(seat, &server.input->seats, link) { 955 wl_list_for_each(seat, &server.input->seats, link) {
733 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 956 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
734 cursor_rebase(seat->cursor); 957 cursor_rebase(seat->cursor);
735 } 958 }
959
960 return ok;
736} 961}
737 962
738void reset_outputs(void) { 963void apply_all_output_configs(void) {
739 struct output_config *oc = NULL; 964 size_t configs_len = wl_list_length(&root->all_outputs);
740 int i = list_seq_find(config->output_configs, output_name_cmp, "*"); 965 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
741 if (i >= 0) { 966 if (!configs) {
742 oc = config->output_configs->items[i]; 967 return;
743 } else {
744 oc = store_output_config(new_output_config("*"));
745 } 968 }
746 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);
747} 990}
748 991
749void free_output_config(struct output_config *oc) { 992void free_output_config(struct output_config *oc) {
@@ -810,7 +1053,9 @@ static bool _spawn_swaybg(char **command) {
810 setenv("WAYLAND_SOCKET", wayland_socket_str, true); 1053 setenv("WAYLAND_SOCKET", wayland_socket_str, true);
811 1054
812 execvp(command[0], command); 1055 execvp(command[0], command);
813 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]);
814 _exit(EXIT_FAILURE); 1059 _exit(EXIT_FAILURE);
815 } 1060 }
816 _exit(EXIT_SUCCESS); 1061 _exit(EXIT_SUCCESS);
@@ -820,12 +1065,13 @@ static bool _spawn_swaybg(char **command) {
820 sway_log_errno(SWAY_ERROR, "close failed"); 1065 sway_log_errno(SWAY_ERROR, "close failed");
821 return false; 1066 return false;
822 } 1067 }
823 if (waitpid(pid, NULL, 0) < 0) { 1068 int fork_status = 0;
1069 if (waitpid(pid, &fork_status, 0) < 0) {
824 sway_log_errno(SWAY_ERROR, "waitpid failed"); 1070 sway_log_errno(SWAY_ERROR, "waitpid failed");
825 return false; 1071 return false;
826 } 1072 }
827 1073
828 return true; 1074 return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS;
829} 1075}
830 1076
831bool spawn_swaybg(void) { 1077bool spawn_swaybg(void) {