aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sway/config.h5
-rw-r--r--sway/commands/workspace.c4
-rw-r--r--sway/config/output.c328
-rw-r--r--sway/desktop/layer_shell.c7
-rw-r--r--sway/desktop/output.c3
-rw-r--r--sway/sway_text_node.c8
-rw-r--r--sway/tree/container.c4
7 files changed, 302 insertions, 57 deletions
diff --git a/include/sway/config.h b/include/sway/config.h
index 0be1cd22..5ccc3e77 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -689,10 +689,13 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt
689struct output_config *new_output_config(const char *name); 689struct output_config *new_output_config(const char *name);
690 690
691bool apply_output_configs(struct matched_output_config *configs, 691bool apply_output_configs(struct matched_output_config *configs,
692 size_t configs_len, bool test_only); 692 size_t configs_len, bool test_only, bool degrade_to_off);
693 693
694void apply_all_output_configs(void); 694void apply_all_output_configs(void);
695 695
696void sort_output_configs_by_priority(struct matched_output_config *configs,
697 size_t configs_len);
698
696/** 699/**
697 * store_output_config stores a new output config. An output may be matched by 700 * store_output_config stores a new output config. An output may be matched by
698 * three different config types, in order of precedence: Identifier, name and 701 * three different config types, in order of precedence: Identifier, name and
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/output.c b/sway/config/output.c
index 54af5d8e..fb1956df 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -210,7 +210,7 @@ static void merge_output_config(struct output_config *dst, struct output_config
210void store_output_config(struct output_config *oc) { 210void store_output_config(struct output_config *oc) {
211 bool merged = false; 211 bool merged = false;
212 bool wildcard = strcmp(oc->name, "*") == 0; 212 bool wildcard = strcmp(oc->name, "*") == 0;
213 struct sway_output *output = wildcard ? NULL : output_by_name_or_id(oc->name); 213 struct sway_output *output = wildcard ? NULL : all_output_by_name_or_id(oc->name);
214 214
215 char id[128]; 215 char id[128];
216 if (output) { 216 if (output) {
@@ -386,22 +386,18 @@ static int compute_default_scale(struct wlr_output *output,
386 return 2; 386 return 2;
387} 387}
388 388
389/* Lists of formats to try, in order, when a specific render bit depth has 389static bool render_format_is_10bit(uint32_t render_format) {
390 * been asked for. The second to last format in each list should always 390 return render_format == DRM_FORMAT_XRGB2101010 ||
391 * be XRGB8888, as a reliable backup in case the others are not available; 391 render_format == DRM_FORMAT_XBGR2101010;
392 * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ 392}
393static const uint32_t *bit_depth_preferences[] = { 393
394 [RENDER_BIT_DEPTH_8] = (const uint32_t []){ 394static bool render_format_is_bgr(uint32_t fmt) {
395 DRM_FORMAT_XRGB8888, 395 return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888;
396 DRM_FORMAT_INVALID, 396}
397 }, 397
398 [RENDER_BIT_DEPTH_10] = (const uint32_t []){ 398static bool output_config_is_disabling(struct output_config *oc) {
399 DRM_FORMAT_XRGB2101010, 399 return oc && (!oc->enabled || oc->power == 0);
400 DRM_FORMAT_XBGR2101010, 400}
401 DRM_FORMAT_XRGB8888,
402 DRM_FORMAT_INVALID,
403 },
404};
405 401
406static void queue_output_config(struct output_config *oc, 402static void queue_output_config(struct output_config *oc,
407 struct sway_output *output, struct wlr_output_state *pending) { 403 struct sway_output *output, struct wlr_output_state *pending) {
@@ -411,7 +407,7 @@ static void queue_output_config(struct output_config *oc,
411 407
412 struct wlr_output *wlr_output = output->wlr_output; 408 struct wlr_output *wlr_output = output->wlr_output;
413 409
414 if (oc && (!oc->enabled || oc->power == 0)) { 410 if (output_config_is_disabling(oc)) {
415 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); 411 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
416 wlr_output_state_set_enabled(pending, false); 412 wlr_output_state_set_enabled(pending, false);
417 return; 413 return;
@@ -434,22 +430,6 @@ static void queue_output_config(struct output_config *oc,
434 struct wlr_output_mode *preferred_mode = 430 struct wlr_output_mode *preferred_mode =
435 wlr_output_preferred_mode(wlr_output); 431 wlr_output_preferred_mode(wlr_output);
436 wlr_output_state_set_mode(pending, preferred_mode); 432 wlr_output_state_set_mode(pending, preferred_mode);
437
438 if (!wlr_output_test_state(wlr_output, pending)) {
439 sway_log(SWAY_DEBUG, "Preferred mode rejected, "
440 "falling back to another mode");
441 struct wlr_output_mode *mode;
442 wl_list_for_each(mode, &wlr_output->modes, link) {
443 if (mode == preferred_mode) {
444 continue;
445 }
446
447 wlr_output_state_set_mode(pending, mode);
448 if (wlr_output_test_state(wlr_output, pending)) {
449 break;
450 }
451 }
452 }
453 } 433 }
454 434
455 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 435 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
@@ -500,25 +480,17 @@ static void queue_output_config(struct output_config *oc,
500 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,
501 oc->adaptive_sync); 481 oc->adaptive_sync);
502 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);
503 if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) {
504 sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring");
505 wlr_output_state_set_adaptive_sync_enabled(pending, false);
506 }
507 } 483 }
508 484
509 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { 485 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
510 const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; 486 if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 &&
511 assert(fmts); 487 render_format_is_10bit(output->wlr_output->render_format)) {
512 488 // 10-bit was set successfully before, try to save some tests by reusing the format
513 for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { 489 wlr_output_state_set_render_format(pending, output->wlr_output->render_format);
514 wlr_output_state_set_render_format(pending, fmts[i]); 490 } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) {
515 if (wlr_output_test_state(wlr_output, pending)) { 491 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010);
516 break; 492 } else {
517 } 493 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888);
518
519 sway_log(SWAY_DEBUG, "Preferred output format 0x%08x "
520 "failed to work, falling back to next in "
521 "list, 0x%08x", fmts[i], fmts[i + 1]);
522 } 494 }
523 } 495 }
524} 496}
@@ -649,8 +621,245 @@ struct output_config *find_output_config(struct sway_output *sway_output) {
649 return result; 621 return result;
650} 622}
651 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)" : "");
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) {
678
679 // Clear and disable all output states after this one to avoid conflict
680 // with previous tests.
681 for (size_t idx = output_idx+1; idx < configs_len; idx++) {
682 struct wlr_backend_output_state *backend_state = &states[idx];
683 struct wlr_output_state *state = &backend_state->base;
684
685 reset_output_state(state);
686 wlr_output_state_set_enabled(state, false);
687 }
688}
689
690static bool search_finish(struct search_context *ctx, size_t output_idx) {
691 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
692 struct wlr_output_state *state = &backend_state->base;
693 struct wlr_output *wlr_output = backend_state->output;
694
695 clear_later_output_states(ctx->states, ctx->configs_len, output_idx);
696 dump_output_state(wlr_output, state);
697 return wlr_output_swapchain_manager_prepare(ctx->swapchain_mgr, ctx->states, ctx->configs_len) &&
698 search_valid_config(ctx, output_idx+1);
699}
700
701static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) {
702 struct matched_output_config *cfg = &ctx->configs[output_idx];
703 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
704 struct wlr_output_state *state = &backend_state->base;
705
706 if (cfg->config && cfg->config->adaptive_sync == 1) {
707 wlr_output_state_set_adaptive_sync_enabled(state, true);
708 if (search_finish(ctx, output_idx)) {
709 return true;
710 }
711 }
712 if (!cfg->config || cfg->config->adaptive_sync != -1) {
713 wlr_output_state_set_adaptive_sync_enabled(state, false);
714 if (search_finish(ctx, output_idx)) {
715 return true;
716 }
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}
723
724static bool search_mode(struct search_context *ctx, size_t output_idx) {
725 struct matched_output_config *cfg = &ctx->configs[output_idx];
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);
732 }
733
734 struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
735 if (preferred_mode) {
736 wlr_output_state_set_mode(state, preferred_mode);
737 if (search_adaptive_sync(ctx, output_idx)) {
738 return true;
739 }
740 }
741
742 if (wl_list_empty(&wlr_output->modes)) {
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;
759}
760
761static bool search_render_format(struct search_context *ctx, size_t output_idx) {
762 struct matched_output_config *cfg = &ctx->configs[output_idx];
763 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
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;
796}
797
798static bool search_valid_config(struct search_context *ctx, size_t output_idx) {
799 if (output_idx >= ctx->configs_len) {
800 // We reached the end of the search, all good!
801 return true;
802 }
803
804 struct matched_output_config *cfg = &ctx->configs[output_idx];
805 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
806 struct wlr_output_state *state = &backend_state->base;
807 struct wlr_output *wlr_output = backend_state->output;
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
652bool apply_output_configs(struct matched_output_config *configs, 861bool apply_output_configs(struct matched_output_config *configs,
653 size_t configs_len, bool test_only) { 862 size_t configs_len, bool test_only, bool degrade_to_off) {
654 struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); 863 struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states));
655 if (!states) { 864 if (!states) {
656 return false; 865 return false;
@@ -674,8 +883,18 @@ bool apply_output_configs(struct matched_output_config *configs,
674 883
675 bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len); 884 bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len);
676 if (!ok) { 885 if (!ok) {
677 sway_log(SWAY_ERROR, "Swapchain prepare failed"); 886 sway_log(SWAY_ERROR, "Requested backend configuration failed, searching for valid fallbacks");
678 goto out; 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;
897 }
679 } 898 }
680 899
681 if (test_only) { 900 if (test_only) {
@@ -761,7 +980,8 @@ void apply_all_output_configs(void) {
761 config->config = find_output_config(sway_output); 980 config->config = find_output_config(sway_output);
762 } 981 }
763 982
764 apply_output_configs(configs, configs_len, false); 983 sort_output_configs_by_priority(configs, configs_len);
984 apply_output_configs(configs, configs_len, false, true);
765 for (size_t idx = 0; idx < configs_len; idx++) { 985 for (size_t idx = 0; idx < configs_len; idx++) {
766 struct matched_output_config *cfg = &configs[idx]; 986 struct matched_output_config *cfg = &configs[idx];
767 free_output_config(cfg->config); 987 free_output_config(cfg->config);
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 70987feb..2722e556 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -609,7 +609,8 @@ static void output_manager_apply(struct sway_server *server,
609 } 609 }
610 } 610 }
611 611
612 bool ok = apply_output_configs(configs, configs_len, test_only); 612 sort_output_configs_by_priority(configs, configs_len);
613 bool ok = apply_output_configs(configs, configs_len, test_only, false);
613 for (size_t idx = 0; idx < configs_len; idx++) { 614 for (size_t idx = 0; idx < configs_len; idx++) {
614 struct matched_output_config *cfg = &configs[idx]; 615 struct matched_output_config *cfg = &configs[idx];
615 616
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);