Skip to content

Commit

Permalink
config: Add per-container colors
Browse files Browse the repository at this point in the history
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.<class> 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.<class> 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 <[email protected]>
  • Loading branch information
michaelweiser committed Jan 21, 2024
1 parent 3ab64f7 commit 3fd61e5
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 63 deletions.
17 changes: 1 addition & 16 deletions include/sway/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;

Expand Down
23 changes: 23 additions & 0 deletions include/sway/tree/container.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
108 changes: 97 additions & 11 deletions sway/commands/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) ||
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}
Expand Down
78 changes: 49 additions & 29 deletions sway/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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};
Expand Down
4 changes: 3 additions & 1 deletion sway/input/seatop_move_tiling.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 5 additions & 1 deletion sway/sway.5.scd
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ runtime.
*client.background* <color>
This command is ignored and is only present for i3 compatibility.

*client.<class>* <border> <background> <text> [<indicator> [<child_border>]]
*client.<class>* [global|default] <border> <background> <text> [<indicator> [<child_border>]]
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
Expand Down Expand Up @@ -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 [<n>]
Set default border style for new tiled windows. Config reload won't affect
Expand Down
Loading

0 comments on commit 3fd61e5

Please sign in to comment.