274
src/Config.c
274
src/Config.c
@@ -1,4 +1,5 @@
|
|||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -9,13 +10,14 @@
|
|||||||
#include <lualib.h>
|
#include <lualib.h>
|
||||||
|
|
||||||
#include <wlr/types/wlr_keyboard.h>
|
#include <wlr/types/wlr_keyboard.h>
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
|
||||||
char const *get_config_path(void)
|
char const *get_config_path(void)
|
||||||
{
|
{
|
||||||
char const *paths[] = {
|
static char const *paths[] = {
|
||||||
"lunarwm/init.lua",
|
"lunarwm/init.lua",
|
||||||
};
|
};
|
||||||
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
|
for (size_t i = 0; i < ARRAY_SZ(paths); ++i) {
|
||||||
char const *p = paths[i];
|
char const *p = paths[i];
|
||||||
struct stat s;
|
struct stat s;
|
||||||
if (stat(p, &s) == 0)
|
if (stat(p, &s) == 0)
|
||||||
@@ -64,7 +66,6 @@ static int parse_bind(
|
|||||||
strncpy(buf, bind, sizeof buf - 1);
|
strncpy(buf, bind, sizeof buf - 1);
|
||||||
buf[sizeof buf - 1] = 0;
|
buf[sizeof buf - 1] = 0;
|
||||||
|
|
||||||
uint32_t mods = 0;
|
|
||||||
char *save = NULL;
|
char *save = NULL;
|
||||||
char *tok = strtok_r(buf, "-", &save);
|
char *tok = strtok_r(buf, "-", &save);
|
||||||
char *last = tok;
|
char *last = tok;
|
||||||
@@ -72,10 +73,12 @@ static int parse_bind(
|
|||||||
last = tok;
|
last = tok;
|
||||||
tok = strtok_r(NULL, "-", &save);
|
tok = strtok_r(NULL, "-", &save);
|
||||||
}
|
}
|
||||||
/* walk again to accumulate modifiers (all but last) */
|
|
||||||
strncpy(buf, bind, sizeof buf - 1);
|
strncpy(buf, bind, sizeof buf - 1);
|
||||||
buf[sizeof buf - 1] = 0;
|
buf[sizeof buf - 1] = 0;
|
||||||
save = NULL;
|
save = NULL;
|
||||||
|
|
||||||
|
uint32_t mods = 0;
|
||||||
for (char *t = strtok_r(buf, "-", &save); t;
|
for (char *t = strtok_r(buf, "-", &save); t;
|
||||||
t = strtok_r(NULL, "-", &save)) {
|
t = strtok_r(NULL, "-", &save)) {
|
||||||
if (t == last)
|
if (t == last)
|
||||||
@@ -83,7 +86,6 @@ static int parse_bind(
|
|||||||
mods |= mod_from_token(t);
|
mods |= mod_from_token(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* keysym from last token */
|
|
||||||
int flags = XKB_KEYSYM_CASE_INSENSITIVE;
|
int flags = XKB_KEYSYM_CASE_INSENSITIVE;
|
||||||
xkb_keysym_t sym = xkb_keysym_from_name(last, flags);
|
xkb_keysym_t sym = xkb_keysym_from_name(last, flags);
|
||||||
if (sym == XKB_KEY_NoSymbol)
|
if (sym == XKB_KEY_NoSymbol)
|
||||||
@@ -96,13 +98,56 @@ static int parse_bind(
|
|||||||
|
|
||||||
static int push_config_table_from_idx(lua_State *L, int idx_abs)
|
static int push_config_table_from_idx(lua_State *L, int idx_abs)
|
||||||
{
|
{
|
||||||
if (!lua_istable(L, idx_abs)) {
|
if (!lua_istable(L, idx_abs))
|
||||||
return luaL_error(L, "config: expected table at index %d", idx_abs);
|
return luaL_error(L, "config: expected table at index %d", idx_abs);
|
||||||
}
|
|
||||||
lua_pushvalue(L, idx_abs);
|
lua_pushvalue(L, idx_abs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int join_string_array(lua_State *L, int idx_abs, char **out)
|
||||||
|
{
|
||||||
|
*out = NULL;
|
||||||
|
if (!lua_istable(L, idx_abs))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
size_t n = (size_t)lua_rawlen(L, idx_abs);
|
||||||
|
if (n == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
size_t total = 1;
|
||||||
|
for (size_t i = 1; i <= n; ++i) {
|
||||||
|
lua_rawgeti(L, idx_abs, (lua_Integer)i);
|
||||||
|
size_t len = 0;
|
||||||
|
(void)lua_tolstring(L, -1, &len);
|
||||||
|
total += len + (i < n ? 1 : 0);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *buf = (char *)malloc(total);
|
||||||
|
if (!buf)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
size_t off = 0;
|
||||||
|
for (size_t i = 1; i <= n; ++i) {
|
||||||
|
lua_rawgeti(L, idx_abs, (lua_Integer)i);
|
||||||
|
size_t len = 0;
|
||||||
|
char const *s = lua_tolstring(L, -1, &len);
|
||||||
|
if (!s) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
free(buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(buf + off, s, len);
|
||||||
|
off += len;
|
||||||
|
lua_pop(L, 1);
|
||||||
|
if (i != n)
|
||||||
|
buf[off++] = ',';
|
||||||
|
}
|
||||||
|
buf[off] = 0;
|
||||||
|
*out = buf;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int config_load_ref(lua_State *L, int idx, Config *out)
|
int config_load_ref(lua_State *L, int idx, Config *out)
|
||||||
{
|
{
|
||||||
if (!L || !out)
|
if (!L || !out)
|
||||||
@@ -110,8 +155,8 @@ int config_load_ref(lua_State *L, int idx, Config *out)
|
|||||||
|
|
||||||
memset(out, 0, sizeof(*out));
|
memset(out, 0, sizeof(*out));
|
||||||
|
|
||||||
int idx_abs = lua_absindex(L, idx);
|
int cfg_abs = lua_absindex(L, idx);
|
||||||
if (push_config_table_from_idx(L, idx_abs) != 0)
|
if (push_config_table_from_idx(L, cfg_abs) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
lua_getfield(L, -1, "keybindings");
|
lua_getfield(L, -1, "keybindings");
|
||||||
@@ -121,92 +166,130 @@ int config_load_ref(lua_State *L, int idx, Config *out)
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t n = (size_t)lua_rawlen(L, -1);
|
size_t n = (size_t)lua_rawlen(L, -1);
|
||||||
if (n == 0) {
|
if (n) {
|
||||||
lua_pop(L, 2);
|
BindingRef *arr = (BindingRef *)calloc(n, sizeof(*arr));
|
||||||
return 0;
|
if (!arr) {
|
||||||
}
|
lua_pop(L, 2);
|
||||||
|
return luaL_error(L, "config: OOM");
|
||||||
|
}
|
||||||
|
|
||||||
BindingRef *arr = calloc(n, sizeof(BindingRef));
|
size_t ok = 0;
|
||||||
if (!arr) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
lua_pop(L, 2);
|
lua_rawgeti(L, -1, (lua_Integer)(i + 1));
|
||||||
return luaL_error(L, "config: OOM allocating bindings");
|
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);
|
||||||
|
|
||||||
|
arr[ok].mods_mask = mods;
|
||||||
|
arr[ok].sym = sym;
|
||||||
|
arr[ok].action_ref = ref;
|
||||||
|
++ok;
|
||||||
|
|
||||||
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);
|
lua_pop(L, 1);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_getfield(L, -1, "bind");
|
if (ok == 0) {
|
||||||
char const *bind = lua_tostring(L, -1);
|
free(arr);
|
||||||
if (!bind) {
|
} else if (ok < n) {
|
||||||
lua_pop(L, 2);
|
BindingRef *shr = (BindingRef *)realloc(arr, ok * sizeof(*arr));
|
||||||
continue;
|
if (shr)
|
||||||
|
arr = shr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t mods = 0;
|
out->keybindings.items = (ok ? arr : NULL);
|
||||||
xkb_keysym_t sym = XKB_KEY_NoSymbol;
|
out->keybindings.count = ok;
|
||||||
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, 1);
|
||||||
|
|
||||||
lua_pop(L, 2); // pop keybindings table + config table
|
lua_getfield(L, -1, "input");
|
||||||
|
if (lua_istable(L, -1)) {
|
||||||
if (ok_count == 0) {
|
lua_getfield(L, -1, "keyboard");
|
||||||
free(arr);
|
if (lua_istable(L, -1)) {
|
||||||
out->bindings = NULL;
|
lua_getfield(L, -1, "xkb_options");
|
||||||
out->count = 0;
|
if (lua_istable(L, -1)) {
|
||||||
return 0;
|
(void)join_string_array(
|
||||||
|
L, lua_absindex(L, -1), &out->input.keyboard.xkb_options);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
if (ok_count < n) {
|
lua_pop(L, 1);
|
||||||
BindingRef *shr = realloc(arr, ok_count * sizeof(BindingRef));
|
|
||||||
if (shr)
|
|
||||||
arr = shr;
|
|
||||||
}
|
|
||||||
|
|
||||||
out->bindings = arr;
|
|
||||||
out->count = ok_count;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void config_unref(lua_State *L, Config *cfg)
|
void config_unref(lua_State *L, Config *cfg)
|
||||||
{
|
{
|
||||||
if (!cfg || !cfg->bindings) {
|
if (!cfg)
|
||||||
if (cfg)
|
|
||||||
cfg->count = 0;
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < cfg->keybindings.count; ++i) {
|
||||||
|
int r = cfg->keybindings.items ? cfg->keybindings.items[i].action_ref
|
||||||
|
: LUA_NOREF;
|
||||||
|
if (r != LUA_NOREF && r != LUA_REFNIL)
|
||||||
|
luaL_unref(L, LUA_REGISTRYINDEX, r);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < cfg->count; i++) {
|
free(cfg->keybindings.items);
|
||||||
if (cfg->bindings[i].action_ref != LUA_NOREF
|
cfg->keybindings.items = NULL;
|
||||||
&& cfg->bindings[i].action_ref != LUA_REFNIL) {
|
cfg->keybindings.count = 0;
|
||||||
luaL_unref(L, LUA_REGISTRYINDEX, cfg->bindings[i].action_ref);
|
|
||||||
|
free(cfg->input.keyboard.xkb_options);
|
||||||
|
cfg->input.keyboard.xkb_options = 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->keybindings.count; ++i) {
|
||||||
|
BindingRef const *br = &cfg->keybindings.items[i];
|
||||||
|
if (br->sym != sym)
|
||||||
|
continue;
|
||||||
|
if ((mods & br->mods_mask) != br->mods_mask)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
free(cfg->bindings);
|
return 1;
|
||||||
cfg->bindings = NULL;
|
|
||||||
cfg->count = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load_config_file(lua_State *L, char const *path)
|
static int load_config_file(lua_State *L, char const *path)
|
||||||
@@ -251,12 +334,10 @@ ConfigManager *config_manager_create(char const *path)
|
|||||||
|
|
||||||
if (!path)
|
if (!path)
|
||||||
path = get_config_path();
|
path = get_config_path();
|
||||||
if (path) {
|
if (path && dupstr(path, &cm->path) != 0) {
|
||||||
if (dupstr(path, &cm->path) != 0) {
|
lua_close(cm->L);
|
||||||
lua_close(cm->L);
|
free(cm);
|
||||||
free(cm);
|
return NULL;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cm->path && load_config_file(cm->L, cm->path) == 0) {
|
if (cm->path && load_config_file(cm->L, cm->path) == 0) {
|
||||||
@@ -287,48 +368,19 @@ int config_manager_reload(ConfigManager *cm)
|
|||||||
|
|
||||||
config_unref(cm->L, &cm->cfg);
|
config_unref(cm->L, &cm->cfg);
|
||||||
|
|
||||||
if (load_config_file(cm->L, cm->path) != 0) {
|
if (load_config_file(cm->L, cm->path) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
int rc = config_load_ref(cm->L, -1, &cm->cfg);
|
int rc = config_load_ref(cm->L, -1, &cm->cfg);
|
||||||
lua_pop(cm->L, 1);
|
lua_pop(cm->L, 1);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_State *config_manager_lua(ConfigManager *cm) { return cm ? cm->L : NULL; }
|
lua_State *config_manager_lua(ConfigManager *cm) { return cm ? cm->L : NULL; }
|
||||||
|
|
||||||
Config const *config_manager_get(ConfigManager *cm)
|
Config const *config_manager_get(ConfigManager *cm)
|
||||||
{
|
{
|
||||||
return cm ? &cm->cfg : NULL;
|
return cm ? &cm->cfg : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char const *config_manager_path(ConfigManager *cm)
|
char const *config_manager_path(ConfigManager *cm)
|
||||||
{
|
{
|
||||||
return cm ? cm->path : NULL;
|
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;
|
|
||||||
}
|
|
||||||
|
|||||||
14
src/Config.h
14
src/Config.h
@@ -9,12 +9,20 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
xkb_keysym_t sym;
|
xkb_keysym_t sym;
|
||||||
uint32_t mods_mask;
|
uint32_t mods_mask;
|
||||||
int action_ref; // luaL_ref(L, LUA_REGISTRYINDEX)
|
int action_ref;
|
||||||
} BindingRef;
|
} BindingRef;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
BindingRef *bindings;
|
struct {
|
||||||
size_t count;
|
struct {
|
||||||
|
char *xkb_options;
|
||||||
|
} keyboard;
|
||||||
|
} input;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
BindingRef *items;
|
||||||
|
size_t count;
|
||||||
|
} keybindings;
|
||||||
} Config;
|
} Config;
|
||||||
|
|
||||||
char const *get_config_path(void);
|
char const *get_config_path(void);
|
||||||
|
|||||||
@@ -257,9 +257,16 @@ static void new_input_listener_notify(struct wl_listener *listener, void *data)
|
|||||||
keyboard->server = wm;
|
keyboard->server = wm;
|
||||||
keyboard->wlr_keyboard = wlr_keyboard;
|
keyboard->wlr_keyboard = wlr_keyboard;
|
||||||
|
|
||||||
|
struct xkb_rule_names const rule_names = {
|
||||||
|
.options = wm->cman->cfg.input.keyboard.xkb_options,
|
||||||
|
};
|
||||||
|
|
||||||
|
wlr_log(LOG_INFO, "xkb_options=%s",
|
||||||
|
wm->cman->cfg.input.keyboard.xkb_options);
|
||||||
|
|
||||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
struct xkb_keymap *keymap = xkb_keymap_new_from_names(
|
struct xkb_keymap *keymap = xkb_keymap_new_from_names(
|
||||||
context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
context, &rule_names, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
|
|
||||||
wlr_keyboard_set_keymap(wlr_keyboard, keymap);
|
wlr_keyboard_set_keymap(wlr_keyboard, keymap);
|
||||||
xkb_keymap_unref(keymap);
|
xkb_keymap_unref(keymap);
|
||||||
|
|||||||
@@ -159,7 +159,6 @@ typedef struct LunarWM {
|
|||||||
Camera3D camera;
|
Camera3D camera;
|
||||||
} renderer;
|
} renderer;
|
||||||
|
|
||||||
Config config;
|
|
||||||
ConfigManager *cman;
|
ConfigManager *cman;
|
||||||
|
|
||||||
bool initialized;
|
bool initialized;
|
||||||
|
|||||||
Reference in New Issue
Block a user