hikari/src/keyboard_config.c

383 lines
10 KiB
C

#include <hikari/keyboard_config.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#define HIKARI_KEYBOARD_CONFIG_DEFAULT_REPEAT_RATE 25
#define HIKARI_KEYBOARD_CONFIG_DEFAULT_REPEAT_DELAY 600
static void
init_default_repeat(struct hikari_keyboard_config *keyboard_config)
{
hikari_keyboard_config_set_repeat_rate(
keyboard_config, HIKARI_KEYBOARD_CONFIG_DEFAULT_REPEAT_RATE);
hikari_keyboard_config_set_repeat_delay(
keyboard_config, HIKARI_KEYBOARD_CONFIG_DEFAULT_REPEAT_DELAY);
}
static bool
parse_xkb_rules(
struct hikari_xkb_config *xkb_config, const ucl_object_t *xkb_obj)
{
bool success = false;
const ucl_object_t *cur;
ucl_object_iter_t it = ucl_object_iterate_new(xkb_obj);
while ((cur = ucl_object_iterate_safe(it, false)) != NULL) {
const char *key = ucl_object_key(cur);
if (!strcmp("rules", key)) {
const char *rules;
if (!ucl_object_tostring_safe(cur, &rules)) {
fprintf(stderr, "configuration error: expected string for \"rules\"\n");
goto done;
}
hikari_xkb_config_set_rules(xkb_config, strdup(rules));
} else if (!strcmp("model", key)) {
const char *model;
if (!ucl_object_tostring_safe(cur, &model)) {
fprintf(stderr, "configuration error: expected string for \"model\"\n");
goto done;
}
hikari_xkb_config_set_model(xkb_config, strdup(model));
} else if (!strcmp("layout", key)) {
const char *layout;
if (!ucl_object_tostring_safe(cur, &layout)) {
fprintf(
stderr, "configuration error: expected string for \"layout\"\n");
goto done;
}
hikari_xkb_config_set_layout(xkb_config, strdup(layout));
} else if (!strcmp("variant", key)) {
const char *variant;
if (!ucl_object_tostring_safe(cur, &variant)) {
fprintf(
stderr, "configuration error: expected string for \"variant\"\n");
goto done;
}
hikari_xkb_config_set_variant(xkb_config, strdup(variant));
} else if (!strcmp("options", key)) {
const char *options;
if (!ucl_object_tostring_safe(cur, &options)) {
fprintf(
stderr, "configuration error: expected string for \"options\"\n");
goto done;
}
hikari_xkb_config_set_options(xkb_config, strdup(options));
} else {
fprintf(stderr, "configuration error: invalid \"xkb\" key \"%s\"\n", key);
goto done;
}
}
success = true;
done:
ucl_object_iterate_free(it);
return success;
}
static bool
load_xkb_file(struct hikari_xkb *xkb, const char *xkb_file)
{
bool success = false;
FILE *keymap_file = fopen(xkb_file, "r");
if (!keymap_file) {
fprintf(stderr,
"configuration error: failed to open \"xkb\" file \"%s\"\n",
xkb_file);
goto done;
}
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
struct xkb_keymap *keymap = xkb_keymap_new_from_file(context,
keymap_file,
XKB_KEYMAP_FORMAT_TEXT_V1,
XKB_KEYMAP_COMPILE_NO_FLAGS);
xkb_context_unref(context);
if (keymap == NULL) {
goto done;
}
xkb->type = HIKARI_XKB_TYPE_RULES;
xkb->value.keymap = keymap;
success = true;
done:
return success;
}
bool
hikari_keyboard_config_parse(struct hikari_keyboard_config *keyboard_config,
const ucl_object_t *keyboard_config_obj)
{
bool success = false;
const ucl_object_t *cur;
ucl_object_iter_t it = ucl_object_iterate_new(keyboard_config_obj);
while ((cur = ucl_object_iterate_safe(it, false)) != NULL) {
const char *key = ucl_object_key(cur);
if (!strcmp("xkb", key)) {
ucl_type_t type = ucl_object_type(cur);
if (type == UCL_OBJECT) {
keyboard_config->xkb.type = HIKARI_XKB_TYPE_RULES;
if (!parse_xkb_rules(&keyboard_config->xkb.value.rules, cur)) {
fprintf(stderr, "configuration error: failed to parse xkb rules\n");
goto done;
}
} else if (type == UCL_STRING) {
const char *xkb_file;
if (!ucl_object_tostring_safe(cur, &xkb_file) ||
!load_xkb_file(&keyboard_config->xkb, xkb_file)) {
goto done;
}
} else {
fprintf(stderr,
"configuration error: expected string or object for \"xkb\"\n");
goto done;
}
} else if (!strcmp("repeat-rate", key)) {
int64_t repeat_rate;
if (!ucl_object_toint_safe(cur, &repeat_rate) || repeat_rate < 0) {
fprintf(stderr,
"configuration error: expected positive integer \"repeat-rate\"\n");
goto done;
}
hikari_keyboard_config_set_repeat_rate(keyboard_config, repeat_rate);
} else if (!strcmp("repeat-delay", key)) {
int64_t repeat_delay;
if (!ucl_object_toint_safe(cur, &repeat_delay) || repeat_delay < 0) {
fprintf(stderr,
"configuration error: expected positive integer "
"\"repeat-delay\"\n");
goto done;
}
hikari_keyboard_config_set_repeat_delay(keyboard_config, repeat_delay);
} else {
fprintf(stderr,
"configuration error: invalid \"keyboard\" key \"%s\"\n",
key);
goto done;
}
}
success = true;
done:
ucl_object_iterate_free(it);
return success;
}
static void
xkb_init(struct hikari_xkb_config *xkb_config)
{
hikari_xkb_config_init_rules(xkb_config, NULL);
hikari_xkb_config_init_model(xkb_config, NULL);
hikari_xkb_config_init_layout(xkb_config, NULL);
hikari_xkb_config_init_variant(xkb_config, NULL);
hikari_xkb_config_init_options(xkb_config, NULL);
}
void
hikari_keyboard_config_init(
struct hikari_keyboard_config *keyboard_config, const char *keyboard_name)
{
keyboard_config->xkb.type = HIKARI_XKB_TYPE_RULES;
xkb_init(&keyboard_config->xkb.value.rules);
keyboard_config->keyboard_name = strdup(keyboard_name);
if (!strcmp(keyboard_name, "*")) {
init_default_repeat(keyboard_config);
} else {
hikari_keyboard_config_init_repeat_rate(
keyboard_config, HIKARI_KEYBOARD_CONFIG_DEFAULT_REPEAT_RATE);
hikari_keyboard_config_init_repeat_delay(
keyboard_config, HIKARI_KEYBOARD_CONFIG_DEFAULT_REPEAT_DELAY);
}
}
static void
xkb_fini(struct hikari_xkb_config *xkb_config)
{
free(xkb_config->rules.value);
free(xkb_config->model.value);
free(xkb_config->layout.value);
free(xkb_config->variant.value);
free(xkb_config->options.value);
}
void
hikari_keyboard_config_fini(struct hikari_keyboard_config *keyboard_config)
{
switch (keyboard_config->xkb.type) {
case HIKARI_XKB_TYPE_KEYMAP:
xkb_keymap_unref(keyboard_config->xkb.value.keymap);
break;
case HIKARI_XKB_TYPE_RULES:
xkb_fini(&keyboard_config->xkb.value.rules);
break;
}
free(keyboard_config->keyboard_name);
}
static void
default_init(struct hikari_xkb_config *xkb_config)
{
char *rules = getenv("XKB_DEFAULT_RULES");
char *model = getenv("XKB_DEFAULT_MODEL");
char *layout = getenv("XKB_DEFAULT_LAYOUT");
char *variant = getenv("XKB_DEFAULT_VARIANT");
char *options = getenv("XKB_DEFAULT_OPTIONS");
if (rules == NULL) {
hikari_xkb_config_set_rules(xkb_config, strdup("evdev"));
} else {
hikari_xkb_config_set_rules(xkb_config, strdup(rules));
}
if (model != NULL) {
hikari_xkb_config_set_model(xkb_config, strdup(model));
}
if (layout != NULL) {
hikari_xkb_config_set_layout(xkb_config, strdup(layout));
}
if (variant != NULL) {
hikari_xkb_config_set_variant(xkb_config, strdup(variant));
}
if (options != NULL) {
hikari_xkb_config_set_options(xkb_config, strdup(options));
}
}
void
hikari_keyboard_config_default(struct hikari_keyboard_config *keyboard_config)
{
keyboard_config->xkb.type = HIKARI_XKB_TYPE_RULES;
xkb_init(&keyboard_config->xkb.value.rules);
default_init(&keyboard_config->xkb.value.rules);
keyboard_config->keyboard_name = malloc(2 * sizeof(char));
strcpy(keyboard_config->keyboard_name, "*");
init_default_repeat(keyboard_config);
}
static bool
has_set_options(struct hikari_xkb_config *xkb_config)
{
return hikari_xkb_config_has_rules(xkb_config) ||
hikari_xkb_config_has_model(xkb_config) ||
hikari_xkb_config_has_layout(xkb_config) ||
hikari_xkb_config_has_variant(xkb_config) ||
hikari_xkb_config_has_options(xkb_config);
}
#define MERGE(option) \
if (hikari_xkb_config_merge_##option(config_xkb, default_xkb) && \
default_xkb->option.value != NULL) { \
config_xkb->option.value = strdup(default_xkb->option.value); \
}
void
hikari_keyboard_config_merge(struct hikari_keyboard_config *keyboard_config,
struct hikari_keyboard_config *default_config)
{
hikari_keyboard_config_merge_repeat_rate(keyboard_config, default_config);
hikari_keyboard_config_merge_repeat_delay(keyboard_config, default_config);
if (default_config->xkb.type == HIKARI_XKB_TYPE_KEYMAP) {
if (keyboard_config->xkb.type == HIKARI_XKB_TYPE_RULES) {
if (!has_set_options(&keyboard_config->xkb.value.rules)) {
keyboard_config->xkb.type = HIKARI_XKB_TYPE_KEYMAP;
keyboard_config->xkb.value.keymap =
xkb_keymap_ref(default_config->xkb.value.keymap);
}
}
} else {
assert(default_config->xkb.type == HIKARI_XKB_TYPE_RULES);
if (keyboard_config->xkb.type == HIKARI_XKB_TYPE_RULES) {
struct hikari_xkb_config *config_xkb = &keyboard_config->xkb.value.rules;
struct hikari_xkb_config *default_xkb = &default_config->xkb.value.rules;
MERGE(rules);
MERGE(model);
MERGE(layout);
MERGE(variant);
MERGE(options);
}
}
}
#undef MERGE
static struct xkb_keymap *
compile_keymap(struct hikari_xkb_config *xkb_config)
{
struct xkb_rule_names rules = { 0 };
rules.rules = xkb_config->rules.value;
rules.model = xkb_config->model.value;
rules.layout = xkb_config->layout.value;
rules.variant = xkb_config->variant.value;
rules.options = xkb_config->options.value;
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
struct xkb_keymap *keymap =
xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
xkb_context_unref(context);
return keymap;
}
bool
hikari_keyboard_config_compile_keymap(
struct hikari_keyboard_config *keyboard_config)
{
if (keyboard_config->xkb.type == HIKARI_XKB_TYPE_KEYMAP) {
return true;
}
assert(keyboard_config->xkb.type == HIKARI_XKB_TYPE_RULES);
struct xkb_keymap *keymap = compile_keymap(&keyboard_config->xkb.value.rules);
if (keymap == NULL) {
return false;
}
xkb_fini(&keyboard_config->xkb.value.rules);
keyboard_config->xkb.type = HIKARI_XKB_TYPE_KEYMAP;
keyboard_config->xkb.value.keymap = keymap;
return true;
}