aboutsummaryrefslogtreecommitdiff
path: root/src/keyboard.c
blob: af79006ced6913282ed5e93aab6611f63ceac18c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include "keyboard.h"

#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <xkbcommon/xkbcommon.h>

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);
}

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);
}