Created
July 12, 2022 21:44
-
-
Save imawizard/388fda54cad2c09ff4736c675202d40e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* based on dvorak.c and resources linked by Thomas Bocek | |
* compile with `gcc qw2mydv.c -o qw2mydv -std=c99 -pedantic -Wall -O3 -D_BSD_SOURCE` | |
* execute with `sudo ./qw2mydv /dev/input/event0` | |
*/ | |
/* TODO: | |
* change keystate.shift to .lshift and .rshift | |
* add RSHIFT keymapping | |
* add MOD_CUSTOM, doesn't emit a key but sets custom_mod | |
* check on arrow keys through caps for custom_mod and change accordingly | |
*/ | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <linux/input.h> | |
#include <linux/uinput.h> | |
#define die(str) \ | |
do { \ | |
perror(str); \ | |
exit(EXIT_FAILURE); \ | |
} while(0) | |
#define MOD_DEAD (1 << 1) | |
#define MOD_LCTRL (1 << 2) | |
#define MOD_RCTRL (1 << 3) | |
#define MOD_LSHIFT (1 << 4) | |
#define MOD_RSHIFT (1 << 5) | |
#define MOD_LALT (1 << 6) | |
#define MOD_RALT (1 << 7) | |
#define MOD_CAPS (1 << 8) | |
#define MOD_NUM (1 << 9) | |
#define MOD_SCROLL (1 << 10) | |
#define MOD_CTRL (MOD_LCTRL | MOD_RCTRL) | |
#define MOD_SHIFT (MOD_LSHIFT | MOD_RSHIFT) | |
#define MOD_ALT (MOD_LALT | MOD_RALT) | |
struct key { | |
int code; | |
int mods; | |
}; | |
struct keystate { | |
struct key nomod; | |
struct key shift; | |
struct key caps; | |
}; | |
enum evval_t { | |
RELEASED, | |
PRESSED, | |
REPEATED | |
}; | |
static void adjust_mods(int fd, int mods, int newmods); | |
#define emitsyn(fd) emit((fd), EV_SYN, 0, SYN_REPORT) | |
#define emitkd(fd, key) emit((fd), EV_KEY, (key), PRESSED) | |
#define emitku(fd, key) emit((fd), EV_KEY, (key), RELEASED) | |
#define emitkp(fd, key) \ | |
do { \ | |
emitkd((fd), (key)); \ | |
emitku((fd), (key)); \ | |
} while (0) | |
static int emit(int fd, int type, int code, int value) | |
{ | |
struct input_event ev; | |
ev.type = type; | |
ev.code = code; | |
ev.value = value; | |
/* timestamp values below are ignored */ | |
ev.time.tv_sec = 0; | |
ev.time.tv_usec = 0; | |
return write(fd, &ev, sizeof(ev)); | |
} | |
static void emitcp(int fd, int codepoint) | |
{ | |
static int hexkeys[] = { | |
KEY_0, KEY_1, KEY_2, KEY_3, | |
KEY_4, KEY_5, KEY_6, KEY_7, | |
KEY_8, KEY_9, KEY_A, KEY_B, | |
KEY_C, KEY_D, KEY_E, KEY_F | |
}; | |
int buf[4]; | |
int n = 0; | |
emitkd(fd, KEY_LEFTCTRL); | |
emitkd(fd, KEY_LEFTSHIFT); | |
emitkp(fd, KEY_U); | |
emitku(fd, KEY_LEFTCTRL); | |
emitku(fd, KEY_LEFTSHIFT); | |
for (int rest = codepoint; rest; rest /= 16) | |
buf[n++] = rest % 16; | |
for (int i = n; i--;) | |
emitkp(fd, hexkeys[buf[i]]); | |
emitkp(fd, KEY_ENTER); | |
} | |
static void emitkey(int fd, int key, int keymods, int mods) | |
{ | |
adjust_mods(fd, mods, keymods); | |
if (key > 0) { | |
/* generate single key presses */ | |
emitkp(fd, key); | |
} else { | |
/* generate unicode codepoints */ | |
emitcp(fd, abs(key)); | |
} | |
adjust_mods(fd, keymods, mods); | |
} | |
static void print_event(struct input_event *ev) { | |
if (ev->type != EV_KEY) { | |
char *type; | |
switch (ev->type) { | |
case EV_SYN: type = "EV_SYN"; break; | |
case EV_REL: type = "EV_REL"; break; | |
case EV_ABS: type = "EV_ABS"; break; | |
case EV_MSC: type = "EV_MSC"; break; | |
case EV_SW: type = "EV_SW"; break; | |
case EV_LED: type = "EV_LED"; break; | |
case EV_SND: type = "EV_SND"; break; | |
case EV_REP: type = "EV_REP"; break; | |
case EV_FF: type = "EV_FF"; break; | |
case EV_PWR: type = "EV_PWR"; break; | |
case EV_FF_STATUS: type = "EV_FF_STATUS"; break; | |
default: type = "EV_???"; break; | |
} | |
printf("%s: %d 0x%02x (%d)\n", type, ev->value, (int)ev->code, (int)ev->code); | |
} else { | |
static char *evval[] = { | |
"RELEASED", | |
"PRESSED", | |
"REPEATED" | |
}; | |
printf("EV_KEY: %s 0x%02x (%d)\n", evval[ev->value], (int)ev->code, (int)ev->code); | |
} | |
} | |
static int mod_bit(int key) | |
{ | |
switch (key) { | |
case KEY_LEFTCTRL: | |
return MOD_LCTRL; | |
case KEY_RIGHTCTRL: | |
return MOD_RCTRL; | |
case KEY_LEFTSHIFT: | |
return MOD_LSHIFT; | |
case KEY_RIGHTSHIFT: | |
return MOD_RSHIFT; | |
case KEY_LEFTALT: | |
return MOD_LALT; | |
case KEY_RIGHTALT: | |
return MOD_RALT; | |
case KEY_CAPSLOCK: | |
return MOD_CAPS; | |
case KEY_NUMLOCK: | |
return MOD_NUM; | |
case KEY_SCROLLLOCK: | |
return MOD_SCROLL; | |
} | |
return 0; | |
} | |
static void adjust_mods(int fd, int mods, int newmods) | |
{ | |
static struct { | |
int mod; | |
int key; | |
} modifiers[] = { | |
{MOD_LCTRL, KEY_LEFTCTRL}, | |
{MOD_RCTRL, KEY_RIGHTCTRL}, | |
{MOD_LSHIFT, KEY_LEFTSHIFT}, | |
{MOD_RSHIFT, KEY_RIGHTSHIFT}, | |
{MOD_LALT, KEY_LEFTALT}, | |
{MOD_RALT, KEY_RIGHTALT} | |
/* TODO: add the rest? */ | |
}; | |
static int mods_len = sizeof(modifiers) / sizeof(modifiers[0]); | |
if (mods == newmods) | |
return; | |
for (int i = 0; i < mods_len; i++) { | |
if (mods & modifiers[i].mod && !(newmods & modifiers[i].mod)) { | |
emitku(fd, modifiers[i].key); | |
} else if (!(mods & modifiers[i].mod) && newmods & modifiers[i].mod) { | |
emitkd(fd, modifiers[i].key); | |
} | |
} | |
} | |
static struct keystate mapping[] = { | |
/* | NORMAL | | SHIFT | | CAPS | */ | |
/* KEY_RESERVED */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_ESC */ {{-1, 0 }, {-1, 0 }, {KEY_SCREENLOCK, 0}}, | |
/* KEY_1 */ {{0, 0 }, {0, 0 }, {0, 0}}, | |
/* KEY_2 */ {{0, 0 }, {0, 0 }, {0, 0}}, | |
/* KEY_3 */ {{KEY_9, MOD_LSHIFT}, /* => ) */ {0, 0 }, {0, 0}}, | |
/* KEY_4 */ {{KEY_8, MOD_LSHIFT}, /* => ( */ {0, 0 }, {KEY_VOLUMEDOWN, 0}}, /* => 🔉 */ | |
/* KEY_5 */ {{KEY_102ND, MOD_RALT }, /* => | */ {0, 0 }, {KEY_PREVIOUSSONG, 0}}, /* => « */ | |
/* KEY_6 */ {{0, 0 }, {0, 0 }, {0, 0}}, | |
/* KEY_7 */ {{0, 0 }, {0, 0 }, {0, 0}}, | |
/* KEY_8 */ {{KEY_RIGHTBRACE, 0 }, /* => + */ {0, 0 }, {0, 0}}, | |
/* KEY_9 */ {{KEY_RIGHTBRACE, MOD_LSHIFT}, /* => * */ {0, 0 }, {0, 0}}, | |
/* KEY_0 */ {{KEY_6, MOD_LSHIFT}, /* => & */ {0, 0 }, {0, 0}}, | |
/* KEY_MINUS */ {{0, 0 }, {0, 0 }, {0, 0}}, | |
/* KEY_EQUAL */ {{0, 0 }, {0, 0 }, {0, 0}}, | |
/* KEY_BACKSPACE */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_TAB */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_Q */ {{KEY_DOT, 0 }, /* => . */ {KEY_MINUS, MOD_LSHIFT}, /* => ? */ {0, 0}}, | |
/* KEY_W */ {{KEY_COMMA, MOD_LSHIFT}, /* => ; */ {-0x00b4, MOD_DEAD }, /* => ´ */ {0, 0}}, | |
/* KEY_E */ {{KEY_COMMA, MOD_DEAD }, /* => , */ {-0x0060, MOD_DEAD }, /* => ` */ {KEY_UP, 0}}, /* => ↑ */ | |
/* KEY_R */ {{KEY_P, 0 }, /* => p */ {KEY_P, MOD_LSHIFT}, /* => P */ {KEY_VOLUMEUP, 0}}, /* => 🔊 */ | |
/* KEY_T */ {{KEY_Z, 0 }, /* => y */ {KEY_Z, MOD_LSHIFT}, /* => Y */ {KEY_NEXTSONG, 0}}, /* => » */ | |
/* KEY_Y */ {{KEY_F, 0 }, /* => f */ {KEY_F, MOD_LSHIFT}, /* => F */ {0, 0}}, | |
/* KEY_U */ {{KEY_G, 0 }, /* => g */ {KEY_G, MOD_LSHIFT}, /* => G */ {KEY_KP7, 0}}, /* => 7 */ | |
/* KEY_I */ {{KEY_C, 0 }, /* => c */ {KEY_C, MOD_LSHIFT}, /* => C */ {KEY_KP8, 0}}, /* => 8 */ | |
/* KEY_O */ {{KEY_R, 0 }, /* => r */ {KEY_R, MOD_LSHIFT}, /* => R */ {KEY_KP9, 0}}, /* => 9 */ | |
/* KEY_P */ {{KEY_L, 0 }, /* => l */ {KEY_L, MOD_LSHIFT}, /* => L */ {0, 0}}, | |
/* KEY_LEFTBRACE */ {{0, 0 }, {0, 0 }, {0, 0}}, | |
/* KEY_RIGHTBRACE */ {{0, 0 }, {0, 0 }, {0, 0}}, | |
/* KEY_ENTER */ {{-1, 0 }, {-1, 0 }, {KEY_MUTE, 0}}, /* => 🔇 */ | |
/* KEY_LEFTCTRL */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_A */ {{KEY_A, 0 }, /* => a */ {KEY_A, MOD_LSHIFT}, /* => A */ {0, 0}}, | |
/* KEY_S */ {{KEY_O, 0 }, /* => o */ {KEY_O, MOD_LSHIFT}, /* => O */ {KEY_LEFT, 0}}, /* => ← */ | |
/* KEY_D */ {{KEY_E, 0 }, /* => e */ {KEY_E, MOD_LSHIFT}, /* => E */ {KEY_DOWN, 0}}, /* => ↓ */ | |
/* KEY_F */ {{KEY_I, 0 }, /* => i */ {KEY_I, MOD_LSHIFT}, /* => I */ {KEY_RIGHT, 0}}, /* => → */ | |
/* KEY_G */ {{KEY_U, 0 }, /* => u */ {KEY_U, MOD_LSHIFT}, /* => U */ {0, 0}}, | |
/* KEY_H */ {{KEY_D, 0 }, /* => d */ {KEY_D, MOD_LSHIFT}, /* => D */ {0, 0}}, | |
/* KEY_J */ {{KEY_H, 0 }, /* => h */ {KEY_H, MOD_LSHIFT}, /* => H */ {KEY_KP4, 0}}, /* => 4 */ | |
/* KEY_K */ {{KEY_T, 0 }, /* => t */ {KEY_T, MOD_LSHIFT}, /* => T */ {KEY_KP5, 0}}, /* => 5 */ | |
/* KEY_L */ {{KEY_N, 0 }, /* => n */ {KEY_N, MOD_LSHIFT}, /* => N */ {KEY_KP6, 0}}, /* => 6 */ | |
/* KEY_SEMICOLON */ {{KEY_S, 0 }, /* => s */ {KEY_S, MOD_LSHIFT}, /* => S */ {KEY_DELETE, 0}}, | |
/* KEY_APOSTROPHE */ {{KEY_1, MOD_LSHIFT}, /* => ! */ {KEY_MINUS, 0 }, /* => ß */ {0, 0}}, | |
/* KEY_GRAVE */ {{0, 0 }, {0, 0 }, {0, 0}}, | |
/* KEY_LEFTSHIFT */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_BACKSLASH */ {{0, 0 }, {0, 0 }, {0, 0}}, | |
/* KEY_Z */ {{-0x005e, MOD_DEAD }, /* => ^ */ {0, 0 }, {0, 0}}, | |
/* KEY_X */ {{KEY_Q, 0 }, /* => q */ {KEY_Q, MOD_LSHIFT}, /* => Q */ {0, 0}}, | |
/* KEY_C */ {{KEY_J, 0 }, /* => j */ {KEY_J, MOD_LSHIFT}, /* => J */ {0, 0}}, | |
/* KEY_V */ {{KEY_K, 0 }, /* => k */ {KEY_K, MOD_LSHIFT}, /* => K */ {0, 0}}, | |
/* KEY_B */ {{KEY_X, 0 }, /* => x */ {KEY_X, MOD_LSHIFT}, /* => X */ {0, 0}}, | |
/* KEY_N */ {{KEY_B, 0 }, /* => b */ {KEY_B, MOD_LSHIFT}, /* => B */ {0, 0}}, | |
/* KEY_M */ {{KEY_M, 0 }, /* => m */ {KEY_M, MOD_LSHIFT}, /* => M */ {KEY_KP1, 0}}, /* => 1 */ | |
/* KEY_COMMA */ {{KEY_W, 0 }, /* => w */ {KEY_W, MOD_LSHIFT}, /* => W */ {KEY_KP2, 0}}, /* => 2 */ | |
/* KEY_DOT */ {{KEY_V, 0 }, /* => v */ {KEY_V, MOD_LSHIFT}, /* => V */ {KEY_KP3, 0}}, /* => 3 */ | |
/* KEY_SLASH */ {{KEY_Y, 0 }, /* => z */ {KEY_Y, MOD_LSHIFT}, /* => Z */ {KEY_KP0, 0}}, /* => 0 */ | |
/* KEY_RIGHTSHIFT */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_KPASTERISK */ {{0, 0 }, {0, 0 }, {0, 0}}, | |
/* KEY_LEFTALT */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_SPACE */ {{KEY_SPACE, 0 }, /* => */ {KEY_SPACE, 0 }, /* => */ {KEY_PLAYPAUSE, 0}}, /* => ⏸ */ | |
/* KEY_CAPSLOCK */ {{0, 0 }, {0, 0 }, {0, 0}}, | |
/* KEY_F1 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_F2 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_F3 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_F4 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_F5 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_F6 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_F7 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_F8 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_F9 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_F10 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_NUMLOCK */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_SCROLLLOCK */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_KP7 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_KP8 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_KP9 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_KPMINUS */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_KP4 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_KP5 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_KP6 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_KPPLUS */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_KP1 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_KP2 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_KP3 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_KP0 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_KPDOT */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_ZENKAKUHANKAKU */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_102ND */ {{0, 0 }, {0, 0 }, {0, 0}}, | |
/* KEY_F11 */ {{-1, 0 }, {-1, 0 }, {-1, 0}}, | |
/* KEY_F12 */ {{-1, 0 }, {-1, 0 }, {-1, 0}} | |
}; | |
int main(int argc, char* argv[]) | |
{ | |
struct uinput_user_dev usetup = {0}; | |
struct input_event ev; | |
int fdi, fdo; | |
ssize_t n; | |
char *input_device = "/dev/input/event0"; | |
if (setuid(0) < 0) | |
perror("setuid failed"); | |
if (argc > 1) | |
input_device = argv[1]; | |
sleep(1); | |
if ((fdi = open(input_device, O_RDONLY)) < 0) | |
die("Cannot open input device"); | |
/* consume events through reading */ | |
if (ioctl(fdi, EVIOCGRAB, 1) < 0) | |
die("Cannot grab input device"); | |
if ((fdo = open("/dev/uinput", O_WRONLY | O_NONBLOCK)) < 0) | |
die("Cannot open /dev/uinput"); | |
/* | |
* The ioctls below will enable the device that is about to be | |
* created, to pass key events it reads from the input device. | |
*/ | |
if (ioctl(fdo, UI_SET_EVBIT, EV_KEY) < 0) | |
die("Cannot set ev bits, key"); | |
if (ioctl(fdo, UI_SET_EVBIT, EV_SYN) < 0) | |
die("Cannot set ev bits, syn"); | |
if (ioctl(fdo, UI_SET_EVBIT, EV_MSC) < 0) | |
die("Cannot set ev bits, msc"); | |
for (int i = 0; i < KEY_CNT; i++) { | |
if (ioctl(fdo, UI_SET_KEYBIT, i) < 0) | |
die("Cannot set key bit"); | |
} | |
/* set up input driver */ | |
usetup.id.bustype = BUS_USB; | |
usetup.id.vendor = 0x1488; | |
usetup.id.product = 0x1488; | |
usetup.id.version = 1; | |
snprintf(usetup.name, UINPUT_MAX_NAME_SIZE, "qwertz2mydvorak"); | |
if (write(fdo, &usetup, sizeof(usetup)) < 0) /* ioctl(fdo, UI_DEV_SETUP, &usetup) fails */ | |
die("Cannot set device data"); | |
if (ioctl(fdo, UI_DEV_CREATE) < 0) | |
die("Cannot create device"); | |
/* main loop */ | |
for (;;) { | |
n = read(fdi, &ev, sizeof(ev)); | |
if (n < 0) { | |
if (errno == EINTR) | |
continue; | |
else | |
break; | |
} else if (n != sizeof(ev)) { | |
errno = EIO; | |
break; | |
} | |
if (ev.type == EV_KEY) { | |
/* handle key events */ | |
static int mapping_len = sizeof(mapping) / sizeof(mapping[0]); | |
static int mods = 0; | |
static int gotany = 0; | |
static struct key gotdead; | |
int keycode = -1; | |
int keymods = 0; | |
int mod; | |
if (mod = mod_bit(ev.code)) { | |
switch (ev.value) { | |
case PRESSED: | |
mods |= mod; | |
gotany = 0; | |
break; | |
case RELEASED: | |
mods &= ~mod; | |
/* bare capslock generates escape */ | |
/* | |
if (mod == MOD_CAPS && !gotany) | |
emitkp(fdo, KEY_ESC); | |
*/ | |
break; | |
} | |
if (ev.code < mapping_len) | |
keycode = mapping[ev.code].nomod.code; | |
} else if (mods & MOD_CTRL) { | |
/* don't transform when ctrl is pressed */ | |
keycode = ev.code; | |
keymods = mods; | |
} else { | |
for (;;) { | |
struct key *key; | |
if (ev.code >= mapping_len) | |
break; | |
if (mods & MOD_CAPS) { | |
key = &mapping[ev.code].caps; | |
/* FIXME: shift check needed? */ | |
if (mods & MOD_SHIFT) | |
keymods |= MOD_LSHIFT; | |
} else if (mods & MOD_SHIFT) { | |
key = &mapping[ev.code].shift; | |
} else { | |
key = &mapping[ev.code].nomod; | |
} | |
keycode = key->code; | |
keymods |= key->mods; | |
break; | |
} | |
} | |
if (keycode == -1) { | |
/* forward the event */ | |
emit(fdo, ev.type, ev.code, ev.value); | |
} else if (keycode < -1 || keycode > 0) { | |
/* remap it if keycode != 0 */ | |
if (keymods & MOD_DEAD || gotdead.code) { | |
keymods &= ~MOD_DEAD; | |
if (!gotdead.code) { | |
/* dead key got pressed */ | |
if (ev.value != RELEASED) { | |
gotdead.code = keycode; | |
gotdead.mods = keymods; | |
} | |
continue; | |
} else if (ev.value != RELEASED) { | |
/* transform the follow-up key */ | |
switch (gotdead.code) { | |
case KEY_COMMA: | |
switch (ev.code) { | |
case KEY_W: /* ; => ' */ | |
keycode = KEY_BACKSLASH; | |
keymods = MOD_LSHIFT; | |
break; | |
case KEY_E: /* , => , */ | |
keycode = KEY_COMMA; | |
keymods = 0; | |
break; | |
case KEY_R: /* p => " */ | |
keycode = KEY_2; | |
keymods = MOD_LSHIFT; | |
break; | |
case KEY_T: /* y => # */ | |
keycode = KEY_BACKSLASH; | |
keymods = 0; | |
break; | |
case KEY_U: /* g => _ */ | |
keycode = KEY_SLASH; | |
keymods = MOD_LSHIFT; | |
break; | |
case KEY_I: /* c => > */ | |
keycode = KEY_102ND; | |
keymods = MOD_LSHIFT; | |
break; | |
case KEY_O: /* r => < */ | |
keycode = KEY_102ND; | |
keymods = 0; | |
break; | |
case KEY_P: /* l => ~ */ | |
keycode = KEY_RIGHTBRACE; | |
keymods = MOD_RALT; | |
break; | |
case KEY_A: /* a => ä */ | |
keycode = KEY_APOSTROPHE; | |
break; | |
case KEY_S: /* o => ö */ | |
keycode = KEY_SEMICOLON; | |
break; | |
case KEY_D: /* e => ü */ | |
keycode = KEY_LEFTBRACE; | |
break; | |
case KEY_F: /* i => = */ | |
keycode = KEY_0; | |
keymods = MOD_LSHIFT; | |
break; | |
case KEY_G: /* u => \ */ | |
keycode = KEY_MINUS; | |
keymods = MOD_RALT; | |
break; | |
case KEY_H: /* d => $ */ | |
keycode = KEY_4; | |
keymods = MOD_LSHIFT; | |
break; | |
case KEY_J: /* h => - */ | |
keycode = KEY_SLASH; | |
keymods = 0; | |
break; | |
case KEY_K: /* t => } */ | |
keycode = KEY_0; | |
keymods = MOD_RALT; | |
break; | |
case KEY_L: /* n => { */ | |
keycode = KEY_7; | |
keymods = MOD_RALT; | |
break; | |
case KEY_SEMICOLON: /* s => / */ | |
keycode = KEY_7; | |
keymods = MOD_LSHIFT; | |
break; | |
case KEY_X: /* q => ¥ */ | |
keycode = -0x00a5; | |
keymods = 0; | |
break; | |
case KEY_C: /* j => £ */ | |
keycode = -0x00a3; | |
keymods = 0; | |
break; | |
case KEY_V: /* k => € */ | |
keycode = KEY_E; | |
keymods = MOD_RALT; | |
break; | |
case KEY_N: /* b => : */ | |
keycode = KEY_DOT; | |
keymods = MOD_LSHIFT; | |
break; | |
case KEY_M: /* m => @ */ | |
keycode = KEY_Q; | |
keymods = MOD_RALT; | |
break; | |
case KEY_COMMA: /* w => ] */ | |
keycode = KEY_9; | |
keymods = MOD_RALT; | |
break; | |
case KEY_DOT: /* v => [ */ | |
keycode = KEY_8; | |
keymods = MOD_RALT; | |
break; | |
case KEY_SLASH: /* z => % */ | |
keycode = KEY_5; | |
keymods = MOD_LSHIFT; | |
break; | |
default: | |
emitkey(fdo, gotdead.code, gotdead.mods, mods); | |
break; | |
} | |
break; | |
case -0x005e: | |
switch (ev.code) { | |
case KEY_A: /* a => â */ | |
keycode = -0x00e2; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_D: /* e => ê */ | |
keycode = -0x00ea; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_G: /* u => û */ | |
keycode = -0x00fb; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_F: /* i => î */ | |
keycode = -0x00ee; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_S: /* o => ô */ | |
keycode = -0x00f4; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_SPACE: /* => ^ */ | |
keycode = gotdead.code; | |
keymods = 0; | |
break; | |
default: | |
emitkey(fdo, gotdead.code, gotdead.mods, mods); | |
break; | |
} | |
break; | |
case -0x0060: | |
switch (ev.code) { | |
case KEY_A: /* a => à */ | |
keycode = -0x00e0; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_D: /* e => è */ | |
keycode = -0x00e8; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_G: /* u => ù */ | |
keycode = -0x00f9; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_F: /* i => ì */ | |
keycode = -0x00ec; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_S: /* o => ò */ | |
keycode = -0x00f2; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_SPACE: /* => ` */ | |
keycode = gotdead.code; | |
keymods = 0; | |
break; | |
default: | |
emitkey(fdo, gotdead.code, gotdead.mods, mods); | |
break; | |
} | |
break; | |
case -0x00b4: | |
switch (ev.code) { | |
case KEY_T: /* y => ý */ | |
keycode = -0x00fd; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_A: /* a => á */ | |
keycode = -0x00e1; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_D: /* e => é */ | |
keycode = -0x00e9; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_G: /* u => ú */ | |
keycode = -0x00fa; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_F: /* i => í */ | |
keycode = -0x00ed; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_S: /* o => ó */ | |
keycode = -0x00f3; | |
if (keymods & MOD_LSHIFT) | |
keycode += 32; | |
keymods = 0; | |
break; | |
case KEY_SPACE: /* => ´ */ | |
keycode = gotdead.code; | |
keymods = 0; | |
break; | |
default: | |
emitkey(fdo, gotdead.code, gotdead.mods, mods); | |
break; | |
} | |
break; | |
} | |
gotdead.code = 0; | |
} | |
} | |
if (ev.value == RELEASED) | |
continue; | |
emitkey(fdo, keycode, keymods, mods); | |
gotany = 1; | |
} | |
} else { | |
/* forward everything else */ | |
emit(fdo, ev.type, ev.code, ev.value); | |
} | |
} | |
close(fdi); | |
ioctl(fdo, UI_DEV_DESTROY); | |
close(fdo); | |
die("read failed"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment