#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <linux/input.h>
|
|
#include <linux/uinput.h>
|
|
#include <linux/keyboard.h>
|
|
#include <linux/kd.h>
|
|
#include <ctype.h>
|
|
|
|
#include "jsmn.h"
|
|
|
|
|
|
#define kbd_err(str) { perror("kbderr: "str); exit(EXIT_FAILURE); }
|
|
|
|
void sleep_us(unsigned long microseconds)
|
|
{
|
|
struct timespec ts;
|
|
ts.tv_sec = microseconds / 1000000; // whole seconds
|
|
ts.tv_nsec = (microseconds % 1000000) * 1000; // remainder, in nanoseconds
|
|
nanosleep(&ts, NULL);
|
|
}
|
|
|
|
|
|
typedef struct kbd_state_t
|
|
{
|
|
int key[16];
|
|
int num;
|
|
int pressed;
|
|
int totalpressed;
|
|
char chord;
|
|
char lshift;
|
|
char rshift;
|
|
} kbd_state_t;
|
|
|
|
typedef struct kbd_chord_tree_t
|
|
{
|
|
char *result;
|
|
int len;
|
|
struct kbd_chord_tree_t *next;
|
|
} kbd_chord_tree_t;
|
|
|
|
void kbd_emit(int fdo, int type, int code, int value)
|
|
{
|
|
struct input_event ie;
|
|
memset(&ie, 0, sizeof(struct input_event));
|
|
ie.type = type;
|
|
ie.code = code;
|
|
ie.value = value;
|
|
if(write(fdo, &ie, sizeof(struct input_event)) < 0) kbd_err("cannot write to fake keyboard");
|
|
}
|
|
|
|
void kbd_presskey(int fdo, int key)
|
|
{
|
|
kbd_emit(fdo, EV_KEY, key, 1);
|
|
kbd_emit(fdo, EV_SYN, SYN_REPORT, 0);
|
|
}
|
|
|
|
void kbd_releasekey(int fdo, int key)
|
|
{
|
|
kbd_emit(fdo, EV_KEY, key, 0);
|
|
kbd_emit(fdo, EV_SYN, SYN_REPORT, 0);
|
|
}
|
|
|
|
void kbd_tapkey(int fdo, int key)
|
|
{
|
|
kbd_presskey(fdo, key);
|
|
kbd_releasekey(fdo, key);
|
|
}
|
|
|
|
void kbd_add_chord(kbd_chord_tree_t *node, int depth, char *chord, int chordlen, char *result, int resultlen)
|
|
{
|
|
if (depth == chordlen)
|
|
{
|
|
node->result = strndup(result, resultlen);
|
|
node->len = resultlen;
|
|
//printf("%*.*s %s %*.*s\n", resultlen, resultlen, result, node->result, chordlen, chordlen, chord);
|
|
return;
|
|
}
|
|
|
|
|
|
int loc = tolower(chord[depth]);
|
|
if (!islower(loc)) kbd_err("Found a symbol in a chord that I don't recognize")
|
|
loc = loc - 97; //a==0
|
|
|
|
if (node->next == NULL)
|
|
node->next = calloc(26, sizeof(kbd_chord_tree_t));
|
|
|
|
kbd_add_chord(&node->next[loc], depth+1, chord, chordlen, result, resultlen);
|
|
}
|
|
|
|
|
|
char kbd_keysym(int f, int key)
|
|
{
|
|
struct kbentry x;
|
|
x.kb_table = 0;
|
|
x.kb_index = key;
|
|
x.kb_value = 0;
|
|
if(ioctl(f, KDGKBENT, &x) < 0) kbd_err("Cannot get keycode");
|
|
|
|
return KVAL(x.kb_value);
|
|
//printf("%i %i %i %i %i %i %i %i \n", x.kb_index, KTYP(x.kb_value), KVAL(x.kb_value), 't', x.kb_value, KEY_T, K_HOLE, K_NOSUCHMAP);
|
|
}
|
|
|
|
int kbd_compare_int(const void * a, const void * b) {
|
|
//printf("%i %i \n",*(char*)a,*(char*)b);
|
|
return ( *(char*)a < *(char*)b );
|
|
}
|
|
int kbd_compare (const void * a, const void * b) {
|
|
//printf("%i %i \n",*(char*)a,*(char*)b);
|
|
return ( *(char*)a < *(char*)b );
|
|
}
|
|
|
|
char *kbd_find_result(kbd_chord_tree_t *node, int ftty, int depth, int *chord, int chordlen, int *resultlen)
|
|
{
|
|
if (depth == chordlen)
|
|
{
|
|
*resultlen = node->len;
|
|
return node->result;
|
|
}
|
|
|
|
if (!islower(chord[depth]))
|
|
return kbd_find_result(node, ftty, depth+1, chord, chordlen, resultlen);
|
|
|
|
if (node->next != NULL)
|
|
return kbd_find_result(&node->next[chord[depth] - 97], ftty, depth+1, chord, chordlen, resultlen);
|
|
return NULL;
|
|
}
|
|
|
|
#define KBD_DICT_LINE_SIZE 2
|
|
void kbd_load_json(kbd_chord_tree_t *root)
|
|
{
|
|
jsmn_parser p;
|
|
jsmn_init(&p);
|
|
|
|
jsmntok_t t[KBD_DICT_LINE_SIZE];
|
|
|
|
char *str = NULL;
|
|
size_t len = 0;
|
|
ssize_t read;
|
|
|
|
FILE *fp = fopen("dict", "r");
|
|
if (fp == NULL) kbd_err("failed to open dict");
|
|
|
|
//skip first {
|
|
if ((read = getline(&str, &len, fp)) == -1) kbd_err("failed to read dict initially");
|
|
|
|
if ((read = getline(&str, &len, fp)) == -1) kbd_err("failed to read dict");
|
|
|
|
while (str[0] != '}')
|
|
{
|
|
jsmn_init(&p);
|
|
switch (jsmn_parse(&p, str, read, t, KBD_DICT_LINE_SIZE)) {
|
|
case 2:
|
|
break;
|
|
case JSMN_ERROR_INVAL:
|
|
kbd_err("invalid json");
|
|
case JSMN_ERROR_NOMEM:
|
|
kbd_err("jsmn ran out of space");
|
|
case JSMN_ERROR_PART:
|
|
kbd_err("json string too short");
|
|
case 1:
|
|
kbd_err("json line with 1 token found");
|
|
case 3:
|
|
kbd_err("json line with 3 tokens found");
|
|
default:
|
|
kbd_err("json line with weird number of tokens found");
|
|
break;
|
|
}
|
|
|
|
if (t[0].type != JSMN_STRING || t[1].type != JSMN_STRING) kbd_err("json type other than string detected");
|
|
|
|
if (str[t[0].start] != '?')
|
|
{
|
|
if (t[0].end-t[0].start > 8) kbd_err("chord for squid detected. You are not a squid.");
|
|
//printf("%*.*s \n", t[0].end-t[0].start, t[0].end-t[0].start, &str[t[0].start]);
|
|
qsort(&str[t[0].start], t[0].end-t[0].start, sizeof(char), kbd_compare);
|
|
//printf("%*.*s \n", t[0].end-t[0].start, t[0].end-t[0].start, &str[t[0].start]);
|
|
kbd_add_chord(root, 0, &str[t[0].start], t[0].end-t[0].start, &str[t[1].start], t[1].end-t[1].start);
|
|
//printf("Found one! \n");
|
|
}
|
|
|
|
if ((read = getline(&str, &len, fp)) == -1) kbd_err("failed to read dict");
|
|
}
|
|
|
|
fclose(fp);
|
|
if (str)
|
|
free(str);
|
|
|
|
}
|
|
|
|
void kbd_load_tty(int *f)
|
|
{
|
|
*f = open("/dev/console", O_RDONLY);
|
|
|
|
if(*f < 0) kbd_err("can't open tty");
|
|
|
|
long mode = 0;
|
|
if(ioctl(*f, KDGETMODE, &mode) < 0) kbd_err("KDGETMODE failed");
|
|
|
|
//printf("%ld %i \n", mode, K_XLATE);
|
|
|
|
if (mode == K_UNICODE)
|
|
kbd_err("unicode-mode keyboards currently unimplemented");
|
|
|
|
if (mode != K_XLATE)
|
|
perror("Got a strange keyboard mode. I'll try anyway, but be ready to report bugs.");
|
|
|
|
}
|
|
|
|
void kbd_init(int *letters, int *fdo, int *fdi, int *ftty, char *str, kbd_chord_tree_t *root)
|
|
{
|
|
struct uinput_user_dev uidev;
|
|
int i;
|
|
|
|
memset(root, 0, sizeof(struct kbd_chord_tree_t));
|
|
|
|
memset(&uidev, 0, sizeof(struct uinput_user_dev));
|
|
|
|
//Output. This takes time for linux to notice, so we make it first
|
|
*fdo = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
|
|
if(*fdo < 0) kbd_err("uinput open");
|
|
|
|
if(ioctl(*fdo, UI_SET_EVBIT, EV_SYN) < 0) kbd_err("ev_syn bit failed");
|
|
if(ioctl(*fdo, UI_SET_EVBIT, EV_KEY) < 0) kbd_err("ev_key bit failed - Are you sure that was a keyboard?");
|
|
//if(ioctl(*fdo, UI_SET_EVBIT, EV_REP) < 0) kbd_err("error: ioctl");
|
|
//if(ioctl(fdo, UI_SET_EVBIT, EV_MSC) < 0) kbd_err("error: ioctl"); //Doesn't do anything useful on my keyboard
|
|
|
|
kbd_load_tty(ftty);
|
|
|
|
|
|
//We want literally all the keys available
|
|
//for(i = 0; i < KEY_MAX; ++i)
|
|
for(i = 0; i < 256; ++i)
|
|
{
|
|
if (islower(kbd_keysym(*ftty, i)))
|
|
letters[kbd_keysym(*ftty, i) - 97] = i;
|
|
if(ioctl(*fdo, UI_SET_KEYBIT, i) < 0) kbd_err("error setting keyboard");
|
|
}
|
|
|
|
snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-kbd");
|
|
uidev.id.bustype = BUS_USB;
|
|
uidev.id.vendor = 0x1;
|
|
uidev.id.product = 0x1;
|
|
uidev.id.version = 1;
|
|
|
|
if(write(*fdo, &uidev, sizeof(uidev)) < 0) kbd_err("uidev write failed");
|
|
if(ioctl(*fdo, UI_DEV_CREATE) < 0) kbd_err("ui device create failed");
|
|
|
|
//input
|
|
*fdi = open(str, O_RDONLY);
|
|
if(*fdi < 0) kbd_err("failed to open device");
|
|
|
|
/*
|
|
memset(afdi, 0, sizeof(struct input_event));
|
|
//unsigned char afdi_buf[KBD_BUF_SIZE];
|
|
afdi->aio_fildes = *fdi;
|
|
//afdi.aio_buf = afdi_buf;
|
|
//afdi.aio_nbytes = KBD_BUF_SIZE;
|
|
afdi->aio_buf = ev;
|
|
afdi->aio_nbytes = sizeof(struct input_event);
|
|
afdi->aio_lio_opcode = LIO_READ;
|
|
*/
|
|
|
|
|
|
kbd_load_json(root);
|
|
|
|
//We need time for the user's finger to come off the enter button
|
|
//This also doubles at time for linux to notice the new ui device
|
|
sleep_us(100000);
|
|
|
|
if(ioctl(*fdi, EVIOCGRAB, 1) < 0) kbd_err("eviocgrab failed");
|
|
}
|
|
|
|
void kbd_event(int *letters, struct input_event ev, int fdo, int ftty, kbd_state_t *kbd_state, kbd_chord_tree_t *root)
|
|
{
|
|
char *result;
|
|
int resultlen;
|
|
int i;
|
|
int j;
|
|
if (ev.type == EV_KEY)
|
|
{
|
|
//output time is ignored
|
|
//ev.time.tv_sec = 0;
|
|
//ev.time.tv_usec = 0;
|
|
//if (ev.value == 0)
|
|
//{
|
|
switch (ev.code)
|
|
{
|
|
case KEY_RIGHTMETA:
|
|
if (ev.value == 1)
|
|
{
|
|
//printf("caught meta press\n");
|
|
kbd_state->chord = 1;
|
|
if (kbd_state->lshift)
|
|
{
|
|
kbd_state->lshift = 1;
|
|
kbd_releasekey(fdo, KEY_LEFTSHIFT);
|
|
}
|
|
if (kbd_state->rshift)
|
|
{
|
|
kbd_state->rshift = 1;
|
|
kbd_releasekey(fdo, KEY_RIGHTSHIFT);
|
|
}
|
|
if (kbd_state->pressed != 0)
|
|
{
|
|
for (i = kbd_state->pressed-1; i > -1; i--)
|
|
{
|
|
kbd_tapkey(fdo, KEY_BACKSPACE);
|
|
//Release any keys pressed before switching to meta mode
|
|
kbd_releasekey(fdo, kbd_state->key[i]);
|
|
}
|
|
}
|
|
}
|
|
else if (ev.value == 0 && kbd_state->pressed == 0)
|
|
kbd_state->chord = 0;
|
|
break;
|
|
case KEY_RIGHTSHIFT:
|
|
if (ev.value == 1)
|
|
kbd_state->rshift = 1;
|
|
if (!kbd_state->chord)
|
|
{
|
|
if (ev.value == 0)
|
|
kbd_state->rshift = 0;
|
|
if(write(fdo, &ev, sizeof(struct input_event)) < 0) kbd_err("cannot write to fake keyboard");
|
|
kbd_emit(fdo, EV_SYN, SYN_REPORT, 0);
|
|
}
|
|
break;
|
|
case KEY_LEFTSHIFT:
|
|
if (ev.value == 1)
|
|
kbd_state->lshift = 1;
|
|
if (!kbd_state->chord)
|
|
{
|
|
if (ev.value == 0)
|
|
kbd_state->lshift = 0;
|
|
if(write(fdo, &ev, sizeof(struct input_event)) < 0) kbd_err("cannot write to fake keyboard");
|
|
kbd_emit(fdo, EV_SYN, SYN_REPORT, 0);
|
|
}
|
|
break;
|
|
case KEY_LEFTMETA:
|
|
case KEY_LEFTCTRL:
|
|
case KEY_RIGHTCTRL:
|
|
if(write(fdo, &ev, sizeof(struct input_event)) < 0) kbd_err("cannot write to fake keyboard");
|
|
kbd_emit(fdo, EV_SYN, SYN_REPORT, 0);
|
|
break;
|
|
default:
|
|
if (kbd_state->chord == 0)
|
|
{
|
|
if(write(fdo, &ev, sizeof(struct input_event)) < 0) kbd_err("cannot write to fake keyboard");
|
|
kbd_emit(fdo, EV_SYN, SYN_REPORT, 0);
|
|
}
|
|
|
|
if (ev.value == 1)
|
|
{
|
|
if (kbd_state->num<16)
|
|
{
|
|
kbd_state->key[kbd_state->num] = ev.code;
|
|
//kbd_state->sym[kbd_state->num] = kbd_keysym(ftty, ev.code);
|
|
kbd_state->num++;
|
|
}
|
|
kbd_state->pressed++;
|
|
kbd_state->totalpressed++;
|
|
//printf("d: %i %i/%i \n", kbd_state->num, kbd_state->pressed, kbd_state->totalpressed);
|
|
}
|
|
else if (ev.value == 0)
|
|
{
|
|
kbd_state->pressed--;
|
|
//printf("u: %i %i/%i \n", kbd_state->num, kbd_state->pressed, kbd_state->totalpressed);
|
|
if (kbd_state->pressed < 1)
|
|
{
|
|
if (kbd_state->chord == 1)
|
|
{
|
|
kbd_state->chord = 0;
|
|
for (j = 0; j < kbd_state->num; j++)
|
|
kbd_state->key[j] = kbd_keysym(ftty, kbd_state->key[j]);
|
|
qsort(kbd_state->key, kbd_state->num, sizeof(int), kbd_compare_int);
|
|
result = kbd_find_result(root, ftty, 0, kbd_state->key, kbd_state->num, &resultlen);
|
|
if (result != NULL && resultlen > 1)
|
|
{
|
|
if (kbd_state->lshift)
|
|
kbd_presskey(fdo, KEY_LEFTSHIFT);
|
|
if (kbd_state->rshift)
|
|
kbd_presskey(fdo, KEY_RIGHTSHIFT);
|
|
kbd_tapkey(fdo, letters[(int)(tolower(result[0])-97)]);
|
|
if (kbd_state->lshift)
|
|
kbd_releasekey(fdo, KEY_LEFTSHIFT);
|
|
if (kbd_state->rshift)
|
|
kbd_releasekey(fdo, KEY_RIGHTSHIFT);
|
|
for (j = 1; j < resultlen; j++)
|
|
kbd_tapkey(fdo, letters[(int)(tolower(result[j])-97)]);
|
|
kbd_tapkey(fdo, KEY_SPACE);
|
|
if (kbd_state->lshift)
|
|
kbd_presskey(fdo, KEY_LEFTSHIFT);
|
|
if (kbd_state->rshift)
|
|
kbd_presskey(fdo, KEY_RIGHTSHIFT);
|
|
resultlen = 0;
|
|
}
|
|
|
|
}
|
|
kbd_state->pressed = 0;
|
|
kbd_state->totalpressed=0;
|
|
kbd_state->num=0;
|
|
}
|
|
//printf("Forwarding keypress: %i %i %i \n", ev.type, ev.code, ev.value);
|
|
}
|
|
//enqueue
|
|
break;
|
|
}
|
|
//} else {
|
|
//printf("Forwarding keyrelease: %i %i %i \n", ev.type, ev.code, ev.value);
|
|
//enqueue
|
|
//}
|
|
}
|
|
}
|
|
|
|
#define KBD_BUF_SIZE 256
|
|
|
|
void kbd_main_loop(int *letters, int fdi, int fdo, int ftty, kbd_chord_tree_t *root)
|
|
{
|
|
|
|
struct input_event ev;
|
|
kbd_state_t kbd_state;
|
|
|
|
|
|
memset(&ev, 0, sizeof(struct input_event));
|
|
memset(&kbd_state, 0, sizeof(kbd_state_t));
|
|
|
|
while(1)
|
|
{
|
|
read(fdi, &ev, sizeof(struct input_event));
|
|
kbd_event(letters, ev, fdo, ftty, &kbd_state, root);
|
|
}
|
|
}
|
|
|
|
void kbd_cleanup(int fdo, int fdi, int ftty, kbd_chord_tree_t *root)
|
|
{
|
|
if(ioctl(fdi, EVIOCGRAB, 0) < 0) kbd_err("eviocgrab release failed");
|
|
close(fdi);
|
|
|
|
if(ioctl(fdo, UI_DEV_DESTROY) < 0) kbd_err("ui device destroy failure");
|
|
close(fdo);
|
|
|
|
|
|
close(ftty);
|
|
}
|
|
|
|
#include <linux/keyboard.h>
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
int letters[26];
|
|
int fdo, fdi, ftty;
|
|
kbd_chord_tree_t root;
|
|
|
|
|
|
|
|
if(argc != 2) kbd_err("please specify input device");
|
|
|
|
kbd_init(letters, &fdo, &fdi, &ftty, argv[1], &root);
|
|
|
|
//struct input_keymap_entry ke;
|
|
//memset(&ke, 0, sizeof(struct input_keymap_entry));
|
|
|
|
//ke.flags = INPUT_KEYMAP_BY_INDEX;
|
|
//ke.len = sizeof(int);
|
|
//ke.index = 'T';
|
|
//int scancode = KEY_T;
|
|
// printf("%i \n", scancode);
|
|
//memcpy(ke.scancode, &scancode, sizeof(int));
|
|
//ke.len = 1;
|
|
//ke.scancode[0] = KEY_T;
|
|
|
|
//unsigned scancode_keycode[2];
|
|
//scancode_keycode[0] = KEY_T;
|
|
//scancode_keycode[1] = 0;
|
|
|
|
//int keycode;
|
|
//keycode = ioctl(fdi, EVIOCGKEYCODE_V2, &ke);
|
|
// keycode = ioctl(fdi, EVIOCGKEYCODE, scancode_keycode);
|
|
// printf("%i %i %i %i %i %i \n", keycode, ke.keycode, 't', KEY_T, scancode_keycode[0], scancode_keycode[1]);
|
|
|
|
kbd_main_loop(letters, fdi, fdo, ftty, &root);
|
|
|
|
kbd_cleanup(fdo, fdi, ftty, &root);
|
|
|
|
return 0;
|
|
}
|