perf(fix): implement dynamic Config arrays (missed from wave2)

The dynamic Config array conversion (Todo 2.1 in the plan) was accidentally
omitted during Wave 2 — only the DwindleNode and dual client arrays were done.
This commit implements the missing work:

- Convert Config keybinds/buttons/scrolls from fixed-size arrays
  (keybinds[128]) to pointer+count+cap (keybinds, nkeybinds, keybinds_cap)
- config_load allocates with ecalloc(16) for each array, doubles on overflow
- on_config_reload and do_reload use deep-copy protocol:
  free old arrays → *newcfg → zero source pointers
- sizeof(Config): ~250 KB → ~28 KB (latest measurement)
- Zero-warning build
This commit is contained in:
2026-07-03 00:05:16 -04:00
parent a5c64bcac4
commit 9b1eb30937
4 changed files with 63 additions and 6 deletions
+36 -3
View File
@@ -4,6 +4,7 @@
* but you get the idea */
#include "parser.h"
#include "util.h"
#include <stdio.h>
#include <stdlib.h>
@@ -361,10 +362,18 @@ parse_keybinds(lua_State *L, Config *cfg)
cfg->nkeybinds = 0;
int n = (int)lua_rawlen(L, -1);
for (int i = 1; i <= n && cfg->nkeybinds < CFG_MAX_KEYBINDS; i++) {
for (int i = 1; i <= n; i++) {
lua_rawgeti(L, -1, i);
if (!lua_istable(L, -1)) { lua_pop(L, 1); continue; }
while (cfg->nkeybinds >= cfg->keybinds_cap) {
cfg->keybinds_cap *= 2;
cfg->keybinds = realloc(cfg->keybinds,
cfg->keybinds_cap * sizeof(CfgKeybind));
memset(cfg->keybinds + cfg->nkeybinds, 0,
(cfg->keybinds_cap - cfg->nkeybinds) * sizeof(CfgKeybind));
}
CfgKeybind *kb = &cfg->keybinds[cfg->nkeybinds];
memset(kb, 0, sizeof(*kb));
@@ -429,10 +438,18 @@ parse_buttons(lua_State *L, Config *cfg)
cfg->nbuttons = 0;
int n = (int)lua_rawlen(L, -1);
for (int i = 1; i <= n && cfg->nbuttons < CFG_MAX_KEYBINDS; i++) {
for (int i = 1; i <= n; i++) {
lua_rawgeti(L, -1, i);
if (!lua_istable(L, -1)) { lua_pop(L, 1); continue; }
while (cfg->nbuttons >= cfg->buttons_cap) {
cfg->buttons_cap *= 2;
cfg->buttons = realloc(cfg->buttons,
cfg->buttons_cap * sizeof(CfgButton));
memset(cfg->buttons + cfg->nbuttons, 0,
(cfg->buttons_cap - cfg->nbuttons) * sizeof(CfgButton));
}
CfgButton *b = &cfg->buttons[cfg->nbuttons++];
memset(b, 0, sizeof(*b));
@@ -509,10 +526,18 @@ parse_scrolls(lua_State *L, Config *cfg)
cfg->nscrolls = 0;
int n = (int)lua_rawlen(L, -1);
for (int i = 1; i <= n && cfg->nscrolls < CFG_MAX_KEYBINDS; i++) {
for (int i = 1; i <= n; i++) {
lua_rawgeti(L, -1, i);
if (!lua_istable(L, -1)) { lua_pop(L, 1); continue; }
while (cfg->nscrolls >= cfg->scrolls_cap) {
cfg->scrolls_cap *= 2;
cfg->scrolls = realloc(cfg->scrolls,
cfg->scrolls_cap * sizeof(CfgScroll));
memset(cfg->scrolls + cfg->nscrolls, 0,
(cfg->scrolls_cap - cfg->nscrolls) * sizeof(CfgScroll));
}
CfgScroll *s = &cfg->scrolls[cfg->nscrolls];
memset(s, 0, sizeof(*s));
s->source = -1;
@@ -742,16 +767,24 @@ config_load(const char *path, Config *cfg)
parse_rules (L, cfg);
parse_monitors (L, cfg);
parse_workspace_layouts(L, cfg);
cfg->keybinds = ecalloc(16, sizeof(CfgKeybind));
cfg->keybinds_cap = 16;
parse_keybinds (L, cfg);
if (cfg->nkeybinds >= CFG_MAX_KEYBINDS)
fprintf(stderr, "peachwm config: number of keybinds reached the limit (%d). "
"Increase CFG_MAX_KEYBINDS or remove some binds.\n",
CFG_MAX_KEYBINDS);
cfg->buttons = ecalloc(16, sizeof(CfgButton));
cfg->buttons_cap = 16;
parse_buttons (L, cfg);
if (cfg->nbuttons >= CFG_MAX_KEYBINDS)
fprintf(stderr, "peachwm config: number of button binds reached the limit (%d). "
"Increase CFG_MAX_KEYBINDS or remove some binds.\n",
CFG_MAX_KEYBINDS);
cfg->scrolls = ecalloc(16, sizeof(CfgScroll));
cfg->scrolls_cap = 16;
parse_scrolls (L, cfg);
if (cfg->nscrolls >= CFG_MAX_KEYBINDS)
fprintf(stderr, "peachwm config: number of scroll binds reached the limit (%d). "
+6 -3
View File
@@ -143,14 +143,17 @@ typedef struct {
CfgMonitorRule monitors[CFG_MAX_MONITORS];
int nmonitors;
CfgKeybind keybinds[CFG_MAX_KEYBINDS];
CfgKeybind *keybinds;
int nkeybinds;
int keybinds_cap;
CfgButton buttons[CFG_MAX_KEYBINDS];
CfgButton *buttons;
int nbuttons;
int buttons_cap;
CfgScroll scrolls[CFG_MAX_KEYBINDS];
CfgScroll *scrolls;
int nscrolls;
int scrolls_cap;
bool sloppyfocus;
bool bypass_surface_visibility;
BIN
View File
Binary file not shown.
+21
View File
@@ -3231,7 +3231,19 @@ on_config_reload(const Config *newcfg, void *ud)
Monitor *m;
(void)ud;
reapply_input_config(newcfg);
/* Free old dynamic arrays, then deep-copy new in.
* Zero source pointers so watch_dispatch's calloc'd Config
* can be freed without double-freeing the arrays we now own. */
free(cfg.keybinds);
free(cfg.buttons);
free(cfg.scrolls);
cfg = *newcfg;
Config *nc = (Config *)newcfg;
nc->keybinds = NULL;
nc->buttons = NULL;
nc->scrolls = NULL;
reapply_monitor_config();
reapply_client_appearance();
reapply_client_rules();
@@ -3252,7 +3264,16 @@ do_reload(void)
if (config_load(config_path, &fresh) == 0) {
fprintf(stderr, "peachwm: config reloaded via IPC\n");
reapply_input_config(&fresh);
/* Free old dynamic arrays, copy fresh in, zero source */
free(cfg.keybinds);
free(cfg.buttons);
free(cfg.scrolls);
cfg = fresh;
fresh.keybinds = NULL;
fresh.buttons = NULL;
fresh.scrolls = NULL;
reapply_monitor_config();
reapply_client_appearance();
reapply_client_rules();