You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
274 lines
5.3 KiB
274 lines
5.3 KiB
/* |
|
* keyd - A key remapping daemon. |
|
* |
|
* © 2019 Raheman Vaiya (see also: LICENSE). |
|
*/ |
|
#include <ctype.h> |
|
#include <string.h> |
|
#include <assert.h> |
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
|
|
#include "unicode.h" |
|
#include "macro.h" |
|
#include "layer.h" |
|
#include "descriptor.h" |
|
#include "ini.h" |
|
#include "command.h" |
|
#include "config.h" |
|
#include "error.h" |
|
#include "keys.h" |
|
#include "string.h" |
|
|
|
static struct { |
|
const char *name; |
|
uint8_t op; |
|
enum { |
|
ARG_EMPTY, |
|
|
|
ARG_MACRO, |
|
ARG_LAYER, |
|
ARG_LAYOUT, |
|
ARG_TIMEOUT, |
|
ARG_DESCRIPTOR, |
|
} args[MAX_DESCRIPTOR_ARGS]; |
|
} actions[] = { |
|
{ "swap", OP_SWAP, { ARG_LAYER } }, |
|
{ "swap2", OP_SWAP2, { ARG_LAYER, ARG_MACRO } }, |
|
{ "oneshot", OP_ONESHOT, { ARG_LAYER } }, |
|
{ "toggle", OP_TOGGLE, { ARG_LAYER } }, |
|
{ "toggle2", OP_TOGGLE2, { ARG_LAYER, ARG_MACRO } }, |
|
{ "layer", OP_LAYER, { ARG_LAYER } }, |
|
{ "overload", OP_OVERLOAD, { ARG_LAYER, ARG_DESCRIPTOR } }, |
|
{ "timeout", OP_TIMEOUT, { ARG_DESCRIPTOR, ARG_TIMEOUT, ARG_DESCRIPTOR } }, |
|
{ "macro2", OP_MACRO2, { ARG_TIMEOUT, ARG_TIMEOUT, ARG_MACRO } }, |
|
{ "setlayout", OP_LAYOUT, { ARG_LAYOUT } }, |
|
}; |
|
|
|
/* Modifies the input string */ |
|
static int parse_fn(char *s, |
|
char **name, |
|
char *args[MAX_DESCRIPTOR_ARGS], |
|
size_t *nargs) |
|
{ |
|
char *c, *arg; |
|
|
|
c = s; |
|
while (*c && *c != '(') |
|
c++; |
|
|
|
if (!*c) |
|
return -1; |
|
|
|
*name = s; |
|
*c++ = 0; |
|
|
|
while (*c == ' ') |
|
c++; |
|
|
|
*nargs = 0; |
|
arg = c; |
|
while (1) { |
|
int plvl = 0; |
|
|
|
while (*c) { |
|
switch (*c) { |
|
case '\\': |
|
if (*(c+1)) { |
|
c+=2; |
|
continue; |
|
} |
|
break; |
|
case '(': |
|
plvl++; |
|
break; |
|
case ')': |
|
plvl--; |
|
|
|
if (plvl == -1) |
|
goto exit; |
|
break; |
|
case ',': |
|
if (plvl == 0) |
|
goto exit; |
|
break; |
|
} |
|
|
|
c++; |
|
} |
|
exit: |
|
|
|
if (!*c) |
|
return -1; |
|
|
|
assert(*nargs < MAX_DESCRIPTOR_ARGS); |
|
args[(*nargs)++] = arg; |
|
|
|
if (*c == ')') { |
|
*c = 0; |
|
return 0; |
|
} |
|
|
|
*c++ = 0; |
|
while (*c == ' ') |
|
c++; |
|
arg = c; |
|
} |
|
} |
|
|
|
int parse_descriptor(const char *descstr, |
|
struct descriptor *d, |
|
struct config *config) |
|
{ |
|
char *fn = NULL; |
|
char *args[MAX_DESCRIPTOR_ARGS]; |
|
size_t nargs = 0; |
|
uint8_t code, mods; |
|
int ret; |
|
struct macro macro; |
|
struct command cmd; |
|
|
|
char fnstr[MAX_EXP_LEN+1]; |
|
|
|
if (strlen(descstr) > MAX_EXP_LEN) { |
|
err("maximum descriptor length exceeded"); |
|
return -1; |
|
} |
|
|
|
strcpy(fnstr, descstr); |
|
|
|
if (!parse_key_sequence(descstr, &code, &mods)) { |
|
d->op = OP_KEYSEQUENCE; |
|
d->args[0].code = code; |
|
d->args[1].mods = mods; |
|
|
|
/* TODO: fixme. */ |
|
if (keycode_to_mod(code)) |
|
fprintf(stderr, |
|
"WARNING: mapping modifier keycodes directly may produce unintended results, you probably want layer(<modifier name>) instead\n"); |
|
|
|
return 0; |
|
} else if ((ret=parse_command(descstr, &cmd)) >= 0) { |
|
if (ret) { |
|
err("max command length (%d) exceeded\n", MAX_COMMAND_LEN); |
|
return -1; |
|
} |
|
|
|
if (config->nr_commands >= MAX_COMMANDS) { |
|
err("max commands (%d), exceeded", MAX_COMMANDS); |
|
return -1; |
|
} |
|
|
|
|
|
d->op = OP_COMMAND; |
|
d->args[0].idx = config->nr_commands; |
|
|
|
config->commands[config->nr_commands++] = cmd; |
|
|
|
return 0; |
|
} else if ((ret=parse_macro(descstr, ¯o)) >= 0) { |
|
if (ret) { |
|
return -1; |
|
} |
|
|
|
if (config->nr_macros >= MAX_MACROS) { |
|
err("max macros (%d), exceeded", MAX_MACROS); |
|
return -1; |
|
} |
|
|
|
d->op = OP_MACRO; |
|
d->args[0].idx = config->nr_macros; |
|
|
|
config->macros[config->nr_macros++] = macro; |
|
|
|
return 0; |
|
} else if (!parse_fn(fnstr, &fn, args, &nargs)) { |
|
int i; |
|
|
|
for (i = 0; i < (int)(sizeof(actions)/sizeof(actions[0])); i++) { |
|
if (!strcmp(actions[i].name, fn)) { |
|
int j; |
|
|
|
d->op = actions[i].op; |
|
|
|
for (j = 0; j < MAX_DESCRIPTOR_ARGS; j++) { |
|
if (!actions[i].args[j]) |
|
break; |
|
} |
|
|
|
if ((int)nargs != j) { |
|
err("%s requires %d %s", actions[i].name, j, j == 1 ? "argument" : "arguments"); |
|
return -1; |
|
} |
|
|
|
while (j--) { |
|
int type = actions[i].args[j]; |
|
descriptor_arg_t *arg = &d->args[j]; |
|
const char *argstr = args[j]; |
|
struct descriptor desc; |
|
|
|
switch (type) { |
|
case ARG_LAYER: |
|
if (!strcmp(argstr, "main")) { |
|
err("the main layer cannot be toggled"); |
|
return -1; |
|
} |
|
|
|
arg->idx = config_get_layer_index(config, argstr); |
|
if (arg->idx == -1 || config->layers[arg->idx].type == LT_LAYOUT) { |
|
err("%s is not a valid layer", argstr); |
|
return -1; |
|
} |
|
|
|
break; |
|
case ARG_LAYOUT: |
|
arg->idx = config_get_layer_index(config, argstr); |
|
if (arg->idx == -1 || config->layers[arg->idx].type != LT_LAYOUT) { |
|
err("%s is not a valid layout", argstr); |
|
return -1; |
|
} |
|
|
|
break; |
|
case ARG_DESCRIPTOR: |
|
if (parse_descriptor(argstr, &desc, config)) |
|
return -1; |
|
|
|
if (config->nr_descriptors >= MAX_AUX_DESCRIPTORS) { |
|
err("maximum descriptors exceeded"); |
|
return -1; |
|
} |
|
|
|
config->descriptors[config->nr_descriptors] = desc; |
|
arg->idx = config->nr_descriptors++; |
|
break; |
|
case ARG_TIMEOUT: |
|
arg->timeout = atoi(argstr); |
|
break; |
|
case ARG_MACRO: |
|
if (config->nr_macros >= MAX_MACROS) { |
|
err("max macros (%d), exceeded", MAX_MACROS); |
|
return -1; |
|
} |
|
|
|
if (parse_macro(argstr, &config->macros[config->nr_macros])) { |
|
return -1; |
|
} |
|
|
|
arg->idx = config->nr_macros; |
|
config->nr_macros++; |
|
|
|
break; |
|
default: |
|
assert(0); |
|
break; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
} |
|
} |
|
|
|
err("invalid key or action"); |
|
return -1; |
|
}
|
|
|