diff options
Diffstat (limited to 'sway/config/output.c')
-rw-r--r-- | sway/config/output.c | 299 |
1 files changed, 202 insertions, 97 deletions
diff --git a/sway/config/output.c b/sway/config/output.c index c9ec6745..1a5215fe 100644 --- a/sway/config/output.c +++ b/sway/config/output.c | |||
@@ -1,10 +1,12 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <assert.h> | 2 | #include <assert.h> |
3 | #include <drm_fourcc.h> | ||
3 | #include <stdbool.h> | 4 | #include <stdbool.h> |
4 | #include <string.h> | 5 | #include <string.h> |
5 | #include <sys/socket.h> | 6 | #include <sys/socket.h> |
6 | #include <sys/wait.h> | 7 | #include <sys/wait.h> |
7 | #include <unistd.h> | 8 | #include <unistd.h> |
9 | #include <wlr/config.h> | ||
8 | #include <wlr/types/wlr_cursor.h> | 10 | #include <wlr/types/wlr_cursor.h> |
9 | #include <wlr/types/wlr_output_layout.h> | 11 | #include <wlr/types/wlr_output_layout.h> |
10 | #include <wlr/types/wlr_output.h> | 12 | #include <wlr/types/wlr_output.h> |
@@ -15,6 +17,10 @@ | |||
15 | #include "log.h" | 17 | #include "log.h" |
16 | #include "util.h" | 18 | #include "util.h" |
17 | 19 | ||
20 | #if WLR_HAS_DRM_BACKEND | ||
21 | #include <wlr/backend/drm.h> | ||
22 | #endif | ||
23 | |||
18 | int output_name_cmp(const void *item, const void *data) { | 24 | int output_name_cmp(const void *item, const void *data) { |
19 | const struct output_config *output = item; | 25 | const struct output_config *output = item; |
20 | const char *name = data; | 26 | const char *name = data; |
@@ -25,8 +31,10 @@ int output_name_cmp(const void *item, const void *data) { | |||
25 | void output_get_identifier(char *identifier, size_t len, | 31 | void output_get_identifier(char *identifier, size_t len, |
26 | struct sway_output *output) { | 32 | struct sway_output *output) { |
27 | struct wlr_output *wlr_output = output->wlr_output; | 33 | struct wlr_output *wlr_output = output->wlr_output; |
28 | snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, | 34 | snprintf(identifier, len, "%s %s %s", |
29 | wlr_output->serial); | 35 | wlr_output->make ? wlr_output->make : "Unknown", |
36 | wlr_output->model ? wlr_output->model : "Unknown", | ||
37 | wlr_output->serial ? wlr_output->serial : "Unknown"); | ||
30 | } | 38 | } |
31 | 39 | ||
32 | const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { | 40 | const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { |
@@ -58,6 +66,7 @@ struct output_config *new_output_config(const char *name) { | |||
58 | oc->width = oc->height = -1; | 66 | oc->width = oc->height = -1; |
59 | oc->refresh_rate = -1; | 67 | oc->refresh_rate = -1; |
60 | oc->custom_mode = -1; | 68 | oc->custom_mode = -1; |
69 | oc->drm_mode.type = -1; | ||
61 | oc->x = oc->y = -1; | 70 | oc->x = oc->y = -1; |
62 | oc->scale = -1; | 71 | oc->scale = -1; |
63 | oc->scale_filter = SCALE_FILTER_DEFAULT; | 72 | oc->scale_filter = SCALE_FILTER_DEFAULT; |
@@ -65,6 +74,8 @@ struct output_config *new_output_config(const char *name) { | |||
65 | oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; | 74 | oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; |
66 | oc->max_render_time = -1; | 75 | oc->max_render_time = -1; |
67 | oc->adaptive_sync = -1; | 76 | oc->adaptive_sync = -1; |
77 | oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; | ||
78 | oc->power = -1; | ||
68 | return oc; | 79 | return oc; |
69 | } | 80 | } |
70 | 81 | ||
@@ -99,6 +110,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
99 | if (src->custom_mode != -1) { | 110 | if (src->custom_mode != -1) { |
100 | dst->custom_mode = src->custom_mode; | 111 | dst->custom_mode = src->custom_mode; |
101 | } | 112 | } |
113 | if (src->drm_mode.type != (uint32_t) -1) { | ||
114 | memcpy(&dst->drm_mode, &src->drm_mode, sizeof(src->drm_mode)); | ||
115 | } | ||
102 | if (src->transform != -1) { | 116 | if (src->transform != -1) { |
103 | dst->transform = src->transform; | 117 | dst->transform = src->transform; |
104 | } | 118 | } |
@@ -108,6 +122,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
108 | if (src->adaptive_sync != -1) { | 122 | if (src->adaptive_sync != -1) { |
109 | dst->adaptive_sync = src->adaptive_sync; | 123 | dst->adaptive_sync = src->adaptive_sync; |
110 | } | 124 | } |
125 | if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { | ||
126 | dst->render_bit_depth = src->render_bit_depth; | ||
127 | } | ||
111 | if (src->background) { | 128 | if (src->background) { |
112 | free(dst->background); | 129 | free(dst->background); |
113 | dst->background = strdup(src->background); | 130 | dst->background = strdup(src->background); |
@@ -120,8 +137,8 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
120 | free(dst->background_fallback); | 137 | free(dst->background_fallback); |
121 | dst->background_fallback = strdup(src->background_fallback); | 138 | dst->background_fallback = strdup(src->background_fallback); |
122 | } | 139 | } |
123 | if (src->dpms_state != 0) { | 140 | if (src->power != -1) { |
124 | dst->dpms_state = src->dpms_state; | 141 | dst->power = src->power; |
125 | } | 142 | } |
126 | } | 143 | } |
127 | 144 | ||
@@ -136,25 +153,16 @@ static void merge_wildcard_on_all(struct output_config *wildcard) { | |||
136 | } | 153 | } |
137 | 154 | ||
138 | static void merge_id_on_name(struct output_config *oc) { | 155 | static void merge_id_on_name(struct output_config *oc) { |
139 | char *id_on_name = NULL; | 156 | struct sway_output *output = all_output_by_name_or_id(oc->name); |
140 | char id[128]; | 157 | if (output == NULL) { |
141 | char *name = NULL; | 158 | return; |
142 | struct sway_output *output; | ||
143 | wl_list_for_each(output, &root->all_outputs, link) { | ||
144 | name = output->wlr_output->name; | ||
145 | output_get_identifier(id, sizeof(id), output); | ||
146 | if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) { | ||
147 | size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1; | ||
148 | id_on_name = malloc(length); | ||
149 | if (!id_on_name) { | ||
150 | sway_log(SWAY_ERROR, "Failed to allocate id on name string"); | ||
151 | return; | ||
152 | } | ||
153 | snprintf(id_on_name, length, "%s on %s", id, name); | ||
154 | break; | ||
155 | } | ||
156 | } | 159 | } |
157 | 160 | ||
161 | const char *name = output->wlr_output->name; | ||
162 | char id[128]; | ||
163 | output_get_identifier(id, sizeof(id), output); | ||
164 | |||
165 | char *id_on_name = format_str("%s on %s", id, name); | ||
158 | if (!id_on_name) { | 166 | if (!id_on_name) { |
159 | return; | 167 | return; |
160 | } | 168 | } |
@@ -180,11 +188,11 @@ static void merge_id_on_name(struct output_config *oc) { | |||
180 | list_add(config->output_configs, ion_oc); | 188 | list_add(config->output_configs, ion_oc); |
181 | sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" | 189 | sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" |
182 | " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f " | 190 | " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f " |
183 | "transform %d) (bg %s %s) (dpms %d) (max render time: %d)", | 191 | "transform %d) (bg %s %s) (power %d) (max render time: %d)", |
184 | ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height, | 192 | ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height, |
185 | ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale, | 193 | ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale, |
186 | ion_oc->transform, ion_oc->background, | 194 | ion_oc->transform, ion_oc->background, |
187 | ion_oc->background_option, ion_oc->dpms_state, | 195 | ion_oc->background_option, ion_oc->power, |
188 | ion_oc->max_render_time); | 196 | ion_oc->max_render_time); |
189 | } | 197 | } |
190 | } | 198 | } |
@@ -225,50 +233,74 @@ struct output_config *store_output_config(struct output_config *oc) { | |||
225 | } | 233 | } |
226 | 234 | ||
227 | sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " | 235 | sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " |
228 | "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d) " | 236 | "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) " |
229 | "(max render time: %d)", | 237 | "(max render time: %d)", |
230 | oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, | 238 | oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, |
231 | oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), | 239 | oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), |
232 | oc->transform, oc->background, oc->background_option, oc->dpms_state, | 240 | oc->transform, oc->background, oc->background_option, oc->power, |
233 | oc->max_render_time); | 241 | oc->max_render_time); |
234 | 242 | ||
235 | return oc; | 243 | return oc; |
236 | } | 244 | } |
237 | 245 | ||
238 | static void set_mode(struct wlr_output *output, int width, int height, | 246 | static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, |
239 | float refresh_rate, bool custom) { | 247 | int width, int height, float refresh_rate, bool custom) { |
240 | // Not all floating point integers can be represented exactly | 248 | // Not all floating point integers can be represented exactly |
241 | // as (int)(1000 * mHz / 1000.f) | 249 | // as (int)(1000 * mHz / 1000.f) |
242 | // round() the result to avoid any error | 250 | // round() the result to avoid any error |
243 | int mhz = (int)round(refresh_rate * 1000); | 251 | int mhz = (int)roundf(refresh_rate * 1000); |
252 | // If no target refresh rate is given, match highest available | ||
253 | mhz = mhz <= 0 ? INT_MAX : mhz; | ||
244 | 254 | ||
245 | if (wl_list_empty(&output->modes) || custom) { | 255 | if (wl_list_empty(&output->modes) || custom) { |
246 | sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); | 256 | sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); |
247 | wlr_output_set_custom_mode(output, width, height, | 257 | wlr_output_state_set_custom_mode(pending, width, height, |
248 | refresh_rate > 0 ? mhz : 0); | 258 | refresh_rate > 0 ? mhz : 0); |
249 | return; | 259 | return; |
250 | } | 260 | } |
251 | 261 | ||
252 | struct wlr_output_mode *mode, *best = NULL; | 262 | struct wlr_output_mode *mode, *best = NULL; |
263 | int best_diff_mhz = INT_MAX; | ||
253 | wl_list_for_each(mode, &output->modes, link) { | 264 | wl_list_for_each(mode, &output->modes, link) { |
254 | if (mode->width == width && mode->height == height) { | 265 | if (mode->width == width && mode->height == height) { |
255 | if (mode->refresh == mhz) { | 266 | int diff_mhz = abs(mode->refresh - mhz); |
256 | best = mode; | 267 | if (diff_mhz < best_diff_mhz) { |
257 | break; | 268 | best_diff_mhz = diff_mhz; |
258 | } | ||
259 | if (best == NULL || mode->refresh > best->refresh) { | ||
260 | best = mode; | 269 | best = mode; |
270 | if (best_diff_mhz == 0) { | ||
271 | break; | ||
272 | } | ||
261 | } | 273 | } |
262 | } | 274 | } |
263 | } | 275 | } |
264 | if (!best) { | 276 | if (best) { |
265 | sway_log(SWAY_ERROR, "Configured mode for %s not available", output->name); | 277 | sway_log(SWAY_INFO, "Assigning configured mode (%dx%d@%.3fHz) to %s", |
266 | sway_log(SWAY_INFO, "Picking preferred mode instead"); | 278 | best->width, best->height, best->refresh / 1000.f, output->name); |
267 | best = wlr_output_preferred_mode(output); | ||
268 | } else { | 279 | } else { |
269 | sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); | 280 | best = wlr_output_preferred_mode(output); |
281 | sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, " | ||
282 | "applying preferred mode (%dx%d@%.3fHz)", | ||
283 | width, height, refresh_rate, | ||
284 | best->width, best->height, best->refresh / 1000.f); | ||
270 | } | 285 | } |
271 | wlr_output_set_mode(output, best); | 286 | wlr_output_state_set_mode(pending, best); |
287 | } | ||
288 | |||
289 | static void set_modeline(struct wlr_output *output, | ||
290 | struct wlr_output_state *pending, drmModeModeInfo *drm_mode) { | ||
291 | #if WLR_HAS_DRM_BACKEND | ||
292 | if (!wlr_output_is_drm(output)) { | ||
293 | sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); | ||
294 | return; | ||
295 | } | ||
296 | sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name); | ||
297 | struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode); | ||
298 | if (mode) { | ||
299 | wlr_output_state_set_mode(pending, mode); | ||
300 | } | ||
301 | #else | ||
302 | sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); | ||
303 | #endif | ||
272 | } | 304 | } |
273 | 305 | ||
274 | /* Some manufacturers hardcode the aspect-ratio of the output in the physical | 306 | /* Some manufacturers hardcode the aspect-ratio of the output in the physical |
@@ -289,23 +321,24 @@ static bool phys_size_is_aspect_ratio(struct wlr_output *output) { | |||
289 | // 1 inch = 25.4 mm | 321 | // 1 inch = 25.4 mm |
290 | #define MM_PER_INCH 25.4 | 322 | #define MM_PER_INCH 25.4 |
291 | 323 | ||
292 | static int compute_default_scale(struct wlr_output *output) { | 324 | static int compute_default_scale(struct wlr_output *output, |
325 | struct wlr_output_state *pending) { | ||
293 | struct wlr_box box = { .width = output->width, .height = output->height }; | 326 | struct wlr_box box = { .width = output->width, .height = output->height }; |
294 | if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { | 327 | if (pending->committed & WLR_OUTPUT_STATE_MODE) { |
295 | switch (output->pending.mode_type) { | 328 | switch (pending->mode_type) { |
296 | case WLR_OUTPUT_STATE_MODE_FIXED: | 329 | case WLR_OUTPUT_STATE_MODE_FIXED: |
297 | box.width = output->pending.mode->width; | 330 | box.width = pending->mode->width; |
298 | box.height = output->pending.mode->height; | 331 | box.height = pending->mode->height; |
299 | break; | 332 | break; |
300 | case WLR_OUTPUT_STATE_MODE_CUSTOM: | 333 | case WLR_OUTPUT_STATE_MODE_CUSTOM: |
301 | box.width = output->pending.custom_mode.width; | 334 | box.width = pending->custom_mode.width; |
302 | box.height = output->pending.custom_mode.height; | 335 | box.height = pending->custom_mode.height; |
303 | break; | 336 | break; |
304 | } | 337 | } |
305 | } | 338 | } |
306 | enum wl_output_transform transform = output->transform; | 339 | enum wl_output_transform transform = output->transform; |
307 | if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) { | 340 | if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) { |
308 | transform = output->pending.transform; | 341 | transform = pending->transform; |
309 | } | 342 | } |
310 | wlr_box_transform(&box, &box, transform, box.width, box.height); | 343 | wlr_box_transform(&box, &box, transform, box.width, box.height); |
311 | 344 | ||
@@ -334,42 +367,90 @@ static int compute_default_scale(struct wlr_output *output) { | |||
334 | return 2; | 367 | return 2; |
335 | } | 368 | } |
336 | 369 | ||
370 | /* Lists of formats to try, in order, when a specific render bit depth has | ||
371 | * been asked for. The second to last format in each list should always | ||
372 | * be XRGB8888, as a reliable backup in case the others are not available; | ||
373 | * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ | ||
374 | static const uint32_t *bit_depth_preferences[] = { | ||
375 | [RENDER_BIT_DEPTH_8] = (const uint32_t []){ | ||
376 | DRM_FORMAT_XRGB8888, | ||
377 | DRM_FORMAT_INVALID, | ||
378 | }, | ||
379 | [RENDER_BIT_DEPTH_10] = (const uint32_t []){ | ||
380 | DRM_FORMAT_XRGB2101010, | ||
381 | DRM_FORMAT_XBGR2101010, | ||
382 | DRM_FORMAT_XRGB8888, | ||
383 | DRM_FORMAT_INVALID, | ||
384 | }, | ||
385 | }; | ||
386 | |||
337 | static void queue_output_config(struct output_config *oc, | 387 | static void queue_output_config(struct output_config *oc, |
338 | struct sway_output *output) { | 388 | struct sway_output *output, struct wlr_output_state *pending) { |
339 | if (output == root->noop_output) { | 389 | if (output == root->fallback_output) { |
340 | return; | 390 | return; |
341 | } | 391 | } |
342 | 392 | ||
343 | struct wlr_output *wlr_output = output->wlr_output; | 393 | struct wlr_output *wlr_output = output->wlr_output; |
344 | 394 | ||
345 | if (oc && (!oc->enabled || oc->dpms_state == DPMS_OFF)) { | 395 | if (oc && (!oc->enabled || oc->power == 0)) { |
346 | sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); | 396 | sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); |
347 | wlr_output_enable(wlr_output, false); | 397 | wlr_output_state_set_enabled(pending, false); |
348 | return; | 398 | return; |
349 | } | 399 | } |
350 | 400 | ||
351 | sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); | 401 | sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); |
352 | wlr_output_enable(wlr_output, true); | 402 | wlr_output_state_set_enabled(pending, true); |
353 | 403 | ||
354 | if (oc && oc->width > 0 && oc->height > 0) { | 404 | if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) { |
405 | sway_log(SWAY_DEBUG, "Set %s modeline", | ||
406 | wlr_output->name); | ||
407 | set_modeline(wlr_output, pending, &oc->drm_mode); | ||
408 | } else if (oc && oc->width > 0 && oc->height > 0) { | ||
355 | sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", | 409 | sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", |
356 | wlr_output->name, oc->width, oc->height, oc->refresh_rate); | 410 | wlr_output->name, oc->width, oc->height, oc->refresh_rate); |
357 | set_mode(wlr_output, oc->width, oc->height, | 411 | set_mode(wlr_output, pending, oc->width, oc->height, |
358 | oc->refresh_rate, oc->custom_mode == 1); | 412 | oc->refresh_rate, oc->custom_mode == 1); |
359 | } else if (!wl_list_empty(&wlr_output->modes)) { | 413 | } else if (!wl_list_empty(&wlr_output->modes)) { |
360 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); | 414 | sway_log(SWAY_DEBUG, "Set preferred mode"); |
361 | wlr_output_set_mode(wlr_output, mode); | 415 | struct wlr_output_mode *preferred_mode = |
416 | wlr_output_preferred_mode(wlr_output); | ||
417 | wlr_output_state_set_mode(pending, preferred_mode); | ||
418 | |||
419 | if (!wlr_output_test_state(wlr_output, pending)) { | ||
420 | sway_log(SWAY_DEBUG, "Preferred mode rejected, " | ||
421 | "falling back to another mode"); | ||
422 | struct wlr_output_mode *mode; | ||
423 | wl_list_for_each(mode, &wlr_output->modes, link) { | ||
424 | if (mode == preferred_mode) { | ||
425 | continue; | ||
426 | } | ||
427 | |||
428 | wlr_output_state_set_mode(pending, mode); | ||
429 | if (wlr_output_test_state(wlr_output, pending)) { | ||
430 | break; | ||
431 | } | ||
432 | } | ||
433 | } | ||
362 | } | 434 | } |
363 | 435 | ||
364 | if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { | 436 | if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { |
365 | sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, | 437 | sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, |
366 | sway_wl_output_subpixel_to_string(oc->subpixel)); | 438 | sway_wl_output_subpixel_to_string(oc->subpixel)); |
367 | wlr_output_set_subpixel(wlr_output, oc->subpixel); | 439 | wlr_output_state_set_subpixel(pending, oc->subpixel); |
368 | } | 440 | } |
369 | 441 | ||
442 | enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; | ||
370 | if (oc && oc->transform >= 0) { | 443 | if (oc && oc->transform >= 0) { |
371 | sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); | 444 | tr = oc->transform; |
372 | wlr_output_set_transform(wlr_output, oc->transform); | 445 | #if WLR_HAS_DRM_BACKEND |
446 | } else if (wlr_output_is_drm(wlr_output)) { | ||
447 | tr = wlr_drm_connector_get_panel_orientation(wlr_output); | ||
448 | sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); | ||
449 | #endif | ||
450 | } | ||
451 | if (wlr_output->transform != tr) { | ||
452 | sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); | ||
453 | wlr_output_state_set_transform(pending, tr); | ||
373 | } | 454 | } |
374 | 455 | ||
375 | // 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 |
@@ -377,24 +458,54 @@ static void queue_output_config(struct output_config *oc, | |||
377 | float scale; | 458 | float scale; |
378 | if (oc && oc->scale > 0) { | 459 | if (oc && oc->scale > 0) { |
379 | 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 | } | ||
380 | } else { | 471 | } else { |
381 | scale = compute_default_scale(wlr_output); | 472 | scale = compute_default_scale(wlr_output, pending); |
382 | sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); | 473 | sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); |
383 | } | 474 | } |
384 | if (scale != wlr_output->scale) { | 475 | if (scale != wlr_output->scale) { |
385 | 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); |
386 | wlr_output_set_scale(wlr_output, scale); | 477 | wlr_output_state_set_scale(pending, scale); |
387 | } | 478 | } |
388 | 479 | ||
389 | if (oc && oc->adaptive_sync != -1) { | 480 | if (oc && oc->adaptive_sync != -1) { |
390 | 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, |
391 | oc->adaptive_sync); | 482 | oc->adaptive_sync); |
392 | wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); | 483 | wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); |
484 | if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) { | ||
485 | sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring"); | ||
486 | wlr_output_state_set_adaptive_sync_enabled(pending, false); | ||
487 | } | ||
488 | } | ||
489 | |||
490 | if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { | ||
491 | const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; | ||
492 | assert(fmts); | ||
493 | |||
494 | for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { | ||
495 | wlr_output_state_set_render_format(pending, fmts[i]); | ||
496 | if (wlr_output_test_state(wlr_output, pending)) { | ||
497 | break; | ||
498 | } | ||
499 | |||
500 | sway_log(SWAY_DEBUG, "Preferred output format 0x%08x " | ||
501 | "failed to work, falling back to next in " | ||
502 | "list, 0x%08x", fmts[i], fmts[i + 1]); | ||
503 | } | ||
393 | } | 504 | } |
394 | } | 505 | } |
395 | 506 | ||
396 | bool apply_output_config(struct output_config *oc, struct sway_output *output) { | 507 | bool apply_output_config(struct output_config *oc, struct sway_output *output) { |
397 | if (output == root->noop_output) { | 508 | if (output == root->fallback_output) { |
398 | return false; | 509 | return false; |
399 | } | 510 | } |
400 | 511 | ||
@@ -403,14 +514,11 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
403 | // Flag to prevent the output mode event handler from calling us | 514 | // Flag to prevent the output mode event handler from calling us |
404 | output->enabling = (!oc || oc->enabled); | 515 | output->enabling = (!oc || oc->enabled); |
405 | 516 | ||
406 | queue_output_config(oc, output); | 517 | struct wlr_output_state pending = {0}; |
407 | 518 | queue_output_config(oc, output, &pending); | |
408 | if (!oc || oc->dpms_state != DPMS_OFF) { | ||
409 | output->current_mode = wlr_output->pending.mode; | ||
410 | } | ||
411 | 519 | ||
412 | sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); | 520 | sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); |
413 | if (!wlr_output_commit(wlr_output)) { | 521 | if (!wlr_output_commit_state(wlr_output, &pending)) { |
414 | // Failed to commit output changes, maybe the output is missing a CRTC. | 522 | // Failed to commit output changes, maybe the output is missing a CRTC. |
415 | // Leave the output disabled for now and try again when the output gets | 523 | // Leave the output disabled for now and try again when the output gets |
416 | // the mode we asked for. | 524 | // the mode we asked for. |
@@ -430,10 +538,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
430 | return true; | 538 | return true; |
431 | } | 539 | } |
432 | 540 | ||
433 | if (config->reloading) { | ||
434 | output_damage_whole(output); | ||
435 | } | ||
436 | |||
437 | if (oc) { | 541 | if (oc) { |
438 | enum scale_filter_mode scale_filter_old = output->scale_filter; | 542 | enum scale_filter_mode scale_filter_old = output->scale_filter; |
439 | switch (oc->scale_filter) { | 543 | switch (oc->scale_filter) { |
@@ -450,6 +554,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
450 | if (scale_filter_old != output->scale_filter) { | 554 | if (scale_filter_old != output->scale_filter) { |
451 | sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, | 555 | sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, |
452 | sway_output_scale_filter_to_string(output->scale_filter)); | 556 | sway_output_scale_filter_to_string(output->scale_filter)); |
557 | wlr_damage_ring_add_whole(&output->scene_output->damage_ring); | ||
453 | } | 558 | } |
454 | } | 559 | } |
455 | 560 | ||
@@ -462,12 +567,12 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
462 | } | 567 | } |
463 | 568 | ||
464 | // Update output->{lx, ly, width, height} | 569 | // Update output->{lx, ly, width, height} |
465 | struct wlr_box *output_box = | 570 | struct wlr_box output_box; |
466 | wlr_output_layout_get_box(root->output_layout, wlr_output); | 571 | wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box); |
467 | output->lx = output_box->x; | 572 | output->lx = output_box.x; |
468 | output->ly = output_box->y; | 573 | output->ly = output_box.y; |
469 | output->width = output_box->width; | 574 | output->width = output_box.width; |
470 | output->height = output_box->height; | 575 | output->height = output_box.height; |
471 | 576 | ||
472 | if (!output->enabled) { | 577 | if (!output->enabled) { |
473 | output_enable(output); | 578 | output_enable(output); |
@@ -482,24 +587,26 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
482 | // Reconfigure all devices, since input config may have been applied before | 587 | // Reconfigure all devices, since input config may have been applied before |
483 | // this output came online, and some config items (like map_to_output) are | 588 | // this output came online, and some config items (like map_to_output) are |
484 | // dependent on an output being present. | 589 | // dependent on an output being present. |
485 | input_manager_configure_all_inputs(); | 590 | input_manager_configure_all_input_mappings(); |
591 | // Reconfigure the cursor images, since the scale may have changed. | ||
592 | input_manager_configure_xcursor(); | ||
486 | return true; | 593 | return true; |
487 | } | 594 | } |
488 | 595 | ||
489 | bool test_output_config(struct output_config *oc, struct sway_output *output) { | 596 | bool test_output_config(struct output_config *oc, struct sway_output *output) { |
490 | if (output == root->noop_output) { | 597 | if (output == root->fallback_output) { |
491 | return false; | 598 | return false; |
492 | } | 599 | } |
493 | 600 | ||
494 | queue_output_config(oc, output); | 601 | struct wlr_output_state pending = {0}; |
495 | bool ok = wlr_output_test(output->wlr_output); | 602 | queue_output_config(oc, output, &pending); |
496 | wlr_output_rollback(output->wlr_output); | 603 | return wlr_output_test_state(output->wlr_output, &pending); |
497 | return ok; | ||
498 | } | 604 | } |
499 | 605 | ||
500 | static void default_output_config(struct output_config *oc, | 606 | static void default_output_config(struct output_config *oc, |
501 | struct wlr_output *wlr_output) { | 607 | struct wlr_output *wlr_output) { |
502 | oc->enabled = 1; | 608 | oc->enabled = 1; |
609 | oc->power = 1; | ||
503 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); | 610 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); |
504 | if (mode != NULL) { | 611 | if (mode != NULL) { |
505 | oc->width = mode->width; | 612 | oc->width = mode->width; |
@@ -512,7 +619,6 @@ static void default_output_config(struct output_config *oc, | |||
512 | struct sway_output *output = wlr_output->data; | 619 | struct sway_output *output = wlr_output->data; |
513 | oc->subpixel = output->detected_subpixel; | 620 | oc->subpixel = output->detected_subpixel; |
514 | oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; | 621 | oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; |
515 | oc->dpms_state = DPMS_ON; | ||
516 | oc->max_render_time = 0; | 622 | oc->max_render_time = 0; |
517 | } | 623 | } |
518 | 624 | ||
@@ -524,9 +630,7 @@ static struct output_config *get_output_config(char *identifier, | |||
524 | struct output_config *oc_name = NULL; | 630 | struct output_config *oc_name = NULL; |
525 | struct output_config *oc_id = NULL; | 631 | struct output_config *oc_id = NULL; |
526 | 632 | ||
527 | size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; | 633 | char *id_on_name = format_str("%s on %s", identifier, name); |
528 | char *id_on_name = malloc(length); | ||
529 | snprintf(id_on_name, length, "%s on %s", identifier, name); | ||
530 | int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); | 634 | int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); |
531 | if (i >= 0) { | 635 | if (i >= 0) { |
532 | oc_id_on_name = config->output_configs->items[i]; | 636 | oc_id_on_name = config->output_configs->items[i]; |
@@ -567,10 +671,10 @@ static struct output_config *get_output_config(char *identifier, | |||
567 | 671 | ||
568 | sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" | 672 | sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" |
569 | " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" | 673 | " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" |
570 | " (dpms %d) (max render time: %d)", result->name, result->enabled, | 674 | " (power %d) (max render time: %d)", result->name, result->enabled, |
571 | result->width, result->height, result->refresh_rate, | 675 | result->width, result->height, result->refresh_rate, |
572 | result->x, result->y, result->scale, result->transform, | 676 | result->x, result->y, result->scale, result->transform, |
573 | result->background, result->background_option, result->dpms_state, | 677 | result->background, result->background_option, result->power, |
574 | result->max_render_time); | 678 | result->max_render_time); |
575 | } else if (oc_name) { | 679 | } else if (oc_name) { |
576 | // No identifier config, just return a copy of the name config | 680 | // No identifier config, just return a copy of the name config |
@@ -613,12 +717,11 @@ void apply_output_config_to_outputs(struct output_config *oc) { | |||
613 | // this is during startup then there will be no container and config | 717 | // this is during startup then there will be no container and config |
614 | // will be applied during normal "new output" event from wlroots. | 718 | // will be applied during normal "new output" event from wlroots. |
615 | bool wildcard = strcmp(oc->name, "*") == 0; | 719 | bool wildcard = strcmp(oc->name, "*") == 0; |
616 | char id[128]; | ||
617 | struct sway_output *sway_output, *tmp; | 720 | struct sway_output *sway_output, *tmp; |
618 | wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) { | 721 | wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) { |
619 | char *name = sway_output->wlr_output->name; | 722 | if (output_match_name_or_id(sway_output, oc->name)) { |
620 | output_get_identifier(id, sizeof(id), sway_output); | 723 | char id[128]; |
621 | if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { | 724 | output_get_identifier(id, sizeof(id), sway_output); |
622 | struct output_config *current = get_output_config(id, sway_output); | 725 | struct output_config *current = get_output_config(id, sway_output); |
623 | if (!current) { | 726 | if (!current) { |
624 | // No stored output config matched, apply oc directly | 727 | // No stored output config matched, apply oc directly |
@@ -702,6 +805,8 @@ static bool _spawn_swaybg(char **command) { | |||
702 | sway_log_errno(SWAY_ERROR, "fork failed"); | 805 | sway_log_errno(SWAY_ERROR, "fork failed"); |
703 | return false; | 806 | return false; |
704 | } else if (pid == 0) { | 807 | } else if (pid == 0) { |
808 | restore_nofile_limit(); | ||
809 | |||
705 | pid = fork(); | 810 | pid = fork(); |
706 | if (pid < 0) { | 811 | if (pid < 0) { |
707 | sway_log_errno(SWAY_ERROR, "fork failed"); | 812 | sway_log_errno(SWAY_ERROR, "fork failed"); |