summaryrefslogtreecommitdiffstats
path: root/keymap.c
diff options
context:
space:
mode:
Diffstat (limited to 'keymap.c')
-rw-r--r--keymap.c277
1 files changed, 277 insertions, 0 deletions
diff --git a/keymap.c b/keymap.c
new file mode 100644
index 0000000..34d492e
--- /dev/null
+++ b/keymap.c
@@ -0,0 +1,277 @@
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <assert.h>
+#include <linux/input.h>
+
+#include "keymap.h"
+
+/* Mysteriously missing defines from <linux/input.h>. */
+#define EV_MAKE 1
+#define EV_BREAK 0
+#define EV_REPEAT 2
+
+/* Default US keymap */
+static wchar_t char_keys[49] = L"1234567890-=qwertyuiop[]asdfghjkl;'`\\zxcvbnm,./<";
+static wchar_t shift_keys[49] = L"!@#$%^&*()_+QWERTYUIOP{}ASDFGHJKL:\"~|ZXCVBNM<>?>";
+static wchar_t altgr_keys[49] = {0};
+
+static wchar_t func_keys[][8] = {
+ L"<Esc>", L"<BckSp>", L"<Tab>", L"<Enter>", L"<LCtrl>", L"<LShft>",
+ L"<RShft>", L"<KP*>", L"<LAlt>", L" ", L"<CpsLk>", L"<F1>", L"<F2>",
+ L"<F3>", L"<F4>", L"<F5>", L"<F6>", L"<F7>", L"<F8>", L"<F9>", L"<F10>",
+ L"<NumLk>", L"<ScrLk>", L"<KP7>", L"<KP8>", L"<KP9>", L"<KP->", L"<KP4>",
+ L"<KP5>", L"<KP6>", L"<KP+>", L"<KP1>", L"<KP2>", L"<KP3>", L"<KP0>",
+ L"<KP.>", /*"<",*/ L"<F11>", L"<F12>", L"<KPEnt>", L"<RCtrl>", L"<KP/>",
+ L"<PrtSc>", L"<AltGr>", L"<Break>" /*linefeed?*/, L"<Home>", L"<Up>",
+ L"<PgUp>", L"<Left>", L"<Right>", L"<End>", L"<Down>", L"<PgDn>",
+ L"<Ins>", L"<Del>", L"<Pause>", L"<LMeta>", L"<RMeta>", L"<Menu>"
+};
+
+/* c = character key
+ * f = function key
+ * _ = blank/error
+ *
+ * Source: KEY_* defines from <linux/input.h>
+ */
+static const char char_or_func[] =
+ "_fccccccccccccff"
+ "ccccccccccccffcc"
+ "ccccccccccfccccc"
+ "ccccccffffffffff"
+ "ffffffffffffffff"
+ "ffff__cff_______"
+ "ffffffffffffffff"
+ "_______f_____fff";
+
+static int is_char_key(unsigned int code)
+{
+ assert(code < sizeof(char_or_func));
+ return char_or_func[code] == 'c';
+}
+static int is_func_key(unsigned int code)
+{
+ assert(code < sizeof(char_or_func));
+ return char_or_func[code] == 'f';
+}
+static int is_used_key(unsigned int code)
+{
+ assert(code < sizeof(char_or_func));
+ return char_or_func[code] != '_';
+}
+
+/* Translates character keycodes to continuous array indexes. */
+static int to_char_keys_index(unsigned int keycode)
+{
+ if (keycode >= KEY_1 && keycode <= KEY_EQUAL) // keycodes 2-13: US keyboard: 1, 2, ..., 0, -, =
+ return keycode - 2;
+ if (keycode >= KEY_Q && keycode <= KEY_RIGHTBRACE) // keycodes 16-27: q, w, ..., [, ]
+ return keycode - 4;
+ if (keycode >= KEY_A && keycode <= KEY_GRAVE) // keycodes 30-41: a, s, ..., ', `
+ return keycode - 6;
+ if (keycode >= KEY_BACKSLASH && keycode <= KEY_SLASH) // keycodes 43-53: \, z, ..., ., /
+ return keycode - 7;
+ if (keycode == KEY_102ND) // key right to the left of 'Z' on US layout
+ return 47;
+
+ return -1; // not character keycode
+}
+/* Translates function keys keycodes to continuous array indexes. */
+static int to_func_keys_index(unsigned int keycode)
+{
+ if (keycode == KEY_ESC) // 1
+ return 0;
+ if (keycode >= KEY_BACKSPACE && keycode <= KEY_TAB) // 14-15
+ return keycode - 13;
+ if (keycode >= KEY_ENTER && keycode <= KEY_LEFTCTRL) // 28-29
+ return keycode - 25;
+ if (keycode == KEY_LEFTSHIFT) // 42
+ return keycode - 37;
+ if (keycode >= KEY_RIGHTSHIFT && keycode <= KEY_KPDOT) // 54-83
+ return keycode - 48;
+ if (keycode >= KEY_F11 && keycode <= KEY_F12) // 87-88
+ return keycode - 51;
+ if (keycode >= KEY_KPENTER && keycode <= KEY_DELETE) // 96-111
+ return keycode - 58;
+ if (keycode == KEY_PAUSE) // 119
+ return keycode - 65;
+ if (keycode >= KEY_LEFTMETA && keycode <= KEY_COMPOSE) // 125-127
+ return keycode - 70;
+
+ return -1; // not function key keycode
+}
+
+/* Translates struct input_event *event into the string of size buffer_length
+ * pointed to by buffer. Stores state originating from sequence of event structs
+ * in struc input_event_state *state. Returns the number of bytes written to buffer.
+ */
+size_t translate_event(struct input_event *event, struct input_event_state *state, char *buffer, size_t buffer_len)
+{
+ wchar_t wch, *wbuffer;
+ size_t wbuffer_len, len;
+
+ len = 0;
+ wbuffer = (wchar_t*)buffer;
+ wbuffer_len = buffer_len / sizeof(wchar_t);
+
+ if (event->type != EV_KEY)
+ goto out;
+
+ if (event->code >= sizeof(char_or_func)) {
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<E-%x>", event->code);
+ goto out;
+ }
+
+ if (event->value == EV_MAKE || event->value == EV_REPEAT) {
+ if (event->code == KEY_LEFTSHIFT || event->code == KEY_RIGHTSHIFT) {
+ state->shift = 1;
+ goto out;
+ } else if (event->code == KEY_RIGHTALT) {
+ state->altgr = 1;
+ goto out;
+ } else if (event->code == KEY_LEFTALT) {
+ state->alt = 1;
+ goto out;
+ } else if (event->code == KEY_LEFTCTRL || event->code == KEY_RIGHTCTRL) {
+ state->ctrl = 1;
+ goto out;
+ } else if (event->code == KEY_LEFTMETA || event->code == KEY_RIGHTMETA) {
+ state->meta = 1;
+ goto out;
+ } else {
+ if (state->ctrl && state->alt && state->meta)
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<CTRL,ALT,META>+");
+ else if (state->ctrl && state->alt)
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<CTRL,ALT>+");
+ else if (state->alt && state->meta)
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<ALT,META>+");
+ else if (state->ctrl && state->meta)
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<CTRL,META>+");
+ else if (state->meta)
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<META>+");
+ else if (state->ctrl)
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<CTRL>+");
+ else if (state->alt)
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<ALT>+");
+ }
+ if (is_char_key(event->code)) {
+ if (state->altgr) {
+ wch = altgr_keys[to_char_keys_index(event->code)];
+ if (wch == L'\0') {
+ if (state->shift)
+ wch = shift_keys[to_char_keys_index(event->code)];
+ else
+ wch = char_keys[to_char_keys_index(event->code)];
+ }
+ }
+ else if (state->shift) {
+ wch = shift_keys[to_char_keys_index(event->code)];
+ if (wch == L'\0')
+ wch = char_keys[to_char_keys_index(event->code)];
+ }
+ else
+ wch = char_keys[to_char_keys_index(event->code)];
+
+ if (wch != L'\0') {
+ len += swprintf(&wbuffer[len], wbuffer_len, L"%lc", wch);
+ goto out;
+ }
+ }
+ else if (is_func_key(event->code)) {
+ len += swprintf(&wbuffer[len], wbuffer_len, L"%ls", func_keys[to_func_keys_index(event->code)]);
+ goto out;
+ }
+ else {
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<E-%x>", event->code);
+ goto out;
+ }
+ }
+ if (event->value == EV_BREAK) {
+ if (event->code == KEY_LEFTSHIFT || event->code == KEY_RIGHTSHIFT)
+ state->shift = 0;
+ else if (event->code == KEY_RIGHTALT)
+ state->altgr = 0;
+ else if (event->code == KEY_LEFTALT)
+ state->alt = 0;
+ else if (event->code == KEY_LEFTCTRL || event->code == KEY_RIGHTCTRL)
+ state->ctrl = 0;
+ else if (event->code == KEY_LEFTMETA || event->code == KEY_RIGHTMETA)
+ state->meta = 0;
+ }
+
+out:
+ if (!len)
+ *buffer = 0;
+ else
+ wcstombs(buffer, wbuffer, buffer_len);
+ return len;
+}
+
+/* Determines the system keymap via the dump keys program
+ * and some disgusting parsing of it. */
+int load_system_keymap()
+{
+ FILE *dumpkeys;
+ char buffer[256];
+ char *start, *end;
+ unsigned int keycode;
+ int index;
+ wchar_t wch;
+ /* HACK: This is obscenely ugly, and we should really just do this in C... */
+ dumpkeys = popen("/usr/bin/dumpkeys -n | /bin/grep '^\\([[:space:]]shift[[:space:]]\\)*\\([[:space:]]altgr[[:space:]]\\)*keycode' | /bin/sed 's/U+/0x/g' 2>&1", "r");
+ if (!dumpkeys) {
+ perror("popen");
+ return 1;
+ }
+ memset(char_keys, 0, sizeof(char_keys));
+ memset(shift_keys, 0, sizeof(shift_keys));
+ memset(altgr_keys, 0, sizeof(altgr_keys));
+ for (keycode = 1; fgets(buffer, sizeof(buffer), dumpkeys)
+ && keycode < sizeof(char_or_func); ++keycode) {
+ if (!is_char_key(keycode))
+ continue;
+ if (buffer[0] == 'k') {
+ index = to_char_keys_index(keycode);
+
+ start = &buffer[14];
+ wch = (wchar_t)strtoul(start, &end, 16);
+ if (start[0] == '+' && (wch & 0xB00))
+ wch ^= 0xB00;
+ char_keys[index] = wch;
+
+ start = end;
+ while (start[0] == ' ' && start[0] != '\0')
+ ++start;
+ wch = (wchar_t)strtoul(start, &end, 16);
+ if (start[0] == '+' && (wch & 0xB00))
+ wch ^= 0xB00;
+ if (wch == L'\0') {
+ wch = towupper(char_keys[index]);
+ if (wch == char_keys[index])
+ wch = L'\0';
+ }
+ shift_keys[index] = wch;
+
+ start = end;
+ while (start[0] == ' ' && start[0] != '\0')
+ ++start;
+ wch = (wchar_t)strtoul(start, &end, 16);
+ if (start[0] == '+' && (wch & 0xB00))
+ wch ^= 0xB00;
+ altgr_keys[index] = wch;
+ } else {
+ index = to_char_keys_index(keycode == 0 ? 0 : --keycode);
+ wch = (wchar_t)strtoul(&buffer[21], NULL, 16);
+ if (buffer[21] == '+' && (wch & 0xB00))
+ wch ^= 0xB00;
+ if (buffer[1] == 's')
+ shift_keys[index] = wch;
+ else if (buffer[1] == 'a')
+ altgr_keys[index] = wch;
+ }
+ }
+ pclose(dumpkeys);
+ return 0;
+
+}