From 090e5181d54c66bac322d1bd4eb4c76e5440d58e Mon Sep 17 00:00:00 2001 From: Pasha <49.pasha@gmail.com> Date: Fri, 15 May 2026 11:17:54 +0300 Subject: [PATCH] feat: add dynamic border color based on xkb layout --- src/config/parse_config.h | 41 +++++++++++++++++++++++++++++++++ src/mango.c | 48 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 7c0000cb..57a6ef38 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -336,6 +336,14 @@ typedef struct { float globalcolor[4]; float overlaycolor[4]; + float default_focuscolor[4]; + float xkb_layout_focuscolors[8][4]; + int32_t xkb_layout_focuscolors_count; + + float default_bordercolor[4]; + float xkb_layout_bordercolors[8][4]; + int32_t xkb_layout_bordercolors_count; + int32_t log_level; uint32_t capslock; @@ -1778,7 +1786,22 @@ bool parse_option(Config *config, char *key, char *value) { return false; } else { convert_hex_to_rgba(config->bordercolor, color); + memcpy(config->default_bordercolor, config->bordercolor, sizeof(config->bordercolor)); } + } else if (strcmp(key, "xkb_layout_bordercolors") == 0) { + char *value_copy = strdup(value); + char *token = strtok(value_copy, ","); + config->xkb_layout_bordercolors_count = 0; + while (token != NULL && config->xkb_layout_bordercolors_count < 8) { + trim_whitespace(token); + int64_t color = parse_color(token); + if (color != -1) { + convert_hex_to_rgba(config->xkb_layout_bordercolors[config->xkb_layout_bordercolors_count], color); + config->xkb_layout_bordercolors_count++; + } + token = strtok(NULL, ","); + } + free(value_copy); } else if (strcmp(key, "dropcolor") == 0) { int64_t color = parse_color(value); if (color == -1) { @@ -1811,7 +1834,22 @@ bool parse_option(Config *config, char *key, char *value) { return false; } else { convert_hex_to_rgba(config->focuscolor, color); + memcpy(config->default_focuscolor, config->focuscolor, sizeof(config->focuscolor)); } + } else if (strcmp(key, "xkb_layout_focuscolors") == 0) { + char *value_copy = strdup(value); + char *token = strtok(value_copy, ","); + config->xkb_layout_focuscolors_count = 0; + while (token != NULL && config->xkb_layout_focuscolors_count < 8) { + trim_whitespace(token); + int64_t color = parse_color(token); + if (color != -1) { + convert_hex_to_rgba(config->xkb_layout_focuscolors[config->xkb_layout_focuscolors_count], color); + config->xkb_layout_focuscolors_count++; + } + token = strtok(NULL, ","); + } + free(value_copy); } else if (strcmp(key, "maximizescreencolor") == 0) { int64_t color = parse_color(value); if (color == -1) { @@ -3563,6 +3601,9 @@ void set_value_default() { config.overlaycolor[1] = 0xa5 / 255.0f; config.overlaycolor[2] = 0x7c / 255.0f; config.overlaycolor[3] = 1.0f; + + memcpy(config.default_focuscolor, config.focuscolor, sizeof(config.focuscolor)); + memcpy(config.default_bordercolor, config.bordercolor, sizeof(config.bordercolor)); } void set_default_key_bindings(Config *config) { diff --git a/src/mango.c b/src/mango.c index ea1c928c..c510f246 100644 --- a/src/mango.c +++ b/src/mango.c @@ -877,6 +877,8 @@ Client *scroll_get_stack_tail_client(Client *c); static DwindleNode *dwindle_find_leaf(DwindleNode *node, Client *c); static void overview_backup_surface(Client *c); +static void apply_xkb_layout_colors(struct wlr_keyboard *keyboard); + #include "data/static_keymap.h" #include "dispatch/bind_declare.h" #include "layout/layout.h" @@ -4132,6 +4134,8 @@ void keypress(struct wl_listener *listener, void *data) { KeyboardGroup *group = wl_container_of(listener, group, key); struct wlr_keyboard_key_event *event = data; + apply_xkb_layout_colors(&group->wlr_group->keyboard); + struct wlr_surface *last_surface = seat->keyboard_state.focused_surface; struct wlr_xdg_surface *xdg_surface = last_surface ? wlr_xdg_surface_try_from_wlr_surface(last_surface) @@ -4223,6 +4227,8 @@ void keypressmod(struct wl_listener *listener, void *data) { * pressed. We simply communicate this to the client. */ KeyboardGroup *group = wl_container_of(listener, group, modifiers); + apply_xkb_layout_colors(&group->wlr_group->keyboard); + if (!dwl_im_keyboard_grab_forward_modifiers(group)) { wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); @@ -5518,6 +5524,43 @@ void setgaps(int32_t oh, int32_t ov, int32_t ih, int32_t iv) { arrange(selmon, false, false); } +static uint32_t last_xkb_group = UINT32_MAX; + +static void apply_xkb_layout_colors(struct wlr_keyboard *keyboard) { + if (!keyboard) return; + uint32_t current_group = keyboard->modifiers.group; + + if (current_group != last_xkb_group) { + last_xkb_group = current_group; + bool changed = false; + + if (config.xkb_layout_focuscolors_count > 0) { + if (current_group < (uint32_t)config.xkb_layout_focuscolors_count) { + memcpy(config.focuscolor, config.xkb_layout_focuscolors[current_group], sizeof(float) * 4); + } else { + memcpy(config.focuscolor, config.default_focuscolor, sizeof(float) * 4); + } + changed = true; + } + + if (config.xkb_layout_bordercolors_count > 0) { + if (current_group < (uint32_t)config.xkb_layout_bordercolors_count) { + memcpy(config.bordercolor, config.xkb_layout_bordercolors[current_group], sizeof(float) * 4); + } else { + memcpy(config.bordercolor, config.default_bordercolor, sizeof(float) * 4); + } + changed = true; + } + + if (changed) { + Client *c; + wl_list_for_each(c, &clients, link) { + setborder_color(c); + } + } + } +} + void reset_keyboard_layout(void) { if (!kb_group || !kb_group->wlr_group || !seat) { wlr_log(WLR_ERROR, "Invalid keyboard group or seat"); @@ -5608,6 +5651,11 @@ void reset_keyboard_layout(void) { cleanup_context: xkb_context_unref(context); + + last_xkb_group = UINT32_MAX; + if (kb_group && kb_group->wlr_group) { + apply_xkb_layout_colors(&kb_group->wlr_group->keyboard); + } } void setmon(Client *c, Monitor *m, uint32_t newtags, bool focus) {