#include "Config.h" #include #include #include #include #include #include #include char const *get_config_path(void) { char const *paths[] = { "lunarwm/init.lua", }; for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) { char const *p = paths[i]; struct stat s; if (stat(p, &s) == 0) return p; } return NULL; } static int dupstr(char const *s, char **out) { if (!s) { *out = NULL; return 0; } size_t n = strlen(s) + 1; char *m = (char *)malloc(n); if (!m) return -1; memcpy(m, s, n); *out = m; return 0; } static uint32_t mod_from_token(char const *t) { if (!t) return 0; if (strcasecmp(t, "Super") == 0) return WLR_MODIFIER_LOGO; if (strcasecmp(t, "Shift") == 0) return WLR_MODIFIER_SHIFT; if (strcasecmp(t, "Ctrl") == 0 || strcasecmp(t, "Control") == 0) return WLR_MODIFIER_CTRL; if (strcasecmp(t, "Alt") == 0) return WLR_MODIFIER_ALT; return 0; } static int parse_bind( char const *bind, uint32_t *mods_out, xkb_keysym_t *sym_out) { if (!bind || !mods_out || !sym_out) return -1; char buf[256]; strncpy(buf, bind, sizeof buf - 1); buf[sizeof buf - 1] = 0; uint32_t mods = 0; char *save = NULL; char *tok = strtok_r(buf, "-", &save); char *last = tok; while (tok) { last = tok; tok = strtok_r(NULL, "-", &save); } /* walk again to accumulate modifiers (all but last) */ strncpy(buf, bind, sizeof buf - 1); buf[sizeof buf - 1] = 0; save = NULL; for (char *t = strtok_r(buf, "-", &save); t; t = strtok_r(NULL, "-", &save)) { if (t == last) break; mods |= mod_from_token(t); } /* keysym from last token */ int flags = XKB_KEYSYM_CASE_INSENSITIVE; xkb_keysym_t sym = xkb_keysym_from_name(last, flags); if (sym == XKB_KEY_NoSymbol) return -1; *mods_out = mods; *sym_out = sym; return 0; } static int push_config_table_from_idx(lua_State *L, int idx_abs) { if (!lua_istable(L, idx_abs)) { return luaL_error(L, "config: expected table at index %d", idx_abs); } lua_pushvalue(L, idx_abs); return 0; } int config_load_ref(lua_State *L, int idx, Config *out) { if (!L || !out) return -1; memset(out, 0, sizeof(*out)); int idx_abs = lua_absindex(L, idx); if (push_config_table_from_idx(L, idx_abs) != 0) return -1; lua_getfield(L, -1, "keybindings"); if (!lua_istable(L, -1)) { lua_pop(L, 2); return luaL_error(L, "config: 'keybindings' must be a table (array)"); } size_t n = (size_t)lua_rawlen(L, -1); if (n == 0) { lua_pop(L, 2); return 0; } BindingRef *arr = calloc(n, sizeof(BindingRef)); if (!arr) { lua_pop(L, 2); return luaL_error(L, "config: OOM allocating bindings"); } size_t ok_count = 0; for (size_t i = 0; i < n; i++) { lua_rawgeti(L, -1, (lua_Integer)(i + 1)); if (!lua_istable(L, -1)) { lua_pop(L, 1); continue; } lua_getfield(L, -1, "bind"); char const *bind = lua_tostring(L, -1); if (!bind) { lua_pop(L, 2); continue; } uint32_t mods = 0; xkb_keysym_t sym = XKB_KEY_NoSymbol; if (parse_bind(bind, &mods, &sym) != 0) { lua_pop(L, 2); continue; } lua_getfield(L, -2, "action"); if (!lua_isfunction(L, -1)) { lua_pop(L, 3); continue; } int ref = luaL_ref(L, LUA_REGISTRYINDEX); lua_pop(L, 1); // pop bind string arr[ok_count].mods_mask = mods; arr[ok_count].sym = sym; arr[ok_count].action_ref = ref; ok_count++; lua_pop(L, 1); // pop table entry } lua_pop(L, 2); // pop keybindings table + config table if (ok_count == 0) { free(arr); out->bindings = NULL; out->count = 0; return 0; } if (ok_count < n) { BindingRef *shr = realloc(arr, ok_count * sizeof(BindingRef)); if (shr) arr = shr; } out->bindings = arr; out->count = ok_count; return 0; } void config_unref(lua_State *L, Config *cfg) { if (!cfg || !cfg->bindings) { if (cfg) cfg->count = 0; return; } for (size_t i = 0; i < cfg->count; i++) { if (cfg->bindings[i].action_ref != LUA_NOREF && cfg->bindings[i].action_ref != LUA_REFNIL) { luaL_unref(L, LUA_REGISTRYINDEX, cfg->bindings[i].action_ref); } } free(cfg->bindings); cfg->bindings = NULL; cfg->count = 0; } static int load_config_file(lua_State *L, char const *path) { if (!path || !path[0]) return -1; if (luaL_loadfile(L, path) != LUA_OK) { char const *err = lua_tostring(L, -1); fprintf( stderr, "config: loadfile failed: %s\n", err ? err : "(unknown)"); lua_pop(L, 1); return -1; } if (lua_pcall(L, 0, 1, 0) != LUA_OK) { char const *err = lua_tostring(L, -1); fprintf(stderr, "config: executing '%s' failed: %s\n", path, err ? err : "(unknown)"); lua_pop(L, 1); return -1; } if (!lua_istable(L, -1)) { lua_pop(L, 1); fprintf(stderr, "config: '%s' did not return a table\n", path); return -1; } return 0; } ConfigManager *config_manager_create(char const *path) { ConfigManager *cm = (ConfigManager *)calloc(1, sizeof(*cm)); if (!cm) return NULL; cm->L = luaL_newstate(); if (!cm->L) { free(cm); return NULL; } luaL_openlibs(cm->L); if (!path) path = get_config_path(); if (path) { if (dupstr(path, &cm->path) != 0) { lua_close(cm->L); free(cm); return NULL; } } if (cm->path && load_config_file(cm->L, cm->path) == 0) { if (config_load_ref(cm->L, -1, &cm->cfg) != 0) { lua_pop(cm->L, 1); } else { lua_pop(cm->L, 1); } } return cm; } void config_manager_destroy(ConfigManager *cm) { if (!cm) return; config_unref(cm->L, &cm->cfg); if (cm->L) lua_close(cm->L); free(cm->path); free(cm); } int config_manager_reload(ConfigManager *cm) { if (!cm || !cm->path) return -1; config_unref(cm->L, &cm->cfg); if (load_config_file(cm->L, cm->path) != 0) { return -1; } int rc = config_load_ref(cm->L, -1, &cm->cfg); lua_pop(cm->L, 1); return rc; } lua_State *config_manager_lua(ConfigManager *cm) { return cm ? cm->L : NULL; } Config const *config_manager_get(ConfigManager *cm) { return cm ? &cm->cfg : NULL; } char const *config_manager_path(ConfigManager *cm) { return cm ? cm->path : NULL; } int trigger_ref_modsym( lua_State *L, Config const *cfg, uint32_t mods, xkb_keysym_t sym) { if (!L || !cfg) return -1; for (size_t i = 0; i < cfg->count; i++) { BindingRef const *br = &cfg->bindings[i]; if (br->sym != sym) continue; if ((mods & br->mods_mask) != br->mods_mask) continue; // require all mods lua_rawgeti(L, LUA_REGISTRYINDEX, br->action_ref); if (!lua_isfunction(L, -1)) { lua_pop(L, 1); return -2; } if (lua_pcall(L, 0, 0, 0) != LUA_OK) { fprintf(stderr, "config: action error: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); return -3; } return 0; } return 1; }