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.
 
 
 
 
 
 

618 lines
10 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 <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <dirent.h>
#include <ctype.h>
#include <limits.h>
#include "config.h"
#include "keys.h"
#define MAX_ARGS 10
struct keyboard_config *configs = NULL;
static char layers[MAX_LAYERS][MAX_LAYER_NAME_LEN]; //Layer names
static size_t nlayers;
static int parents[MAX_LAYERS];
static int lnum = 0;
static char path[PATH_MAX];
#define err(fmt, ...) fprintf(stderr, "%s: ERROR on line %d: "fmt"\n", path, lnum, ##__VA_ARGS__)
static const char *modseq_to_string(uint8_t mods) {
static char s[32];
int i = 0;
s[0] = '\0';
if(mods & MOD_CTRL) {
strcpy(s+i, "-C");
i+=2;
}
if(mods & MOD_SHIFT) {
strcpy(s+i, "-S");
i+=2;
}
if(mods & MOD_SUPER) {
strcpy(s+i, "-M");
i+=2;
}
if(mods & MOD_ALT) {
strcpy(s+i, "-A");
i+=2;
}
if(mods & MOD_ALT_GR) {
strcpy(s+i, "-I");
i+=2;
}
if(i)
return s+1;
else
return s;
}
static const char *keyseq_to_string(uint32_t keyseq) {
int i = 0;
static char s[256];
uint8_t mods = keyseq >> 16;
uint16_t code = keyseq & 0x00FF;
const char *key = keycode_strings[code];
if(mods & MOD_CTRL) {
strcpy(s+i, "C-");
i+=2;
}
if(mods & MOD_SHIFT) {
strcpy(s+i, "S-");
i+=2;
}
if(mods & MOD_SUPER) {
strcpy(s+i, "M-");
i+=2;
}
if(mods & MOD_ALT) {
strcpy(s+i, "A-");
i+=2;
}
if(mods & MOD_ALT_GR) {
strcpy(s+i, "I-");
i+=2;
}
if(key)
strcpy(s+i, keycode_strings[code]);
else
strcpy(s+i, "UNKNOWN");
return s;
}
static int parse_mods(const char *s, uint8_t *mods)
{
*mods = 0;
while(*s) {
switch(*s) {
case 'C':
*mods |= MOD_CTRL;
break;
case 'M':
*mods |= MOD_SUPER;
break;
case 'A':
*mods |= MOD_ALT;
break;
case 'S':
*mods |= MOD_SHIFT;
break;
case 'I':
*mods |= MOD_ALT_GR;
break;
default:
return -1;
break;
}
if(s[1] == 0)
return 0;
else if(s[1] != '-') {
err("%s is not a valid modifier set", s);
return -1;
}
s+=2;
}
return 0;
}
static int parse_keyseq(const char *s, uint16_t *keycode, uint8_t *mods) {
const char *c = s;
size_t i;
*mods = 0;
*keycode = 0;
while(*c && c[1] == '-') {
switch(*c) {
case 'C':
*mods |= MOD_CTRL;
break;
case 'M':
*mods |= MOD_SUPER;
break;
case 'A':
*mods |= MOD_ALT;
break;
case 'S':
*mods |= MOD_SHIFT;
break;
case 'I':
*mods |= MOD_ALT_GR;
break;
default:
return -1;
break;
}
c += 2;
}
for(i = 0;i < sizeof keycode_strings / sizeof keycode_strings[0];i++) {
if(keycode_strings[i] && !strcmp(keycode_strings[i], c)) {
*keycode |= i;
return 0;
}
}
return -1;
}
static int parse_kvp(char *s, char **_k, char **_v)
{
char *v = NULL, *k = NULL;
while(*s && isspace(*s)) s++;
if(!*s) return -1;
k = s;
while(*s && !isspace(*s) && *s != '=') s++;
if(!*s) return -1;
while(*s && *s != '=') {
*s = 0;
s++;
}
if(!*s) return -1;
*s++ = 0;
while(*s && isspace(*s)) *s++ = 0;
if(!*s) return -1;
v = s;
*_k = k;
*_v = v;
return 0;
}
static void parse_layer_names()
{
size_t i;
char *line = NULL;
size_t n;
ssize_t len;
char strparents[MAX_LAYERS][MAX_LAYER_NAME_LEN] = {0};
FILE *fh = fopen(path, "r");
if(!fh) {
fprintf(stderr, "ERROR: Failed to open %s\n", path);
perror("fopen");
exit(-1);
}
nlayers = 0;
strcpy(layers[nlayers++], "default");
while((len=getline(&line, &n, fh)) != -1) {
char *s = line;
while(len && isspace(s[0])) {
s++;
len--;
}
if(len > 2 && s[0] == '[' && s[len-2] == ']') {
const char *name = s+1;
size_t idx = nlayers;
char *c;
s[len-2] = '\0';
if((c=strchr(name, ':'))) {
const char *parent = c+1;
*c = '\0';
strcpy(strparents[idx], parent);
}
strcpy(layers[idx], name);
nlayers++;
}
free(line);
line = NULL;
n = 0;
}
free(line);
fclose(fh);
for(i = 0;i < nlayers;i++) {
size_t j;
parents[i] = -1;
for(j = 0;j < nlayers;j++) {
if(!strcmp(layers[j], strparents[i])) {
parents[i] = j;
}
}
if(strparents[i][0] != 0 && parents[i] == -1) {
err("%s is not a valid parent layer", strparents[i]);
}
}
}
static int parse_fn(char *s, char **fn_name, char *args[MAX_ARGS], size_t *nargs)
{
int openparen = 0;
*fn_name = s;
*nargs = 0;
while(*s && *s != ')') {
switch(*s) {
case '(':
openparen++;
*s = '\0';
s++;
while(*s && isspace(*s))
s++;
if(!*s) {
err("Missing closing parenthesis.");
return -1;
}
if(*s == ')') { //No args
*s = '\0';
return 0;
}
args[(*nargs)++] = s;
break;
case ',':
*s = '\0';
s++;
while(*s && isspace(*s))
s++;
if(!*s) {
err("Missing closing parenthesis.");
return -1;
}
args[(*nargs)++] = s;
break;
}
s++;
}
if(*s != ')') {
if(openparen)
err("Missing closing parenthesis.");
else
err("Invalid function or key sequence.");
return -1;
}
*s = '\0';
return 0;
}
static int parse_val(const char *_s, struct key_descriptor *desc)
{
uint16_t code;
uint8_t mods;
char *s = strdup(_s);
char *fn;
char *args[MAX_ARGS];
size_t nargs;
if(!parse_keyseq(s, &code, &mods)) {
desc->action = ACTION_KEYSEQ;
desc->arg.keyseq = ((uint32_t)mods << 16) | (uint32_t)code;
goto cleanup;
}
if(parse_fn(s, &fn, args, &nargs))
goto fail;
if(!strcmp(fn, "oneshot_layer") && nargs == 1) {
size_t i;
for(i = 0;i < nlayers;i++)
if(!strcmp(args[0], layers[i])) {
desc->action = ACTION_LAYER_ONESHOT;
desc->arg.layer = i;
goto cleanup;
}
err("%s is not a valid layer name.", args[0]);
goto fail;
} else if(!strcmp(fn, "layer") && nargs == 1) {
size_t i;
for(i = 0;i < nlayers;i++)
if(!strcmp(args[0], layers[i])) {
desc->action = ACTION_LAYER;
desc->arg.layer = i;
goto cleanup;
}
err("%s is not a valid layer name.", args[0]);
goto fail;
} else if(!strcmp(fn, "layer_toggle") && nargs == 1) {
size_t i;
for(i = 0;i < nlayers;i++)
if(!strcmp(args[0], layers[i])) {
desc->action = ACTION_LAYER_TOGGLE;
desc->arg.layer = i;
goto cleanup;
}
err("%s is not a valid layer name.", args[0]);
goto fail;
} else if(!strcmp(fn, "oneshot") && nargs == 1) {
if(parse_mods(args[0], &mods))
goto fail;
desc->action = ACTION_ONESHOT;
desc->arg.mods = mods;
} else if(!strcmp(fn, "layer_on_hold") && nargs == 2) {
size_t i;
desc->action = ACTION_DOUBLE_LAYER;
if(parse_keyseq(args[1], &code, &mods)) {
err("%s is not a vaid keysequence.", args[1]);
goto fail;
}
desc->arg2.keyseq = ((uint32_t)mods << 16) | (uint32_t)code;
for(i = 0;i < nlayers;i++)
if(!strcmp(args[0], layers[i])) {
desc->arg.layer = i;
goto cleanup;
}
err("%s is not a valid layer.", args[0]);
goto fail;
} else if(!strcmp(fn, "mods_on_hold") && nargs == 2) {
desc->action = ACTION_DOUBLE_MODIFIER;
if(parse_mods(args[0], &mods))
goto fail;
desc->arg.mods = mods;
if(parse_keyseq(args[1], &code, &mods)) {
err("%s is not a vaid keysequence.", args[1]);
goto fail;
}
desc->arg2.keyseq = ((uint32_t)mods << 16) | (uint32_t)code;
} else {
err("%s is not a valid action or key.", _s);
goto fail;
}
cleanup:
free(s);
return 0;
fail:
free(s);
return -1;
}
static void parse(struct keyboard_config *cfg)
{
size_t i, j;
char *line = NULL;
size_t n = 0;
ssize_t len;
lnum = 0;
parse_layer_names();
FILE *fh = fopen(path, "r");
if(!fh) {
perror("fopen");
exit(-1);
}
int current_layer = 0;
struct key_descriptor *layer = cfg->layers[0];
lnum = 0;
while((len=getline(&line, &n, fh)) != -1) {
lnum++;
char *s = line;
while(len && isspace(s[0])) { //Trim leading whitespace
s++;
len--;
}
if(len && s[len-1] == '\n') //Strip tailing newline (not present on EOF)
s[--len] = '\0';
if(len == 0 || s[0] == '#')
continue;
if(s[0] == '[' && s[len-1] == ']') {
layer = cfg->layers[++current_layer];
} else if(layer) {
struct key_descriptor desc;
char *key, *val;
uint16_t code;
uint8_t mods;
if(parse_kvp(s, &key, &val)) {
err("Invalid key value pair.");
goto next;
}
if(parse_keyseq(key, &code, &mods)) {
err("'%s' is not a valid key.", key);
goto next;
}
if(mods != 0) {
err("key cannot contain modifiers.");
goto next;
}
if(parse_val(val, &desc))
goto next;
layer[code] = desc;
}
next:
free(line);
line = NULL;
n = 0;
}
free(line);
fclose(fh);
for(i = 0;i < nlayers;i++) {
int p = parents[i];
if(p != -1) {
for(j = 0;j < KEY_CNT;j++) {
if(cfg->layers[i][j].action == ACTION_DEFAULT)
cfg->layers[i][j] = cfg->layers[p][j];
}
}
}
}
void config_free()
{
struct keyboard_config *cfg = configs;
while(cfg) {
struct keyboard_config *tmp = cfg;
cfg = cfg->next;
free(tmp);
};
}
void config_generate()
{
struct dirent *ent;
DIR *dh = opendir(CONFIG_DIR);
if(!dh) {
perror("opendir");
exit(-1);
}
while((ent=readdir(dh))) {
size_t i;
struct keyboard_config *cfg;
int len = strlen(ent->d_name);
if(len <= 4 || strcmp(ent->d_name+len-4, ".cfg"))
continue;
sprintf(path, "%s/%s", CONFIG_DIR, ent->d_name);
cfg = calloc(1, sizeof(struct keyboard_config));
for(i = 0;i < KEY_CNT;i++) {
struct key_descriptor *desc = &cfg->layers[0][i];
desc->action = ACTION_KEYSEQ;
desc->arg.keyseq = i;
}
strcpy(cfg->name, ent->d_name);
cfg->name[len-4] = '\0';
cfg->next = configs;
parse(cfg);
configs = cfg;
}
closedir(dh);
}