#include "keyboard.h" #include #include #include #include #include #include struct imv_keyboard { struct xkb_context *context; struct xkb_keymap *keymap; struct xkb_state *state; }; struct imv_keyboard *imv_keyboard_create(void) { struct imv_keyboard *keyboard = calloc(1, sizeof *keyboard); keyboard->context = xkb_context_new(0); assert(keyboard->context); struct xkb_rule_names names = { .rules = NULL, .model = NULL, .layout = NULL, .variant = NULL, .options = NULL, }; keyboard->keymap = xkb_keymap_new_from_names(keyboard->context, &names, 0); assert(keyboard->keymap); keyboard->state = xkb_state_new(keyboard->keymap); assert(keyboard->state); return keyboard; } void imv_keyboard_free(struct imv_keyboard *keyboard) { if (!keyboard) { return; } xkb_state_unref(keyboard->state); keyboard->state = NULL; xkb_keymap_unref(keyboard->keymap); keyboard->keymap = NULL; xkb_context_unref(keyboard->context); keyboard->context = NULL; free(keyboard); } static const int scancode_offset = 8; void imv_keyboard_update_key(struct imv_keyboard *keyboard, int scancode, bool pressed) { xkb_state_update_key(keyboard->state, scancode + scancode_offset, pressed ? XKB_KEY_DOWN : XKB_KEY_UP); } void imv_keyboard_update_mods(struct imv_keyboard *keyboard, int depressed, int latched, int locked) { xkb_state_update_mask(keyboard->state, depressed, latched, locked, 0, 0, 0); } static const char *describe_prefix(struct imv_keyboard *keyboard) { const bool ctrl = (xkb_state_mod_name_is_active(keyboard->state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0); const bool alt = (xkb_state_mod_name_is_active(keyboard->state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) > 0); const bool shift = (xkb_state_mod_name_is_active(keyboard->state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) > 0); if (ctrl && !alt && !shift) { return "Ctrl+"; } else if (!ctrl && alt && !shift) { return "Meta+"; } else if (!ctrl && !alt && shift) { return "Shift+"; } else if (ctrl && alt && !shift) { return "Ctrl+Meta+"; } else if (ctrl && !alt && shift) { return "Ctrl+Shift+"; } else if (!ctrl && alt && shift) { return "Meta+Shift+"; } else if (ctrl && alt && shift) { return "Ctrl+Meta+Shift+"; } else { return ""; } } size_t imv_keyboard_keyname(struct imv_keyboard *keyboard, int scancode, char *buf, size_t buflen) { xkb_keysym_t keysym = xkb_state_key_get_one_sym(keyboard->state, scancode + scancode_offset); return xkb_keysym_get_name(keysym, buf, buflen); } char *imv_keyboard_describe_key(struct imv_keyboard *keyboard, int scancode) { char keyname[128] = {0}; imv_keyboard_keyname(keyboard, scancode, keyname, sizeof keyname); /* Modifier keys don't count on their own, only when pressed with another key */ if (!strcmp(keyname, "Control_L") || !strcmp(keyname, "Control_R") || !strcmp(keyname, "Alt_L") || !strcmp(keyname, "Alt_R") || !strcmp(keyname, "Shift_L") || !strcmp(keyname, "Shift_R") || !strcmp(keyname, "Meta_L") || !strcmp(keyname, "Meta_R") || !strcmp(keyname, "Super_L") || !strcmp(keyname, "Super_R")) { return NULL; } const char *prefix = describe_prefix(keyboard); char buf[128]; snprintf(buf, sizeof buf, "%s%s", prefix, keyname); return strdup(buf); } size_t imv_keyboard_get_text(struct imv_keyboard *keyboard, int scancode, char *buf, size_t buflen) { return xkb_state_key_get_utf8(keyboard->state, scancode + scancode_offset, buf, buflen); } void imv_keyboard_set_keymap(struct imv_keyboard *keyboard, const char *keymap) { xkb_keymap_unref(keyboard->keymap); keyboard->keymap = xkb_keymap_new_from_string(keyboard->context, keymap, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); xkb_state_unref(keyboard->state); keyboard->state = xkb_state_new(keyboard->keymap); } bool imv_keyboard_should_key_repeat(struct imv_keyboard *keyboard, int scancode) { return xkb_keymap_key_repeats(keyboard->keymap, scancode + scancode_offset); }