aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sway/config.h29
-rw-r--r--sway/commands/output.c4
-rw-r--r--sway/commands/output/toggle.c2
-rw-r--r--sway/commands/workspace.c4
-rw-r--r--sway/config.c2
-rw-r--r--sway/config/output.c772
-rw-r--r--sway/desktop/layer_shell.c7
-rw-r--r--sway/desktop/output.c124
-rw-r--r--sway/input/keyboard.c1
-rw-r--r--sway/input/seatop_down.c6
-rw-r--r--sway/server.c9
-rw-r--r--sway/sway-ipc.7.scd2
-rw-r--r--sway/sway.5.scd6
-rw-r--r--sway/sway_text_node.c8
-rw-r--r--sway/tree/container.c4
15 files changed, 643 insertions, 337 deletions
diff --git a/include/sway/config.h b/include/sway/config.h
index f9da1967..5ccc3e77 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -292,6 +292,14 @@ struct output_config {
292}; 292};
293 293
294/** 294/**
295 * An output config pre-matched to an output
296 */
297struct matched_output_config {
298 struct sway_output *output;
299 struct output_config *config;
300};
301
302/**
295 * Stores size of gaps for each side 303 * Stores size of gaps for each side
296 */ 304 */
297struct side_gaps { 305struct side_gaps {
@@ -680,20 +688,25 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt
680 688
681struct output_config *new_output_config(const char *name); 689struct output_config *new_output_config(const char *name);
682 690
683void merge_output_config(struct output_config *dst, struct output_config *src); 691bool apply_output_configs(struct matched_output_config *configs,
692 size_t configs_len, bool test_only, bool degrade_to_off);
684 693
685bool apply_output_config(struct output_config *oc, struct sway_output *output); 694void apply_all_output_configs(void);
686 695
687bool test_output_config(struct output_config *oc, struct sway_output *output); 696void sort_output_configs_by_priority(struct matched_output_config *configs,
697 size_t configs_len);
688 698
689struct output_config *store_output_config(struct output_config *oc); 699/**
700 * store_output_config stores a new output config. An output may be matched by
701 * three different config types, in order of precedence: Identifier, name and
702 * wildcard. When storing a config type of lower precedence, assume that the
703 * user wants the config to take immediate effect by superseding (clearing) the
704 * same values from higher presedence configuration.
705 */
706void store_output_config(struct output_config *oc);
690 707
691struct output_config *find_output_config(struct sway_output *output); 708struct output_config *find_output_config(struct sway_output *output);
692 709
693void apply_output_config_to_outputs(struct output_config *oc);
694
695void reset_outputs(void);
696
697void free_output_config(struct output_config *oc); 710void free_output_config(struct output_config *oc);
698 711
699bool spawn_swaybg(void); 712bool spawn_swaybg(void);
diff --git a/sway/commands/output.c b/sway/commands/output.c
index 462dffd2..5e5d31b3 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -103,13 +103,13 @@ struct cmd_results *cmd_output(int argc, char **argv) {
103 103
104 bool background = output->background; 104 bool background = output->background;
105 105
106 output = store_output_config(output); 106 store_output_config(output);
107 107
108 // If reloading, the output configs will be applied after reading the 108 // If reloading, the output configs will be applied after reading the
109 // entire config and before the deferred commands so that an auto generated 109 // entire config and before the deferred commands so that an auto generated
110 // workspace name is not given to re-enabled outputs. 110 // workspace name is not given to re-enabled outputs.
111 if (!config->reloading && !config->validating) { 111 if (!config->reloading && !config->validating) {
112 apply_output_config_to_outputs(output); 112 apply_all_output_configs();
113 if (background) { 113 if (background) {
114 if (!spawn_swaybg()) { 114 if (!spawn_swaybg()) {
115 return cmd_results_new(CMD_FAILURE, 115 return cmd_results_new(CMD_FAILURE,
diff --git a/sway/commands/output/toggle.c b/sway/commands/output/toggle.c
index 6342d526..c6b72845 100644
--- a/sway/commands/output/toggle.c
+++ b/sway/commands/output/toggle.c
@@ -29,7 +29,7 @@ struct cmd_results *output_cmd_toggle(int argc, char **argv) {
29 config->handler_context.output_config->enabled = 1; 29 config->handler_context.output_config->enabled = 1;
30 } 30 }
31 31
32 free(oc); 32 free_output_config(oc);
33 config->handler_context.leftovers.argc = argc; 33 config->handler_context.leftovers.argc = argc;
34 config->handler_context.leftovers.argv = argv; 34 config->handler_context.leftovers.argv = argv;
35 return NULL; 35 return NULL;
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index a14ebb20..37a201b4 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -157,6 +157,10 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
157 return cmd_results_new(CMD_FAILURE, 157 return cmd_results_new(CMD_FAILURE,
158 "Unable to allocate workspace output"); 158 "Unable to allocate workspace output");
159 } 159 }
160 if (output_location + 1 < argc) {
161 list_free_items_and_destroy(wsc->outputs);
162 wsc->outputs = create_list();
163 }
160 for (int i = output_location + 1; i < argc; ++i) { 164 for (int i = output_location + 1; i < argc; ++i) {
161 list_add(wsc->outputs, strdup(argv[i])); 165 list_add(wsc->outputs, strdup(argv[i]));
162 } 166 }
diff --git a/sway/config.c b/sway/config.c
index 72fc41e7..f9131e0f 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -532,7 +532,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
532 } 532 }
533 sway_switch_retrigger_bindings_for_all(); 533 sway_switch_retrigger_bindings_for_all();
534 534
535 reset_outputs(); 535 apply_all_output_configs();
536 spawn_swaybg(); 536 spawn_swaybg();
537 537
538 config->reloading = false; 538 config->reloading = false;
diff --git a/sway/config/output.c b/sway/config/output.c
index 1b2332e9..fb1956df 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -9,6 +9,7 @@
9#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
10#include <wlr/types/wlr_output_layout.h> 10#include <wlr/types/wlr_output_layout.h>
11#include <wlr/types/wlr_output.h> 11#include <wlr/types/wlr_output.h>
12#include <wlr/types/wlr_output_swapchain_manager.h>
12#include "sway/config.h" 13#include "sway/config.h"
13#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
14#include "sway/output.h" 15#include "sway/output.h"
@@ -78,7 +79,72 @@ struct output_config *new_output_config(const char *name) {
78 return oc; 79 return oc;
79} 80}
80 81
81void 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) {
82 if (src->enabled != -1) { 148 if (src->enabled != -1) {
83 dst->enabled = src->enabled; 149 dst->enabled = src->enabled;
84 } 150 }
@@ -141,94 +207,42 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
141 } 207 }
142} 208}
143 209
144static void merge_wildcard_on_all(struct output_config *wildcard) { 210void store_output_config(struct output_config *oc) {
145 for (int i = 0; i < config->output_configs->length; i++) { 211 bool merged = false;
146 struct output_config *oc = config->output_configs->items[i]; 212 bool wildcard = strcmp(oc->name, "*") == 0;
147 if (strcmp(wildcard->name, oc->name) != 0) { 213 struct sway_output *output = wildcard ? NULL : all_output_by_name_or_id(oc->name);
148 sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name);
149 merge_output_config(oc, wildcard);
150 }
151 }
152}
153
154static void merge_id_on_name(struct output_config *oc) {
155 struct sway_output *output = all_output_by_name_or_id(oc->name);
156 if (output == NULL) {
157 return;
158 }
159 214
160 const char *name = output->wlr_output->name;
161 char id[128]; 215 char id[128];
162 output_get_identifier(id, sizeof(id), output); 216 if (output) {
163 217 output_get_identifier(id, sizeof(id), output);
164 char *id_on_name = format_str("%s on %s", id, name);
165 if (!id_on_name) {
166 return;
167 } 218 }
168 219
169 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 220 for (int i = 0; i < config->output_configs->length; i++) {
170 if (i >= 0) { 221 struct output_config *old = config->output_configs->items[i];
171 sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); 222
172 merge_output_config(config->output_configs->items[i], oc); 223 // If the old config matches the new config's name, regardless of
173 } else { 224 // whether it was name or identifier, merge on top of the existing
174 // If both a name and identifier config, exist generate an id on name 225 // config. If the new config is a wildcard, this also merges on top of
175 int ni = list_seq_find(config->output_configs, output_name_cmp, name); 226 // old wildcard configs.
176 int ii = list_seq_find(config->output_configs, output_name_cmp, id); 227 if (strcmp(old->name, oc->name) == 0) {
177 if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) 228 merge_output_config(old, oc);
178 || (ii >= 0 && strcmp(oc->name, name) == 0)) { 229 merged = true;
179 struct output_config *ion_oc = new_output_config(id_on_name); 230 continue;
180 if (ni >= 0) {
181 merge_output_config(ion_oc, config->output_configs->items[ni]);
182 }
183 if (ii >= 0) {
184 merge_output_config(ion_oc, config->output_configs->items[ii]);
185 }
186 merge_output_config(ion_oc, oc);
187 list_add(config->output_configs, ion_oc);
188 sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\""
189 " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f "
190 "transform %d) (bg %s %s) (power %d) (max render time: %d)",
191 ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height,
192 ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale,
193 ion_oc->transform, ion_oc->background,
194 ion_oc->background_option, ion_oc->power,
195 ion_oc->max_render_time);
196 } 231 }
197 }
198 free(id_on_name);
199}
200 232
201struct output_config *store_output_config(struct output_config *oc) { 233 // If the new config is a wildcard config we supersede all non-wildcard
202 bool wildcard = strcmp(oc->name, "*") == 0; 234 // configs. Old wildcard configs have already been handled above.
203 if (wildcard) { 235 if (wildcard) {
204 merge_wildcard_on_all(oc); 236 supersede_output_config(old, oc);
205 } else { 237 continue;
206 merge_id_on_name(oc); 238 }
207 }
208 239
209 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
210 if (i >= 0) { 241 // matches on that output's identifier, supersede it.
211 sway_log(SWAY_DEBUG, "Merging on top of existing output config"); 242 if (output && strcmp(old->name, id) == 0 &&
212 struct output_config *current = config->output_configs->items[i]; 243 strcmp(oc->name, output->wlr_output->name) == 0) {
213 merge_output_config(current, oc); 244 supersede_output_config(old, oc);
214 free_output_config(oc);
215 oc = current;
216 } else if (!wildcard) {
217 sway_log(SWAY_DEBUG, "Adding non-wildcard output config");
218 i = list_seq_find(config->output_configs, output_name_cmp, "*");
219 if (i >= 0) {
220 sway_log(SWAY_DEBUG, "Merging on top of output * config");
221 struct output_config *current = new_output_config(oc->name);
222 merge_output_config(current, config->output_configs->items[i]);
223 merge_output_config(current, oc);
224 free_output_config(oc);
225 oc = current;
226 } 245 }
227 list_add(config->output_configs, oc);
228 } else {
229 // New wildcard config. Just add it
230 sway_log(SWAY_DEBUG, "Adding output * config");
231 list_add(config->output_configs, oc);
232 } 246 }
233 247
234 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 "
@@ -239,7 +253,13 @@ struct output_config *store_output_config(struct output_config *oc) {
239 oc->transform, oc->background, oc->background_option, oc->power, 253 oc->transform, oc->background, oc->background_option, oc->power,
240 oc->max_render_time); 254 oc->max_render_time);
241 255
242 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 }
243} 263}
244 264
245static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, 265static void set_mode(struct wlr_output *output, struct wlr_output_state *pending,
@@ -366,22 +386,18 @@ static int compute_default_scale(struct wlr_output *output,
366 return 2; 386 return 2;
367} 387}
368 388
369/* Lists of formats to try, in order, when a specific render bit depth has 389static bool render_format_is_10bit(uint32_t render_format) {
370 * been asked for. The second to last format in each list should always 390 return render_format == DRM_FORMAT_XRGB2101010 ||
371 * be XRGB8888, as a reliable backup in case the others are not available; 391 render_format == DRM_FORMAT_XBGR2101010;
372 * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ 392}
373static const uint32_t *bit_depth_preferences[] = { 393
374 [RENDER_BIT_DEPTH_8] = (const uint32_t []){ 394static bool render_format_is_bgr(uint32_t fmt) {
375 DRM_FORMAT_XRGB8888, 395 return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888;
376 DRM_FORMAT_INVALID, 396}
377 }, 397
378 [RENDER_BIT_DEPTH_10] = (const uint32_t []){ 398static bool output_config_is_disabling(struct output_config *oc) {
379 DRM_FORMAT_XRGB2101010, 399 return oc && (!oc->enabled || oc->power == 0);
380 DRM_FORMAT_XBGR2101010, 400}
381 DRM_FORMAT_XRGB8888,
382 DRM_FORMAT_INVALID,
383 },
384};
385 401
386static void queue_output_config(struct output_config *oc, 402static void queue_output_config(struct output_config *oc,
387 struct sway_output *output, struct wlr_output_state *pending) { 403 struct sway_output *output, struct wlr_output_state *pending) {
@@ -391,7 +407,7 @@ static void queue_output_config(struct output_config *oc,
391 407
392 struct wlr_output *wlr_output = output->wlr_output; 408 struct wlr_output *wlr_output = output->wlr_output;
393 409
394 if (oc && (!oc->enabled || oc->power == 0)) { 410 if (output_config_is_disabling(oc)) {
395 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); 411 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
396 wlr_output_state_set_enabled(pending, false); 412 wlr_output_state_set_enabled(pending, false);
397 return; 413 return;
@@ -414,22 +430,6 @@ static void queue_output_config(struct output_config *oc,
414 struct wlr_output_mode *preferred_mode = 430 struct wlr_output_mode *preferred_mode =
415 wlr_output_preferred_mode(wlr_output); 431 wlr_output_preferred_mode(wlr_output);
416 wlr_output_state_set_mode(pending, preferred_mode); 432 wlr_output_state_set_mode(pending, preferred_mode);
417
418 if (!wlr_output_test_state(wlr_output, pending)) {
419 sway_log(SWAY_DEBUG, "Preferred mode rejected, "
420 "falling back to another mode");
421 struct wlr_output_mode *mode;
422 wl_list_for_each(mode, &wlr_output->modes, link) {
423 if (mode == preferred_mode) {
424 continue;
425 }
426
427 wlr_output_state_set_mode(pending, mode);
428 if (wlr_output_test_state(wlr_output, pending)) {
429 break;
430 }
431 }
432 }
433 } 433 }
434 434
435 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 435 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
@@ -480,48 +480,27 @@ static void queue_output_config(struct output_config *oc,
480 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,
481 oc->adaptive_sync); 481 oc->adaptive_sync);
482 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); 482 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1);
483 if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) {
484 sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring");
485 wlr_output_state_set_adaptive_sync_enabled(pending, false);
486 }
487 } 483 }
488 484
489 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { 485 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
490 const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; 486 if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 &&
491 assert(fmts); 487 render_format_is_10bit(output->wlr_output->render_format)) {
492 488 // 10-bit was set successfully before, try to save some tests by reusing the format
493 for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { 489 wlr_output_state_set_render_format(pending, output->wlr_output->render_format);
494 wlr_output_state_set_render_format(pending, fmts[i]); 490 } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) {
495 if (wlr_output_test_state(wlr_output, pending)) { 491 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010);
496 break; 492 } else {
497 } 493 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888);
498
499 sway_log(SWAY_DEBUG, "Preferred output format 0x%08x "
500 "failed to work, falling back to next in "
501 "list, 0x%08x", fmts[i], fmts[i + 1]);
502 } 494 }
503 } 495 }
504} 496}
505 497
506bool apply_output_config(struct output_config *oc, struct sway_output *output) { 498static bool finalize_output_config(struct output_config *oc, struct sway_output *output) {
507 if (output == root->fallback_output) { 499 if (output == root->fallback_output) {
508 return false; 500 return false;
509 } 501 }
510 502
511 struct wlr_output *wlr_output = output->wlr_output; 503 struct wlr_output *wlr_output = output->wlr_output;
512
513 struct wlr_output_state pending = {0};
514 queue_output_config(oc, output, &pending);
515
516 sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name);
517 if (!wlr_output_commit_state(wlr_output, &pending)) {
518 // Failed to commit output changes, maybe the output is missing a CRTC.
519 // Leave the output disabled for now and try again when the output gets
520 // the mode we asked for.
521 sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name);
522 return false;
523 }
524
525 if (oc && !oc->enabled) { 504 if (oc && !oc->enabled) {
526 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); 505 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name);
527 if (output->enabled) { 506 if (output->enabled) {
@@ -577,25 +556,9 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
577 output->max_render_time = oc->max_render_time; 556 output->max_render_time = oc->max_render_time;
578 } 557 }
579 558
580 // Reconfigure all devices, since input config may have been applied before
581 // this output came online, and some config items (like map_to_output) are
582 // dependent on an output being present.
583 input_manager_configure_all_input_mappings();
584 // Reconfigure the cursor images, since the scale may have changed.
585 input_manager_configure_xcursor();
586 return true; 559 return true;
587} 560}
588 561
589bool test_output_config(struct output_config *oc, struct sway_output *output) {
590 if (output == root->fallback_output) {
591 return false;
592 }
593
594 struct wlr_output_state pending = {0};
595 queue_output_config(oc, output, &pending);
596 return wlr_output_test_state(output->wlr_output, &pending);
597}
598
599static void default_output_config(struct output_config *oc, 562static void default_output_config(struct output_config *oc,
600 struct wlr_output *wlr_output) { 563 struct wlr_output *wlr_output) {
601 oc->enabled = 1; 564 oc->enabled = 1;
@@ -615,140 +578,415 @@ static void default_output_config(struct output_config *oc,
615 oc->max_render_time = 0; 578 oc->max_render_time = 0;
616} 579}
617 580
618static struct output_config *get_output_config(char *identifier, 581// find_output_config returns a merged output_config containing all stored
619 struct sway_output *sway_output) { 582// configuration that applies to the specified output.
583struct output_config *find_output_config(struct sway_output *sway_output) {
620 const char *name = sway_output->wlr_output->name; 584 const char *name = sway_output->wlr_output->name;
585 struct output_config *oc = NULL;
621 586
622 struct output_config *oc_id_on_name = NULL; 587 struct output_config *result = new_output_config(name);
623 struct output_config *oc_name = NULL; 588 if (config->reloading) {
624 struct output_config *oc_id = NULL; 589 default_output_config(result, sway_output->wlr_output);
590 }
625 591
626 char *id_on_name = format_str("%s on %s", identifier, name); 592 char id[128];
627 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 593 output_get_identifier(id, sizeof(id), sway_output);
628 if (i >= 0) { 594
629 oc_id_on_name = config->output_configs->items[i]; 595 int i;
630 } else { 596 bool match = false;
631 i = list_seq_find(config->output_configs, output_name_cmp, name); 597 if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) {
632 if (i >= 0) { 598 match = true;
633 oc_name = config->output_configs->items[i]; 599 oc = config->output_configs->items[i];
600 merge_output_config(result, oc);
601 }
602 if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) {
603 match = true;
604 oc = config->output_configs->items[i];
605 merge_output_config(result, oc);
606 }
607 if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) {
608 match = true;
609 oc = config->output_configs->items[i];
610 merge_output_config(result, oc);
611 }
612
613 if (!match && !config->reloading) {
614 // No name, identifier, or wildcard config. Since we are not
615 // reloading with defaults, the output config will be empty, so
616 // just return NULL
617 free_output_config(result);
618 return NULL;
619 }
620
621 return result;
622}
623
624static bool config_has_auto_mode(struct output_config *oc) {
625 if (!oc) {
626 return true;
627 }
628 if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) {
629 return true;
630 } else if (oc->width > 0 && oc->height > 0) {
631 return true;
632 }
633 return false;
634}
635
636struct search_context {
637 struct wlr_output_swapchain_manager *swapchain_mgr;
638 struct wlr_backend_output_state *states;
639 struct matched_output_config *configs;
640 size_t configs_len;
641 bool degrade_to_off;
642};
643
644static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_state *state) {
645 sway_log(SWAY_DEBUG, "Output state for %s", wlr_output->name);
646 if (state->committed & WLR_OUTPUT_STATE_ENABLED) {
647 sway_log(SWAY_DEBUG, " enabled: %s", state->enabled ? "yes" : "no");
648 }
649 if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) {
650 sway_log(SWAY_DEBUG, " render_format: %d", state->render_format);
651 }
652 if (state->committed & WLR_OUTPUT_STATE_MODE) {
653 if (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) {
654 sway_log(SWAY_DEBUG, " custom mode: %dx%d@%dmHz",
655 state->custom_mode.width, state->custom_mode.height, state->custom_mode.refresh);
656 } else {
657 sway_log(SWAY_DEBUG, " mode: %dx%d@%dmHz%s",
658 state->mode->width, state->mode->height, state->mode->refresh,
659 state->mode->preferred ? " (preferred)" : "");
634 } 660 }
661 }
662 if (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) {
663 sway_log(SWAY_DEBUG, " adaptive_sync: %s",
664 state->adaptive_sync_enabled ? "enabled": "disabled");
665 }
666}
667
668static bool search_valid_config(struct search_context *ctx, size_t output_idx);
669
670static void reset_output_state(struct wlr_output_state *state) {
671 wlr_output_state_finish(state);
672 wlr_output_state_init(state);
673 state->committed = 0;
674}
675
676static void clear_later_output_states(struct wlr_backend_output_state *states,
677 size_t configs_len, size_t output_idx) {
635 678
636 i = list_seq_find(config->output_configs, output_name_cmp, identifier); 679 // Clear and disable all output states after this one to avoid conflict
637 if (i >= 0) { 680 // with previous tests.
638 oc_id = config->output_configs->items[i]; 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;
639 } 716 }
640 } 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}
641 723
642 struct output_config *result = new_output_config("temp"); 724static bool search_mode(struct search_context *ctx, size_t output_idx) {
643 if (config->reloading) { 725 struct matched_output_config *cfg = &ctx->configs[output_idx];
644 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);
645 } 732 }
646 if (oc_id_on_name) { 733
647 // Already have an identifier on name config, use that 734 struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
648 free(result->name); 735 if (preferred_mode) {
649 result->name = strdup(id_on_name); 736 wlr_output_state_set_mode(state, preferred_mode);
650 merge_output_config(result, oc_id_on_name); 737 if (search_adaptive_sync(ctx, output_idx)) {
651 } else if (oc_name && oc_id) { 738 return true;
652 // Generate a config named `<identifier> on <name>` which contains a
653 // merged copy of the identifier on name. This will make sure that both
654 // identifier and name configs are respected, with identifier getting
655 // priority
656 struct output_config *temp = new_output_config(id_on_name);
657 merge_output_config(temp, oc_name);
658 merge_output_config(temp, oc_id);
659 list_add(config->output_configs, temp);
660
661 free(result->name);
662 result->name = strdup(id_on_name);
663 merge_output_config(result, temp);
664
665 sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)"
666 " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
667 " (power %d) (max render time: %d)", result->name, result->enabled,
668 result->width, result->height, result->refresh_rate,
669 result->x, result->y, result->scale, result->transform,
670 result->background, result->background_option, result->power,
671 result->max_render_time);
672 } else if (oc_name) {
673 // No identifier config, just return a copy of the name config
674 free(result->name);
675 result->name = strdup(name);
676 merge_output_config(result, oc_name);
677 } else if (oc_id) {
678 // No name config, just return a copy of the identifier config
679 free(result->name);
680 result->name = strdup(identifier);
681 merge_output_config(result, oc_id);
682 } else {
683 i = list_seq_find(config->output_configs, output_name_cmp, "*");
684 if (i >= 0) {
685 // No name or identifier config, but there is a wildcard config
686 free(result->name);
687 result->name = strdup("*");
688 merge_output_config(result, config->output_configs->items[i]);
689 } else if (!config->reloading) {
690 // No name, identifier, or wildcard config. Since we are not
691 // reloading with defaults, the output config will be empty, so
692 // just return NULL
693 free_output_config(result);
694 result = NULL;
695 } 739 }
696 } 740 }
697 741
698 free(id_on_name); 742 if (wl_list_empty(&wlr_output->modes)) {
699 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;
700} 759}
701 760
702struct output_config *find_output_config(struct sway_output *output) { 761static bool search_render_format(struct search_context *ctx, size_t output_idx) {
703 char id[128]; 762 struct matched_output_config *cfg = &ctx->configs[output_idx];
704 output_get_identifier(id, sizeof(id), output); 763 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
705 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;
706} 796}
707 797
708void apply_output_config_to_outputs(struct output_config *oc) { 798static bool search_valid_config(struct search_context *ctx, size_t output_idx) {
709 // Try to find the output container and apply configuration now. If 799 if (output_idx >= ctx->configs_len) {
710 // this is during startup then there will be no container and config 800 // We reached the end of the search, all good!
711 // will be applied during normal "new output" event from wlroots. 801 return true;
712 bool wildcard = strcmp(oc->name, "*") == 0; 802 }
713 struct sway_output *sway_output, *tmp;
714 wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) {
715 if (output_match_name_or_id(sway_output, oc->name)) {
716 char id[128];
717 output_get_identifier(id, sizeof(id), sway_output);
718 struct output_config *current = get_output_config(id, sway_output);
719 if (!current) {
720 // No stored output config matched, apply oc directly
721 sway_log(SWAY_DEBUG, "Applying oc directly");
722 current = new_output_config(oc->name);
723 merge_output_config(current, oc);
724 }
725 apply_output_config(current, sway_output);
726 free_output_config(current);
727 803
728 if (!wildcard) { 804 struct matched_output_config *cfg = &ctx->configs[output_idx];
729 // Stop looking if the output config isn't applicable to all 805 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
730 // outputs 806 struct wlr_output_state *state = &backend_state->base;
731 break; 807 struct wlr_output *wlr_output = backend_state->output;
732 } 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;
733 } 897 }
734 } 898 }
735 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
736 struct sway_seat *seat; 954 struct sway_seat *seat;
737 wl_list_for_each(seat, &server.input->seats, link) { 955 wl_list_for_each(seat, &server.input->seats, link) {
738 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 956 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
739 cursor_rebase(seat->cursor); 957 cursor_rebase(seat->cursor);
740 } 958 }
959
960 return ok;
741} 961}
742 962
743void reset_outputs(void) { 963void apply_all_output_configs(void) {
744 struct output_config *oc = NULL; 964 size_t configs_len = wl_list_length(&root->all_outputs);
745 int i = list_seq_find(config->output_configs, output_name_cmp, "*"); 965 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
746 if (i >= 0) { 966 if (!configs) {
747 oc = config->output_configs->items[i]; 967 return;
748 } else { 968 }
749 oc = store_output_config(new_output_config("*")); 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);
750 } 988 }
751 apply_output_config_to_outputs(oc); 989 free(configs);
752} 990}
753 991
754void free_output_config(struct output_config *oc) { 992void free_output_config(struct output_config *oc) {
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index 4b2584b6..6221b7b9 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -2,6 +2,7 @@
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h> 3#include <string.h>
4#include <wayland-server-core.h> 4#include <wayland-server-core.h>
5#include <wlr/types/wlr_fractional_scale_v1.h>
5#include <wlr/types/wlr_layer_shell_v1.h> 6#include <wlr/types/wlr_layer_shell_v1.h>
6#include <wlr/types/wlr_output.h> 7#include <wlr/types/wlr_output.h>
7#include <wlr/types/wlr_scene.h> 8#include <wlr/types/wlr_scene.h>
@@ -432,6 +433,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
432 433
433 surface->output = output; 434 surface->output = output;
434 435
436 // now that the surface's output is known, we can advertise its scale
437 wlr_fractional_scale_v1_notify_scale(surface->layer_surface->surface,
438 layer_surface->output->scale);
439 wlr_surface_set_preferred_buffer_scale(surface->layer_surface->surface,
440 ceil(layer_surface->output->scale));
441
435 surface->surface_commit.notify = handle_surface_commit; 442 surface->surface_commit.notify = handle_surface_commit;
436 wl_signal_add(&layer_surface->surface->events.commit, 443 wl_signal_add(&layer_surface->surface->events.commit,
437 &surface->surface_commit); 444 &surface->surface_commit);
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index b8f2d32d..2722e556 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -521,9 +521,7 @@ void handle_new_output(struct wl_listener *listener, void *data) {
521 sway_session_lock_add_output(server->session_lock.lock, output); 521 sway_session_lock_add_output(server->session_lock.lock, output);
522 } 522 }
523 523
524 struct output_config *oc = find_output_config(output); 524 apply_all_output_configs();
525 apply_output_config(oc, output);
526 free_output_config(oc);
527 525
528 transaction_commit_dirty(); 526 transaction_commit_dirty();
529 527
@@ -552,63 +550,89 @@ void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) {
552 wlr_output_schedule_frame(output->wlr_output); 550 wlr_output_schedule_frame(output->wlr_output);
553} 551}
554 552
553static struct output_config *output_config_for_config_head(
554 struct wlr_output_configuration_head_v1 *config_head,
555 struct sway_output *output) {
556 struct output_config *oc = new_output_config(output->wlr_output->name);
557 oc->enabled = config_head->state.enabled;
558 if (!oc->enabled) {
559 return oc;
560 }
561
562 if (config_head->state.mode != NULL) {
563 struct wlr_output_mode *mode = config_head->state.mode;
564 oc->width = mode->width;
565 oc->height = mode->height;
566 oc->refresh_rate = mode->refresh / 1000.f;
567 } else {
568 oc->width = config_head->state.custom_mode.width;
569 oc->height = config_head->state.custom_mode.height;
570 oc->refresh_rate =
571 config_head->state.custom_mode.refresh / 1000.f;
572 }
573 oc->x = config_head->state.x;
574 oc->y = config_head->state.y;
575 oc->transform = config_head->state.transform;
576 oc->scale = config_head->state.scale;
577 oc->adaptive_sync = config_head->state.adaptive_sync_enabled;
578 return oc;
579}
580
555static void output_manager_apply(struct sway_server *server, 581static void output_manager_apply(struct sway_server *server,
556 struct wlr_output_configuration_v1 *config, bool test_only) { 582 struct wlr_output_configuration_v1 *config, bool test_only) {
557 // TODO: perform atomic tests on the whole backend atomically 583 size_t configs_len = wl_list_length(&root->all_outputs);
558 584 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
559 struct wlr_output_configuration_head_v1 *config_head; 585 if (!configs) {
560 // First disable outputs we need to disable 586 return;
561 bool ok = true; 587 }
562 wl_list_for_each(config_head, &config->heads, link) { 588
563 struct wlr_output *wlr_output = config_head->state.output; 589 int config_idx = 0;
564 struct sway_output *output = wlr_output->data; 590 struct sway_output *sway_output;
565 if (!output->enabled || config_head->state.enabled) { 591 wl_list_for_each(sway_output, &root->all_outputs, link) {
592 if (sway_output == root->fallback_output) {
593 configs_len--;
566 continue; 594 continue;
567 } 595 }
568 struct output_config *oc = new_output_config(output->wlr_output->name);
569 oc->enabled = false;
570 596
571 if (test_only) { 597 struct matched_output_config *cfg = &configs[config_idx++];
572 ok &= test_output_config(oc, output); 598 cfg->output = sway_output;
573 } else { 599
574 oc = store_output_config(oc); 600 struct wlr_output_configuration_head_v1 *config_head;
575 ok &= apply_output_config(oc, output); 601 wl_list_for_each(config_head, &config->heads, link) {
602 if (config_head->state.output == sway_output->wlr_output) {
603 cfg->config = output_config_for_config_head(config_head, sway_output);
604 break;
605 }
606 }
607 if (!cfg->config) {
608 cfg->config = find_output_config(sway_output);
576 } 609 }
577 } 610 }
578 611
579 // Then enable outputs that need to 612 sort_output_configs_by_priority(configs, configs_len);
580 wl_list_for_each(config_head, &config->heads, link) { 613 bool ok = apply_output_configs(configs, configs_len, test_only, false);
581 struct wlr_output *wlr_output = config_head->state.output; 614 for (size_t idx = 0; idx < configs_len; idx++) {
582 struct sway_output *output = wlr_output->data; 615 struct matched_output_config *cfg = &configs[idx];
583 if (!config_head->state.enabled) { 616
584 continue; 617 // Only store new configs for successful non-test commits. Old configs,
585 } 618 // test-only and failed commits just get freed.
586 struct output_config *oc = new_output_config(output->wlr_output->name); 619 bool store_config = false;
587 oc->enabled = true; 620 if (!test_only && ok) {
588 if (config_head->state.mode != NULL) { 621 struct wlr_output_configuration_head_v1 *config_head;
589 struct wlr_output_mode *mode = config_head->state.mode; 622 wl_list_for_each(config_head, &config->heads, link) {
590 oc->width = mode->width; 623 if (config_head->state.output == cfg->output->wlr_output) {
591 oc->height = mode->height; 624 store_config = true;
592 oc->refresh_rate = mode->refresh / 1000.f; 625 break;
593 } else { 626 }
594 oc->width = config_head->state.custom_mode.width; 627 }
595 oc->height = config_head->state.custom_mode.height;
596 oc->refresh_rate =
597 config_head->state.custom_mode.refresh / 1000.f;
598 } 628 }
599 oc->x = config_head->state.x; 629 if (store_config) {
600 oc->y = config_head->state.y; 630 store_output_config(cfg->config);
601 oc->transform = config_head->state.transform;
602 oc->scale = config_head->state.scale;
603 oc->adaptive_sync = config_head->state.adaptive_sync_enabled;
604
605 if (test_only) {
606 ok &= test_output_config(oc, output);
607 } else { 631 } else {
608 oc = store_output_config(oc); 632 free_output_config(cfg->config);
609 ok &= apply_output_config(oc, output);
610 } 633 }
611 } 634 }
635 free(configs);
612 636
613 if (ok) { 637 if (ok) {
614 wlr_output_configuration_v1_send_succeeded(config); 638 wlr_output_configuration_v1_send_succeeded(config);
@@ -652,6 +676,6 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
652 oc->power = 1; 676 oc->power = 1;
653 break; 677 break;
654 } 678 }
655 oc = store_output_config(oc); 679 store_output_config(oc);
656 apply_output_config(oc, output); 680 apply_all_output_configs();
657} 681}
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index b97f0152..f74d0658 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -32,6 +32,7 @@ static struct modifier_key {
32 { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 }, 32 { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 },
33 { "Mod3", WLR_MODIFIER_MOD3 }, 33 { "Mod3", WLR_MODIFIER_MOD3 },
34 { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO }, 34 { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO },
35 { "Super", WLR_MODIFIER_LOGO },
35 { "Mod5", WLR_MODIFIER_MOD5 }, 36 { "Mod5", WLR_MODIFIER_MOD5 },
36}; 37};
37 38
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c
index 35fd3bcb..340e334b 100644
--- a/sway/input/seatop_down.c
+++ b/sway/input/seatop_down.c
@@ -117,7 +117,11 @@ static void handle_touch_cancel(struct sway_seat *seat,
117 } 117 }
118 118
119 if (e->surface) { 119 if (e->surface) {
120 wlr_seat_touch_notify_cancel(seat->wlr_seat, e->surface); 120 struct wl_client *client = wl_resource_get_client(e->surface->resource);
121 struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(seat->wlr_seat, client);
122 if (seat_client != NULL) {
123 wlr_seat_touch_notify_cancel(seat->wlr_seat, seat_client);
124 }
121 } 125 }
122 126
123 if (wl_list_empty(&e->point_events)) { 127 if (wl_list_empty(&e->point_events)) {
diff --git a/sway/server.c b/sway/server.c
index d159dc9b..180d3a6b 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -240,13 +240,12 @@ bool server_init(struct sway_server *server) {
240 240
241 wlr_renderer_init_wl_shm(server->renderer, server->wl_display); 241 wlr_renderer_init_wl_shm(server->renderer, server->wl_display);
242 242
243 if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) { 243 if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) {
244 server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( 244 server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer(
245 server->wl_display, 4, server->renderer); 245 server->wl_display, 4, server->renderer);
246 } 246 if (debug.legacy_wl_drm) {
247 if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL && 247 wlr_drm_create(server->wl_display, server->renderer);
248 debug.legacy_wl_drm) { 248 }
249 wlr_drm_create(server->wl_display, server->renderer);
250 } 249 }
251 250
252 server->allocator = wlr_allocator_autocreate(server->backend, 251 server->allocator = wlr_allocator_autocreate(server->backend,
diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd
index c9895e52..2f697248 100644
--- a/sway/sway-ipc.7.scd
+++ b/sway/sway-ipc.7.scd
@@ -1046,7 +1046,7 @@ An object with a single string property containing the contents of the config
1046*Example Reply:* 1046*Example Reply:*
1047``` 1047```
1048{ 1048{
1049 "config": "set $mod Mod4\nbindsym $mod+q exit\n" 1049 "config": "set $mod Mod4\\nbindsym $mod+q exit\\n"
1050} 1050}
1051``` 1051```
1052 1052
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 7e58b528..9f823947 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -400,6 +400,12 @@ runtime.
400 only be available for that group. By default, if you overwrite a binding, 400 only be available for that group. By default, if you overwrite a binding,
401 swaynag will give you a warning. To silence this, use the _--no-warn_ flag. 401 swaynag will give you a warning. To silence this, use the _--no-warn_ flag.
402 402
403 For specifying modifier keys, you can use the XKB modifier names _Shift_,
404 _Lock_ (for Caps Lock), _Control_, _Mod1_ (for Alt), _Mod2_ (for Num Lock),
405 _Mod3_ (for XKB modifier Mod3), _Mod4_ (for the Logo key), and _Mod5_ (for
406 AltGr). In addition, you can use the aliases _Ctrl_ (for Control), _Alt_
407 (for Alt), and _Super_ (for the Logo key).
408
403 Unless the flag _--locked_ is set, the command will not be run when a 409 Unless the flag _--locked_ is set, the command will not be run when a
404 screen locking program is active. If there is a matching binding with 410 screen locking program is active. If there is a matching binding with
405 and without _--locked_, the one with will be preferred when locked and the 411 and without _--locked_, the one with will be preferred when locked and the
diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c
index 5eba53ba..4b7ee999 100644
--- a/sway/sway_text_node.c
+++ b/sway/sway_text_node.c
@@ -58,7 +58,7 @@ struct text_buffer {
58 58
59static int get_text_width(struct sway_text_node *props) { 59static int get_text_width(struct sway_text_node *props) {
60 int width = props->width; 60 int width = props->width;
61 if (props->max_width) { 61 if (props->max_width >= 0) {
62 width = MIN(width, props->max_width); 62 width = MIN(width, props->max_width);
63 } 63 }
64 return MAX(width, 0); 64 return MAX(width, 0);
@@ -81,6 +81,11 @@ static void render_backing_buffer(struct text_buffer *buffer) {
81 return; 81 return;
82 } 82 }
83 83
84 if (buffer->props.max_width == 0) {
85 wlr_scene_buffer_set_buffer(buffer->buffer_node, NULL);
86 return;
87 }
88
84 float scale = buffer->scale; 89 float scale = buffer->scale;
85 int width = ceil(buffer->props.width * scale); 90 int width = ceil(buffer->props.width * scale);
86 int height = ceil(buffer->props.height * scale); 91 int height = ceil(buffer->props.height * scale);
@@ -236,6 +241,7 @@ struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent,
236 241
237 buffer->buffer_node = node; 242 buffer->buffer_node = node;
238 buffer->props.node = &node->node; 243 buffer->props.node = &node->node;
244 buffer->props.max_width = -1;
239 buffer->text = strdup(text); 245 buffer->text = strdup(text);
240 if (!buffer->text) { 246 if (!buffer->text) {
241 free(buffer); 247 free(buffer);
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 9224b4fb..80ef34fe 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -352,6 +352,8 @@ void container_arrange_title_bar(struct sway_container *con) {
352 352
353 int alloc_width = MIN((int)node->width, 353 int alloc_width = MIN((int)node->width,
354 width - h_padding - config->titlebar_h_padding); 354 width - h_padding - config->titlebar_h_padding);
355 alloc_width = MAX(alloc_width, 0);
356
355 sway_text_node_set_max_width(node, alloc_width); 357 sway_text_node_set_max_width(node, alloc_width);
356 wlr_scene_node_set_position(node->node, 358 wlr_scene_node_set_position(node->node,
357 h_padding, (height - node->height) >> 1); 359 h_padding, (height - node->height) >> 1);
@@ -376,6 +378,8 @@ void container_arrange_title_bar(struct sway_container *con) {
376 378
377 int alloc_width = MIN((int) node->width, 379 int alloc_width = MIN((int) node->width,
378 width - h_padding - config->titlebar_h_padding); 380 width - h_padding - config->titlebar_h_padding);
381 alloc_width = MAX(alloc_width, 0);
382
379 sway_text_node_set_max_width(node, alloc_width); 383 sway_text_node_set_max_width(node, alloc_width);
380 wlr_scene_node_set_position(node->node, 384 wlr_scene_node_set_position(node->node,
381 h_padding, (height - node->height) >> 1); 385 h_padding, (height - node->height) >> 1);