diff options
-rw-r--r-- | include/sway/commands.h | 1 | ||||
-rw-r--r-- | include/sway/tree/container.h | 6 | ||||
-rw-r--r-- | include/sway/tree/view.h | 10 | ||||
-rw-r--r-- | sway/commands.c | 1 | ||||
-rw-r--r-- | sway/commands/title_format.c | 31 | ||||
-rw-r--r-- | sway/desktop/xdg_shell_v6.c | 3 | ||||
-rw-r--r-- | sway/desktop/xwayland.c | 2 | ||||
-rw-r--r-- | sway/meson.build | 1 | ||||
-rw-r--r-- | sway/tree/container.c | 46 | ||||
-rw-r--r-- | sway/tree/view.c | 142 |
10 files changed, 201 insertions, 42 deletions
diff --git a/include/sway/commands.h b/include/sway/commands.h index 0732a90a..d39ac56c 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h | |||
@@ -144,6 +144,7 @@ sway_cmd cmd_splitt; | |||
144 | sway_cmd cmd_splitv; | 144 | sway_cmd cmd_splitv; |
145 | sway_cmd cmd_sticky; | 145 | sway_cmd cmd_sticky; |
146 | sway_cmd cmd_swaybg_command; | 146 | sway_cmd cmd_swaybg_command; |
147 | sway_cmd cmd_title_format; | ||
147 | sway_cmd cmd_unmark; | 148 | sway_cmd cmd_unmark; |
148 | sway_cmd cmd_workspace; | 149 | sway_cmd cmd_workspace; |
149 | sway_cmd cmd_ws_auto_back_and_forth; | 150 | sway_cmd cmd_ws_auto_back_and_forth; |
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index b07af72c..61ab7ca1 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h | |||
@@ -63,7 +63,8 @@ struct sway_container { | |||
63 | */ | 63 | */ |
64 | size_t id; | 64 | size_t id; |
65 | 65 | ||
66 | char *name; | 66 | char *name; // The view's title (unformatted) |
67 | char *formatted_title; // The title displayed in the title bar | ||
67 | 68 | ||
68 | enum sway_container_type type; | 69 | enum sway_container_type type; |
69 | enum sway_container_layout layout; | 70 | enum sway_container_layout layout; |
@@ -204,7 +205,6 @@ void container_update_title_textures(struct sway_container *container); | |||
204 | */ | 205 | */ |
205 | void container_calculate_title_height(struct sway_container *container); | 206 | void container_calculate_title_height(struct sway_container *container); |
206 | 207 | ||
207 | void container_update_title(struct sway_container *container, | 208 | void container_notify_child_title_changed(struct sway_container *container); |
208 | const char *new_title); | ||
209 | 209 | ||
210 | #endif | 210 | #endif |
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 21127ab1..9d4256f7 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h | |||
@@ -48,6 +48,7 @@ struct sway_view { | |||
48 | 48 | ||
49 | bool is_fullscreen; | 49 | bool is_fullscreen; |
50 | 50 | ||
51 | char *title_format; | ||
51 | enum sway_container_border border; | 52 | enum sway_container_border border; |
52 | int border_thickness; | 53 | int border_thickness; |
53 | 54 | ||
@@ -164,6 +165,8 @@ const char *view_get_class(struct sway_view *view); | |||
164 | 165 | ||
165 | const char *view_get_instance(struct sway_view *view); | 166 | const char *view_get_instance(struct sway_view *view); |
166 | 167 | ||
168 | const char *view_get_type(struct sway_view *view); | ||
169 | |||
167 | void view_configure(struct sway_view *view, double ox, double oy, int width, | 170 | void view_configure(struct sway_view *view, double ox, double oy, int width, |
168 | int height); | 171 | int height); |
169 | 172 | ||
@@ -207,4 +210,11 @@ void view_child_init(struct sway_view_child *child, | |||
207 | 210 | ||
208 | void view_child_destroy(struct sway_view_child *child); | 211 | void view_child_destroy(struct sway_view_child *child); |
209 | 212 | ||
213 | /** | ||
214 | * Re-read the view's title property and update any relevant title bars. | ||
215 | * The force argument makes it recreate the title bars even if the title hasn't | ||
216 | * changed. | ||
217 | */ | ||
218 | void view_update_title(struct sway_view *view, bool force); | ||
219 | |||
210 | #endif | 220 | #endif |
diff --git a/sway/commands.c b/sway/commands.c index efed7ddf..2e1cdc2c 100644 --- a/sway/commands.c +++ b/sway/commands.c | |||
@@ -182,6 +182,7 @@ static struct cmd_handler command_handlers[] = { | |||
182 | { "splith", cmd_splith }, | 182 | { "splith", cmd_splith }, |
183 | { "splitt", cmd_splitt }, | 183 | { "splitt", cmd_splitt }, |
184 | { "splitv", cmd_splitv }, | 184 | { "splitv", cmd_splitv }, |
185 | { "title_format", cmd_title_format }, | ||
185 | }; | 186 | }; |
186 | 187 | ||
187 | static int handler_compare(const void *_a, const void *_b) { | 188 | static int handler_compare(const void *_a, const void *_b) { |
diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c new file mode 100644 index 00000000..a2f6e6ff --- /dev/null +++ b/sway/commands/title_format.c | |||
@@ -0,0 +1,31 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/config.h" | ||
5 | #include "sway/tree/arrange.h" | ||
6 | #include "sway/tree/view.h" | ||
7 | #include "log.h" | ||
8 | #include "stringop.h" | ||
9 | |||
10 | struct cmd_results *cmd_title_format(int argc, char **argv) { | ||
11 | struct cmd_results *error = NULL; | ||
12 | if ((error = checkarg(argc, "title_format", EXPECTED_AT_LEAST, 1))) { | ||
13 | return error; | ||
14 | } | ||
15 | struct sway_container *container = | ||
16 | config->handler_context.current_container; | ||
17 | if (container->type != C_VIEW) { | ||
18 | return cmd_results_new(CMD_INVALID, "title_format", | ||
19 | "Only views can have a title_format"); | ||
20 | } | ||
21 | struct sway_view *view = container->sway_view; | ||
22 | char *format = join_args(argv, argc); | ||
23 | if (view->title_format) { | ||
24 | free(view->title_format); | ||
25 | } | ||
26 | view->title_format = strdup(format); | ||
27 | view_update_title(view, true); | ||
28 | config_find_font_height(true); | ||
29 | arrange_root(); | ||
30 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
31 | } | ||
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 5880e9a9..fcee8ce9 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c | |||
@@ -176,8 +176,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
176 | // TODO: Let floating views do whatever | 176 | // TODO: Let floating views do whatever |
177 | view_update_size(view, xdg_shell_v6_view->pending_width, | 177 | view_update_size(view, xdg_shell_v6_view->pending_width, |
178 | xdg_shell_v6_view->pending_height); | 178 | xdg_shell_v6_view->pending_height); |
179 | container_update_title(view->swayc, | 179 | view_update_title(view, false); |
180 | view->wlr_xdg_surface_v6->toplevel->title); | ||
181 | view_damage(view, false); | 180 | view_damage(view, false); |
182 | } | 181 | } |
183 | 182 | ||
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index a1e672ce..b4eda71f 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c | |||
@@ -223,7 +223,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
223 | view_update_size(view, xwayland_view->pending_width, | 223 | view_update_size(view, xwayland_view->pending_width, |
224 | xwayland_view->pending_height); | 224 | xwayland_view->pending_height); |
225 | view_damage(view, false); | 225 | view_damage(view, false); |
226 | container_update_title(view->swayc, view->wlr_xwayland_surface->title); | 226 | view_update_title(view, false); |
227 | } | 227 | } |
228 | 228 | ||
229 | static void handle_unmap(struct wl_listener *listener, void *data) { | 229 | static void handle_unmap(struct wl_listener *listener, void *data) { |
diff --git a/sway/meson.build b/sway/meson.build index 7c93b5c4..f70a8e44 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -60,6 +60,7 @@ sway_sources = files( | |||
60 | 'commands/set.c', | 60 | 'commands/set.c', |
61 | 'commands/split.c', | 61 | 'commands/split.c', |
62 | 'commands/swaybg_command.c', | 62 | 'commands/swaybg_command.c', |
63 | 'commands/title_format.c', | ||
63 | 'commands/workspace.c', | 64 | 'commands/workspace.c', |
64 | 'commands/ws_auto_back_and_forth.c', | 65 | 'commands/ws_auto_back_and_forth.c', |
65 | 66 | ||
diff --git a/sway/tree/container.c b/sway/tree/container.c index 9fb47020..e1be32d1 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -348,7 +348,6 @@ struct sway_container *container_view_create(struct sway_container *sibling, | |||
348 | swayc, title, sibling, sibling ? sibling->type : 0, sibling->name); | 348 | swayc, title, sibling, sibling ? sibling->type : 0, sibling->name); |
349 | // Setup values | 349 | // Setup values |
350 | swayc->sway_view = sway_view; | 350 | swayc->sway_view = sway_view; |
351 | container_update_title(swayc, title); | ||
352 | swayc->width = 0; | 351 | swayc->width = 0; |
353 | swayc->height = 0; | 352 | swayc->height = 0; |
354 | 353 | ||
@@ -572,7 +571,7 @@ static void update_title_texture(struct sway_container *con, | |||
572 | if (*texture) { | 571 | if (*texture) { |
573 | wlr_texture_destroy(*texture); | 572 | wlr_texture_destroy(*texture); |
574 | } | 573 | } |
575 | if (!con->name) { | 574 | if (!con->formatted_title) { |
576 | return; | 575 | return; |
577 | } | 576 | } |
578 | 577 | ||
@@ -581,7 +580,8 @@ static void update_title_texture(struct sway_container *con, | |||
581 | int height = config->font_height * scale; | 580 | int height = config->font_height * scale; |
582 | 581 | ||
583 | cairo_t *c = cairo_create(NULL); | 582 | cairo_t *c = cairo_create(NULL); |
584 | get_text_size(c, config->font, &width, NULL, scale, false, "%s", con->name); | 583 | get_text_size(c, config->font, &width, NULL, scale, false, |
584 | "%s", con->formatted_title); | ||
585 | cairo_destroy(c); | 585 | cairo_destroy(c); |
586 | 586 | ||
587 | cairo_surface_t *surface = cairo_image_surface_create( | 587 | cairo_surface_t *surface = cairo_image_surface_create( |
@@ -596,7 +596,7 @@ static void update_title_texture(struct sway_container *con, | |||
596 | class->text[2], class->text[3]); | 596 | class->text[2], class->text[3]); |
597 | cairo_move_to(cairo, 0, 0); | 597 | cairo_move_to(cairo, 0, 0); |
598 | 598 | ||
599 | pango_printf(cairo, config->font, scale, false, "%s", con->name); | 599 | pango_printf(cairo, config->font, scale, false, "%s", con->formatted_title); |
600 | 600 | ||
601 | cairo_surface_flush(surface); | 601 | cairo_surface_flush(surface); |
602 | unsigned char *data = cairo_image_surface_get_data(surface); | 602 | unsigned char *data = cairo_image_surface_get_data(surface); |
@@ -622,57 +622,31 @@ void container_update_title_textures(struct sway_container *container) { | |||
622 | } | 622 | } |
623 | 623 | ||
624 | void container_calculate_title_height(struct sway_container *container) { | 624 | void container_calculate_title_height(struct sway_container *container) { |
625 | if (!container->name) { | 625 | if (!container->formatted_title) { |
626 | container->title_height = 0; | 626 | container->title_height = 0; |
627 | return; | 627 | return; |
628 | } | 628 | } |
629 | cairo_t *cairo = cairo_create(NULL); | 629 | cairo_t *cairo = cairo_create(NULL); |
630 | int height; | 630 | int height; |
631 | get_text_size(cairo, config->font, NULL, &height, 1, false, | 631 | get_text_size(cairo, config->font, NULL, &height, 1, false, |
632 | "%s", container->name); | 632 | "%s", container->formatted_title); |
633 | cairo_destroy(cairo); | 633 | cairo_destroy(cairo); |
634 | container->title_height = height; | 634 | container->title_height = height; |
635 | } | 635 | } |
636 | 636 | ||
637 | static void container_notify_child_title_changed( | 637 | void container_notify_child_title_changed(struct sway_container *container) { |
638 | struct sway_container *container) { | ||
639 | if (!container || container->type != C_CONTAINER) { | 638 | if (!container || container->type != C_CONTAINER) { |
640 | return; | 639 | return; |
641 | } | 640 | } |
642 | if (container->layout != L_TABBED && container->layout != L_STACKED) { | 641 | if (container->layout != L_TABBED && container->layout != L_STACKED) { |
643 | return; | 642 | return; |
644 | } | 643 | } |
645 | if (container->name) { | 644 | if (container->formatted_title) { |
646 | free(container->name); | 645 | free(container->formatted_title); |
647 | } | 646 | } |
648 | // TODO: iterate children and concatenate their titles | 647 | // TODO: iterate children and concatenate their titles |
649 | container->name = strdup(""); | 648 | container->formatted_title = strdup(""); |
650 | container_calculate_title_height(container); | 649 | container_calculate_title_height(container); |
651 | container_update_title_textures(container); | 650 | container_update_title_textures(container); |
652 | container_notify_child_title_changed(container->parent); | 651 | container_notify_child_title_changed(container->parent); |
653 | } | 652 | } |
654 | |||
655 | void container_update_title(struct sway_container *container, | ||
656 | const char *new_title) { | ||
657 | if (!new_title) { | ||
658 | new_title = ""; | ||
659 | } | ||
660 | |||
661 | if (container->name && strcmp(container->name, new_title) == 0) { | ||
662 | return; | ||
663 | } | ||
664 | |||
665 | if (container->name) { | ||
666 | free(container->name); | ||
667 | } | ||
668 | container->name = strdup(new_title); | ||
669 | container_calculate_title_height(container); | ||
670 | container_update_title_textures(container); | ||
671 | container_notify_child_title_changed(container->parent); | ||
672 | |||
673 | size_t prev_max_height = config->font_height; | ||
674 | config_find_font_height(false); | ||
675 | if (config->font_height != prev_max_height) { | ||
676 | arrange_root(); | ||
677 | } | ||
678 | } | ||
diff --git a/sway/tree/view.c b/sway/tree/view.c index 84962306..4a01f096 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -1,3 +1,4 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
1 | #include <stdlib.h> | 2 | #include <stdlib.h> |
2 | #include <wayland-server.h> | 3 | #include <wayland-server.h> |
3 | #include <wlr/render/wlr_renderer.h> | 4 | #include <wlr/render/wlr_renderer.h> |
@@ -67,6 +68,18 @@ const char *view_get_instance(struct sway_view *view) { | |||
67 | return NULL; | 68 | return NULL; |
68 | } | 69 | } |
69 | 70 | ||
71 | const char *view_get_type(struct sway_view *view) { | ||
72 | switch(view->type) { | ||
73 | case SWAY_VIEW_WL_SHELL: | ||
74 | return "wl_shell"; | ||
75 | case SWAY_VIEW_XDG_SHELL_V6: | ||
76 | return "xdg_shell_v6"; | ||
77 | case SWAY_VIEW_XWAYLAND: | ||
78 | return "xwayland"; | ||
79 | } | ||
80 | return "unknown"; | ||
81 | } | ||
82 | |||
70 | void view_configure(struct sway_view *view, double ox, double oy, int width, | 83 | void view_configure(struct sway_view *view, double ox, double oy, int width, |
71 | int height) { | 84 | int height) { |
72 | if (view->impl->configure) { | 85 | if (view->impl->configure) { |
@@ -348,6 +361,11 @@ void view_unmap(struct sway_view *view) { | |||
348 | view->swayc = NULL; | 361 | view->swayc = NULL; |
349 | view->surface = NULL; | 362 | view->surface = NULL; |
350 | 363 | ||
364 | if (view->title_format) { | ||
365 | free(view->title_format); | ||
366 | view->title_format = NULL; | ||
367 | } | ||
368 | |||
351 | if (parent->type == C_OUTPUT) { | 369 | if (parent->type == C_OUTPUT) { |
352 | arrange_output(parent); | 370 | arrange_output(parent); |
353 | } else { | 371 | } else { |
@@ -475,3 +493,127 @@ void view_child_destroy(struct sway_view_child *child) { | |||
475 | free(child); | 493 | free(child); |
476 | } | 494 | } |
477 | } | 495 | } |
496 | |||
497 | static char *parse_title_format(struct sway_view *view) { | ||
498 | if (!view->title_format || strcmp(view->title_format, "%title") == 0) { | ||
499 | return strdup(view_get_title(view)); | ||
500 | } | ||
501 | const char *title = view_get_title(view); | ||
502 | const char *class = view_get_class(view); | ||
503 | const char *instance = view_get_instance(view); | ||
504 | const char *shell = view_get_type(view); | ||
505 | size_t title_len = title ? strlen(title) : 0; | ||
506 | size_t class_len = class ? strlen(class) : 0; | ||
507 | size_t instance_len = instance ? strlen(instance) : 0; | ||
508 | size_t shell_len = shell ? strlen(shell) : 0; | ||
509 | |||
510 | // First, determine the length | ||
511 | size_t len = 0; | ||
512 | char *format = view->title_format; | ||
513 | char *next = strchr(format, '%'); | ||
514 | while (next) { | ||
515 | len += next - format; | ||
516 | format = next; | ||
517 | |||
518 | if (strncmp(next, "%title", 6) == 0) { | ||
519 | len += title_len; | ||
520 | format += 6; | ||
521 | } else if (strncmp(next, "%class", 6) == 0) { | ||
522 | len += class_len; | ||
523 | format += 6; | ||
524 | } else if (strncmp(next, "%instance", 9) == 0) { | ||
525 | len += instance_len; | ||
526 | format += 9; | ||
527 | } else if (strncmp(next, "%shell", 6) == 0) { | ||
528 | len += shell_len; | ||
529 | format += 6; | ||
530 | } else { | ||
531 | ++format; | ||
532 | ++len; | ||
533 | } | ||
534 | next = strchr(format, '%'); | ||
535 | } | ||
536 | len += strlen(format); | ||
537 | |||
538 | char *buffer = calloc(len + 1, 1); | ||
539 | if (!sway_assert(buffer, "Unable to allocate title string")) { | ||
540 | return NULL; | ||
541 | } | ||
542 | |||
543 | // Now build the title | ||
544 | format = view->title_format; | ||
545 | next = strchr(format, '%'); | ||
546 | while (next) { | ||
547 | // Copy everything up to the % | ||
548 | strncat(buffer, format, next - format); | ||
549 | format = next; | ||
550 | |||
551 | if (strncmp(next, "%title", 6) == 0) { | ||
552 | if (title) { | ||
553 | strcat(buffer, title); | ||
554 | } | ||
555 | format += 6; | ||
556 | } else if (strncmp(next, "%class", 6) == 0) { | ||
557 | if (class) { | ||
558 | strcat(buffer, class); | ||
559 | } | ||
560 | format += 6; | ||
561 | } else if (strncmp(next, "%instance", 9) == 0) { | ||
562 | if (instance) { | ||
563 | strcat(buffer, instance); | ||
564 | } | ||
565 | format += 9; | ||
566 | } else if (strncmp(next, "%shell", 6) == 0) { | ||
567 | strcat(buffer, shell); | ||
568 | format += 6; | ||
569 | } else { | ||
570 | strcat(buffer, "%"); | ||
571 | ++format; | ||
572 | } | ||
573 | next = strchr(format, '%'); | ||
574 | } | ||
575 | strcat(buffer, format); | ||
576 | |||
577 | return buffer; | ||
578 | } | ||
579 | |||
580 | void view_update_title(struct sway_view *view, bool force) { | ||
581 | if (!view->swayc) { | ||
582 | return; | ||
583 | } | ||
584 | const char *title = view_get_title(view); | ||
585 | |||
586 | if (!force) { | ||
587 | if (title && view->swayc->name && strcmp(title, view->swayc->name) == 0) { | ||
588 | return; | ||
589 | } | ||
590 | if (!title && !view->swayc->name) { | ||
591 | return; | ||
592 | } | ||
593 | } | ||
594 | |||
595 | if (title) { | ||
596 | if (view->swayc->name) { | ||
597 | free(view->swayc->name); | ||
598 | } | ||
599 | if (view->swayc->formatted_title) { | ||
600 | free(view->swayc->formatted_title); | ||
601 | } | ||
602 | view->swayc->name = strdup(title); | ||
603 | view->swayc->formatted_title = parse_title_format(view); | ||
604 | } else { | ||
605 | free(view->swayc->name); | ||
606 | free(view->swayc->formatted_title); | ||
607 | view->swayc->name = NULL; | ||
608 | view->swayc->formatted_title = NULL; | ||
609 | } | ||
610 | container_calculate_title_height(view->swayc); | ||
611 | container_update_title_textures(view->swayc); | ||
612 | container_notify_child_title_changed(view->swayc->parent); | ||
613 | |||
614 | size_t prev_max_height = config->font_height; | ||
615 | config_find_font_height(false); | ||
616 | if (config->font_height != prev_max_height) { | ||
617 | arrange_root(); | ||
618 | } | ||
619 | } | ||