aboutsummaryrefslogtreecommitdiffstats
path: root/sway/config
diff options
context:
space:
mode:
Diffstat (limited to 'sway/config')
-rw-r--r--sway/config/bar.c3
-rw-r--r--sway/config/input.c14
-rw-r--r--sway/config/output.c922
-rw-r--r--sway/config/seat.c2
4 files changed, 600 insertions, 341 deletions
diff --git a/sway/config/bar.c b/sway/config/bar.c
index d1b342e6..ecefb61a 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <signal.h> 1#include <signal.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdio.h> 3#include <stdio.h>
@@ -13,6 +12,7 @@
13#include "sway/config.h" 12#include "sway/config.h"
14#include "sway/input/keyboard.h" 13#include "sway/input/keyboard.h"
15#include "sway/output.h" 14#include "sway/output.h"
15#include "sway/server.h"
16#include "config.h" 16#include "config.h"
17#include "list.h" 17#include "list.h"
18#include "log.h" 18#include "log.h"
@@ -256,7 +256,6 @@ static void invoke_swaybar(struct bar_config *bar) {
256 } 256 }
257 257
258 sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); 258 sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id);
259 return;
260} 259}
261 260
262void load_swaybar(struct bar_config *bar) { 261void load_swaybar(struct bar_config *bar) {
diff --git a/sway/config/input.c b/sway/config/input.c
index a998e170..613270df 100644
--- a/sway/config/input.c
+++ b/sway/config/input.c
@@ -1,9 +1,9 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <limits.h> 2#include <limits.h>
4#include <float.h> 3#include <float.h>
5#include "sway/config.h" 4#include "sway/config.h"
6#include "sway/input/keyboard.h" 5#include "sway/input/keyboard.h"
6#include "sway/server.h"
7#include "log.h" 7#include "log.h"
8 8
9struct input_config *new_input_config(const char* identifier) { 9struct input_config *new_input_config(const char* identifier) {
@@ -25,14 +25,17 @@ struct input_config *new_input_config(const char* identifier) {
25 input->drag = INT_MIN; 25 input->drag = INT_MIN;
26 input->drag_lock = INT_MIN; 26 input->drag_lock = INT_MIN;
27 input->dwt = INT_MIN; 27 input->dwt = INT_MIN;
28 input->dwtp = INT_MIN;
28 input->send_events = INT_MIN; 29 input->send_events = INT_MIN;
29 input->click_method = INT_MIN; 30 input->click_method = INT_MIN;
30 input->middle_emulation = INT_MIN; 31 input->middle_emulation = INT_MIN;
31 input->natural_scroll = INT_MIN; 32 input->natural_scroll = INT_MIN;
32 input->accel_profile = INT_MIN; 33 input->accel_profile = INT_MIN;
34 input->rotation_angle = FLT_MIN;
33 input->pointer_accel = FLT_MIN; 35 input->pointer_accel = FLT_MIN;
34 input->scroll_factor = FLT_MIN; 36 input->scroll_factor = FLT_MIN;
35 input->scroll_button = INT_MIN; 37 input->scroll_button = INT_MIN;
38 input->scroll_button_lock = INT_MIN;
36 input->scroll_method = INT_MIN; 39 input->scroll_method = INT_MIN;
37 input->left_handed = INT_MIN; 40 input->left_handed = INT_MIN;
38 input->repeat_delay = INT_MIN; 41 input->repeat_delay = INT_MIN;
@@ -61,6 +64,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
61 if (src->dwt != INT_MIN) { 64 if (src->dwt != INT_MIN) {
62 dst->dwt = src->dwt; 65 dst->dwt = src->dwt;
63 } 66 }
67 if (src->dwtp != INT_MIN) {
68 dst->dwtp = src->dwtp;
69 }
64 if (src->left_handed != INT_MIN) { 70 if (src->left_handed != INT_MIN) {
65 dst->left_handed = src->left_handed; 71 dst->left_handed = src->left_handed;
66 } 72 }
@@ -70,6 +76,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
70 if (src->natural_scroll != INT_MIN) { 76 if (src->natural_scroll != INT_MIN) {
71 dst->natural_scroll = src->natural_scroll; 77 dst->natural_scroll = src->natural_scroll;
72 } 78 }
79 if (src->rotation_angle != FLT_MIN) {
80 dst->rotation_angle = src->rotation_angle;
81 }
73 if (src->pointer_accel != FLT_MIN) { 82 if (src->pointer_accel != FLT_MIN) {
74 dst->pointer_accel = src->pointer_accel; 83 dst->pointer_accel = src->pointer_accel;
75 } 84 }
@@ -88,6 +97,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
88 if (src->scroll_button != INT_MIN) { 97 if (src->scroll_button != INT_MIN) {
89 dst->scroll_button = src->scroll_button; 98 dst->scroll_button = src->scroll_button;
90 } 99 }
100 if (src->scroll_button_lock != INT_MIN) {
101 dst->scroll_button_lock = src->scroll_button_lock;
102 }
91 if (src->send_events != INT_MIN) { 103 if (src->send_events != INT_MIN) {
92 dst->send_events = src->send_events; 104 dst->send_events = src->send_events;
93 } 105 }
diff --git a/sway/config/output.c b/sway/config/output.c
index aa4cf946..9a447388 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,17 +5,23 @@
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"
16#include "sway/server.h"
16#include "sway/tree/root.h" 17#include "sway/tree/root.h"
17#include "log.h" 18#include "log.h"
18#include "util.h" 19#include "util.h"
19 20
21#if WLR_HAS_DRM_BACKEND
22#include <wlr/backend/drm.h>
23#endif
24
20int output_name_cmp(const void *item, const void *data) { 25int output_name_cmp(const void *item, const void *data) {
21 const struct output_config *output = item; 26 const struct output_config *output = item;
22 const char *name = data; 27 const char *name = data;
@@ -71,10 +76,76 @@ struct output_config *new_output_config(const char *name) {
71 oc->max_render_time = -1; 76 oc->max_render_time = -1;
72 oc->adaptive_sync = -1; 77 oc->adaptive_sync = -1;
73 oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; 78 oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
79 oc->power = -1;
74 return oc; 80 return oc;
75} 81}
76 82
77void merge_output_config(struct output_config *dst, struct output_config *src) { 83// supersede_output_config clears all fields in dst that were set in src
84static void supersede_output_config(struct output_config *dst, struct output_config *src) {
85 if (src->enabled != -1) {
86 dst->enabled = -1;
87 }
88 if (src->width != -1) {
89 dst->width = -1;
90 }
91 if (src->height != -1) {
92 dst->height = -1;
93 }
94 if (src->x != -1) {
95 dst->x = -1;
96 }
97 if (src->y != -1) {
98 dst->y = -1;
99 }
100 if (src->scale != -1) {
101 dst->scale = -1;
102 }
103 if (src->scale_filter != SCALE_FILTER_DEFAULT) {
104 dst->scale_filter = SCALE_FILTER_DEFAULT;
105 }
106 if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) {
107 dst->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
108 }
109 if (src->refresh_rate != -1) {
110 dst->refresh_rate = -1;
111 }
112 if (src->custom_mode != -1) {
113 dst->custom_mode = -1;
114 }
115 if (src->drm_mode.type != (uint32_t) -1) {
116 dst->drm_mode.type = -1;
117 }
118 if (src->transform != -1) {
119 dst->transform = -1;
120 }
121 if (src->max_render_time != -1) {
122 dst->max_render_time = -1;
123 }
124 if (src->adaptive_sync != -1) {
125 dst->adaptive_sync = -1;
126 }
127 if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
128 dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
129 }
130 if (src->background) {
131 free(dst->background);
132 dst->background = NULL;
133 }
134 if (src->background_option) {
135 free(dst->background_option);
136 dst->background_option = NULL;
137 }
138 if (src->background_fallback) {
139 free(dst->background_fallback);
140 dst->background_fallback = NULL;
141 }
142 if (src->power != -1) {
143 dst->power = -1;
144 }
145}
146
147// merge_output_config sets all fields in dst that were set in src
148static void merge_output_config(struct output_config *dst, struct output_config *src) {
78 if (src->enabled != -1) { 149 if (src->enabled != -1) {
79 dst->enabled = src->enabled; 150 dst->enabled = src->enabled;
80 } 151 }
@@ -132,158 +203,112 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
132 free(dst->background_fallback); 203 free(dst->background_fallback);
133 dst->background_fallback = strdup(src->background_fallback); 204 dst->background_fallback = strdup(src->background_fallback);
134 } 205 }
135 if (src->dpms_state != 0) { 206 if (src->power != -1) {
136 dst->dpms_state = src->dpms_state; 207 dst->power = src->power;
137 } 208 }
138} 209}
139 210
140static void merge_wildcard_on_all(struct output_config *wildcard) { 211void store_output_config(struct output_config *oc) {
141 for (int i = 0; i < config->output_configs->length; i++) { 212 bool merged = false;
142 struct output_config *oc = config->output_configs->items[i]; 213 bool wildcard = strcmp(oc->name, "*") == 0;
143 if (strcmp(wildcard->name, oc->name) != 0) { 214 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 215
150static void merge_id_on_name(struct output_config *oc) {
151 char *id_on_name = NULL;
152 char id[128]; 216 char id[128];
153 char *name = NULL; 217 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); 218 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 } 219 }
173 220
174 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 221 for (int i = 0; i < config->output_configs->length; i++) {
175 if (i >= 0) { 222 struct output_config *old = config->output_configs->items[i];
176 sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); 223
177 merge_output_config(config->output_configs->items[i], oc); 224 // If the old config matches the new config's name, regardless of
178 } else { 225 // 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 226 // 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); 227 // old wildcard configs.
181 int ii = list_seq_find(config->output_configs, output_name_cmp, id); 228 if (strcmp(old->name, oc->name) == 0) {
182 if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) 229 merge_output_config(old, oc);
183 || (ii >= 0 && strcmp(oc->name, name) == 0)) { 230 merged = true;
184 struct output_config *ion_oc = new_output_config(id_on_name); 231 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 } 232 }
202 }
203 free(id_on_name);
204}
205 233
206struct output_config *store_output_config(struct output_config *oc) { 234 // If the new config is a wildcard config we supersede all non-wildcard
207 bool wildcard = strcmp(oc->name, "*") == 0; 235 // configs. Old wildcard configs have already been handled above.
208 if (wildcard) { 236 if (wildcard) {
209 merge_wildcard_on_all(oc); 237 supersede_output_config(old, oc);
210 } else { 238 continue;
211 merge_id_on_name(oc); 239 }
212 }
213 240
214 int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); 241 // If the new config matches an output's name, and the old config
215 if (i >= 0) { 242 // matches on that output's identifier, supersede it.
216 sway_log(SWAY_DEBUG, "Merging on top of existing output config"); 243 if (output && strcmp(old->name, id) == 0 &&
217 struct output_config *current = config->output_configs->items[i]; 244 strcmp(oc->name, output->wlr_output->name) == 0) {
218 merge_output_config(current, oc); 245 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 } 246 }
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 } 247 }
238 248
239 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " 249 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
240 "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d) " 250 "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) "
241 "(max render time: %d)", 251 "(max render time: %d)",
242 oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, 252 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), 253 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, 254 oc->transform, oc->background, oc->background_option, oc->power,
245 oc->max_render_time); 255 oc->max_render_time);
246 256
247 return oc; 257 // If the configuration was not merged into an existing configuration, add
258 // it to the list. Otherwise we're done with it and can free it.
259 if (!merged) {
260 list_add(config->output_configs, oc);
261 } else {
262 free_output_config(oc);
263 }
248} 264}
249 265
250static void set_mode(struct wlr_output *output, int width, int height, 266static void set_mode(struct wlr_output *output, struct wlr_output_state *pending,
251 float refresh_rate, bool custom) { 267 int width, int height, float refresh_rate, bool custom) {
252 // Not all floating point integers can be represented exactly 268 // Not all floating point integers can be represented exactly
253 // as (int)(1000 * mHz / 1000.f) 269 // as (int)(1000 * mHz / 1000.f)
254 // round() the result to avoid any error 270 // round() the result to avoid any error
255 int mhz = (int)round(refresh_rate * 1000); 271 int mhz = (int)roundf(refresh_rate * 1000);
272 // If no target refresh rate is given, match highest available
273 mhz = mhz <= 0 ? INT_MAX : mhz;
256 274
257 if (wl_list_empty(&output->modes) || custom) { 275 if (wl_list_empty(&output->modes) || custom) {
258 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); 276 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name);
259 wlr_output_set_custom_mode(output, width, height, 277 wlr_output_state_set_custom_mode(pending, width, height,
260 refresh_rate > 0 ? mhz : 0); 278 refresh_rate > 0 ? mhz : 0);
261 return; 279 return;
262 } 280 }
263 281
264 struct wlr_output_mode *mode, *best = NULL; 282 struct wlr_output_mode *mode, *best = NULL;
283 int best_diff_mhz = INT_MAX;
265 wl_list_for_each(mode, &output->modes, link) { 284 wl_list_for_each(mode, &output->modes, link) {
266 if (mode->width == width && mode->height == height) { 285 if (mode->width == width && mode->height == height) {
267 if (mode->refresh == mhz) { 286 int diff_mhz = abs(mode->refresh - mhz);
268 best = mode; 287 if (diff_mhz < best_diff_mhz) {
269 break; 288 best_diff_mhz = diff_mhz;
270 }
271 if (best == NULL || mode->refresh > best->refresh) {
272 best = mode; 289 best = mode;
290 if (best_diff_mhz == 0) {
291 break;
292 }
273 } 293 }
274 } 294 }
275 } 295 }
276 if (!best) { 296 if (best) {
277 sway_log(SWAY_ERROR, "Configured mode for %s not available", output->name); 297 sway_log(SWAY_INFO, "Assigning configured mode (%dx%d@%.3fHz) to %s",
278 sway_log(SWAY_INFO, "Picking preferred mode instead"); 298 best->width, best->height, best->refresh / 1000.f, output->name);
279 best = wlr_output_preferred_mode(output);
280 } else { 299 } else {
281 sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); 300 best = wlr_output_preferred_mode(output);
301 sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, "
302 "applying preferred mode (%dx%d@%.3fHz)",
303 width, height, refresh_rate,
304 best->width, best->height, best->refresh / 1000.f);
282 } 305 }
283 wlr_output_set_mode(output, best); 306 wlr_output_state_set_mode(pending, best);
284} 307}
285 308
286static void set_modeline(struct wlr_output *output, drmModeModeInfo *drm_mode) { 309static void set_modeline(struct wlr_output *output,
310 struct wlr_output_state *pending, drmModeModeInfo *drm_mode) {
311#if WLR_HAS_DRM_BACKEND
287 if (!wlr_output_is_drm(output)) { 312 if (!wlr_output_is_drm(output)) {
288 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); 313 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
289 return; 314 return;
@@ -291,8 +316,11 @@ static void set_modeline(struct wlr_output *output, drmModeModeInfo *drm_mode) {
291 sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name); 316 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); 317 struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode);
293 if (mode) { 318 if (mode) {
294 wlr_output_set_mode(output, mode); 319 wlr_output_state_set_mode(pending, mode);
295 } 320 }
321#else
322 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
323#endif
296} 324}
297 325
298/* Some manufacturers hardcode the aspect-ratio of the output in the physical 326/* Some manufacturers hardcode the aspect-ratio of the output in the physical
@@ -313,23 +341,24 @@ static bool phys_size_is_aspect_ratio(struct wlr_output *output) {
313// 1 inch = 25.4 mm 341// 1 inch = 25.4 mm
314#define MM_PER_INCH 25.4 342#define MM_PER_INCH 25.4
315 343
316static int compute_default_scale(struct wlr_output *output) { 344static int compute_default_scale(struct wlr_output *output,
345 struct wlr_output_state *pending) {
317 struct wlr_box box = { .width = output->width, .height = output->height }; 346 struct wlr_box box = { .width = output->width, .height = output->height };
318 if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { 347 if (pending->committed & WLR_OUTPUT_STATE_MODE) {
319 switch (output->pending.mode_type) { 348 switch (pending->mode_type) {
320 case WLR_OUTPUT_STATE_MODE_FIXED: 349 case WLR_OUTPUT_STATE_MODE_FIXED:
321 box.width = output->pending.mode->width; 350 box.width = pending->mode->width;
322 box.height = output->pending.mode->height; 351 box.height = pending->mode->height;
323 break; 352 break;
324 case WLR_OUTPUT_STATE_MODE_CUSTOM: 353 case WLR_OUTPUT_STATE_MODE_CUSTOM:
325 box.width = output->pending.custom_mode.width; 354 box.width = pending->custom_mode.width;
326 box.height = output->pending.custom_mode.height; 355 box.height = pending->custom_mode.height;
327 break; 356 break;
328 } 357 }
329 } 358 }
330 enum wl_output_transform transform = output->transform; 359 enum wl_output_transform transform = output->transform;
331 if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) { 360 if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) {
332 transform = output->pending.transform; 361 transform = pending->transform;
333 } 362 }
334 wlr_box_transform(&box, &box, transform, box.width, box.height); 363 wlr_box_transform(&box, &box, transform, box.width, box.height);
335 364
@@ -358,88 +387,70 @@ static int compute_default_scale(struct wlr_output *output) {
358 return 2; 387 return 2;
359} 388}
360 389
361/* Lists of formats to try, in order, when a specific render bit depth has 390static bool render_format_is_10bit(uint32_t render_format) {
362 * been asked for. The second to last format in each list should always 391 return render_format == DRM_FORMAT_XRGB2101010 ||
363 * be XRGB8888, as a reliable backup in case the others are not available; 392 render_format == DRM_FORMAT_XBGR2101010;
364 * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ 393}
365static const uint32_t *bit_depth_preferences[] = { 394
366 [RENDER_BIT_DEPTH_8] = (const uint32_t []){ 395static bool render_format_is_bgr(uint32_t fmt) {
367 DRM_FORMAT_XRGB8888, 396 return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888;
368 DRM_FORMAT_INVALID, 397}
369 }, 398
370 [RENDER_BIT_DEPTH_10] = (const uint32_t []){ 399static bool output_config_is_disabling(struct output_config *oc) {
371 DRM_FORMAT_XRGB2101010, 400 return oc && (!oc->enabled || oc->power == 0);
372 DRM_FORMAT_XBGR2101010, 401}
373 DRM_FORMAT_XRGB8888,
374 DRM_FORMAT_INVALID,
375 },
376};
377 402
378static void queue_output_config(struct output_config *oc, 403static void queue_output_config(struct output_config *oc,
379 struct sway_output *output) { 404 struct sway_output *output, struct wlr_output_state *pending) {
380 if (output == root->fallback_output) { 405 if (output == root->fallback_output) {
381 return; 406 return;
382 } 407 }
383 408
384 struct wlr_output *wlr_output = output->wlr_output; 409 struct wlr_output *wlr_output = output->wlr_output;
385 410
386 if (oc && (!oc->enabled || oc->dpms_state == DPMS_OFF)) { 411 if (output_config_is_disabling(oc)) {
387 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); 412 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
388 wlr_output_enable(wlr_output, false); 413 wlr_output_state_set_enabled(pending, false);
389 return; 414 return;
390 } 415 }
391 416
392 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); 417 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name);
393 wlr_output_enable(wlr_output, true); 418 wlr_output_state_set_enabled(pending, true);
394 419
395 if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) { 420 if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) {
396 sway_log(SWAY_DEBUG, "Set %s modeline", 421 sway_log(SWAY_DEBUG, "Set %s modeline",
397 wlr_output->name); 422 wlr_output->name);
398 set_modeline(wlr_output, &oc->drm_mode); 423 set_modeline(wlr_output, pending, &oc->drm_mode);
399 } else if (oc && oc->width > 0 && oc->height > 0) { 424 } else if (oc && oc->width > 0 && oc->height > 0) {
400 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", 425 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)",
401 wlr_output->name, oc->width, oc->height, oc->refresh_rate); 426 wlr_output->name, oc->width, oc->height, oc->refresh_rate);
402 set_mode(wlr_output, oc->width, oc->height, 427 set_mode(wlr_output, pending, oc->width, oc->height,
403 oc->refresh_rate, oc->custom_mode == 1); 428 oc->refresh_rate, oc->custom_mode == 1);
404 } else if (!wl_list_empty(&wlr_output->modes)) { 429 } else if (!wl_list_empty(&wlr_output->modes)) {
405 sway_log(SWAY_DEBUG, "Set preferred mode"); 430 sway_log(SWAY_DEBUG, "Set preferred mode");
406 struct wlr_output_mode *preferred_mode = 431 struct wlr_output_mode *preferred_mode =
407 wlr_output_preferred_mode(wlr_output); 432 wlr_output_preferred_mode(wlr_output);
408 wlr_output_set_mode(wlr_output, preferred_mode); 433 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 } 434 }
426 435
427 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 436 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
428 sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, 437 sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name,
429 sway_wl_output_subpixel_to_string(oc->subpixel)); 438 sway_wl_output_subpixel_to_string(oc->subpixel));
430 wlr_output_set_subpixel(wlr_output, oc->subpixel); 439 wlr_output_state_set_subpixel(pending, oc->subpixel);
431 } 440 }
432 441
433 enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; 442 enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL;
434 if (oc && oc->transform >= 0) { 443 if (oc && oc->transform >= 0) {
435 tr = oc->transform; 444 tr = oc->transform;
445#if WLR_HAS_DRM_BACKEND
436 } else if (wlr_output_is_drm(wlr_output)) { 446 } else if (wlr_output_is_drm(wlr_output)) {
437 tr = wlr_drm_connector_get_panel_orientation(wlr_output); 447 tr = wlr_drm_connector_get_panel_orientation(wlr_output);
438 sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); 448 sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr);
449#endif
439 } 450 }
440 if (wlr_output->transform != tr) { 451 if (wlr_output->transform != tr) {
441 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); 452 sway_log(SWAY_DEBUG, "Set %s transform to %d", wlr_output->name, tr);
442 wlr_output_set_transform(wlr_output, tr); 453 wlr_output_state_set_transform(pending, tr);
443 } 454 }
444 455
445 // Apply the scale last before the commit, because the scale auto-detection 456 // Apply the scale last before the commit, because the scale auto-detection
@@ -447,66 +458,50 @@ static void queue_output_config(struct output_config *oc,
447 float scale; 458 float scale;
448 if (oc && oc->scale > 0) { 459 if (oc && oc->scale > 0) {
449 scale = oc->scale; 460 scale = oc->scale;
461
462 // The factional-scale-v1 protocol uses increments of 120ths to send
463 // the scale factor to the client. Adjust the scale so that we use the
464 // same value as the clients'.
465 float adjusted_scale = round(scale * 120) / 120;
466 if (scale != adjusted_scale) {
467 sway_log(SWAY_INFO, "Adjusting output scale from %f to %f",
468 scale, adjusted_scale);
469 scale = adjusted_scale;
470 }
450 } else { 471 } else {
451 scale = compute_default_scale(wlr_output); 472 scale = compute_default_scale(wlr_output, pending);
452 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); 473 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale);
453 } 474 }
454 if (scale != wlr_output->scale) { 475 if (scale != wlr_output->scale) {
455 sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); 476 sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale);
456 wlr_output_set_scale(wlr_output, scale); 477 wlr_output_state_set_scale(pending, scale);
457 } 478 }
458 479
459 if (oc && oc->adaptive_sync != -1) { 480 if (oc && oc->adaptive_sync != -1) {
460 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, 481 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name,
461 oc->adaptive_sync); 482 oc->adaptive_sync);
462 wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); 483 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1);
463 } 484 }
464 485
465 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { 486 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
466 const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; 487 if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 &&
467 assert(fmts); 488 render_format_is_10bit(output->wlr_output->render_format)) {
468 489 // 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++) { 490 wlr_output_state_set_render_format(pending, output->wlr_output->render_format);
470 wlr_output_set_render_format(wlr_output, fmts[i]); 491 } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) {
471 if (wlr_output_test(wlr_output)) { 492 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010);
472 break; 493 } else {
473 } 494 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 } 495 }
479 } 496 }
480} 497}
481 498
482bool apply_output_config(struct output_config *oc, struct sway_output *output) { 499static bool finalize_output_config(struct output_config *oc, struct sway_output *output) {
483 if (output == root->fallback_output) { 500 if (output == root->fallback_output) {
484 return false; 501 return false;
485 } 502 }
486 503
487 struct wlr_output *wlr_output = output->wlr_output; 504 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) { 505 if (oc && !oc->enabled) {
511 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); 506 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name);
512 if (output->enabled) { 507 if (output->enabled) {
@@ -516,10 +511,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
516 return true; 511 return true;
517 } 512 }
518 513
519 if (config->reloading) {
520 output_damage_whole(output);
521 }
522
523 if (oc) { 514 if (oc) {
524 enum scale_filter_mode scale_filter_old = output->scale_filter; 515 enum scale_filter_mode scale_filter_old = output->scale_filter;
525 switch (oc->scale_filter) { 516 switch (oc->scale_filter) {
@@ -536,6 +527,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
536 if (scale_filter_old != output->scale_filter) { 527 if (scale_filter_old != output->scale_filter) {
537 sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, 528 sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name,
538 sway_output_scale_filter_to_string(output->scale_filter)); 529 sway_output_scale_filter_to_string(output->scale_filter));
530 wlr_damage_ring_add_whole(&output->scene_output->damage_ring);
539 } 531 }
540 } 532 }
541 533
@@ -565,29 +557,13 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
565 output->max_render_time = oc->max_render_time; 557 output->max_render_time = oc->max_render_time;
566 } 558 }
567 559
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; 560 return true;
575} 561}
576 562
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, 563static void default_output_config(struct output_config *oc,
589 struct wlr_output *wlr_output) { 564 struct wlr_output *wlr_output) {
590 oc->enabled = 1; 565 oc->enabled = 1;
566 oc->power = 1;
591 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); 567 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
592 if (mode != NULL) { 568 if (mode != NULL) {
593 oc->width = mode->width; 569 oc->width = mode->width;
@@ -600,147 +576,418 @@ static void default_output_config(struct output_config *oc,
600 struct sway_output *output = wlr_output->data; 576 struct sway_output *output = wlr_output->data;
601 oc->subpixel = output->detected_subpixel; 577 oc->subpixel = output->detected_subpixel;
602 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; 578 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
603 oc->dpms_state = DPMS_ON;
604 oc->max_render_time = 0; 579 oc->max_render_time = 0;
605} 580}
606 581
607static struct output_config *get_output_config(char *identifier, 582// find_output_config returns a merged output_config containing all stored
608 struct sway_output *sway_output) { 583// configuration that applies to the specified output.
584struct output_config *find_output_config(struct sway_output *sway_output) {
609 const char *name = sway_output->wlr_output->name; 585 const char *name = sway_output->wlr_output->name;
586 struct output_config *oc = NULL;
610 587
611 struct output_config *oc_id_on_name = NULL; 588 struct output_config *result = new_output_config(name);
612 struct output_config *oc_name = NULL; 589 if (config->reloading) {
613 struct output_config *oc_id = NULL; 590 default_output_config(result, sway_output->wlr_output);
591 }
614 592
615 size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; 593 char id[128];
616 char *id_on_name = malloc(length); 594 output_get_identifier(id, sizeof(id), sway_output);
617 snprintf(id_on_name, length, "%s on %s", identifier, name); 595
618 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 596 int i;
619 if (i >= 0) { 597 bool match = false;
620 oc_id_on_name = config->output_configs->items[i]; 598 if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) {
621 } else { 599 match = true;
622 i = list_seq_find(config->output_configs, output_name_cmp, name); 600 oc = config->output_configs->items[i];
623 if (i >= 0) { 601 merge_output_config(result, oc);
624 oc_name = config->output_configs->items[i]; 602 }
603 if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) {
604 match = true;
605 oc = config->output_configs->items[i];
606 merge_output_config(result, oc);
607 }
608 if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) {
609 match = true;
610 oc = config->output_configs->items[i];
611 merge_output_config(result, oc);
612 }
613
614 if (!match && !config->reloading) {
615 // No name, identifier, or wildcard config. Since we are not
616 // reloading with defaults, the output config will be empty, so
617 // just return NULL
618 free_output_config(result);
619 return NULL;
620 }
621
622 return result;
623}
624
625static bool config_has_auto_mode(struct output_config *oc) {
626 if (!oc) {
627 return true;
628 }
629 if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) {
630 return true;
631 } else if (oc->width > 0 && oc->height > 0) {
632 return true;
633 }
634 return false;
635}
636
637struct search_context {
638 struct wlr_output_swapchain_manager *swapchain_mgr;
639 struct wlr_backend_output_state *states;
640 struct matched_output_config *configs;
641 size_t configs_len;
642 bool degrade_to_off;
643};
644
645static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_state *state) {
646 sway_log(SWAY_DEBUG, "Output state for %s", wlr_output->name);
647 if (state->committed & WLR_OUTPUT_STATE_ENABLED) {
648 sway_log(SWAY_DEBUG, " enabled: %s", state->enabled ? "yes" : "no");
649 }
650 if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) {
651 sway_log(SWAY_DEBUG, " render_format: %d", state->render_format);
652 }
653 if (state->committed & WLR_OUTPUT_STATE_MODE) {
654 if (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) {
655 sway_log(SWAY_DEBUG, " custom mode: %dx%d@%dmHz",
656 state->custom_mode.width, state->custom_mode.height, state->custom_mode.refresh);
657 } else {
658 sway_log(SWAY_DEBUG, " mode: %dx%d@%dmHz%s",
659 state->mode->width, state->mode->height, state->mode->refresh,
660 state->mode->preferred ? " (preferred)" : "");
661 }
662 }
663 if (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) {
664 sway_log(SWAY_DEBUG, " adaptive_sync: %s",
665 state->adaptive_sync_enabled ? "enabled": "disabled");
666 }
667}
668
669static bool search_valid_config(struct search_context *ctx, size_t output_idx);
670
671static void reset_output_state(struct wlr_output_state *state) {
672 wlr_output_state_finish(state);
673 wlr_output_state_init(state);
674 state->committed = 0;
675}
676
677static void clear_later_output_states(struct wlr_backend_output_state *states,
678 size_t configs_len, size_t output_idx) {
679
680 // Clear and disable all output states after this one to avoid conflict
681 // with previous tests.
682 for (size_t idx = output_idx+1; idx < configs_len; idx++) {
683 struct wlr_backend_output_state *backend_state = &states[idx];
684 struct wlr_output_state *state = &backend_state->base;
685
686 reset_output_state(state);
687 wlr_output_state_set_enabled(state, false);
688 }
689}
690
691static bool search_finish(struct search_context *ctx, size_t output_idx) {
692 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
693 struct wlr_output_state *state = &backend_state->base;
694 struct wlr_output *wlr_output = backend_state->output;
695
696 clear_later_output_states(ctx->states, ctx->configs_len, output_idx);
697 dump_output_state(wlr_output, state);
698 return wlr_output_swapchain_manager_prepare(ctx->swapchain_mgr, ctx->states, ctx->configs_len) &&
699 search_valid_config(ctx, output_idx+1);
700}
701
702static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) {
703 struct matched_output_config *cfg = &ctx->configs[output_idx];
704 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
705 struct wlr_output_state *state = &backend_state->base;
706
707 if (cfg->config && cfg->config->adaptive_sync == 1) {
708 wlr_output_state_set_adaptive_sync_enabled(state, true);
709 if (search_finish(ctx, output_idx)) {
710 return true;
711 }
712 }
713 if (!cfg->config || cfg->config->adaptive_sync != -1) {
714 wlr_output_state_set_adaptive_sync_enabled(state, false);
715 if (search_finish(ctx, output_idx)) {
716 return true;
625 } 717 }
718 }
719 // If adaptive sync has not been set, or fallback in case we are on a
720 // backend that cannot disable adaptive sync such as the wayland backend.
721 state->committed &= ~WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED;
722 return search_finish(ctx, output_idx);
723}
724
725static bool search_mode(struct search_context *ctx, size_t output_idx) {
726 struct matched_output_config *cfg = &ctx->configs[output_idx];
727 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
728 struct wlr_output_state *state = &backend_state->base;
729 struct wlr_output *wlr_output = backend_state->output;
626 730
627 i = list_seq_find(config->output_configs, output_name_cmp, identifier); 731 if (!config_has_auto_mode(cfg->config)) {
628 if (i >= 0) { 732 return search_adaptive_sync(ctx, output_idx);
629 oc_id = config->output_configs->items[i]; 733 }
734
735 struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
736 if (preferred_mode) {
737 wlr_output_state_set_mode(state, preferred_mode);
738 if (search_adaptive_sync(ctx, output_idx)) {
739 return true;
630 } 740 }
631 } 741 }
632 742
633 struct output_config *result = new_output_config("temp"); 743 if (wl_list_empty(&wlr_output->modes)) {
634 if (config->reloading) { 744 state->committed &= ~WLR_OUTPUT_STATE_MODE;
635 default_output_config(result, sway_output->wlr_output); 745 return search_adaptive_sync(ctx, output_idx);
636 } 746 }
637 if (oc_id_on_name) { 747
638 // Already have an identifier on name config, use that 748 struct wlr_output_mode *mode;
639 free(result->name); 749 wl_list_for_each(mode, &backend_state->output->modes, link) {
640 result->name = strdup(id_on_name); 750 if (mode == preferred_mode) {
641 merge_output_config(result, oc_id_on_name); 751 continue;
642 } else if (oc_name && oc_id) { 752 }
643 // Generate a config named `<identifier> on <name>` which contains a 753 wlr_output_state_set_mode(state, mode);
644 // merged copy of the identifier on name. This will make sure that both 754 if (search_adaptive_sync(ctx, output_idx)) {
645 // identifier and name configs are respected, with identifier getting 755 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 } 756 }
687 } 757 }
688 758
689 free(id_on_name); 759 return false;
690 return result;
691} 760}
692 761
693struct output_config *find_output_config(struct sway_output *output) { 762static bool search_render_format(struct search_context *ctx, size_t output_idx) {
694 char id[128]; 763 struct matched_output_config *cfg = &ctx->configs[output_idx];
695 output_get_identifier(id, sizeof(id), output); 764 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
696 return get_output_config(id, output); 765 struct wlr_output_state *state = &backend_state->base;
766 struct wlr_output *wlr_output = backend_state->output;
767
768 uint32_t fmts[] = {
769 DRM_FORMAT_XRGB2101010,
770 DRM_FORMAT_XBGR2101010,
771 DRM_FORMAT_XRGB8888,
772 DRM_FORMAT_INVALID,
773 };
774 if (render_format_is_bgr(wlr_output->render_format)) {
775 // Start with BGR in the unlikely event that we previously required it.
776 fmts[0] = DRM_FORMAT_XBGR2101010;
777 fmts[1] = DRM_FORMAT_XRGB2101010;
778 }
779
780 const struct wlr_drm_format_set *primary_formats =
781 wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF);
782 bool need_10bit = cfg->config && cfg->config->render_bit_depth == RENDER_BIT_DEPTH_10;
783 for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) {
784 if (!need_10bit && render_format_is_10bit(fmts[idx])) {
785 continue;
786 }
787 if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) {
788 // This is not a supported format for this output
789 continue;
790 }
791 wlr_output_state_set_render_format(state, fmts[idx]);
792 if (search_mode(ctx, output_idx)) {
793 return true;
794 }
795 }
796 return false;
697} 797}
698 798
699void apply_output_config_to_outputs(struct output_config *oc) { 799static bool search_valid_config(struct search_context *ctx, size_t output_idx) {
700 // Try to find the output container and apply configuration now. If 800 if (output_idx >= ctx->configs_len) {
701 // this is during startup then there will be no container and config 801 // We reached the end of the search, all good!
702 // will be applied during normal "new output" event from wlroots. 802 return true;
703 bool wildcard = strcmp(oc->name, "*") == 0; 803 }
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 804
720 if (!wildcard) { 805 struct matched_output_config *cfg = &ctx->configs[output_idx];
721 // Stop looking if the output config isn't applicable to all 806 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
722 // outputs 807 struct wlr_output_state *state = &backend_state->base;
723 break; 808 struct wlr_output *wlr_output = backend_state->output;
724 } 809
810 if (!output_config_is_disabling(cfg->config)) {
811 // Search through our possible configurations, doing a depth-first
812 // through render_format, modes, adaptive_sync and the next output's
813 // config.
814 queue_output_config(cfg->config, cfg->output, &backend_state->base);
815 if (search_render_format(ctx, output_idx)) {
816 return true;
817 } else if (!ctx->degrade_to_off) {
818 return false;
819 }
820 // We could not get anything to work, try to disable this output to see
821 // if we can at least make the outputs before us work.
822 sway_log(SWAY_DEBUG, "Unable to find valid config with output %s, disabling",
823 wlr_output->name);
824 reset_output_state(state);
825 }
826
827 wlr_output_state_set_enabled(state, false);
828 return search_finish(ctx, output_idx);
829}
830
831static int compare_matched_output_config_priority(const void *a, const void *b) {
832
833 const struct matched_output_config *amc = a;
834 const struct matched_output_config *bmc = b;
835 bool a_disabling = output_config_is_disabling(amc->config);
836 bool b_disabling = output_config_is_disabling(bmc->config);
837 bool a_enabled = amc->output->enabled;
838 bool b_enabled = bmc->output->enabled;
839
840 // We want to give priority to existing enabled outputs. To do so, we want
841 // the configuration order to be:
842 // 1. Existing, enabled outputs
843 // 2. Outputs that need to be enabled
844 // 3. Disabled or disabling outputs
845 if (a_enabled && !a_disabling) {
846 return -1;
847 } else if (b_enabled && !b_disabling) {
848 return 1;
849 } else if (b_disabling && !a_disabling) {
850 return -1;
851 } else if (a_disabling && !b_disabling) {
852 return 1;
853 }
854 return 0;
855}
856
857void sort_output_configs_by_priority(struct matched_output_config *configs,
858 size_t configs_len) {
859 qsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority);
860}
861
862bool apply_output_configs(struct matched_output_config *configs,
863 size_t configs_len, bool test_only, bool degrade_to_off) {
864 struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states));
865 if (!states) {
866 return false;
867 }
868
869 sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len);
870 for (size_t idx = 0; idx < configs_len; idx++) {
871 struct matched_output_config *cfg = &configs[idx];
872 struct wlr_backend_output_state *backend_state = &states[idx];
873
874 backend_state->output = cfg->output->wlr_output;
875 wlr_output_state_init(&backend_state->base);
876
877 sway_log(SWAY_DEBUG, "Preparing config for %s",
878 cfg->output->wlr_output->name);
879 queue_output_config(cfg->config, cfg->output, &backend_state->base);
880 }
881
882 struct wlr_output_swapchain_manager swapchain_mgr;
883 wlr_output_swapchain_manager_init(&swapchain_mgr, server.backend);
884
885 bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len);
886 if (!ok) {
887 sway_log(SWAY_ERROR, "Requested backend configuration failed, searching for valid fallbacks");
888 struct search_context ctx = {
889 .swapchain_mgr = &swapchain_mgr,
890 .states = states,
891 .configs = configs,
892 .configs_len = configs_len,
893 .degrade_to_off = degrade_to_off,
894 };
895 if (!search_valid_config(&ctx, 0)) {
896 sway_log(SWAY_ERROR, "Search for valid config failed");
897 goto out;
725 } 898 }
726 } 899 }
727 900
901 if (test_only) {
902 // The swapchain manager already did a test for us
903 goto out;
904 }
905
906 for (size_t idx = 0; idx < configs_len; idx++) {
907 struct matched_output_config *cfg = &configs[idx];
908 struct wlr_backend_output_state *backend_state = &states[idx];
909
910 struct wlr_scene_output_state_options opts = {
911 .swapchain = wlr_output_swapchain_manager_get_swapchain(
912 &swapchain_mgr, backend_state->output),
913 };
914 struct wlr_scene_output *scene_output = cfg->output->scene_output;
915 struct wlr_output_state *state = &backend_state->base;
916 if (!wlr_scene_output_build_state(scene_output, state, &opts)) {
917 sway_log(SWAY_ERROR, "Building output state for '%s' failed",
918 backend_state->output->name);
919 goto out;
920 }
921 }
922
923 ok = wlr_backend_commit(server.backend, states, configs_len);
924 if (!ok) {
925 sway_log(SWAY_ERROR, "Backend commit failed");
926 goto out;
927 }
928
929 sway_log(SWAY_DEBUG, "Commit of %zd outputs succeeded", configs_len);
930
931 wlr_output_swapchain_manager_apply(&swapchain_mgr);
932
933 for (size_t idx = 0; idx < configs_len; idx++) {
934 struct matched_output_config *cfg = &configs[idx];
935 sway_log(SWAY_DEBUG, "Finalizing config for %s",
936 cfg->output->wlr_output->name);
937 finalize_output_config(cfg->config, cfg->output);
938 }
939
940out:
941 wlr_output_swapchain_manager_finish(&swapchain_mgr);
942 for (size_t idx = 0; idx < configs_len; idx++) {
943 struct wlr_backend_output_state *backend_state = &states[idx];
944 wlr_output_state_finish(&backend_state->base);
945 }
946 free(states);
947
948 // Reconfigure all devices, since input config may have been applied before
949 // this output came online, and some config items (like map_to_output) are
950 // dependent on an output being present.
951 input_manager_configure_all_input_mappings();
952 // Reconfigure the cursor images, since the scale may have changed.
953 input_manager_configure_xcursor();
954
728 struct sway_seat *seat; 955 struct sway_seat *seat;
729 wl_list_for_each(seat, &server.input->seats, link) { 956 wl_list_for_each(seat, &server.input->seats, link) {
730 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 957 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
731 cursor_rebase(seat->cursor); 958 cursor_rebase(seat->cursor);
732 } 959 }
960
961 return ok;
733} 962}
734 963
735void reset_outputs(void) { 964void apply_all_output_configs(void) {
736 struct output_config *oc = NULL; 965 size_t configs_len = wl_list_length(&root->all_outputs);
737 int i = list_seq_find(config->output_configs, output_name_cmp, "*"); 966 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
738 if (i >= 0) { 967 if (!configs) {
739 oc = config->output_configs->items[i]; 968 return;
740 } else {
741 oc = store_output_config(new_output_config("*"));
742 } 969 }
743 apply_output_config_to_outputs(oc); 970
971 int config_idx = 0;
972 struct sway_output *sway_output;
973 wl_list_for_each(sway_output, &root->all_outputs, link) {
974 if (sway_output == root->fallback_output) {
975 configs_len--;
976 continue;
977 }
978
979 struct matched_output_config *config = &configs[config_idx++];
980 config->output = sway_output;
981 config->config = find_output_config(sway_output);
982 }
983
984 sort_output_configs_by_priority(configs, configs_len);
985 apply_output_configs(configs, configs_len, false, true);
986 for (size_t idx = 0; idx < configs_len; idx++) {
987 struct matched_output_config *cfg = &configs[idx];
988 free_output_config(cfg->config);
989 }
990 free(configs);
744} 991}
745 992
746void free_output_config(struct output_config *oc) { 993void free_output_config(struct output_config *oc) {
@@ -807,7 +1054,9 @@ static bool _spawn_swaybg(char **command) {
807 setenv("WAYLAND_SOCKET", wayland_socket_str, true); 1054 setenv("WAYLAND_SOCKET", wayland_socket_str, true);
808 1055
809 execvp(command[0], command); 1056 execvp(command[0], command);
810 sway_log_errno(SWAY_ERROR, "execvp failed"); 1057 sway_log_errno(SWAY_ERROR, "failed to execute '%s' "
1058 "(background configuration probably not applied)",
1059 command[0]);
811 _exit(EXIT_FAILURE); 1060 _exit(EXIT_FAILURE);
812 } 1061 }
813 _exit(EXIT_SUCCESS); 1062 _exit(EXIT_SUCCESS);
@@ -817,12 +1066,13 @@ static bool _spawn_swaybg(char **command) {
817 sway_log_errno(SWAY_ERROR, "close failed"); 1066 sway_log_errno(SWAY_ERROR, "close failed");
818 return false; 1067 return false;
819 } 1068 }
820 if (waitpid(pid, NULL, 0) < 0) { 1069 int fork_status = 0;
1070 if (waitpid(pid, &fork_status, 0) < 0) {
821 sway_log_errno(SWAY_ERROR, "waitpid failed"); 1071 sway_log_errno(SWAY_ERROR, "waitpid failed");
822 return false; 1072 return false;
823 } 1073 }
824 1074
825 return true; 1075 return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS;
826} 1076}
827 1077
828bool spawn_swaybg(void) { 1078bool spawn_swaybg(void) {
diff --git a/sway/config/seat.c b/sway/config/seat.c
index 84260aa3..f2326189 100644
--- a/sway/config/seat.c
+++ b/sway/config/seat.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
@@ -99,7 +98,6 @@ static void seat_attachment_config_free(
99 struct seat_attachment_config *attachment) { 98 struct seat_attachment_config *attachment) {
100 free(attachment->identifier); 99 free(attachment->identifier);
101 free(attachment); 100 free(attachment);
102 return;
103} 101}
104 102
105static struct seat_attachment_config *seat_attachment_config_copy( 103static struct seat_attachment_config *seat_attachment_config_copy(