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.c937
1 files changed, 637 insertions, 300 deletions
diff --git a/sway/config/output.c b/sway/config/output.c
index 7d0ed395..fb1956df 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -1,13 +1,15 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
2#include <drm_fourcc.h>
3#include <stdbool.h> 3#include <stdbool.h>
4#include <string.h> 4#include <string.h>
5#include <sys/socket.h> 5#include <sys/socket.h>
6#include <sys/wait.h> 6#include <sys/wait.h>
7#include <unistd.h> 7#include <unistd.h>
8#include <wlr/config.h>
8#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
9#include <wlr/types/wlr_output_layout.h> 10#include <wlr/types/wlr_output_layout.h>
10#include <wlr/types/wlr_output.h> 11#include <wlr/types/wlr_output.h>
12#include <wlr/types/wlr_output_swapchain_manager.h>
11#include "sway/config.h" 13#include "sway/config.h"
12#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
13#include "sway/output.h" 15#include "sway/output.h"
@@ -15,6 +17,10 @@
15#include "log.h" 17#include "log.h"
16#include "util.h" 18#include "util.h"
17 19
20#if WLR_HAS_DRM_BACKEND
21#include <wlr/backend/drm.h>
22#endif
23
18int output_name_cmp(const void *item, const void *data) { 24int output_name_cmp(const void *item, const void *data) {
19 const struct output_config *output = item; 25 const struct output_config *output = item;
20 const char *name = data; 26 const char *name = data;
@@ -25,8 +31,10 @@ int output_name_cmp(const void *item, const void *data) {
25void output_get_identifier(char *identifier, size_t len, 31void output_get_identifier(char *identifier, size_t len,
26 struct sway_output *output) { 32 struct sway_output *output) {
27 struct wlr_output *wlr_output = output->wlr_output; 33 struct wlr_output *wlr_output = output->wlr_output;
28 snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, 34 snprintf(identifier, len, "%s %s %s",
29 wlr_output->serial); 35 wlr_output->make ? wlr_output->make : "Unknown",
36 wlr_output->model ? wlr_output->model : "Unknown",
37 wlr_output->serial ? wlr_output->serial : "Unknown");
30} 38}
31 39
32const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { 40const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) {
@@ -58,6 +66,7 @@ struct output_config *new_output_config(const char *name) {
58 oc->width = oc->height = -1; 66 oc->width = oc->height = -1;
59 oc->refresh_rate = -1; 67 oc->refresh_rate = -1;
60 oc->custom_mode = -1; 68 oc->custom_mode = -1;
69 oc->drm_mode.type = -1;
61 oc->x = oc->y = -1; 70 oc->x = oc->y = -1;
62 oc->scale = -1; 71 oc->scale = -1;
63 oc->scale_filter = SCALE_FILTER_DEFAULT; 72 oc->scale_filter = SCALE_FILTER_DEFAULT;
@@ -65,10 +74,77 @@ struct output_config *new_output_config(const char *name) {
65 oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; 74 oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
66 oc->max_render_time = -1; 75 oc->max_render_time = -1;
67 oc->adaptive_sync = -1; 76 oc->adaptive_sync = -1;
77 oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
78 oc->power = -1;
68 return oc; 79 return oc;
69} 80}
70 81
71void 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) {
72 if (src->enabled != -1) { 148 if (src->enabled != -1) {
73 dst->enabled = src->enabled; 149 dst->enabled = src->enabled;
74 } 150 }
@@ -99,6 +175,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
99 if (src->custom_mode != -1) { 175 if (src->custom_mode != -1) {
100 dst->custom_mode = src->custom_mode; 176 dst->custom_mode = src->custom_mode;
101 } 177 }
178 if (src->drm_mode.type != (uint32_t) -1) {
179 memcpy(&dst->drm_mode, &src->drm_mode, sizeof(src->drm_mode));
180 }
102 if (src->transform != -1) { 181 if (src->transform != -1) {
103 dst->transform = src->transform; 182 dst->transform = src->transform;
104 } 183 }
@@ -108,6 +187,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
108 if (src->adaptive_sync != -1) { 187 if (src->adaptive_sync != -1) {
109 dst->adaptive_sync = src->adaptive_sync; 188 dst->adaptive_sync = src->adaptive_sync;
110 } 189 }
190 if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
191 dst->render_bit_depth = src->render_bit_depth;
192 }
111 if (src->background) { 193 if (src->background) {
112 free(dst->background); 194 free(dst->background);
113 dst->background = strdup(src->background); 195 dst->background = strdup(src->background);
@@ -120,155 +202,124 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
120 free(dst->background_fallback); 202 free(dst->background_fallback);
121 dst->background_fallback = strdup(src->background_fallback); 203 dst->background_fallback = strdup(src->background_fallback);
122 } 204 }
123 if (src->dpms_state != 0) { 205 if (src->power != -1) {
124 dst->dpms_state = src->dpms_state; 206 dst->power = src->power;
125 } 207 }
126} 208}
127 209
128static void merge_wildcard_on_all(struct output_config *wildcard) { 210void store_output_config(struct output_config *oc) {
129 for (int i = 0; i < config->output_configs->length; i++) { 211 bool merged = false;
130 struct output_config *oc = config->output_configs->items[i]; 212 bool wildcard = strcmp(oc->name, "*") == 0;
131 if (strcmp(wildcard->name, oc->name) != 0) { 213 struct sway_output *output = wildcard ? NULL : all_output_by_name_or_id(oc->name);
132 sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name);
133 merge_output_config(oc, wildcard);
134 }
135 }
136}
137 214
138static void merge_id_on_name(struct output_config *oc) {
139 char *id_on_name = NULL;
140 char id[128]; 215 char id[128];
141 char *name = NULL; 216 if (output) {
142 struct sway_output *output;
143 wl_list_for_each(output, &root->all_outputs, link) {
144 name = output->wlr_output->name;
145 output_get_identifier(id, sizeof(id), output); 217 output_get_identifier(id, sizeof(id), output);
146 if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) {
147 size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1;
148 id_on_name = malloc(length);
149 if (!id_on_name) {
150 sway_log(SWAY_ERROR, "Failed to allocate id on name string");
151 return;
152 }
153 snprintf(id_on_name, length, "%s on %s", id, name);
154 break;
155 }
156 } 218 }
157 219
158 if (!id_on_name) { 220 for (int i = 0; i < config->output_configs->length; i++) {
159 return; 221 struct output_config *old = config->output_configs->items[i];
160 } 222
161 223 // If the old config matches the new config's name, regardless of
162 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
163 if (i >= 0) { 225 // config. If the new config is a wildcard, this also merges on top of
164 sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); 226 // old wildcard configs.
165 merge_output_config(config->output_configs->items[i], oc); 227 if (strcmp(old->name, oc->name) == 0) {
166 } else { 228 merge_output_config(old, oc);
167 // If both a name and identifier config, exist generate an id on name 229 merged = true;
168 int ni = list_seq_find(config->output_configs, output_name_cmp, name); 230 continue;
169 int ii = list_seq_find(config->output_configs, output_name_cmp, id);
170 if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0)
171 || (ii >= 0 && strcmp(oc->name, name) == 0)) {
172 struct output_config *ion_oc = new_output_config(id_on_name);
173 if (ni >= 0) {
174 merge_output_config(ion_oc, config->output_configs->items[ni]);
175 }
176 if (ii >= 0) {
177 merge_output_config(ion_oc, config->output_configs->items[ii]);
178 }
179 merge_output_config(ion_oc, oc);
180 list_add(config->output_configs, ion_oc);
181 sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\""
182 " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f "
183 "transform %d) (bg %s %s) (dpms %d) (max render time: %d)",
184 ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height,
185 ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale,
186 ion_oc->transform, ion_oc->background,
187 ion_oc->background_option, ion_oc->dpms_state,
188 ion_oc->max_render_time);
189 } 231 }
190 }
191 free(id_on_name);
192}
193 232
194struct output_config *store_output_config(struct output_config *oc) { 233 // If the new config is a wildcard config we supersede all non-wildcard
195 bool wildcard = strcmp(oc->name, "*") == 0; 234 // configs. Old wildcard configs have already been handled above.
196 if (wildcard) { 235 if (wildcard) {
197 merge_wildcard_on_all(oc); 236 supersede_output_config(old, oc);
198 } else { 237 continue;
199 merge_id_on_name(oc); 238 }
200 }
201 239
202 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
203 if (i >= 0) { 241 // matches on that output's identifier, supersede it.
204 sway_log(SWAY_DEBUG, "Merging on top of existing output config"); 242 if (output && strcmp(old->name, id) == 0 &&
205 struct output_config *current = config->output_configs->items[i]; 243 strcmp(oc->name, output->wlr_output->name) == 0) {
206 merge_output_config(current, oc); 244 supersede_output_config(old, oc);
207 free_output_config(oc);
208 oc = current;
209 } else if (!wildcard) {
210 sway_log(SWAY_DEBUG, "Adding non-wildcard output config");
211 i = list_seq_find(config->output_configs, output_name_cmp, "*");
212 if (i >= 0) {
213 sway_log(SWAY_DEBUG, "Merging on top of output * config");
214 struct output_config *current = new_output_config(oc->name);
215 merge_output_config(current, config->output_configs->items[i]);
216 merge_output_config(current, oc);
217 free_output_config(oc);
218 oc = current;
219 } 245 }
220 list_add(config->output_configs, oc);
221 } else {
222 // New wildcard config. Just add it
223 sway_log(SWAY_DEBUG, "Adding output * config");
224 list_add(config->output_configs, oc);
225 } 246 }
226 247
227 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 "
228 "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) "
229 "(max render time: %d)", 250 "(max render time: %d)",
230 oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, 251 oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate,
231 oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), 252 oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel),
232 oc->transform, oc->background, oc->background_option, oc->dpms_state, 253 oc->transform, oc->background, oc->background_option, oc->power,
233 oc->max_render_time); 254 oc->max_render_time);
234 255
235 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 }
236} 263}
237 264
238static void set_mode(struct wlr_output *output, int width, int height, 265static void set_mode(struct wlr_output *output, struct wlr_output_state *pending,
239 float refresh_rate, bool custom) { 266 int width, int height, float refresh_rate, bool custom) {
240 // Not all floating point integers can be represented exactly 267 // Not all floating point integers can be represented exactly
241 // as (int)(1000 * mHz / 1000.f) 268 // as (int)(1000 * mHz / 1000.f)
242 // round() the result to avoid any error 269 // round() the result to avoid any error
243 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;
244 273
245 if (wl_list_empty(&output->modes) || custom) { 274 if (wl_list_empty(&output->modes) || custom) {
246 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); 275 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name);
247 wlr_output_set_custom_mode(output, width, height, 276 wlr_output_state_set_custom_mode(pending, width, height,
248 refresh_rate > 0 ? mhz : 0); 277 refresh_rate > 0 ? mhz : 0);
249 return; 278 return;
250 } 279 }
251 280
252 struct wlr_output_mode *mode, *best = NULL; 281 struct wlr_output_mode *mode, *best = NULL;
282 int best_diff_mhz = INT_MAX;
253 wl_list_for_each(mode, &output->modes, link) { 283 wl_list_for_each(mode, &output->modes, link) {
254 if (mode->width == width && mode->height == height) { 284 if (mode->width == width && mode->height == height) {
255 if (mode->refresh == mhz) { 285 int diff_mhz = abs(mode->refresh - mhz);
256 best = mode; 286 if (diff_mhz < best_diff_mhz) {
257 break; 287 best_diff_mhz = diff_mhz;
258 }
259 if (best == NULL || mode->refresh > best->refresh) {
260 best = mode; 288 best = mode;
289 if (best_diff_mhz == 0) {
290 break;
291 }
261 } 292 }
262 } 293 }
263 } 294 }
264 if (!best) { 295 if (best) {
265 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",
266 sway_log(SWAY_INFO, "Picking preferred mode instead"); 297 best->width, best->height, best->refresh / 1000.f, output->name);
267 best = wlr_output_preferred_mode(output);
268 } else { 298 } else {
269 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);
304 }
305 wlr_output_state_set_mode(pending, best);
306}
307
308static void set_modeline(struct wlr_output *output,
309 struct wlr_output_state *pending, drmModeModeInfo *drm_mode) {
310#if WLR_HAS_DRM_BACKEND
311 if (!wlr_output_is_drm(output)) {
312 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
313 return;
270 } 314 }
271 wlr_output_set_mode(output, best); 315 sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name);
316 struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode);
317 if (mode) {
318 wlr_output_state_set_mode(pending, mode);
319 }
320#else
321 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
322#endif
272} 323}
273 324
274/* 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
@@ -289,23 +340,24 @@ static bool phys_size_is_aspect_ratio(struct wlr_output *output) {
289// 1 inch = 25.4 mm 340// 1 inch = 25.4 mm
290#define MM_PER_INCH 25.4 341#define MM_PER_INCH 25.4
291 342
292static int compute_default_scale(struct wlr_output *output) { 343static int compute_default_scale(struct wlr_output *output,
344 struct wlr_output_state *pending) {
293 struct wlr_box box = { .width = output->width, .height = output->height }; 345 struct wlr_box box = { .width = output->width, .height = output->height };
294 if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { 346 if (pending->committed & WLR_OUTPUT_STATE_MODE) {
295 switch (output->pending.mode_type) { 347 switch (pending->mode_type) {
296 case WLR_OUTPUT_STATE_MODE_FIXED: 348 case WLR_OUTPUT_STATE_MODE_FIXED:
297 box.width = output->pending.mode->width; 349 box.width = pending->mode->width;
298 box.height = output->pending.mode->height; 350 box.height = pending->mode->height;
299 break; 351 break;
300 case WLR_OUTPUT_STATE_MODE_CUSTOM: 352 case WLR_OUTPUT_STATE_MODE_CUSTOM:
301 box.width = output->pending.custom_mode.width; 353 box.width = pending->custom_mode.width;
302 box.height = output->pending.custom_mode.height; 354 box.height = pending->custom_mode.height;
303 break; 355 break;
304 } 356 }
305 } 357 }
306 enum wl_output_transform transform = output->transform; 358 enum wl_output_transform transform = output->transform;
307 if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) { 359 if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) {
308 transform = output->pending.transform; 360 transform = pending->transform;
309 } 361 }
310 wlr_box_transform(&box, &box, transform, box.width, box.height); 362 wlr_box_transform(&box, &box, transform, box.width, box.height);
311 363
@@ -334,42 +386,70 @@ static int compute_default_scale(struct wlr_output *output) {
334 return 2; 386 return 2;
335} 387}
336 388
389static bool render_format_is_10bit(uint32_t render_format) {
390 return render_format == DRM_FORMAT_XRGB2101010 ||
391 render_format == DRM_FORMAT_XBGR2101010;
392}
393
394static bool render_format_is_bgr(uint32_t fmt) {
395 return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888;
396}
397
398static bool output_config_is_disabling(struct output_config *oc) {
399 return oc && (!oc->enabled || oc->power == 0);
400}
401
337static void queue_output_config(struct output_config *oc, 402static void queue_output_config(struct output_config *oc,
338 struct sway_output *output) { 403 struct sway_output *output, struct wlr_output_state *pending) {
339 if (output == root->noop_output) { 404 if (output == root->fallback_output) {
340 return; 405 return;
341 } 406 }
342 407
343 struct wlr_output *wlr_output = output->wlr_output; 408 struct wlr_output *wlr_output = output->wlr_output;
344 409
345 if (oc && (!oc->enabled || oc->dpms_state == DPMS_OFF)) { 410 if (output_config_is_disabling(oc)) {
346 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); 411 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
347 wlr_output_enable(wlr_output, false); 412 wlr_output_state_set_enabled(pending, false);
348 return; 413 return;
349 } 414 }
350 415
351 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); 416 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name);
352 wlr_output_enable(wlr_output, true); 417 wlr_output_state_set_enabled(pending, true);
353 418
354 if (oc && oc->width > 0 && oc->height > 0) { 419 if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) {
420 sway_log(SWAY_DEBUG, "Set %s modeline",
421 wlr_output->name);
422 set_modeline(wlr_output, pending, &oc->drm_mode);
423 } else if (oc && oc->width > 0 && oc->height > 0) {
355 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)",
356 wlr_output->name, oc->width, oc->height, oc->refresh_rate); 425 wlr_output->name, oc->width, oc->height, oc->refresh_rate);
357 set_mode(wlr_output, oc->width, oc->height, 426 set_mode(wlr_output, pending, oc->width, oc->height,
358 oc->refresh_rate, oc->custom_mode == 1); 427 oc->refresh_rate, oc->custom_mode == 1);
359 } else if (!wl_list_empty(&wlr_output->modes)) { 428 } else if (!wl_list_empty(&wlr_output->modes)) {
360 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); 429 sway_log(SWAY_DEBUG, "Set preferred mode");
361 wlr_output_set_mode(wlr_output, mode); 430 struct wlr_output_mode *preferred_mode =
431 wlr_output_preferred_mode(wlr_output);
432 wlr_output_state_set_mode(pending, preferred_mode);
362 } 433 }
363 434
364 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 435 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
365 sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, 436 sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name,
366 sway_wl_output_subpixel_to_string(oc->subpixel)); 437 sway_wl_output_subpixel_to_string(oc->subpixel));
367 wlr_output_set_subpixel(wlr_output, oc->subpixel); 438 wlr_output_state_set_subpixel(pending, oc->subpixel);
368 } 439 }
369 440
441 enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL;
370 if (oc && oc->transform >= 0) { 442 if (oc && oc->transform >= 0) {
371 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); 443 tr = oc->transform;
372 wlr_output_set_transform(wlr_output, oc->transform); 444#if WLR_HAS_DRM_BACKEND
445 } else if (wlr_output_is_drm(wlr_output)) {
446 tr = wlr_drm_connector_get_panel_orientation(wlr_output);
447 sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr);
448#endif
449 }
450 if (wlr_output->transform != tr) {
451 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr);
452 wlr_output_state_set_transform(pending, tr);
373 } 453 }
374 454
375 // 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
@@ -377,50 +457,50 @@ static void queue_output_config(struct output_config *oc,
377 float scale; 457 float scale;
378 if (oc && oc->scale > 0) { 458 if (oc && oc->scale > 0) {
379 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 }
380 } else { 470 } else {
381 scale = compute_default_scale(wlr_output); 471 scale = compute_default_scale(wlr_output, pending);
382 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); 472 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale);
383 } 473 }
384 if (scale != wlr_output->scale) { 474 if (scale != wlr_output->scale) {
385 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);
386 wlr_output_set_scale(wlr_output, scale); 476 wlr_output_state_set_scale(pending, scale);
387 } 477 }
388 478
389 if (oc && oc->adaptive_sync != -1) { 479 if (oc && oc->adaptive_sync != -1) {
390 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,
391 oc->adaptive_sync); 481 oc->adaptive_sync);
392 wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); 482 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1);
393 }
394}
395
396bool apply_output_config(struct output_config *oc, struct sway_output *output) {
397 if (output == root->noop_output) {
398 return false;
399 } 483 }
400 484
401 struct wlr_output *wlr_output = output->wlr_output; 485 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
402 486 if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 &&
403 // Flag to prevent the output mode event handler from calling us 487 render_format_is_10bit(output->wlr_output->render_format)) {
404 output->enabling = (!oc || oc->enabled); 488 // 10-bit was set successfully before, try to save some tests by reusing the format
405 489 wlr_output_state_set_render_format(pending, output->wlr_output->render_format);
406 queue_output_config(oc, output); 490 } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) {
407 491 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010);
408 if (!oc || oc->dpms_state != DPMS_OFF) { 492 } else {
409 output->current_mode = wlr_output->pending.mode; 493 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888);
494 }
410 } 495 }
496}
411 497
412 sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); 498static bool finalize_output_config(struct output_config *oc, struct sway_output *output) {
413 if (!wlr_output_commit(wlr_output)) { 499 if (output == root->fallback_output) {
414 // Failed to commit output changes, maybe the output is missing a CRTC.
415 // Leave the output disabled for now and try again when the output gets
416 // the mode we asked for.
417 sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name);
418 output->enabling = false;
419 return false; 500 return false;
420 } 501 }
421 502
422 output->enabling = false; 503 struct wlr_output *wlr_output = output->wlr_output;
423
424 if (oc && !oc->enabled) { 504 if (oc && !oc->enabled) {
425 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); 505 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name);
426 if (output->enabled) { 506 if (output->enabled) {
@@ -430,10 +510,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
430 return true; 510 return true;
431 } 511 }
432 512
433 if (config->reloading) {
434 output_damage_whole(output);
435 }
436
437 if (oc) { 513 if (oc) {
438 enum scale_filter_mode scale_filter_old = output->scale_filter; 514 enum scale_filter_mode scale_filter_old = output->scale_filter;
439 switch (oc->scale_filter) { 515 switch (oc->scale_filter) {
@@ -450,6 +526,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
450 if (scale_filter_old != output->scale_filter) { 526 if (scale_filter_old != output->scale_filter) {
451 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,
452 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);
453 } 530 }
454 } 531 }
455 532
@@ -462,12 +539,12 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
462 } 539 }
463 540
464 // Update output->{lx, ly, width, height} 541 // Update output->{lx, ly, width, height}
465 struct wlr_box *output_box = 542 struct wlr_box output_box;
466 wlr_output_layout_get_box(root->output_layout, wlr_output); 543 wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box);
467 output->lx = output_box->x; 544 output->lx = output_box.x;
468 output->ly = output_box->y; 545 output->ly = output_box.y;
469 output->width = output_box->width; 546 output->width = output_box.width;
470 output->height = output_box->height; 547 output->height = output_box.height;
471 548
472 if (!output->enabled) { 549 if (!output->enabled) {
473 output_enable(output); 550 output_enable(output);
@@ -479,29 +556,13 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
479 output->max_render_time = oc->max_render_time; 556 output->max_render_time = oc->max_render_time;
480 } 557 }
481 558
482 // Reconfigure all devices, since input config may have been applied before
483 // this output came online, and some config items (like map_to_output) are
484 // dependent on an output being present.
485 input_manager_configure_all_inputs();
486 // Reconfigure the cursor images, since the scale may have changed.
487 input_manager_configure_xcursor();
488 return true; 559 return true;
489} 560}
490 561
491bool test_output_config(struct output_config *oc, struct sway_output *output) {
492 if (output == root->noop_output) {
493 return false;
494 }
495
496 queue_output_config(oc, output);
497 bool ok = wlr_output_test(output->wlr_output);
498 wlr_output_rollback(output->wlr_output);
499 return ok;
500}
501
502static void default_output_config(struct output_config *oc, 562static void default_output_config(struct output_config *oc,
503 struct wlr_output *wlr_output) { 563 struct wlr_output *wlr_output) {
504 oc->enabled = 1; 564 oc->enabled = 1;
565 oc->power = 1;
505 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); 566 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
506 if (mode != NULL) { 567 if (mode != NULL) {
507 oc->width = mode->width; 568 oc->width = mode->width;
@@ -514,147 +575,418 @@ static void default_output_config(struct output_config *oc,
514 struct sway_output *output = wlr_output->data; 575 struct sway_output *output = wlr_output->data;
515 oc->subpixel = output->detected_subpixel; 576 oc->subpixel = output->detected_subpixel;
516 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; 577 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
517 oc->dpms_state = DPMS_ON;
518 oc->max_render_time = 0; 578 oc->max_render_time = 0;
519} 579}
520 580
521static struct output_config *get_output_config(char *identifier, 581// find_output_config returns a merged output_config containing all stored
522 struct sway_output *sway_output) { 582// configuration that applies to the specified output.
583struct output_config *find_output_config(struct sway_output *sway_output) {
523 const char *name = sway_output->wlr_output->name; 584 const char *name = sway_output->wlr_output->name;
585 struct output_config *oc = NULL;
586
587 struct output_config *result = new_output_config(name);
588 if (config->reloading) {
589 default_output_config(result, sway_output->wlr_output);
590 }
524 591
525 struct output_config *oc_id_on_name = NULL; 592 char id[128];
526 struct output_config *oc_name = NULL; 593 output_get_identifier(id, sizeof(id), sway_output);
527 struct output_config *oc_id = NULL;
528 594
529 size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; 595 int i;
530 char *id_on_name = malloc(length); 596 bool match = false;
531 snprintf(id_on_name, length, "%s on %s", identifier, name); 597 if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) {
532 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 598 match = true;
533 if (i >= 0) { 599 oc = config->output_configs->items[i];
534 oc_id_on_name = config->output_configs->items[i]; 600 merge_output_config(result, oc);
535 } else { 601 }
536 i = list_seq_find(config->output_configs, output_name_cmp, name); 602 if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) {
537 if (i >= 0) { 603 match = true;
538 oc_name = config->output_configs->items[i]; 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)" : "");
539 } 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}
540 675
541 i = list_seq_find(config->output_configs, output_name_cmp, identifier); 676static void clear_later_output_states(struct wlr_backend_output_state *states,
542 if (i >= 0) { 677 size_t configs_len, size_t output_idx) {
543 oc_id = config->output_configs->items[i]; 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;
544 } 716 }
545 } 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}
546 723
547 struct output_config *result = new_output_config("temp"); 724static bool search_mode(struct search_context *ctx, size_t output_idx) {
548 if (config->reloading) { 725 struct matched_output_config *cfg = &ctx->configs[output_idx];
549 default_output_config(result, sway_output->wlr_output); 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);
550 } 732 }
551 if (oc_id_on_name) { 733
552 // Already have an identifier on name config, use that 734 struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
553 free(result->name); 735 if (preferred_mode) {
554 result->name = strdup(id_on_name); 736 wlr_output_state_set_mode(state, preferred_mode);
555 merge_output_config(result, oc_id_on_name); 737 if (search_adaptive_sync(ctx, output_idx)) {
556 } else if (oc_name && oc_id) { 738 return true;
557 // Generate a config named `<identifier> on <name>` which contains a
558 // merged copy of the identifier on name. This will make sure that both
559 // identifier and name configs are respected, with identifier getting
560 // priority
561 struct output_config *temp = new_output_config(id_on_name);
562 merge_output_config(temp, oc_name);
563 merge_output_config(temp, oc_id);
564 list_add(config->output_configs, temp);
565
566 free(result->name);
567 result->name = strdup(id_on_name);
568 merge_output_config(result, temp);
569
570 sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)"
571 " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
572 " (dpms %d) (max render time: %d)", result->name, result->enabled,
573 result->width, result->height, result->refresh_rate,
574 result->x, result->y, result->scale, result->transform,
575 result->background, result->background_option, result->dpms_state,
576 result->max_render_time);
577 } else if (oc_name) {
578 // No identifier config, just return a copy of the name config
579 free(result->name);
580 result->name = strdup(name);
581 merge_output_config(result, oc_name);
582 } else if (oc_id) {
583 // No name config, just return a copy of the identifier config
584 free(result->name);
585 result->name = strdup(identifier);
586 merge_output_config(result, oc_id);
587 } else {
588 i = list_seq_find(config->output_configs, output_name_cmp, "*");
589 if (i >= 0) {
590 // No name or identifier config, but there is a wildcard config
591 free(result->name);
592 result->name = strdup("*");
593 merge_output_config(result, config->output_configs->items[i]);
594 } else if (!config->reloading) {
595 // No name, identifier, or wildcard config. Since we are not
596 // reloading with defaults, the output config will be empty, so
597 // just return NULL
598 free_output_config(result);
599 result = NULL;
600 } 739 }
601 } 740 }
602 741
603 free(id_on_name); 742 if (wl_list_empty(&wlr_output->modes)) {
604 return result; 743 state->committed &= ~WLR_OUTPUT_STATE_MODE;
744 return search_adaptive_sync(ctx, output_idx);
745 }
746
747 struct wlr_output_mode *mode;
748 wl_list_for_each(mode, &backend_state->output->modes, link) {
749 if (mode == preferred_mode) {
750 continue;
751 }
752 wlr_output_state_set_mode(state, mode);
753 if (search_adaptive_sync(ctx, output_idx)) {
754 return true;
755 }
756 }
757
758 return false;
605} 759}
606 760
607struct output_config *find_output_config(struct sway_output *output) { 761static bool search_render_format(struct search_context *ctx, size_t output_idx) {
608 char id[128]; 762 struct matched_output_config *cfg = &ctx->configs[output_idx];
609 output_get_identifier(id, sizeof(id), output); 763 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
610 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;
611} 796}
612 797
613void apply_output_config_to_outputs(struct output_config *oc) { 798static bool search_valid_config(struct search_context *ctx, size_t output_idx) {
614 // Try to find the output container and apply configuration now. If 799 if (output_idx >= ctx->configs_len) {
615 // this is during startup then there will be no container and config 800 // We reached the end of the search, all good!
616 // will be applied during normal "new output" event from wlroots. 801 return true;
617 bool wildcard = strcmp(oc->name, "*") == 0; 802 }
618 char id[128];
619 struct sway_output *sway_output, *tmp;
620 wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) {
621 char *name = sway_output->wlr_output->name;
622 output_get_identifier(id, sizeof(id), sway_output);
623 if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) {
624 struct output_config *current = get_output_config(id, sway_output);
625 if (!current) {
626 // No stored output config matched, apply oc directly
627 sway_log(SWAY_DEBUG, "Applying oc directly");
628 current = new_output_config(oc->name);
629 merge_output_config(current, oc);
630 }
631 apply_output_config(current, sway_output);
632 free_output_config(current);
633 803
634 if (!wildcard) { 804 struct matched_output_config *cfg = &ctx->configs[output_idx];
635 // Stop looking if the output config isn't applicable to all 805 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
636 // outputs 806 struct wlr_output_state *state = &backend_state->base;
637 break; 807 struct wlr_output *wlr_output = backend_state->output;
638 } 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;
639 } 897 }
640 } 898 }
641 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
642 struct sway_seat *seat; 954 struct sway_seat *seat;
643 wl_list_for_each(seat, &server.input->seats, link) { 955 wl_list_for_each(seat, &server.input->seats, link) {
644 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 956 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
645 cursor_rebase(seat->cursor); 957 cursor_rebase(seat->cursor);
646 } 958 }
959
960 return ok;
647} 961}
648 962
649void reset_outputs(void) { 963void apply_all_output_configs(void) {
650 struct output_config *oc = NULL; 964 size_t configs_len = wl_list_length(&root->all_outputs);
651 int i = list_seq_find(config->output_configs, output_name_cmp, "*"); 965 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
652 if (i >= 0) { 966 if (!configs) {
653 oc = config->output_configs->items[i]; 967 return;
654 } else {
655 oc = store_output_config(new_output_config("*"));
656 } 968 }
657 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);
658} 990}
659 991
660void free_output_config(struct output_config *oc) { 992void free_output_config(struct output_config *oc) {
@@ -704,6 +1036,8 @@ static bool _spawn_swaybg(char **command) {
704 sway_log_errno(SWAY_ERROR, "fork failed"); 1036 sway_log_errno(SWAY_ERROR, "fork failed");
705 return false; 1037 return false;
706 } else if (pid == 0) { 1038 } else if (pid == 0) {
1039 restore_nofile_limit();
1040
707 pid = fork(); 1041 pid = fork();
708 if (pid < 0) { 1042 if (pid < 0) {
709 sway_log_errno(SWAY_ERROR, "fork failed"); 1043 sway_log_errno(SWAY_ERROR, "fork failed");
@@ -719,7 +1053,9 @@ static bool _spawn_swaybg(char **command) {
719 setenv("WAYLAND_SOCKET", wayland_socket_str, true); 1053 setenv("WAYLAND_SOCKET", wayland_socket_str, true);
720 1054
721 execvp(command[0], command); 1055 execvp(command[0], command);
722 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]);
723 _exit(EXIT_FAILURE); 1059 _exit(EXIT_FAILURE);
724 } 1060 }
725 _exit(EXIT_SUCCESS); 1061 _exit(EXIT_SUCCESS);
@@ -729,12 +1065,13 @@ static bool _spawn_swaybg(char **command) {
729 sway_log_errno(SWAY_ERROR, "close failed"); 1065 sway_log_errno(SWAY_ERROR, "close failed");
730 return false; 1066 return false;
731 } 1067 }
732 if (waitpid(pid, NULL, 0) < 0) { 1068 int fork_status = 0;
1069 if (waitpid(pid, &fork_status, 0) < 0) {
733 sway_log_errno(SWAY_ERROR, "waitpid failed"); 1070 sway_log_errno(SWAY_ERROR, "waitpid failed");
734 return false; 1071 return false;
735 } 1072 }
736 1073
737 return true; 1074 return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS;
738} 1075}
739 1076
740bool spawn_swaybg(void) { 1077bool spawn_swaybg(void) {