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