diff options
Diffstat (limited to 'src/config.c')
| -rw-r--r-- | src/config.c | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..44bd859 --- /dev/null +++ b/src/config.c @@ -0,0 +1,198 @@ +#include <ctype.h> +#include <err.h> +#include <stdlib.h> +#include <string.h> + +#include <scfg.h> + +#include "waypad.h" +#include "util.h" + +void add_exec(struct action *action, struct scfg_directive *exec) { + size_t argc = exec->params_len - 1; + + action->type = ACTION_EXEC; + action->exec = calloc(argc + 1, sizeof(*action->exec)); + + for (size_t i = 0; i < argc; i++) + action->exec[i] = strdup(exec->params[i + 1]); +} + +void add_mouse(struct action *action, struct scfg_directive *mouse) { + if (mouse->params_len < 2) // mouse <left|right|center> + return; + action->type = ACTION_CLICK; + if (strcmp(mouse->params[1], "left") == 0) + action->click = BTN_LEFT; + else if (strcmp(mouse->params[1], "right") == 0) + action->click = BTN_RIGHT; + else if (strcmp(mouse->params[1], "center") == 0) + action->click = BTN_MIDDLE; +} + +static const struct { + const char *name; + enum keyboard_modifier mod; +} mod_map[] = { + { "shift", KEYMOD_LSHIFT }, + { "lshift", KEYMOD_LSHIFT }, + { "rshift", KEYMOD_RSHIFT }, + { "ctrl", KEYMOD_LCTRL }, + { "lctrl", KEYMOD_LCTRL }, + { "rctrl", KEYMOD_RCTRL }, + { "alt", KEYMOD_LALT }, + { "lalt", KEYMOD_LALT }, + { "ralt", KEYMOD_RALT }, + { "super", KEYMOD_LMETA }, + { "meta", KEYMOD_LMETA }, + { "lmeta", KEYMOD_LMETA }, + { "rmeta", KEYMOD_RMETA }, +}; + +void add_key(struct action *action, struct scfg_directive *key) { + enum keyboard_modifier mod = 0; + const char *param; + size_t i; + + if (key->params_len < 2) // key [<mod>...] <key> + return; + + for (i = 1; i < key->params_len - 1; i++) + for (size_t j = 0; j < lenghtof(mod_map); j++) + if (strcmp(mod_map[j].name, key->params[i]) == 0) + mod |= mod_map[j].mod; + param = key->params[i]; + + char keysym[strlen(param) + sizeof("KEY_")]; + char *sym = stpcpy(keysym, "KEY_"); + for (i = 0; param[i]; i++) + sym[i] = toupper(param[i]); + sym[i] = '\0'; + + action->type = ACTION_KEY; + action->key.mod = mod; + action->key.code = libevdev_event_code_from_code_name(keysym); + + if (action->key.code == -1) + warn("failed to find evdev codename %s", keysym); +} + +void add_mode(struct gamepad *pad, struct action *action, struct scfg_directive *mode_bind) { + if (mode_bind->params_len < 2) // mode <name> + return; + action->type = ACTION_MODE; + action->mode = get_make_mode(pad, mode_bind->params[1]); +} + +static void handle_buttons(struct gamepad *pad, struct scfg_block *buttons) { + for (size_t i = 0; i < buttons->directives_len; i++) { + struct scfg_directive *button_bind = &buttons->directives[i]; + size_t action_idx; + if (sscanf(button_bind->name, "btn%zu", &action_idx) != 1) { + enum gamepad_button map_idx = str_to_button(button_bind->name); + if (map_idx == GAMEPAD_BUTTON_INVALID) + continue; + action_idx = pad->mapping->buttons[map_idx]; + } + + if (action_idx >= pad->nbuttons) { + warn("invalid button \"%s\"", button_bind->name); + continue; + } + + struct action *action = &pad->modes.current->buttons[action_idx]; + if (button_bind->params_len < 2) + continue; + if (strcmp(button_bind->params[0], "exec") == 0) + add_exec(action, button_bind); + else if (strcmp(button_bind->params[0], "key") == 0) + add_key(action, button_bind); + else if (strcmp(button_bind->params[0], "mouse") == 0) + add_mouse(action, button_bind); + else if (strcmp(button_bind->params[0], "mode") == 0) + add_mode(pad, action, button_bind); + else + warn("unknown command %s.", button_bind->params[0]); + } +} + +static void handle_axis(struct gamepad *pad, struct scfg_block *axis) { + for (size_t i = 0; i < axis->directives_len; i++) { + struct scfg_directive *axis_bind = &axis->directives[i]; + size_t action_idx; + + if (sscanf(axis_bind->name, "axis%zu", &action_idx) != 1) { + enum gamepad_axis map_idx = str_to_axis(axis_bind->name); + if (map_idx == GAMEPAD_AXIS_INVALID) + continue; + action_idx = pad->mapping->axis[map_idx]; + } + + if (action_idx >= pad->naxis) { + warn("invalid axis \"%s\"", axis_bind->name); + continue; + } + + if (axis_bind->params_len < 2) + continue; + + struct action *action = &pad->modes.current->axis[action_idx]; + + action->type = ACTION_MOUSE; + switch (axis_bind->params[1][0]) { + case 'x': + action->mouse = MOUSE_X; + break; + case 'y': + action->mouse = MOUSE_Y; + break; + default: + continue; + } + } +} + +static void handle_mode(struct gamepad *pad, const char *name, struct scfg_block *mode) { + if (!name) + return; + pad->modes.current = get_make_mode(pad, name); + for (size_t i = 0; i < mode->directives_len; i++) { + struct scfg_directive *section = &mode->directives[i]; + if (strcmp(section->name, "button") == 0) + handle_buttons(pad, §ion->children); + else if (strcmp(section->name, "axis") == 0) + handle_axis(pad, §ion->children); + else if (strcmp(section->name, "mode") == 0) + warn("can't nest modes."); + } + pad->modes.current = &pad->modes.base; +} + +static void handle_gamepad(struct gamepad *pad, struct scfg_block *cfg) { + for (size_t i = 0; i < cfg->directives_len; i++) { + struct scfg_directive *section = &cfg->directives[i]; + if (strcmp(section->name, "button") == 0) + handle_buttons(pad, §ion->children); + else if (strcmp(section->name, "axis") == 0) + handle_axis(pad, §ion->children); + else if (strcmp(section->name, "mode") == 0) + handle_mode(pad, section->params_len >= 1 ? section->params[0] : NULL, §ion->children); + } +} + +bool load_config(const char *path) { + struct scfg_block config; + if (scfg_load_file(&config, path) != 0) + return false; + + for (size_t i = 0; i < config.directives_len; i++) { + struct gamepad *pad = find_gamepad(config.directives[i].name); + if (!pad) { + fprintf(stderr, "\"%s\" not found.\n", config.directives[i].name); + continue; + } + handle_gamepad(pad, &config.directives[i].children); + } + + return true; +} |
