diff options
Diffstat (limited to 'sway/config/output.c')
-rw-r--r-- | sway/config/output.c | 122 |
1 files changed, 106 insertions, 16 deletions
diff --git a/sway/config/output.c b/sway/config/output.c index c9ec6745..aa4cf946 100644 --- a/sway/config/output.c +++ b/sway/config/output.c | |||
@@ -1,5 +1,6 @@ | |||
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> |
@@ -8,6 +9,7 @@ | |||
8 | #include <wlr/types/wlr_cursor.h> | 9 | #include <wlr/types/wlr_cursor.h> |
9 | #include <wlr/types/wlr_output_layout.h> | 10 | #include <wlr/types/wlr_output_layout.h> |
10 | #include <wlr/types/wlr_output.h> | 11 | #include <wlr/types/wlr_output.h> |
12 | #include <wlr/backend/drm.h> | ||
11 | #include "sway/config.h" | 13 | #include "sway/config.h" |
12 | #include "sway/input/cursor.h" | 14 | #include "sway/input/cursor.h" |
13 | #include "sway/output.h" | 15 | #include "sway/output.h" |
@@ -25,8 +27,10 @@ int output_name_cmp(const void *item, const void *data) { | |||
25 | void output_get_identifier(char *identifier, size_t len, | 27 | void output_get_identifier(char *identifier, size_t len, |
26 | struct sway_output *output) { | 28 | struct sway_output *output) { |
27 | struct wlr_output *wlr_output = output->wlr_output; | 29 | struct wlr_output *wlr_output = output->wlr_output; |
28 | snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, | 30 | snprintf(identifier, len, "%s %s %s", |
29 | wlr_output->serial); | 31 | wlr_output->make ? wlr_output->make : "Unknown", |
32 | wlr_output->model ? wlr_output->model : "Unknown", | ||
33 | wlr_output->serial ? wlr_output->serial : "Unknown"); | ||
30 | } | 34 | } |
31 | 35 | ||
32 | const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { | 36 | const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { |
@@ -58,6 +62,7 @@ struct output_config *new_output_config(const char *name) { | |||
58 | oc->width = oc->height = -1; | 62 | oc->width = oc->height = -1; |
59 | oc->refresh_rate = -1; | 63 | oc->refresh_rate = -1; |
60 | oc->custom_mode = -1; | 64 | oc->custom_mode = -1; |
65 | oc->drm_mode.type = -1; | ||
61 | oc->x = oc->y = -1; | 66 | oc->x = oc->y = -1; |
62 | oc->scale = -1; | 67 | oc->scale = -1; |
63 | oc->scale_filter = SCALE_FILTER_DEFAULT; | 68 | oc->scale_filter = SCALE_FILTER_DEFAULT; |
@@ -65,6 +70,7 @@ struct output_config *new_output_config(const char *name) { | |||
65 | oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; | 70 | oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; |
66 | oc->max_render_time = -1; | 71 | oc->max_render_time = -1; |
67 | oc->adaptive_sync = -1; | 72 | oc->adaptive_sync = -1; |
73 | oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; | ||
68 | return oc; | 74 | return oc; |
69 | } | 75 | } |
70 | 76 | ||
@@ -99,6 +105,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
99 | if (src->custom_mode != -1) { | 105 | if (src->custom_mode != -1) { |
100 | dst->custom_mode = src->custom_mode; | 106 | dst->custom_mode = src->custom_mode; |
101 | } | 107 | } |
108 | if (src->drm_mode.type != (uint32_t) -1) { | ||
109 | memcpy(&dst->drm_mode, &src->drm_mode, sizeof(src->drm_mode)); | ||
110 | } | ||
102 | if (src->transform != -1) { | 111 | if (src->transform != -1) { |
103 | dst->transform = src->transform; | 112 | dst->transform = src->transform; |
104 | } | 113 | } |
@@ -108,6 +117,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
108 | if (src->adaptive_sync != -1) { | 117 | if (src->adaptive_sync != -1) { |
109 | dst->adaptive_sync = src->adaptive_sync; | 118 | dst->adaptive_sync = src->adaptive_sync; |
110 | } | 119 | } |
120 | if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { | ||
121 | dst->render_bit_depth = src->render_bit_depth; | ||
122 | } | ||
111 | if (src->background) { | 123 | if (src->background) { |
112 | free(dst->background); | 124 | free(dst->background); |
113 | dst->background = strdup(src->background); | 125 | dst->background = strdup(src->background); |
@@ -271,6 +283,18 @@ static void set_mode(struct wlr_output *output, int width, int height, | |||
271 | wlr_output_set_mode(output, best); | 283 | wlr_output_set_mode(output, best); |
272 | } | 284 | } |
273 | 285 | ||
286 | static void set_modeline(struct wlr_output *output, drmModeModeInfo *drm_mode) { | ||
287 | if (!wlr_output_is_drm(output)) { | ||
288 | sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); | ||
289 | return; | ||
290 | } | ||
291 | 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); | ||
293 | if (mode) { | ||
294 | wlr_output_set_mode(output, mode); | ||
295 | } | ||
296 | } | ||
297 | |||
274 | /* Some manufacturers hardcode the aspect-ratio of the output in the physical | 298 | /* Some manufacturers hardcode the aspect-ratio of the output in the physical |
275 | * size field. */ | 299 | * size field. */ |
276 | static bool phys_size_is_aspect_ratio(struct wlr_output *output) { | 300 | static bool phys_size_is_aspect_ratio(struct wlr_output *output) { |
@@ -334,9 +358,26 @@ static int compute_default_scale(struct wlr_output *output) { | |||
334 | return 2; | 358 | return 2; |
335 | } | 359 | } |
336 | 360 | ||
361 | /* Lists of formats to try, in order, when a specific render bit depth has | ||
362 | * been asked for. The second to last format in each list should always | ||
363 | * be XRGB8888, as a reliable backup in case the others are not available; | ||
364 | * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ | ||
365 | static const uint32_t *bit_depth_preferences[] = { | ||
366 | [RENDER_BIT_DEPTH_8] = (const uint32_t []){ | ||
367 | DRM_FORMAT_XRGB8888, | ||
368 | DRM_FORMAT_INVALID, | ||
369 | }, | ||
370 | [RENDER_BIT_DEPTH_10] = (const uint32_t []){ | ||
371 | DRM_FORMAT_XRGB2101010, | ||
372 | DRM_FORMAT_XBGR2101010, | ||
373 | DRM_FORMAT_XRGB8888, | ||
374 | DRM_FORMAT_INVALID, | ||
375 | }, | ||
376 | }; | ||
377 | |||
337 | static void queue_output_config(struct output_config *oc, | 378 | static void queue_output_config(struct output_config *oc, |
338 | struct sway_output *output) { | 379 | struct sway_output *output) { |
339 | if (output == root->noop_output) { | 380 | if (output == root->fallback_output) { |
340 | return; | 381 | return; |
341 | } | 382 | } |
342 | 383 | ||
@@ -351,14 +392,36 @@ static void queue_output_config(struct output_config *oc, | |||
351 | sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); | 392 | sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); |
352 | wlr_output_enable(wlr_output, true); | 393 | wlr_output_enable(wlr_output, true); |
353 | 394 | ||
354 | if (oc && oc->width > 0 && oc->height > 0) { | 395 | if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) { |
396 | sway_log(SWAY_DEBUG, "Set %s modeline", | ||
397 | wlr_output->name); | ||
398 | set_modeline(wlr_output, &oc->drm_mode); | ||
399 | } else if (oc && oc->width > 0 && oc->height > 0) { | ||
355 | sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", | 400 | sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", |
356 | wlr_output->name, oc->width, oc->height, oc->refresh_rate); | 401 | wlr_output->name, oc->width, oc->height, oc->refresh_rate); |
357 | set_mode(wlr_output, oc->width, oc->height, | 402 | set_mode(wlr_output, oc->width, oc->height, |
358 | oc->refresh_rate, oc->custom_mode == 1); | 403 | oc->refresh_rate, oc->custom_mode == 1); |
359 | } else if (!wl_list_empty(&wlr_output->modes)) { | 404 | } else if (!wl_list_empty(&wlr_output->modes)) { |
360 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); | 405 | sway_log(SWAY_DEBUG, "Set preferred mode"); |
361 | wlr_output_set_mode(wlr_output, mode); | 406 | struct wlr_output_mode *preferred_mode = |
407 | wlr_output_preferred_mode(wlr_output); | ||
408 | wlr_output_set_mode(wlr_output, 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 | } | ||
362 | } | 425 | } |
363 | 426 | ||
364 | if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { | 427 | if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { |
@@ -367,9 +430,16 @@ static void queue_output_config(struct output_config *oc, | |||
367 | wlr_output_set_subpixel(wlr_output, oc->subpixel); | 430 | wlr_output_set_subpixel(wlr_output, oc->subpixel); |
368 | } | 431 | } |
369 | 432 | ||
433 | enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; | ||
370 | if (oc && oc->transform >= 0) { | 434 | if (oc && oc->transform >= 0) { |
371 | sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); | 435 | tr = oc->transform; |
372 | wlr_output_set_transform(wlr_output, oc->transform); | 436 | } else if (wlr_output_is_drm(wlr_output)) { |
437 | tr = wlr_drm_connector_get_panel_orientation(wlr_output); | ||
438 | sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); | ||
439 | } | ||
440 | if (wlr_output->transform != tr) { | ||
441 | sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); | ||
442 | wlr_output_set_transform(wlr_output, tr); | ||
373 | } | 443 | } |
374 | 444 | ||
375 | // Apply the scale last before the commit, because the scale auto-detection | 445 | // Apply the scale last before the commit, because the scale auto-detection |
@@ -391,10 +461,26 @@ static void queue_output_config(struct output_config *oc, | |||
391 | oc->adaptive_sync); | 461 | oc->adaptive_sync); |
392 | wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); | 462 | wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); |
393 | } | 463 | } |
464 | |||
465 | if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { | ||
466 | const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; | ||
467 | assert(fmts); | ||
468 | |||
469 | for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { | ||
470 | wlr_output_set_render_format(wlr_output, fmts[i]); | ||
471 | if (wlr_output_test(wlr_output)) { | ||
472 | break; | ||
473 | } | ||
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 | } | ||
479 | } | ||
394 | } | 480 | } |
395 | 481 | ||
396 | bool apply_output_config(struct output_config *oc, struct sway_output *output) { | 482 | bool apply_output_config(struct output_config *oc, struct sway_output *output) { |
397 | if (output == root->noop_output) { | 483 | if (output == root->fallback_output) { |
398 | return false; | 484 | return false; |
399 | } | 485 | } |
400 | 486 | ||
@@ -462,12 +548,12 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
462 | } | 548 | } |
463 | 549 | ||
464 | // Update output->{lx, ly, width, height} | 550 | // Update output->{lx, ly, width, height} |
465 | struct wlr_box *output_box = | 551 | struct wlr_box output_box; |
466 | wlr_output_layout_get_box(root->output_layout, wlr_output); | 552 | wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box); |
467 | output->lx = output_box->x; | 553 | output->lx = output_box.x; |
468 | output->ly = output_box->y; | 554 | output->ly = output_box.y; |
469 | output->width = output_box->width; | 555 | output->width = output_box.width; |
470 | output->height = output_box->height; | 556 | output->height = output_box.height; |
471 | 557 | ||
472 | if (!output->enabled) { | 558 | if (!output->enabled) { |
473 | output_enable(output); | 559 | output_enable(output); |
@@ -483,11 +569,13 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
483 | // this output came online, and some config items (like map_to_output) are | 569 | // this output came online, and some config items (like map_to_output) are |
484 | // dependent on an output being present. | 570 | // dependent on an output being present. |
485 | input_manager_configure_all_inputs(); | 571 | input_manager_configure_all_inputs(); |
572 | // Reconfigure the cursor images, since the scale may have changed. | ||
573 | input_manager_configure_xcursor(); | ||
486 | return true; | 574 | return true; |
487 | } | 575 | } |
488 | 576 | ||
489 | bool test_output_config(struct output_config *oc, struct sway_output *output) { | 577 | bool test_output_config(struct output_config *oc, struct sway_output *output) { |
490 | if (output == root->noop_output) { | 578 | if (output == root->fallback_output) { |
491 | return false; | 579 | return false; |
492 | } | 580 | } |
493 | 581 | ||
@@ -702,6 +790,8 @@ static bool _spawn_swaybg(char **command) { | |||
702 | sway_log_errno(SWAY_ERROR, "fork failed"); | 790 | sway_log_errno(SWAY_ERROR, "fork failed"); |
703 | return false; | 791 | return false; |
704 | } else if (pid == 0) { | 792 | } else if (pid == 0) { |
793 | restore_nofile_limit(); | ||
794 | |||
705 | pid = fork(); | 795 | pid = fork(); |
706 | if (pid < 0) { | 796 | if (pid < 0) { |
707 | sway_log_errno(SWAY_ERROR, "fork failed"); | 797 | sway_log_errno(SWAY_ERROR, "fork failed"); |