diff --git a/driver-csi.c b/driver-csi.c index c99c90f..d07c404 100644 --- a/driver-csi.c +++ b/driver-csi.c @@ -4,19 +4,9 @@ #include #include -struct keyinfo { - termkey_type type; - termkey_keysym sym; - int modifier_mask; - int modifier_set; -}; - typedef struct { termkey_t *tk; - // There are 32 C0 codes - struct keyinfo c0[32]; - // There are 64 codes 0x40 - 0x7F struct keyinfo csi_ss3s[64]; struct keyinfo ss3s[64]; @@ -26,12 +16,10 @@ typedef struct { struct keyinfo *csifuncs; } termkey_csi; -static termkey_keysym register_c0(termkey_csi *csi, termkey_keysym sym, unsigned char ctrl, const char *name); static termkey_keysym register_csi_ss3(termkey_csi *csi, termkey_type type, termkey_keysym sym, unsigned char cmd, const char *name); static termkey_keysym register_ss3kpalt(termkey_csi *csi, termkey_type type, termkey_keysym sym, unsigned char cmd, const char *name, char kpalt); static termkey_keysym register_csifunc(termkey_csi *csi, termkey_type type, termkey_keysym sym, int number, const char *name); -static termkey_keysym register_c0_full(termkey_csi *csi, termkey_keysym sym, int modifier_set, int modifier_mask, unsigned char ctrl, const char *name); static termkey_keysym register_csi_ss3_full(termkey_csi *csi, termkey_type type, termkey_keysym sym, int modifier_set, int modifier_mask, unsigned char cmd, const char *name); static termkey_keysym register_ss3kpalt_full(termkey_csi *csi, termkey_type type, termkey_keysym sym, int modifier_set, int modifier_mask, unsigned char cmd, const char *name, char kpalt); static termkey_keysym register_csifunc_full(termkey_csi *csi, termkey_type type, termkey_keysym sym, int modifier_set, int modifier_mask, int number, const char *name); @@ -44,10 +32,6 @@ static void *new_driver(termkey_t *tk) int i; - for(i = 0; i < 32; i++) { - csi->c0[i].sym = TERMKEY_SYM_UNKNOWN; - } - for(i = 0; i < 64; i++) { csi->csi_ss3s[i].sym = TERMKEY_SYM_UNKNOWN; csi->ss3s[i].sym = TERMKEY_SYM_UNKNOWN; @@ -61,11 +45,6 @@ static void *new_driver(termkey_t *tk) for(i = 0; i < csi->ncsifuncs; i++) csi->csifuncs[i].sym = TERMKEY_SYM_UNKNOWN; - register_c0(csi, TERMKEY_SYM_BACKSPACE, 0x08, "Backspace"); - register_c0(csi, TERMKEY_SYM_TAB, 0x09, "Tab"); - register_c0(csi, TERMKEY_SYM_ENTER, 0x0d, "Enter"); - register_c0(csi, TERMKEY_SYM_ESCAPE, 0x1b, "Escape"); - register_csi_ss3(csi, TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_UP, 'A', "Up"); register_csi_ss3(csi, TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DOWN, 'B', "Down"); register_csi_ss3(csi, TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RIGHT, 'C', "Right"); @@ -143,99 +122,6 @@ static void free_driver(void *private) #define UTF8_INVALID 0xFFFD -static int utf8_seqlen(int codepoint) -{ - if(codepoint < 0x0000080) return 1; - if(codepoint < 0x0000800) return 2; - if(codepoint < 0x0010000) return 3; - if(codepoint < 0x0200000) return 4; - if(codepoint < 0x4000000) return 5; - return 6; -} - -static void fill_utf8(termkey_key *key) -{ - int codepoint = key->code.codepoint; - int nbytes = utf8_seqlen(codepoint); - - key->utf8[nbytes] = 0; - - // This is easier done backwards - int b = nbytes; - while(b > 1) { - b--; - key->utf8[b] = 0x80 | (codepoint & 0x3f); - codepoint >>= 6; - } - - switch(nbytes) { - case 1: key->utf8[0] = (codepoint & 0x7f); break; - case 2: key->utf8[0] = 0xc0 | (codepoint & 0x1f); break; - case 3: key->utf8[0] = 0xe0 | (codepoint & 0x0f); break; - case 4: key->utf8[0] = 0xf0 | (codepoint & 0x07); break; - case 5: key->utf8[0] = 0xf8 | (codepoint & 0x03); break; - case 6: key->utf8[0] = 0xfc | (codepoint & 0x01); break; - } -} - -static inline void do_codepoint(termkey_t *tk, int codepoint, termkey_key *key) -{ - termkey_csi *csi = tk->driver_info; - - if(codepoint < 0x20) { - // C0 range - key->code.codepoint = 0; - key->modifiers = 0; - - if(!(tk->flags & TERMKEY_FLAG_NOINTERPRET) && csi->c0[codepoint].sym != TERMKEY_SYM_UNKNOWN) { - key->code.sym = csi->c0[codepoint].sym; - key->modifiers |= csi->c0[codepoint].modifier_set; - } - - if(!key->code.sym) { - key->type = TERMKEY_TYPE_UNICODE; - key->code.codepoint = codepoint + 0x40; - key->modifiers = TERMKEY_KEYMOD_CTRL; - } - else { - key->type = TERMKEY_TYPE_KEYSYM; - } - } - else if(codepoint == 0x20 && !(tk->flags & TERMKEY_FLAG_NOINTERPRET)) { - // ASCII space - key->type = TERMKEY_TYPE_KEYSYM; - key->code.sym = TERMKEY_SYM_SPACE; - key->modifiers = 0; - } - else if(codepoint == 0x7f && !(tk->flags & TERMKEY_FLAG_NOINTERPRET)) { - // ASCII DEL - key->type = TERMKEY_TYPE_KEYSYM; - key->code.sym = TERMKEY_SYM_DEL; - key->modifiers = 0; - } - else if(codepoint >= 0x20 && codepoint < 0x80) { - // ASCII lowbyte range - key->type = TERMKEY_TYPE_UNICODE; - key->code.codepoint = codepoint; - key->modifiers = 0; - } - else if(codepoint >= 0x80 && codepoint < 0xa0) { - // UTF-8 never starts with a C1 byte. So we can be sure of these - key->type = TERMKEY_TYPE_UNICODE; - key->code.codepoint = codepoint - 0x40; - key->modifiers = TERMKEY_KEYMOD_CTRL|TERMKEY_KEYMOD_ALT; - } - else { - // UTF-8 codepoint - key->type = TERMKEY_TYPE_UNICODE; - key->code.codepoint = codepoint; - key->modifiers = 0; - } - - if(key->type == TERMKEY_TYPE_UNICODE) - fill_utf8(key); -} - #define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) static termkey_result getkey_csi(termkey_t *tk, size_t introlen, termkey_key *key) @@ -254,7 +140,7 @@ static termkey_result getkey_csi(termkey_t *tk, size_t introlen, termkey_key *ke if(tk->waittime) return TERMKEY_RES_AGAIN; - do_codepoint(tk, '[', key); + (*tk->method.emit_codepoint)(tk, '[', key); key->modifiers |= TERMKEY_KEYMOD_ALT; (*tk->method.eat_bytes)(tk, introlen); return TERMKEY_RES_KEY; @@ -310,7 +196,7 @@ static termkey_result getkey_csi(termkey_t *tk, size_t introlen, termkey_key *ke if(arg[0] == 27) { int mod = key->modifiers; - do_codepoint(tk, arg[2], key); + (*tk->method.emit_codepoint)(tk, arg[2], key); key->modifiers |= mod; } else if(arg[0] >= 0 && arg[0] < csi->ncsifuncs) { @@ -347,7 +233,7 @@ static termkey_result getkey_ss3(termkey_t *tk, size_t introlen, termkey_key *ke if(tk->waittime) return TERMKEY_RES_AGAIN; - do_codepoint(tk, 'O', key); + (*tk->method.emit_codepoint)(tk, 'O', key); key->modifiers |= TERMKEY_KEYMOD_ALT; (*tk->method.eat_bytes)(tk, tk->buffcount); return TERMKEY_RES_KEY; @@ -402,7 +288,7 @@ static termkey_result getkey(termkey_t *tk, termkey_key *key) if(tk->waittime) return TERMKEY_RES_AGAIN; - do_codepoint(tk, b0, key); + (*tk->method.emit_codepoint)(tk, b0, key); (*tk->method.eat_bytes)(tk, 1); return TERMKEY_RES_KEY; } @@ -416,7 +302,7 @@ static termkey_result getkey(termkey_t *tk, termkey_key *key) return getkey_ss3(tk, 2, key); if(b1 == 0x1b) { - do_codepoint(tk, b0, key); + (*tk->method.emit_codepoint)(tk, b0, key); (*tk->method.eat_bytes)(tk, 1); return TERMKEY_RES_KEY; } @@ -448,7 +334,7 @@ static termkey_result getkey(termkey_t *tk, termkey_key *key) } else if(b0 < 0xa0) { // Single byte C0, G0 or C1 - C1 is never UTF-8 initial byte - do_codepoint(tk, b0, key); + (*tk->method.emit_codepoint)(tk, b0, key); (*tk->method.eat_bytes)(tk, 1); return TERMKEY_RES_KEY; } @@ -462,7 +348,7 @@ static termkey_result getkey(termkey_t *tk, termkey_key *key) if(b0 < 0xc0) { // Starts with a continuation byte - that's not right - do_codepoint(tk, UTF8_INVALID, key); + (*tk->method.emit_codepoint)(tk, UTF8_INVALID, key); (*tk->method.eat_bytes)(tk, 1); return TERMKEY_RES_KEY; } @@ -487,7 +373,7 @@ static termkey_result getkey(termkey_t *tk, termkey_key *key) codepoint = b0 & 0x01; } else { - do_codepoint(tk, UTF8_INVALID, key); + (*tk->method.emit_codepoint)(tk, UTF8_INVALID, key); (*tk->method.eat_bytes)(tk, 1); return TERMKEY_RES_KEY; } @@ -498,7 +384,7 @@ static termkey_result getkey(termkey_t *tk, termkey_key *key) for(int b = 1; b < nbytes; b++) { unsigned char cb = CHARAT(b); if(cb < 0x80 || cb >= 0xc0) { - do_codepoint(tk, UTF8_INVALID, key); + (*tk->method.emit_codepoint)(tk, UTF8_INVALID, key); (*tk->method.eat_bytes)(tk, b - 1); return TERMKEY_RES_KEY; } @@ -517,7 +403,7 @@ static termkey_result getkey(termkey_t *tk, termkey_key *key) codepoint == 0xFFFF) codepoint = UTF8_INVALID; - do_codepoint(tk, codepoint, key); + (*tk->method.emit_codepoint)(tk, codepoint, key); (*tk->method.eat_bytes)(tk, nbytes); return TERMKEY_RES_KEY; } @@ -538,28 +424,6 @@ static termkey_result getkey(termkey_t *tk, termkey_key *key) return TERMKEY_SYM_NONE; } -static termkey_keysym register_c0(termkey_csi *csi, termkey_keysym sym, unsigned char ctrl, const char *name) -{ - return register_c0_full(csi, sym, 0, 0, ctrl, name); -} - -static termkey_keysym register_c0_full(termkey_csi *csi, termkey_keysym sym, int modifier_set, int modifier_mask, unsigned char ctrl, const char *name) -{ - if(ctrl >= 0x20) { - fprintf(stderr, "Cannot register C0 key at ctrl 0x%02x - out of bounds\n", ctrl); - return -1; - } - - if(name) - sym = termkey_register_keyname(csi->tk, sym, name); - - csi->c0[ctrl].sym = sym; - csi->c0[ctrl].modifier_set = modifier_set; - csi->c0[ctrl].modifier_mask = modifier_mask; - - return sym; -} - static termkey_keysym register_csi_ss3(termkey_csi *csi, termkey_type type, termkey_keysym sym, unsigned char cmd, const char *name) { return register_csi_ss3_full(csi, type, sym, 0, 0, cmd, name); diff --git a/termkey-internal.h b/termkey-internal.h index 271e628..9abc43d 100644 --- a/termkey-internal.h +++ b/termkey-internal.h @@ -13,6 +13,13 @@ struct termkey_driver termkey_result (*getkey)(termkey_t *tk, termkey_key *key); }; +struct keyinfo { + termkey_type type; + termkey_keysym sym; + int modifier_mask; + int modifier_set; +}; + struct termkey { int fd; int flags; @@ -31,6 +38,9 @@ struct termkey { int nkeynames; const char **keynames; + // There are 32 C0 codes + struct keyinfo c0[32]; + struct termkey_driver driver; void *driver_info; @@ -38,9 +48,21 @@ struct termkey { // want exported as real symbols in the library struct { void (*eat_bytes)(termkey_t *tk, size_t count); + void (*emit_codepoint)(termkey_t *tk, int codepoint, termkey_key *key); } method; }; extern struct termkey_driver termkey_driver_csi; +// Keep this here for now since it's tiny +static inline int utf8_seqlen(int codepoint) +{ + if(codepoint < 0x0000080) return 1; + if(codepoint < 0x0000800) return 2; + if(codepoint < 0x0010000) return 3; + if(codepoint < 0x0200000) return 4; + if(codepoint < 0x4000000) return 5; + return 6; +} + #endif diff --git a/termkey.c b/termkey.c index e0a110d..ac03b2b 100644 --- a/termkey.c +++ b/termkey.c @@ -15,6 +15,10 @@ static struct termkey_driver *drivers[] = { // Forwards for the "protected" methods static void eat_bytes(termkey_t *tk, size_t count); +static void emit_codepoint(termkey_t *tk, int codepoint, termkey_key *key); + +static termkey_keysym register_c0(termkey_t *tk, termkey_keysym sym, unsigned char ctrl, const char *name); +static termkey_keysym register_c0_full(termkey_t *tk, termkey_keysym sym, int modifier_set, int modifier_mask, unsigned char ctrl, const char *name); termkey_t *termkey_new_full(int fd, int flags, size_t buffsize, int waittime) { @@ -67,7 +71,16 @@ termkey_t *termkey_new_full(int fd, int flags, size_t buffsize, int waittime) for(i = 0; i < tk->nkeynames; i++) tk->keynames[i] = NULL; + for(i = 0; i < 32; i++) + tk->c0[i].sym = TERMKEY_SYM_NONE; + tk->method.eat_bytes = &eat_bytes; + tk->method.emit_codepoint = &emit_codepoint; + + register_c0(tk, TERMKEY_SYM_BACKSPACE, 0x08, "Backspace"); + register_c0(tk, TERMKEY_SYM_TAB, 0x09, "Tab"); + register_c0(tk, TERMKEY_SYM_ENTER, 0x0d, "Enter"); + register_c0(tk, TERMKEY_SYM_ESCAPE, 0x1b, "Escape"); for(i = 0; drivers[i]; i++) { void *driver_info = (*drivers[i]->new_driver)(tk); @@ -157,6 +170,87 @@ static void eat_bytes(termkey_t *tk, size_t count) } } +static void fill_utf8(termkey_key *key) +{ + int codepoint = key->code.codepoint; + int nbytes = utf8_seqlen(codepoint); + + key->utf8[nbytes] = 0; + + // This is easier done backwards + int b = nbytes; + while(b > 1) { + b--; + key->utf8[b] = 0x80 | (codepoint & 0x3f); + codepoint >>= 6; + } + + switch(nbytes) { + case 1: key->utf8[0] = (codepoint & 0x7f); break; + case 2: key->utf8[0] = 0xc0 | (codepoint & 0x1f); break; + case 3: key->utf8[0] = 0xe0 | (codepoint & 0x0f); break; + case 4: key->utf8[0] = 0xf0 | (codepoint & 0x07); break; + case 5: key->utf8[0] = 0xf8 | (codepoint & 0x03); break; + case 6: key->utf8[0] = 0xfc | (codepoint & 0x01); break; + } +} + +static void emit_codepoint(termkey_t *tk, int codepoint, termkey_key *key) +{ + if(codepoint < 0x20) { + // C0 range + key->code.codepoint = 0; + key->modifiers = 0; + + if(!(tk->flags & TERMKEY_FLAG_NOINTERPRET) && tk->c0[codepoint].sym != TERMKEY_SYM_UNKNOWN) { + key->code.sym = tk->c0[codepoint].sym; + key->modifiers |= tk->c0[codepoint].modifier_set; + } + + if(!key->code.sym) { + key->type = TERMKEY_TYPE_UNICODE; + key->code.codepoint = codepoint + 0x40; + key->modifiers = TERMKEY_KEYMOD_CTRL; + } + else { + key->type = TERMKEY_TYPE_KEYSYM; + } + } + else if(codepoint == 0x20 && !(tk->flags & TERMKEY_FLAG_NOINTERPRET)) { + // ASCII space + key->type = TERMKEY_TYPE_KEYSYM; + key->code.sym = TERMKEY_SYM_SPACE; + key->modifiers = 0; + } + else if(codepoint == 0x7f && !(tk->flags & TERMKEY_FLAG_NOINTERPRET)) { + // ASCII DEL + key->type = TERMKEY_TYPE_KEYSYM; + key->code.sym = TERMKEY_SYM_DEL; + key->modifiers = 0; + } + else if(codepoint >= 0x20 && codepoint < 0x80) { + // ASCII lowbyte range + key->type = TERMKEY_TYPE_UNICODE; + key->code.codepoint = codepoint; + key->modifiers = 0; + } + else if(codepoint >= 0x80 && codepoint < 0xa0) { + // UTF-8 never starts with a C1 byte. So we can be sure of these + key->type = TERMKEY_TYPE_UNICODE; + key->code.codepoint = codepoint - 0x40; + key->modifiers = TERMKEY_KEYMOD_CTRL|TERMKEY_KEYMOD_ALT; + } + else { + // UTF-8 codepoint + key->type = TERMKEY_TYPE_UNICODE; + key->code.codepoint = codepoint; + key->modifiers = 0; + } + + if(key->type == TERMKEY_TYPE_UNICODE) + fill_utf8(key); +} + termkey_result termkey_getkey(termkey_t *tk, termkey_key *key) { return (*tk->driver.getkey)(tk, key); @@ -273,6 +367,28 @@ const char *termkey_get_keyname(termkey_t *tk, termkey_keysym sym) return "UNKNOWN"; } +static termkey_keysym register_c0(termkey_t *tk, termkey_keysym sym, unsigned char ctrl, const char *name) +{ + return register_c0_full(tk, sym, 0, 0, ctrl, name); +} + +static termkey_keysym register_c0_full(termkey_t *tk, termkey_keysym sym, int modifier_set, int modifier_mask, unsigned char ctrl, const char *name) +{ + if(ctrl >= 0x20) { + fprintf(stderr, "Cannot register C0 key at ctrl 0x%02x - out of bounds\n", ctrl); + return -1; + } + + if(name) + sym = termkey_register_keyname(tk, sym, name); + + tk->c0[ctrl].sym = sym; + tk->c0[ctrl].modifier_set = modifier_set; + tk->c0[ctrl].modifier_mask = modifier_mask; + + return sym; +} + size_t termkey_snprint_key(termkey_t *tk, char *buffer, size_t len, termkey_key *key, termkey_format format) { size_t pos = 0;