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.
 
 
 
 
 
 

321 lines
6.8 KiB

/* Copyright © 2019 Raheman Vaiya.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include "descriptor.h"
#include "layer.h"
#include "keys.h"
#include "error.h"
#include "config.h"
#define MAX_ARGS 5
static int parse_fn(char *s,
char **name,
char *args[MAX_ARGS],
size_t *nargs)
{
size_t len = strlen(s);
size_t n = 0;
char *arg, *c;
if (len == 0 || s[len-1] != ')')
return -1;
s[len-1] = 0;
if (!(c = strchr(s, '(')))
return -1;
*c = '\0';
*name = s;
s = c + 1;
for (arg = strtok(s, ","); arg; arg = strtok(NULL, ",")) {
while (isspace(arg[0]))
arg++;
if (n >= MAX_ARGS)
return 0;
args[n++] = arg;
}
*nargs = n;
return 0;
}
static int parse_key_sequence(const char *s, struct key_sequence *seq)
{
const char *c = s;
uint16_t code;
if (!*s)
return -1;
seq->mods = 0;
while (c[1] == '-') {
switch (*c) {
case 'C':
seq->mods |= MOD_CTRL;
break;
case 'M':
seq->mods|= MOD_SUPER;
break;
case 'A':
seq->mods |= MOD_ALT;
break;
case 'S':
seq->mods |= MOD_SHIFT;
break;
case 'G':
seq->mods |= MOD_ALT_GR;
break;
default:
return -1;
break;
}
c += 2;
}
for (code = 0; code < KEY_MAX; code++) {
const struct keycode_table_ent *ent = &keycode_table[code];
if (ent->name) {
if (ent->shifted_name &&
!strcmp(ent->shifted_name, c)) {
seq->mods |= MOD_SHIFT;
seq->code = code;
return 0;
} else if (!strcmp(ent->name, c) ||
(ent->alt_name && !strcmp(ent->alt_name, c))) {
seq->code = code;
return 0;
}
}
}
return -1;
}
static int parse_macro_fn(struct macro *macro, char *s)
{
size_t len = strlen(s);
char *tok;
size_t sz = 0;
if (strstr(s, "macro(") != s || s[len-1] != ')')
return -1;
s[len-1] = 0;
macro->sz = 0;
for (tok = strtok(s + 6, " "); tok; tok = strtok(NULL, " ")) {
struct macro_entry ent;
len = strlen(tok);
if (!parse_key_sequence(tok, &ent.data.sequence)) {
assert(sz < MAX_MACRO_SIZE);
ent.type = MACRO_KEYSEQUENCE;
macro->entries[sz++] = ent;
} else if (len > 1 && tok[len-2] == 'm' && tok[len-1] == 's') {
int len = atoi(tok);
ent.type = MACRO_TIMEOUT;
ent.data.timeout = len;
assert(sz < MAX_MACRO_SIZE);
macro->entries[sz++] = ent;
} else {
char *c;
if (strchr(tok, '+')) {
char *saveptr;
char *key;
for (key = strtok_r(tok, "+", &saveptr); key; key = strtok_r(NULL, "+", &saveptr)) {
if (parse_key_sequence(key, &ent.data.sequence) < 0) {
return -1;
}
ent.type = MACRO_HOLD;
assert(sz < MAX_MACRO_SIZE);
macro->entries[sz++] = ent;
}
ent.type = MACRO_RELEASE;
assert(sz < MAX_MACRO_SIZE);
macro->entries[sz++] = ent;
} else {
for (c = tok; *c; c++) {
char s[2];
s[0] = *c;
s[1] = 0;
if (parse_key_sequence(s, &ent.data.sequence) < 0) {
return -1;
}
ent.type = MACRO_KEYSEQUENCE;
assert(sz < MAX_MACRO_SIZE);
macro->entries[sz++] = ent;
}
}
}
}
macro->sz = sz;
return 0;
}
static int config_get_free_macro_idx(struct config *config)
{
size_t i;
/* Scan the macro table for a free entry. */
for (i = 0; i < sizeof(config->macros)/sizeof(config->macros[0]); i++)
if (!config->macros[i].sz)
return i;
die("Max macros exceeded in %s\n", config->name);
return -1;
}
/*
* Modifies the input string, consumes a function which which is used for
* resolving layer names as required.
*/
int parse_descriptor(char *s,
struct descriptor *desc,
struct config *config)
{
struct key_sequence seq;
char *fn;
char *args[MAX_ARGS];
size_t nargs = 0;
int macro_idx = config_get_free_macro_idx(config);
if (!parse_key_sequence(s, &seq)) {
desc->op = OP_KEYSEQ;
if (keycode_to_mod(seq.code))
fprintf(stderr,
"WARNING: mapping modifier keycodes directly may produce unintended results, you probably want layer(<modifier name>) instead\n");
desc->args[0].sequence = seq;
} else if (!parse_macro_fn(&config->macros[macro_idx], s)) {
desc->op = OP_MACRO;
desc->args[0].idx = macro_idx;
} else if (!parse_fn(s, &fn, args, &nargs)) {
int layer_idx;
if (!strcmp(fn, "layer"))
desc->op = OP_LAYER;
else if (!strcmp(fn, "reset"))
desc->op = OP_RESET;
else if (!strcmp(fn, "toggle"))
desc->op = OP_TOGGLE;
else if (!strcmp(fn, "layout"))
desc->op = OP_LAYOUT;
else if (!strcmp(fn, "oneshot"))
desc->op = OP_ONESHOT;
else if (!strcmp(fn, "overload"))
desc->op = OP_OVERLOAD;
else if (!strcmp(fn, "swap"))
desc->op = OP_SWAP;
else {
err("\"%s\" is not a valid action or key sequence.", s);
return -1;
}
if (desc->op == OP_RESET)
return 0;
if (nargs == 0) {
err("%s requires one or more arguments.", fn);
return -1;
}
layer_idx = config_lookup_layer(config, args[0]);
if (layer_idx == -1) {
uint16_t mods;
/* Autovivify modifier layers. */
if (!parse_modset(args[0], &mods)) {
layer_idx = config_create_layer(config, args[0], mods);
assert(layer_idx > 0);
} else {
err("\"%s\" is not a valid layer", args[0]);
return -1;
}
}
if (desc->op == OP_LAYOUT && !config->layers[layer_idx].is_layout) {
err("\"%s\" must be a valid layout.", args[0]);
return -1;
}
desc->args[0].idx = layer_idx;
desc->args[1].sequence = (struct key_sequence){0};
if (nargs > 1) {
int ret;
ret = parse_key_sequence(args[1], &seq);
if (ret < 0) {
err("\"%s\" is not a valid key sequence", args[1]);
return -1;
}
desc->args[1].sequence = seq;
}
if (desc->op == OP_OVERLOAD && nargs == 3) {
desc->op = OP_OVERLOAD_TIMEOUT;
desc->args[2].timeout = atoi(args[2]);
}
} else {
err("\"%s\" is not a valid key sequence or action.", s);
return -1;
}
return 0;
}