Makes typing faster. I hope.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

490 lines
13 KiB

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