summaryrefslogtreecommitdiff
path: root/src/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/config.c')
-rw-r--r--src/config.c198
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, &section->children);
+ else if (strcmp(section->name, "axis") == 0)
+ handle_axis(pad, &section->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, &section->children);
+ else if (strcmp(section->name, "axis") == 0)
+ handle_axis(pad, &section->children);
+ else if (strcmp(section->name, "mode") == 0)
+ handle_mode(pad, section->params_len >= 1 ? section->params[0] : NULL, &section->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;
+}