From 3fd61e5925debc4978dec48212955e985a6f2255 Mon Sep 17 00:00:00 2001 From: Michael Weiser Date: Sat, 21 Mar 2020 23:57:34 +0100 Subject: [PATCH] config: Add per-container colors Per-container color schemes allow a number of use-cases to be implemented, particularly giving visual feedback to the user based on specific properties of a view. This change adds that functionality in a minimalistic manner: We extend the client. commands to accept container context, i.e. work with criteria. sway_container is extended to carry its own local border color classes configuration. container_get_current_colors() is extended to check for custom configuration of the container or its parents. Otherwise the main config values are used as before. To ease detection whether a per-container configuration exists for a particular color class, the configuration structure is switched to pointers referencing dynamically allocated buffers. To allow consistent programmatic access to the various color classes, the the configuration structure is switched to an array indexed using an enum. Command client. is extended with subcommands "default" to remove per-container configuration and "global" to explicitly address the global configuration. Test plan: client.focused #000000 #000000 #ffffff #000000 #000000 for_window [app_id="foo"] client.focused #000000 #900000 #ffffff #000000 #000000 Start app foo and notice that the title bar is red when focused. Signed-off-by: Michael Weiser --- include/sway/config.h | 17 +---- include/sway/tree/container.h | 23 +++++++ sway/commands/client.c | 108 ++++++++++++++++++++++++++++---- sway/config.c | 78 ++++++++++++++--------- sway/input/seatop_move_tiling.c | 4 +- sway/sway.5.scd | 6 +- sway/tree/container.c | 34 ++++++++-- 7 files changed, 207 insertions(+), 63 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 87d3fb42e4..cffb1f8d45 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -415,14 +415,6 @@ struct tray_binding { }; #endif -struct border_colors { - float border[4]; - float background[4]; - float text[4]; - float indicator[4]; - float child_border[4]; -}; - enum edge_border_types { E_NONE, /**< Don't hide edge borders */ E_VERTICAL, /**< hide vertical edge borders */ @@ -558,14 +550,7 @@ struct sway_config { bool hide_lone_tab; // border colors - struct { - struct border_colors focused; - struct border_colors focused_inactive; - struct border_colors focused_tab_title; - struct border_colors unfocused; - struct border_colors urgent; - struct border_colors placeholder; - } border_colors; + struct border_colors *border_colors[border_color_class_count]; bool has_focused_tab_title; diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 93f6bfbb12..3612afdf53 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -10,6 +10,24 @@ struct sway_view; struct sway_seat; +struct border_colors { + float border[4]; + float background[4]; + float text[4]; + float indicator[4]; + float child_border[4]; +}; + +enum border_color_class { + border_color_class_focused = 0, + border_color_class_focused_inactive, + border_color_class_focused_tab_title, + border_color_class_unfocused, + border_color_class_urgent, + border_color_class_placeholder, + border_color_class_count +}; + enum sway_container_layout { L_NONE, L_HORIZ, @@ -120,6 +138,8 @@ struct sway_container { // border which we use to restore when the view returns to SSD. enum sway_container_border saved_border; + struct border_colors *border_colors[border_color_class_count]; + // The share of the space of parent container this container occupies double width_fraction; double height_fraction; @@ -363,4 +383,7 @@ void container_update(struct sway_container *con); void container_update_itself_and_parents(struct sway_container *con); +struct border_colors *container_get_window_colors( + struct sway_container *con, enum border_color_class); + #endif diff --git a/sway/commands/client.c b/sway/commands/client.c index fd2ac7a87c..6641fd9e17 100644 --- a/sway/commands/client.c +++ b/sway/commands/client.c @@ -9,7 +9,7 @@ static void container_update_iterator(struct sway_container *con, void *data) { container_update(con); } -static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, +static struct cmd_results *handle_colors(int argc, char **argv, char *cmd_name, struct border_colors *class, const char *default_indicator) { struct cmd_results *error = NULL; if ((error = checkarg(argc, cmd_name, EXPECTED_AT_LEAST, 3)) || @@ -48,32 +48,118 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, } memcpy(class, &colors, sizeof(struct border_colors)); + return error; +} + +static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, + enum border_color_class class, const char *default_indicator) { + struct cmd_results *error = NULL; + if ((error = handle_colors(argc, argv, cmd_name, config->border_colors[class], + default_indicator))) { + return error; + } if (config->active) { root_for_each_container(container_update_iterator, NULL); } - return cmd_results_new(CMD_SUCCESS, NULL); + return error; +} + +static struct cmd_results *handle_container_command( + int argc, char **argv, char *cmd_name, + struct sway_container *con, + enum border_color_class class, + const char *default_indicator) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, cmd_name, EXPECTED_AT_LEAST, 1))) { + return error; + } + + if (strcmp(argv[0], "default") == 0) { + if ((error = checkarg(argc, cmd_name, EXPECTED_AT_MOST, 1))) { + return error; + } + + /* free the per-container class configuration */ + if (con->border_colors[class]) { + free(con->border_colors[class]); + con->border_colors[class] = NULL; + } + } else { + /* avoid needless reallocation to prevent heap fragmentation */ + struct border_colors *newclass = con->border_colors[class]; + if (!con->border_colors[class]) { + newclass = calloc(1, sizeof(*newclass)); + if (!newclass) { + return cmd_results_new(CMD_INVALID, "Unable " + "to allocate color configuration"); + } + } + + if ((error = handle_colors(argc, argv, cmd_name, + newclass, default_indicator))) { + free(newclass); + return error; + } + + if (!con->border_colors[class]) { + con->border_colors[class] = newclass; + } + } + + if (config->active) { + root_for_each_container(container_update_iterator, NULL); + } + + return error; +} + +static struct cmd_results *handle_command_context(int argc, char **argv, char *cmd_name, + enum border_color_class class, + struct sway_container *con, + const char *default_indicator) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, cmd_name, EXPECTED_AT_LEAST, 1))) { + return error; + } + + if (strcmp(argv[0], "global") == 0) { + error = handle_command(argc - 1, argv + 1, cmd_name, + class, default_indicator); + } else if (con) { + error = handle_container_command(argc, argv, cmd_name, + con, class, default_indicator); + } else { + error = handle_command(argc, argv, cmd_name, + class, default_indicator); + } + + return error ? error : cmd_results_new(CMD_SUCCESS, NULL); } struct cmd_results *cmd_client_focused(int argc, char **argv) { - return handle_command(argc, argv, "client.focused", - &config->border_colors.focused, "#2e9ef4ff"); + struct sway_container *con = config->handler_context.container; + return handle_command_context(argc, argv, "client.focused", + border_color_class_focused, con, "#2e9ef4ff"); } struct cmd_results *cmd_client_focused_inactive(int argc, char **argv) { - return handle_command(argc, argv, "client.focused_inactive", - &config->border_colors.focused_inactive, "#484e50ff"); + struct sway_container *con = config->handler_context.container; + return handle_command_context(argc, argv, "client.focused_inactive", + border_color_class_focused_inactive, con, "#484e50ff"); } struct cmd_results *cmd_client_unfocused(int argc, char **argv) { - return handle_command(argc, argv, "client.unfocused", - &config->border_colors.unfocused, "#292d2eff"); + struct sway_container *con = config->handler_context.container; + return handle_command_context(argc, argv, "client.unfocused", + border_color_class_unfocused, con, "#292d2eff"); } struct cmd_results *cmd_client_urgent(int argc, char **argv) { - return handle_command(argc, argv, "client.urgent", - &config->border_colors.urgent, "#900000ff"); + struct sway_container *con = config->handler_context.container; + return handle_command_context(argc, argv, "client.urgent", + border_color_class_urgent, con, "#900000ff"); } struct cmd_results *cmd_client_noop(int argc, char **argv) { @@ -84,7 +170,7 @@ struct cmd_results *cmd_client_noop(int argc, char **argv) { struct cmd_results *cmd_client_focused_tab_title(int argc, char **argv) { struct cmd_results *result = handle_command(argc, argv, "client.focused_tab_title", - &config->border_colors.focused_tab_title, "#2e9ef4ff"); + border_color_class_focused_tab_title, "#2e9ef4ff"); if (result && result->status == CMD_SUCCESS) { config->has_focused_tab_title = true; } diff --git a/sway/config.c b/sway/config.c index bd19dead5b..c859439481 100644 --- a/sway/config.c +++ b/sway/config.c @@ -160,6 +160,12 @@ void free_config(struct sway_config *config) { list_free(config->no_focus); list_free(config->active_bar_modifiers); list_free_items_and_destroy(config->config_chain); + free(config->border_colors[border_color_class_focused]); + free(config->border_colors[border_color_class_focused_inactive]); + free(config->border_colors[border_color_class_focused_tab_title]); + free(config->border_colors[border_color_class_unfocused]); + free(config->border_colors[border_color_class_urgent]); + free(config->border_colors[border_color_class_placeholder]); free(config->floating_scroll_up_cmd); free(config->floating_scroll_down_cmd); free(config->floating_scroll_left_cmd); @@ -302,35 +308,49 @@ static void config_defaults(struct sway_config *config) { config->has_focused_tab_title = false; // border colors - color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); - color_to_rgba(config->border_colors.focused.background, 0x285577FF); - color_to_rgba(config->border_colors.focused.text, 0xFFFFFFFF); - color_to_rgba(config->border_colors.focused.indicator, 0x2E9EF4FF); - color_to_rgba(config->border_colors.focused.child_border, 0x285577FF); - - color_to_rgba(config->border_colors.focused_inactive.border, 0x333333FF); - color_to_rgba(config->border_colors.focused_inactive.background, 0x5F676AFF); - color_to_rgba(config->border_colors.focused_inactive.text, 0xFFFFFFFF); - color_to_rgba(config->border_colors.focused_inactive.indicator, 0x484E50FF); - color_to_rgba(config->border_colors.focused_inactive.child_border, 0x5F676AFF); - - color_to_rgba(config->border_colors.unfocused.border, 0x333333FF); - color_to_rgba(config->border_colors.unfocused.background, 0x222222FF); - color_to_rgba(config->border_colors.unfocused.text, 0x888888FF); - color_to_rgba(config->border_colors.unfocused.indicator, 0x292D2EFF); - color_to_rgba(config->border_colors.unfocused.child_border, 0x222222FF); - - color_to_rgba(config->border_colors.urgent.border, 0x2F343AFF); - color_to_rgba(config->border_colors.urgent.background, 0x900000FF); - color_to_rgba(config->border_colors.urgent.text, 0xFFFFFFFF); - color_to_rgba(config->border_colors.urgent.indicator, 0x900000FF); - color_to_rgba(config->border_colors.urgent.child_border, 0x900000FF); - - color_to_rgba(config->border_colors.placeholder.border, 0x000000FF); - color_to_rgba(config->border_colors.placeholder.background, 0x0C0C0CFF); - color_to_rgba(config->border_colors.placeholder.text, 0xFFFFFFFF); - color_to_rgba(config->border_colors.placeholder.indicator, 0x000000FF); - color_to_rgba(config->border_colors.placeholder.child_border, 0x0C0C0CFF); + struct border_colors *colors; + if (!(colors = calloc(1, sizeof(*colors)))) goto cleanup; + color_to_rgba(colors->border, 0x4C7899FF); + color_to_rgba(colors->background, 0x285577FF); + color_to_rgba(colors->text, 0xFFFFFFFF); + color_to_rgba(colors->indicator, 0x2E9EF4FF); + color_to_rgba(colors->child_border, 0x285577FF); + config->border_colors[border_color_class_focused] = colors; + + if (!(colors = calloc(1, sizeof(*colors)))) goto cleanup; + color_to_rgba(colors->border, 0x333333FF); + color_to_rgba(colors->background, 0x5F676AFF); + color_to_rgba(colors->text, 0xFFFFFFFF); + color_to_rgba(colors->indicator, 0x484E50FF); + color_to_rgba(colors->child_border, 0x5F676AFF); + config->border_colors[border_color_class_focused_inactive] = colors; + + if (!(colors = calloc(1, sizeof(*colors)))) goto cleanup; + config->border_colors[border_color_class_focused_tab_title] = colors; + + if (!(colors = calloc(1, sizeof(*colors)))) goto cleanup; + color_to_rgba(colors->border, 0x333333FF); + color_to_rgba(colors->background, 0x222222FF); + color_to_rgba(colors->text, 0x888888FF); + color_to_rgba(colors->indicator, 0x292D2EFF); + color_to_rgba(colors->child_border, 0x222222FF); + config->border_colors[border_color_class_unfocused] = colors; + + if (!(colors = calloc(1, sizeof(*colors)))) goto cleanup; + color_to_rgba(colors->border, 0x2F343AFF); + color_to_rgba(colors->background, 0x900000FF); + color_to_rgba(colors->text, 0xFFFFFFFF); + color_to_rgba(colors->indicator, 0x900000FF); + color_to_rgba(colors->child_border, 0x900000FF); + config->border_colors[border_color_class_urgent] = colors; + + if (!(colors = calloc(1, sizeof(*colors)))) goto cleanup; + color_to_rgba(colors->border, 0x000000FF); + color_to_rgba(colors->background, 0x0C0C0CFF); + color_to_rgba(colors->text, 0xFFFFFFFF); + color_to_rgba(colors->indicator, 0x000000FF); + color_to_rgba(colors->child_border, 0x0C0C0CFF); + config->border_colors[border_color_class_urgent] = colors; // The keysym to keycode translation struct xkb_rule_names rules = {0}; diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 7de39ff60f..db67db2c39 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -448,7 +448,9 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat, return; } - const float *indicator = config->border_colors.focused.indicator; + struct border_colors *colors = container_get_window_colors( + con, border_color_class_focused); + const float *indicator = colors->indicator; float color[4] = { indicator[0] * .5, indicator[1] * .5, diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 7e58b5286b..17ca0b632c 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -547,7 +547,7 @@ runtime. *client.background* This command is ignored and is only present for i3 compatibility. -*client.* [ []] +*client.* [global|default] [ []] Configures the color of window borders and title bars. The first three colors are required. When omitted _indicator_ will use a sane default and _child_border_ will use the color set for _background_. Colors may be @@ -647,6 +647,10 @@ The default colors are: : #000000 : #0c0c0c + When run on containers, e.g. from *bindsym* or *for_window*, only those + containers are reconfigured. Subcommand _global_ can be used to + explicitly address the global configuration in this case and _default_ + can be used to remove per-container config. *default_border* normal|none|pixel [] Set default border style for new tiled windows. Config reload won't affect diff --git a/sway/tree/container.c b/sway/tree/container.c index 30cb97bab4..75c982bda5 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -187,6 +187,23 @@ static bool container_is_current_parent_focused(struct sway_container *con) { return false; } +// recursively obtain current color configuration from container, its parents +// or the main config +struct border_colors *container_get_window_colors( + struct sway_container *con, + enum border_color_class class) { + if (!con) { + return config->border_colors[class]; + } + + if (con->border_colors[class]) { + return con->border_colors[class]; + } + + // will hit non-NULL check above at root + return container_get_window_colors(con->current.parent, class); +} + static struct border_colors *container_get_current_colors( struct sway_container *con) { struct border_colors *colors; @@ -204,15 +221,15 @@ static struct border_colors *container_get_current_colors( } if (urgent) { - colors = &config->border_colors.urgent; + colors = container_get_window_colors(con, border_color_class_urgent); } else if (con->current.focused || container_is_current_parent_focused(con)) { - colors = &config->border_colors.focused; + colors = container_get_window_colors(con, border_color_class_focused); } else if (config->has_focused_tab_title && container_has_focused_child(con)) { - colors = &config->border_colors.focused_tab_title; + colors = container_get_window_colors(con, border_color_class_focused_tab_title); } else if (con == active_child) { - colors = &config->border_colors.focused_inactive; + colors = container_get_window_colors(con, border_color_class_focused_inactive); } else { - colors = &config->border_colors.unfocused; + colors = container_get_window_colors(con, border_color_class_unfocused); } return colors; @@ -500,6 +517,13 @@ void container_destroy(struct sway_container *con) { list_free_items_and_destroy(con->marks); + free(con->border_colors[border_color_class_focused]); + free(con->border_colors[border_color_class_focused_inactive]); + free(con->border_colors[border_color_class_focused_tab_title]); + free(con->border_colors[border_color_class_unfocused]); + free(con->border_colors[border_color_class_urgent]); + free(con->border_colors[border_color_class_placeholder]); + if (con->view && con->view->container == con) { con->view->container = NULL; wlr_scene_node_destroy(&con->output_handler->node);