From 228c478e8d11dd14972b237574146fd0d2d6b96c Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 5 May 2018 12:36:50 +1000 Subject: [PATCH 1/5] Implement title_format This implements the title_format command, with a new placeholder %shell which gets substituted with the view type (xwayland, xdg_shell_v6 or wl_shell). Example config: for_window [title=".*"] title_format %title (class=%class instance=%instance shell=%shell) --- include/sway/commands.h | 1 + include/sway/tree/container.h | 6 +- include/sway/tree/view.h | 10 +++ sway/commands.c | 1 + sway/commands/title_format.c | 31 ++++++++ sway/desktop/xdg_shell_v6.c | 3 +- sway/desktop/xwayland.c | 2 +- sway/meson.build | 1 + sway/tree/container.c | 46 +++-------- sway/tree/view.c | 142 ++++++++++++++++++++++++++++++++++ 10 files changed, 201 insertions(+), 42 deletions(-) create mode 100644 sway/commands/title_format.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 0732a90a4..d39ac56ca 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -144,6 +144,7 @@ sway_cmd cmd_splitt; sway_cmd cmd_splitv; sway_cmd cmd_sticky; sway_cmd cmd_swaybg_command; +sway_cmd cmd_title_format; sway_cmd cmd_unmark; sway_cmd cmd_workspace; sway_cmd cmd_ws_auto_back_and_forth; diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index b07af72cc..61ab7ca15 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -63,7 +63,8 @@ struct sway_container { */ size_t id; - char *name; + char *name; // The view's title (unformatted) + char *formatted_title; // The title displayed in the title bar enum sway_container_type type; enum sway_container_layout layout; @@ -204,7 +205,6 @@ void container_update_title_textures(struct sway_container *container); */ void container_calculate_title_height(struct sway_container *container); -void container_update_title(struct sway_container *container, - const char *new_title); +void container_notify_child_title_changed(struct sway_container *container); #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 21127ab19..9d4256f7b 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -48,6 +48,7 @@ struct sway_view { bool is_fullscreen; + char *title_format; enum sway_container_border border; int border_thickness; @@ -164,6 +165,8 @@ const char *view_get_class(struct sway_view *view); const char *view_get_instance(struct sway_view *view); +const char *view_get_type(struct sway_view *view); + void view_configure(struct sway_view *view, double ox, double oy, int width, int height); @@ -207,4 +210,11 @@ void view_child_init(struct sway_view_child *child, void view_child_destroy(struct sway_view_child *child); +/** + * Re-read the view's title property and update any relevant title bars. + * The force argument makes it recreate the title bars even if the title hasn't + * changed. + */ +void view_update_title(struct sway_view *view, bool force); + #endif diff --git a/sway/commands.c b/sway/commands.c index efed7ddf8..2e1cdc2ce 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -182,6 +182,7 @@ static struct cmd_handler command_handlers[] = { { "splith", cmd_splith }, { "splitt", cmd_splitt }, { "splitv", cmd_splitv }, + { "title_format", cmd_title_format }, }; 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 000000000..a2f6e6ff2 --- /dev/null +++ b/sway/commands/title_format.c @@ -0,0 +1,31 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/tree/arrange.h" +#include "sway/tree/view.h" +#include "log.h" +#include "stringop.h" + +struct cmd_results *cmd_title_format(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "title_format", EXPECTED_AT_LEAST, 1))) { + return error; + } + struct sway_container *container = + config->handler_context.current_container; + if (container->type != C_VIEW) { + return cmd_results_new(CMD_INVALID, "title_format", + "Only views can have a title_format"); + } + struct sway_view *view = container->sway_view; + char *format = join_args(argv, argc); + if (view->title_format) { + free(view->title_format); + } + view->title_format = strdup(format); + view_update_title(view, true); + config_find_font_height(true); + arrange_root(); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 5880e9a9f..fcee8ce94 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) { // TODO: Let floating views do whatever view_update_size(view, xdg_shell_v6_view->pending_width, xdg_shell_v6_view->pending_height); - container_update_title(view->swayc, - view->wlr_xdg_surface_v6->toplevel->title); + view_update_title(view, false); view_damage(view, false); } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index a1e672ce8..b4eda71fc 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) { view_update_size(view, xwayland_view->pending_width, xwayland_view->pending_height); view_damage(view, false); - container_update_title(view->swayc, view->wlr_xwayland_surface->title); + view_update_title(view, false); } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/meson.build b/sway/meson.build index 7c93b5c45..f70a8e445 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -60,6 +60,7 @@ sway_sources = files( 'commands/set.c', 'commands/split.c', 'commands/swaybg_command.c', + 'commands/title_format.c', 'commands/workspace.c', 'commands/ws_auto_back_and_forth.c', diff --git a/sway/tree/container.c b/sway/tree/container.c index 9fb47020e..e1be32d15 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, swayc, title, sibling, sibling ? sibling->type : 0, sibling->name); // Setup values swayc->sway_view = sway_view; - container_update_title(swayc, title); swayc->width = 0; swayc->height = 0; @@ -572,7 +571,7 @@ static void update_title_texture(struct sway_container *con, if (*texture) { wlr_texture_destroy(*texture); } - if (!con->name) { + if (!con->formatted_title) { return; } @@ -581,7 +580,8 @@ static void update_title_texture(struct sway_container *con, int height = config->font_height * scale; cairo_t *c = cairo_create(NULL); - get_text_size(c, config->font, &width, NULL, scale, false, "%s", con->name); + get_text_size(c, config->font, &width, NULL, scale, false, + "%s", con->formatted_title); cairo_destroy(c); cairo_surface_t *surface = cairo_image_surface_create( @@ -596,7 +596,7 @@ static void update_title_texture(struct sway_container *con, class->text[2], class->text[3]); cairo_move_to(cairo, 0, 0); - pango_printf(cairo, config->font, scale, false, "%s", con->name); + pango_printf(cairo, config->font, scale, false, "%s", con->formatted_title); cairo_surface_flush(surface); unsigned char *data = cairo_image_surface_get_data(surface); @@ -622,57 +622,31 @@ void container_update_title_textures(struct sway_container *container) { } void container_calculate_title_height(struct sway_container *container) { - if (!container->name) { + if (!container->formatted_title) { container->title_height = 0; return; } cairo_t *cairo = cairo_create(NULL); int height; get_text_size(cairo, config->font, NULL, &height, 1, false, - "%s", container->name); + "%s", container->formatted_title); cairo_destroy(cairo); container->title_height = height; } -static void container_notify_child_title_changed( - struct sway_container *container) { +void container_notify_child_title_changed(struct sway_container *container) { if (!container || container->type != C_CONTAINER) { return; } if (container->layout != L_TABBED && container->layout != L_STACKED) { return; } - if (container->name) { - free(container->name); + if (container->formatted_title) { + free(container->formatted_title); } // TODO: iterate children and concatenate their titles - container->name = strdup(""); + container->formatted_title = strdup(""); container_calculate_title_height(container); container_update_title_textures(container); container_notify_child_title_changed(container->parent); } - -void container_update_title(struct sway_container *container, - const char *new_title) { - if (!new_title) { - new_title = ""; - } - - if (container->name && strcmp(container->name, new_title) == 0) { - return; - } - - if (container->name) { - free(container->name); - } - container->name = strdup(new_title); - container_calculate_title_height(container); - container_update_title_textures(container); - container_notify_child_title_changed(container->parent); - - size_t prev_max_height = config->font_height; - config_find_font_height(false); - if (config->font_height != prev_max_height) { - arrange_root(); - } -} diff --git a/sway/tree/view.c b/sway/tree/view.c index 84962306c..4a01f096a 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include @@ -67,6 +68,18 @@ const char *view_get_instance(struct sway_view *view) { return NULL; } +const char *view_get_type(struct sway_view *view) { + switch(view->type) { + case SWAY_VIEW_WL_SHELL: + return "wl_shell"; + case SWAY_VIEW_XDG_SHELL_V6: + return "xdg_shell_v6"; + case SWAY_VIEW_XWAYLAND: + return "xwayland"; + } + return "unknown"; +} + void view_configure(struct sway_view *view, double ox, double oy, int width, int height) { if (view->impl->configure) { @@ -348,6 +361,11 @@ void view_unmap(struct sway_view *view) { view->swayc = NULL; view->surface = NULL; + if (view->title_format) { + free(view->title_format); + view->title_format = NULL; + } + if (parent->type == C_OUTPUT) { arrange_output(parent); } else { @@ -475,3 +493,127 @@ void view_child_destroy(struct sway_view_child *child) { free(child); } } + +static char *parse_title_format(struct sway_view *view) { + if (!view->title_format || strcmp(view->title_format, "%title") == 0) { + return strdup(view_get_title(view)); + } + const char *title = view_get_title(view); + const char *class = view_get_class(view); + const char *instance = view_get_instance(view); + const char *shell = view_get_type(view); + size_t title_len = title ? strlen(title) : 0; + size_t class_len = class ? strlen(class) : 0; + size_t instance_len = instance ? strlen(instance) : 0; + size_t shell_len = shell ? strlen(shell) : 0; + + // First, determine the length + size_t len = 0; + char *format = view->title_format; + char *next = strchr(format, '%'); + while (next) { + len += next - format; + format = next; + + if (strncmp(next, "%title", 6) == 0) { + len += title_len; + format += 6; + } else if (strncmp(next, "%class", 6) == 0) { + len += class_len; + format += 6; + } else if (strncmp(next, "%instance", 9) == 0) { + len += instance_len; + format += 9; + } else if (strncmp(next, "%shell", 6) == 0) { + len += shell_len; + format += 6; + } else { + ++format; + ++len; + } + next = strchr(format, '%'); + } + len += strlen(format); + + char *buffer = calloc(len + 1, 1); + if (!sway_assert(buffer, "Unable to allocate title string")) { + return NULL; + } + + // Now build the title + format = view->title_format; + next = strchr(format, '%'); + while (next) { + // Copy everything up to the % + strncat(buffer, format, next - format); + format = next; + + if (strncmp(next, "%title", 6) == 0) { + if (title) { + strcat(buffer, title); + } + format += 6; + } else if (strncmp(next, "%class", 6) == 0) { + if (class) { + strcat(buffer, class); + } + format += 6; + } else if (strncmp(next, "%instance", 9) == 0) { + if (instance) { + strcat(buffer, instance); + } + format += 9; + } else if (strncmp(next, "%shell", 6) == 0) { + strcat(buffer, shell); + format += 6; + } else { + strcat(buffer, "%"); + ++format; + } + next = strchr(format, '%'); + } + strcat(buffer, format); + + return buffer; +} + +void view_update_title(struct sway_view *view, bool force) { + if (!view->swayc) { + return; + } + const char *title = view_get_title(view); + + if (!force) { + if (title && view->swayc->name && strcmp(title, view->swayc->name) == 0) { + return; + } + if (!title && !view->swayc->name) { + return; + } + } + + if (title) { + if (view->swayc->name) { + free(view->swayc->name); + } + if (view->swayc->formatted_title) { + free(view->swayc->formatted_title); + } + view->swayc->name = strdup(title); + view->swayc->formatted_title = parse_title_format(view); + } else { + free(view->swayc->name); + free(view->swayc->formatted_title); + view->swayc->name = NULL; + view->swayc->formatted_title = NULL; + } + container_calculate_title_height(view->swayc); + container_update_title_textures(view->swayc); + container_notify_child_title_changed(view->swayc->parent); + + size_t prev_max_height = config->font_height; + config_find_font_height(false); + if (config->font_height != prev_max_height) { + arrange_root(); + } +} From d10a0a8c4156fef6ac64362bf046411f57a3ed5f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 5 May 2018 16:48:10 +1000 Subject: [PATCH 2/5] Fix memory leak in title_format command --- sway/commands/title_format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c index a2f6e6ff2..26ddb2f4d 100644 --- a/sway/commands/title_format.c +++ b/sway/commands/title_format.c @@ -23,7 +23,7 @@ struct cmd_results *cmd_title_format(int argc, char **argv) { if (view->title_format) { free(view->title_format); } - view->title_format = strdup(format); + view->title_format = format; view_update_title(view, true); config_find_font_height(true); arrange_root(); From da7d6642d3d970da5579066f26d83fbd0873c6f4 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 5 May 2018 18:18:54 +1000 Subject: [PATCH 3/5] Refactor parse_title_format() --- sway/tree/view.c | 93 ++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 51 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index 4a01f096a..386144f60 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -494,9 +494,17 @@ void view_child_destroy(struct sway_view_child *child) { } } -static char *parse_title_format(struct sway_view *view) { +/** + * Calculate and return the length of the formatted title. + * If buffer is not NULL, also populate the buffer with the formatted title. + */ +static size_t parse_title_format(struct sway_view *view, char *buffer) { if (!view->title_format || strcmp(view->title_format, "%title") == 0) { - return strdup(view_get_title(view)); + const char *title = view_get_title(view); + if (buffer) { + strcpy(buffer, title); + } + return strlen(title); } const char *title = view_get_title(view); const char *class = view_get_class(view); @@ -507,74 +515,56 @@ static char *parse_title_format(struct sway_view *view) { size_t instance_len = instance ? strlen(instance) : 0; size_t shell_len = shell ? strlen(shell) : 0; - // First, determine the length size_t len = 0; char *format = view->title_format; char *next = strchr(format, '%'); while (next) { + if (buffer) { + // Copy everything up to the % + strncat(buffer, format, next - format); + } len += next - format; format = next; if (strncmp(next, "%title", 6) == 0) { + if (buffer && title) { + strcat(buffer, title); + } len += title_len; format += 6; } else if (strncmp(next, "%class", 6) == 0) { + if (buffer && class) { + strcat(buffer, class); + } len += class_len; format += 6; } else if (strncmp(next, "%instance", 9) == 0) { + if (buffer && instance) { + strcat(buffer, instance); + } len += instance_len; format += 9; } else if (strncmp(next, "%shell", 6) == 0) { + if (buffer) { + strcat(buffer, shell); + } len += shell_len; format += 6; } else { + if (buffer) { + strcat(buffer, "%"); + } ++format; ++len; } next = strchr(format, '%'); } + if (buffer) { + strcat(buffer, format); + } len += strlen(format); - char *buffer = calloc(len + 1, 1); - if (!sway_assert(buffer, "Unable to allocate title string")) { - return NULL; - } - - // Now build the title - format = view->title_format; - next = strchr(format, '%'); - while (next) { - // Copy everything up to the % - strncat(buffer, format, next - format); - format = next; - - if (strncmp(next, "%title", 6) == 0) { - if (title) { - strcat(buffer, title); - } - format += 6; - } else if (strncmp(next, "%class", 6) == 0) { - if (class) { - strcat(buffer, class); - } - format += 6; - } else if (strncmp(next, "%instance", 9) == 0) { - if (instance) { - strcat(buffer, instance); - } - format += 9; - } else if (strncmp(next, "%shell", 6) == 0) { - strcat(buffer, shell); - format += 6; - } else { - strcat(buffer, "%"); - ++format; - } - next = strchr(format, '%'); - } - strcat(buffer, format); - - return buffer; + return len; } void view_update_title(struct sway_view *view, bool force) { @@ -592,18 +582,19 @@ void view_update_title(struct sway_view *view, bool force) { } } + free(view->swayc->name); + free(view->swayc->formatted_title); if (title) { - if (view->swayc->name) { - free(view->swayc->name); - } - if (view->swayc->formatted_title) { - free(view->swayc->formatted_title); + size_t len = parse_title_format(view, NULL); + char *buffer = calloc(len + 1, 1); + if (!sway_assert(buffer, "Unable to allocate title string")) { + return; } + parse_title_format(view, buffer); + view->swayc->name = strdup(title); - view->swayc->formatted_title = parse_title_format(view); + view->swayc->formatted_title = buffer; } else { - free(view->swayc->name); - free(view->swayc->formatted_title); view->swayc->name = NULL; view->swayc->formatted_title = NULL; } From 5d6d24e71aabc026c99ac736c788fa8103658c42 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 5 May 2018 18:25:31 +1000 Subject: [PATCH 4/5] Move code for re-arranging after font height change into a common place --- include/sway/config.h | 5 ++++- sway/commands/font.c | 6 +----- sway/commands/title_format.c | 4 +--- sway/config.c | 9 ++++++++- sway/tree/view.c | 7 +------ 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index db942c3f9..fa40ef4e4 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -465,8 +465,11 @@ void free_bar_config(struct bar_config *bar); * Updates the value of config->font_height based on the max title height * reported by each container. If recalculate is true, the containers will * recalculate their heights before reporting. + * + * If the height has changed, all containers will be rearranged to take on the + * new size. */ -void config_find_font_height(bool recalculate); +void config_update_font_height(bool recalculate); /* Global config singleton. */ extern struct sway_config *config; diff --git a/sway/commands/font.c b/sway/commands/font.c index 38ad88809..8e0b51e39 100644 --- a/sway/commands/font.c +++ b/sway/commands/font.c @@ -2,7 +2,6 @@ #include #include "sway/commands.h" #include "sway/config.h" -#include "sway/tree/arrange.h" #include "log.h" #include "stringop.h" @@ -14,9 +13,6 @@ struct cmd_results *cmd_font(int argc, char **argv) { char *font = join_args(argv, argc); free(config->font); config->font = strdup(font); - config_find_font_height(true); - if (!config->reading) { - arrange_root(); - } + config_update_font_height(true); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c index 26ddb2f4d..3d1c578c6 100644 --- a/sway/commands/title_format.c +++ b/sway/commands/title_format.c @@ -2,7 +2,6 @@ #include #include "sway/commands.h" #include "sway/config.h" -#include "sway/tree/arrange.h" #include "sway/tree/view.h" #include "log.h" #include "stringop.h" @@ -25,7 +24,6 @@ struct cmd_results *cmd_title_format(int argc, char **argv) { } view->title_format = format; view_update_title(view, true); - config_find_font_height(true); - arrange_root(); + config_update_font_height(true); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/config.c b/sway/config.c index 2e5eab2bc..a14f4ec62 100644 --- a/sway/config.c +++ b/sway/config.c @@ -24,6 +24,7 @@ #include "sway/input/seat.h" #include "sway/commands.h" #include "sway/config.h" +#include "sway/tree/arrange.h" #include "sway/tree/layout.h" #include "cairo.h" #include "pango.h" @@ -741,8 +742,14 @@ static void find_font_height_iterator(struct sway_container *container, } } -void config_find_font_height(bool recalculate) { +void config_update_font_height(bool recalculate) { + size_t prev_max_height = config->font_height; config->font_height = 0; + container_for_each_descendant_dfs(&root_container, find_font_height_iterator, &recalculate); + + if (config->font_height != prev_max_height) { + arrange_root(); + } } diff --git a/sway/tree/view.c b/sway/tree/view.c index 386144f60..851348d85 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -601,10 +601,5 @@ void view_update_title(struct sway_view *view, bool force) { container_calculate_title_height(view->swayc); container_update_title_textures(view->swayc); container_notify_child_title_changed(view->swayc->parent); - - size_t prev_max_height = config->font_height; - config_find_font_height(false); - if (config->font_height != prev_max_height) { - arrange_root(); - } + config_update_font_height(false); } From 936226845fc08ee64b6914b233d901ae92994206 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 5 May 2018 18:28:35 +1000 Subject: [PATCH 5/5] Add extra check for view title being NULL --- sway/tree/view.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index 851348d85..fe944466c 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -501,10 +501,10 @@ void view_child_destroy(struct sway_view_child *child) { static size_t parse_title_format(struct sway_view *view, char *buffer) { if (!view->title_format || strcmp(view->title_format, "%title") == 0) { const char *title = view_get_title(view); - if (buffer) { + if (buffer && title) { strcpy(buffer, title); } - return strlen(title); + return title ? strlen(title) : 0; } const char *title = view_get_title(view); const char *class = view_get_class(view);