From 8a019a83c9186ed4f8496528b402dde6ab57fd99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20C=C3=B4rte-Real?= Date: Sat, 17 Aug 2019 19:38:14 +0100 Subject: [PATCH] Optionally add focused window title to the bar Add a "window_title yes|no" bar command that displays the title of the focused window on the bar. --- include/sway/commands.h | 1 + include/sway/config.h | 1 + include/swaybar/config.h | 3 ++ sway/commands/bar.c | 1 + sway/commands/bar/window_title.c | 22 +++++++++++++ sway/ipc-json.c | 2 ++ sway/meson.build | 1 + sway/sway-bar.5.scd | 3 ++ swaybar/ipc.c | 54 +++++++++++++++++++++++++++++++- swaybar/render.c | 44 ++++++++++++++++++++++++++ 10 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 sway/commands/bar/window_title.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 7ff3b651e..f96bdf362 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -221,6 +221,7 @@ sway_cmd bar_cmd_tray_padding; sway_cmd bar_cmd_unbindcode; sway_cmd bar_cmd_unbindsym; sway_cmd bar_cmd_wrap_scroll; +sway_cmd bar_cmd_window_title; sway_cmd bar_cmd_workspace_buttons; sway_cmd bar_colors_cmd_active_workspace; diff --git a/include/sway/config.h b/include/sway/config.h index c65d93533..a64c4c083 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -271,6 +271,7 @@ struct bar_config { bool strip_workspace_name; bool binding_mode_indicator; bool verbose; + bool window_title; struct side_gaps gaps; int status_padding; int status_edge_padding; diff --git a/include/swaybar/config.h b/include/swaybar/config.h index ec042e514..a994438b8 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -34,10 +34,12 @@ struct swaybar_config { char *mode; char *hidden_state; char *modifier; + char *title; bool strip_workspace_numbers; bool strip_workspace_name; bool binding_mode_indicator; bool wrap_scroll; + bool window_title; bool workspace_buttons; list_t *bindings; struct wl_list outputs; // config_output::link @@ -51,6 +53,7 @@ struct swaybar_config { int bottom; int left; } gaps; + uint64_t window_app_id; struct { uint32_t background; diff --git a/sway/commands/bar.c b/sway/commands/bar.c index 88580ffb0..c9cb5f22c 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c @@ -34,6 +34,7 @@ static struct cmd_handler bar_handlers[] = { { "tray_padding", bar_cmd_tray_padding }, { "unbindcode", bar_cmd_unbindcode }, { "unbindsym", bar_cmd_unbindsym }, + { "window_title", bar_cmd_window_title }, { "workspace_buttons", bar_cmd_workspace_buttons }, { "wrap_scroll", bar_cmd_wrap_scroll }, }; diff --git a/sway/commands/bar/window_title.c b/sway/commands/bar/window_title.c new file mode 100644 index 000000000..a47945f6f --- /dev/null +++ b/sway/commands/bar/window_title.c @@ -0,0 +1,22 @@ +#include +#include +#include "sway/commands.h" +#include "log.h" +#include "util.h" + +struct cmd_results *bar_cmd_window_title(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "window_title", EXPECTED_EQUAL_TO, 1))) { + return error; + } + config->current_bar->window_title = + parse_boolean(argv[0], config->current_bar->window_title); + if (config->current_bar->window_title) { + sway_log(SWAY_DEBUG, "Enabling window title on bar: %s", + config->current_bar->id); + } else { + sway_log(SWAY_DEBUG, "Disabling window title on bar: %s", + config->current_bar->id); + } + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 87aef47b2..e1e8310ae 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -958,6 +958,8 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) { json_object_new_int(bar->status_edge_padding)); json_object_object_add(json, "wrap_scroll", json_object_new_boolean(bar->wrap_scroll)); + json_object_object_add(json, "window_title", + json_object_new_boolean(bar->window_title)); json_object_object_add(json, "workspace_buttons", json_object_new_boolean(bar->workspace_buttons)); json_object_object_add(json, "strip_workspace_numbers", diff --git a/sway/meson.build b/sway/meson.build index 157e39961..af3aa2861 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -137,6 +137,7 @@ sway_sources = files( 'commands/bar/tray_bind.c', 'commands/bar/tray_output.c', 'commands/bar/tray_padding.c', + 'commands/bar/window_title.c', 'commands/bar/workspace_buttons.c', 'commands/bar/wrap_scroll.c', diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index 42fb29898..f530035b4 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd @@ -48,6 +48,9 @@ Sway allows configuring swaybar in the sway configuration file. Enables or disables wrapping when scrolling through workspaces with the scroll wheel. Default is _no_. +*window_title* yes|no + Enables or disables focused window title on the bar. Default is _no_. + *workspace_buttons* yes|no Enables or disables workspace buttons on the bar. Default is _yes_. diff --git a/swaybar/ipc.c b/swaybar/ipc.c index a096f01a2..2ad4ebe84 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -173,6 +173,7 @@ static bool ipc_parse_config( json_object_put(bar_config); return false; } + json_object *window_title; json_object *markup, *mode, *hidden_state, *position, *status_command; json_object *font, *gaps, *bar_height, *wrap_scroll, *workspace_buttons; json_object *strip_workspace_numbers, *strip_workspace_name; @@ -186,6 +187,7 @@ static bool ipc_parse_config( json_object_object_get_ex(bar_config, "gaps", &gaps); json_object_object_get_ex(bar_config, "bar_height", &bar_height); json_object_object_get_ex(bar_config, "wrap_scroll", &wrap_scroll); + json_object_object_get_ex(bar_config, "window_title", &window_title); json_object_object_get_ex(bar_config, "workspace_buttons", &workspace_buttons); json_object_object_get_ex(bar_config, "strip_workspace_numbers", &strip_workspace_numbers); json_object_object_get_ex(bar_config, "strip_workspace_name", &strip_workspace_name); @@ -226,6 +228,9 @@ static bool ipc_parse_config( if (wrap_scroll) { config->wrap_scroll = json_object_get_boolean(wrap_scroll); } + if (window_title) { + config->window_title = json_object_get_boolean(window_title); + } if (workspace_buttons) { config->workspace_buttons = json_object_get_boolean(workspace_buttons); } @@ -480,7 +485,8 @@ bool ipc_initialize(struct swaybar *bar) { struct swaybar_config *config = bar->config; char subscribe[128]; // suitably large buffer len = snprintf(subscribe, 128, - "[ \"barconfig_update\" , \"bar_state_update\" %s %s ]", + "[ \"barconfig_update\" , \"bar_state_update\" %s %s %s ]", + config->window_title ? ", \"window\"" : "", config->binding_mode_indicator ? ", \"mode\"" : "", config->workspace_buttons ? ", \"workspace\"" : ""); free(ipc_single_command(bar->ipc_event_socketfd, @@ -509,6 +515,49 @@ static bool handle_bar_state_update(struct swaybar *bar, json_object *event) { return determine_bar_visibility(bar, false); } +static bool handle_title_update(struct swaybar *bar, + json_object *json_window_event) { + json_object *json_container; + json_object_object_get_ex(json_window_event, "container", + &json_container); + struct swaybar_config *config = bar->config; + + json_object *json_change; + json_object_object_get_ex(json_window_event, "change", &json_change); + const char *change = json_object_get_string(json_change); + if (strcmp(change, "close") == 0) { + json_object *json_id; + json_object_object_get_ex(json_container, "id", &json_id); + uint64_t app_id = json_object_get_int64(json_id); + if (config->window_app_id == app_id) { + // The focused window was closed so remove the title + config->window_app_id = 0; + if (config->title) { + free(config->title); + } + config->title = NULL; + } + return true; + } + + json_object *json_focused; + json_object_object_get_ex(json_container, "focused", &json_focused); + if (json_object_get_boolean(json_focused)) { + json_object *json_name; + json_object_object_get_ex(json_container, "name", &json_name); + if (config->title) { + free(config->title); + } + config->title = strdup(json_object_get_string(json_name)); + json_object *json_id; + json_object_object_get_ex(json_container, "id", &json_id); + config->window_app_id = json_object_get_int64(json_id); + return true; + } + + return false; +} + static bool handle_barconfig_update(struct swaybar *bar, json_object *json_config) { json_object *json_id; @@ -576,6 +625,9 @@ bool handle_ipc_readable(struct swaybar *bar) { bool bar_is_dirty = true; switch (resp->type) { + case IPC_EVENT_WINDOW: + bar_is_dirty = handle_title_update(bar, result); + break; case IPC_EVENT_WORKSPACE: bar_is_dirty = ipc_get_workspaces(bar); break; diff --git a/swaybar/render.c b/swaybar/render.c index 36df2d864..89f139b0b 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -536,6 +536,45 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo, return output->height; } +static uint32_t render_window_title(cairo_t *cairo, + struct swaybar_output *output, double x) { + const char *title = output->bar->config->title; + if (!title) { + return 0; + } + + struct swaybar_config *config = output->bar->config; + int text_width, text_height; + get_text_size(cairo, config->font, &text_width, &text_height, NULL, + output->scale, output->bar->mode_pango_markup, + "%s", title); + + int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; + int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; + int border_width = BORDER_WIDTH * output->scale; + + uint32_t ideal_height = text_height + ws_vertical_padding * 2 + + border_width * 2; + uint32_t ideal_surface_height = ideal_height / output->scale; + if (!output->bar->config->height && + output->height < ideal_surface_height) { + return ideal_surface_height; + } + uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; + + uint32_t height = output->height * output->scale; + cairo_set_source_u32(cairo, config->colors.background); + cairo_rectangle(cairo, x, 0, width, height); + cairo_fill(cairo); + + double text_y = height / 2.0 - text_height / 2.0; + cairo_set_source_u32(cairo, config->colors.statusline); + cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); + pango_printf(cairo, config->font, output->scale, + output->bar->mode_pango_markup, "%s", title); + return output->height; +} + static enum hotspot_event_handling workspace_hotspot_callback( struct swaybar_output *output, struct swaybar_hotspot *hotspot, int x, int y, uint32_t button, void *data) { @@ -660,6 +699,11 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) { max_height = h > max_height ? h : max_height; } + if (config->window_title) { + uint32_t h = render_window_title(cairo, output, x); + max_height = h > max_height ? h : max_height; + } + return max_height > output->height ? max_height : output->height; }