mirror of
				https://github.com/swaywm/sway.git
				synced 2025-11-03 09:01:43 -05:00 
			
		
		
		
	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)
			
			
This commit is contained in:
		
							parent
							
								
									8dae168b77
								
							
						
					
					
						commit
						228c478e8d
					
				
					 10 changed files with 201 additions and 42 deletions
				
			
		| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										31
									
								
								sway/commands/title_format.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								sway/commands/title_format.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
#define _POSIX_C_SOURCE 200809L
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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',
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										142
									
								
								sway/tree/view.c
									
										
									
									
									
								
							
							
						
						
									
										142
									
								
								sway/tree/view.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
#define _POSIX_C_SOURCE 200809L
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <wayland-server.h>
 | 
			
		||||
#include <wlr/render/wlr_renderer.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -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();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue