From c3a0c11adc85e0f659fd9a463704a2c757afd72a Mon Sep 17 00:00:00 2001 From: Gerry Hernandez Date: Sun, 15 Mar 2026 18:52:11 -0700 Subject: [PATCH] Refactor after upstream changes; harden get_name_from_tag_number --- mmsg/mmsg.c | 7 ++++--- src/config/parse_config.h | 36 +++++++++++++++++++------------- src/config/preset.h | 3 --- src/dispatch/bind_define.h | 4 ++-- src/ext-protocol/dwl-ipc.h | 4 ++-- src/ext-protocol/ext-workspace.h | 35 ++++++++++++++++++------------- src/fetch/monitor.h | 12 +++++------ src/mango.c | 20 ++++++++++-------- 8 files changed, 67 insertions(+), 54 deletions(-) diff --git a/mmsg/mmsg.c b/mmsg/mmsg.c index 017f3d2a..5b968444 100644 --- a/mmsg/mmsg.c +++ b/mmsg/mmsg.c @@ -351,7 +351,7 @@ static void dwl_ipc_output_frame(void *data, } } - if ((i - 1) > tag_count) + if (i == 0 || (i - 1) > tag_count) die("bad tagset %s", tagset); zdwl_ipc_output_v2_set_tags(dwl_ipc_output, mask, 0); @@ -381,7 +381,7 @@ static void dwl_ipc_output_frame(void *data, break; } } - if ((i - 1) > tag_count) + if (i == 0 || (i - 1) > tag_count) die("bad client tagset %s", client_tags); zdwl_ipc_output_v2_set_client_tags(dwl_ipc_output, and, xor); @@ -400,7 +400,8 @@ static void dwl_ipc_output_frame(void *data, printf("%s clients %u\n", output_name, total_clients); - char occ_str[tag_count + 1], seltags_str[tag_count + 1], urg_str[tag_count + 1]; + char occ_str[tag_count + 1], seltags_str[tag_count + 1], + urg_str[tag_count + 1]; bin_str_nbits(occ_str, occ, tag_count); bin_str_nbits(seltags_str, seltags, tag_count); diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 208bc853..e9397161 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -8,7 +8,11 @@ #define SYSCONFDIR "/etc" #endif -static bool config_initialized = false; +// We don't want to allow config hot-reloading to change the tag_count and we +// want to require a compositor reload. The code to support hot-reloading a +// change in tag_count is a lot more involved than this minimal solution we have +// for configuring the number of tags available in Mango. +static uint32_t active_tag_count = 0; // Clamps value in range while preserving numeric type #define CLAMP(x, min, max) \ @@ -1136,7 +1140,7 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, while (token != NULL) { int32_t num = atoi(token); - if (num > 0 && num <= tag_count) { + if (num > 0 && num <= config.tag_count) { mask |= (1 << (num - 1)); } token = strtok_r(NULL, "|", &saveptr); @@ -1604,14 +1608,17 @@ bool parse_option(Config *config, char *key, char *value) { config->default_nmaster = atoi(value); } else if (strcmp(key, "tag_count") == 0) { uint32_t requested = CLAMP(atoi(value), 1, 32); - config->tag_count = requested; - if (!config_initialized) { - tag_count = requested; - } else if (tag_count != requested) { - wlr_log(WLR_INFO, - "tag_count change requires restart (current: %u, " - "requested: %u)", - tag_count, requested); + bool is_initial_config_load = active_tag_count == 0; + if (is_initial_config_load) { + config->tag_count = requested; + } else { + config->tag_count = active_tag_count; + if (active_tag_count != requested) { + wlr_log(WLR_INFO, + "tag_count change requires restart (current: %u, " + "requested: %u)", + active_tag_count, requested); + } } } else if (strcmp(key, "center_master_overspread") == 0) { config->center_master_overspread = atoi(value); @@ -1936,7 +1943,7 @@ bool parse_option(Config *config, char *key, char *value) { trim_whitespace(val); if (strcmp(key, "id") == 0) { - rule->id = CLAMP_INT(atoi(val), 0, tag_count); + rule->id = CLAMP_INT(atoi(val), 0, config->tag_count); } else if (strcmp(key, "layout_name") == 0) { rule->layout_name = strdup(val); } else if (strcmp(key, "monitor_name") == 0) { @@ -3527,7 +3534,7 @@ bool parse_config(void) { config.scroller_proportion_preset_count = 0; config.circle_layout = NULL; config.circle_layout_count = 0; - config.tag_count = 9; + config.tag_count = 0; config.tag_rules = NULL; config.tag_rules_count = 0; config.cursor_theme = NULL; @@ -3561,7 +3568,6 @@ bool parse_config(void) { parse_correct = parse_config_file(&config, filename, true); set_default_key_bindings(&config); override_config(); - config_initialized = true; return parse_correct; } @@ -3729,7 +3735,7 @@ void reapply_master(void) { uint32_t i; Monitor *m = NULL; - for (i = 0; i <= tag_count; i++) { + for (i = 0; i <= config.tag_count; i++) { wl_list_for_each(m, &mons, link) { if (!m->wlr_output->enabled) { continue; @@ -3808,7 +3814,7 @@ void parse_tagrule(Monitor *m) { } } - for (i = 1; i <= tag_count; i++) { + for (i = 1; i <= config.tag_count; i++) { wl_list_for_each(c, &clients, link) { if ((c->tags & (1 << (i - 1)) & TAGMASK) && ISTILED(c)) { if (m->pertag->mfacts[i] > 0.0f) diff --git a/src/config/preset.h b/src/config/preset.h index 6952518e..3b0cb8dd 100644 --- a/src/config/preset.h +++ b/src/config/preset.h @@ -1,8 +1,5 @@ #define MODKEY WLR_MODIFIER_ALT -static const char *tags[] = { - "1", "2", "3", "4", "5", "6", "7", "8", "9", -}; static const struct xkb_rule_names xkb_fallback_rules = { .layout = "us", diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index cbae132d..deec59d0 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -1539,10 +1539,10 @@ int32_t viewtoright_have_client(const Arg *arg) { return 0; } - if (current >= tag_count) + if (current >= config.tag_count) return 0; - for (n = current + 1; n <= tag_count; n++) { + for (n = current + 1; n <= config.tag_count; n++) { if (get_tag_status(n, selmon)) { found = true; break; diff --git a/src/ext-protocol/dwl-ipc.h b/src/ext-protocol/dwl-ipc.h index 7a5be561..98b6a107 100644 --- a/src/ext-protocol/dwl-ipc.h +++ b/src/ext-protocol/dwl-ipc.h @@ -55,7 +55,7 @@ void dwl_ipc_manager_bind(struct wl_client *client, void *data, &dwl_manager_implementation, NULL, dwl_ipc_manager_destroy); - zdwl_ipc_manager_v2_send_tags(manager_resource, tag_count); + zdwl_ipc_manager_v2_send_tags(manager_resource, config.tag_count); for (uint32_t i = 0; i < LENGTH(layouts); i++) zdwl_ipc_manager_v2_send_layout(manager_resource, layouts[i].symbol); @@ -118,7 +118,7 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) { focused = focustop(monitor); zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon); - for (tag_idx = 0; tag_idx < tag_count; tag_idx++) { + for (tag_idx = 0; tag_idx < config.tag_count; tag_idx++) { numclients = state = focused_client = 0; tagmask = 1 << tag_idx; if ((tagmask & monitor->tagset[monitor->seltags]) != 0) diff --git a/src/ext-protocol/ext-workspace.h b/src/ext-protocol/ext-workspace.h index 3bb9e946..8bd041aa 100644 --- a/src/ext-protocol/ext-workspace.h +++ b/src/ext-protocol/ext-workspace.h @@ -1,7 +1,5 @@ #include "wlr_ext_workspace_v1.h" -#define WORKSPACE_NAME_MAX_SIZE 12 - #define EXT_WORKSPACE_ENABLE_CAPS \ EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE | \ EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_DEACTIVATE @@ -10,8 +8,8 @@ typedef struct Monitor Monitor; struct workspace { struct wl_list link; // Link in global workspaces list - uint32_t tag; // Identifier (tag number) in [0=overview, tag_count] - Monitor *m; // Associated monitor + uint32_t tag; // Identifier (tag number) in [0=overview, tag_count] + Monitor *m; // Associated monitor struct wlr_ext_workspace_handle_v1 *ext_workspace; // Protocol object /* Event listeners */ struct wl_listener activate; @@ -71,14 +69,22 @@ static void handle_ext_workspace_deactivate(struct wl_listener *listener, wlr_log(WLR_INFO, "ext deactivating workspace %d", workspace->tag); } -static void get_name_from_tag_number(char *dst_buf, size_t dst_len, - uint32_t tag_number) { - if (tag_number > tag_count) - die("tag_number %u exceeds tag_count %u", tag_number, tag_count); +// Returns the size_t that would have been written to dst_buf if dst_len was +// large enough. Safe usage of this function is to call it with +// dst_buf == NULL and dst_len == 0, then use the returned value to allocate the +// destination buffer, then call it again passing in the buffer and size. +static size_t get_name_from_tag_number(char *dst_buf, size_t dst_len, + uint32_t tag_number) { + if (tag_number > config.tag_count) + die("tag_number %u exceeds tag_count %u", tag_number, config.tag_count); + int n; if (tag_number == 0) - snprintf(dst_buf, dst_len, "overview"); + n = snprintf(dst_buf, dst_len, "overview"); else - snprintf(dst_buf, dst_len, "%u", tag_number); + n = snprintf(dst_buf, dst_len, "%u", tag_number); + if (n < 0) + die("snprintf failed for tag_number %u", tag_number); + return (size_t)(n + 1); } void destroy_workspace(struct workspace *workspace) { @@ -109,8 +115,9 @@ static void remove_workspace_by_tag(uint32_t tag, Monitor *m) { } static void add_workspace_by_tag(uint32_t tag, Monitor *m) { - char name[WORKSPACE_NAME_MAX_SIZE]; - get_name_from_tag_number(name, sizeof(name), tag); + size_t name_len = get_name_from_tag_number(NULL, 0, tag); + char name[name_len]; + get_name_from_tag_number(name, name_len, tag); struct workspace *workspace = ecalloc(1, sizeof(*workspace)); wl_list_append(&workspaces, &workspace->link); @@ -172,13 +179,13 @@ void refresh_monitors_workspaces_status(Monitor *m) { uint32_t i; if (m->isoverview) { - for (i = 1; i <= tag_count; i++) { + for (i = 1; i <= config.tag_count; i++) { remove_workspace_by_tag(i, m); } add_workspace_by_tag(0, m); } else { remove_workspace_by_tag(0, m); - for (i = 1; i <= tag_count; i++) { + for (i = 1; i <= config.tag_count; i++) { add_workspace_by_tag(i, m); } } diff --git a/src/fetch/monitor.h b/src/fetch/monitor.h index 6c1e8a24..40cec9f2 100644 --- a/src/fetch/monitor.h +++ b/src/fetch/monitor.h @@ -57,14 +57,14 @@ uint32_t get_tags_first_tag_num(uint32_t source_tags) { return selmon->pertag->curtag; } - for (i = 0; !(tag & 1) && source_tags != 0 && i < tag_count; i++) { + for (i = 0; !(tag & 1) && source_tags != 0 && i < config.tag_count; i++) { tag = source_tags >> i; } if (i == 1) { return 1; - } else if (i > tag_count) { - return tag_count; + } else if (i > config.tag_count) { + return config.tag_count; } else { return i; } @@ -79,14 +79,14 @@ uint32_t get_tags_first_tag(uint32_t source_tags) { return selmon->pertag->curtag; } - for (i = 0; !(tag & 1) && source_tags != 0 && i < tag_count; i++) { + for (i = 0; !(tag & 1) && source_tags != 0 && i < config.tag_count; i++) { tag = source_tags >> i; } if (i == 1) { return 1; - } else if (i > tag_count) { - return 1 << (tag_count - 1); + } else if (i > config.tag_count) { + return 1 << (config.tag_count - 1); } else { return 1 << (i - 1); } diff --git a/src/mango.c b/src/mango.c index 8ff831c5..5314bd61 100644 --- a/src/mango.c +++ b/src/mango.c @@ -117,7 +117,7 @@ ((C) && (M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) -#define TAGMASK (tag_count == 32 ? ~0u : ((1u << tag_count) - 1)) +#define TAGMASK (config.tag_count == 32 ? ~0u : ((1u << config.tag_count) - 1)) #define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) #define ISFULLSCREEN(A) \ ((A)->isfullscreen || (A)->ismaximizescreen || \ @@ -3041,14 +3041,16 @@ void createmon(struct wl_listener *listener, void *data) { wlr_output_state_finish(&state); wl_list_insert(&mons, &m->link); + if (active_tag_count == 0) + active_tag_count = config.tag_count; m->pertag = calloc(1, sizeof(Pertag)); if (!m->pertag) die("pertag calloc failed"); - m->pertag->nmasters = calloc(tag_count + 1, sizeof(int32_t)); - m->pertag->mfacts = calloc(tag_count + 1, sizeof(float)); - m->pertag->no_hide = calloc(tag_count + 1, sizeof(bool)); - m->pertag->no_render_border = calloc(tag_count + 1, sizeof(bool)); - m->pertag->ltidxs = calloc(tag_count + 1, sizeof(const Layout *)); + m->pertag->nmasters = calloc(config.tag_count + 1, sizeof(int32_t)); + m->pertag->mfacts = calloc(config.tag_count + 1, sizeof(float)); + m->pertag->no_hide = calloc(config.tag_count + 1, sizeof(bool)); + m->pertag->no_render_border = calloc(config.tag_count + 1, sizeof(bool)); + m->pertag->ltidxs = calloc(config.tag_count + 1, sizeof(const Layout *)); if (!m->pertag->nmasters || !m->pertag->mfacts || !m->pertag->no_hide || !m->pertag->no_render_border || !m->pertag->ltidxs) die("pertag member calloc failed"); @@ -3103,7 +3105,7 @@ void createmon(struct wl_listener *listener, void *data) { ext_manager, EXT_WORKSPACE_ENABLE_CAPS); wlr_ext_workspace_group_handle_v1_output_enter(m->ext_group, m->wlr_output); - for (i = 1; i <= tag_count; i++) { + for (i = 1; i <= config.tag_count; i++) { add_workspace_by_tag(i, m); } @@ -6307,10 +6309,10 @@ void view_in_mon(const Arg *arg, bool want_animation, Monitor *m, if (arg->ui == (~0 & TAGMASK)) m->pertag->curtag = 0; else { - for (i = 0; !(arg->ui & 1 << i) && i < tag_count && arg->ui != 0; + for (i = 0; !(arg->ui & 1u << i) && i < config.tag_count && arg->ui != 0; i++) ; - m->pertag->curtag = i >= tag_count ? tag_count : i + 1; + m->pertag->curtag = i >= config.tag_count ? config.tag_count : i + 1; } m->pertag->prevtag =