libtermkey-0.22/0000755000175000017500000000000013442455536011737 5ustar leoleolibtermkey-0.22/termkey.c0000644000175000017500000011650013442455536013566 0ustar leoleo#include "termkey.h" #include "termkey-internal.h" #include #include #ifndef _WIN32 # include # include # include #endif #include #include #ifdef _MSC_VER # define strcaseeq(a,b) (_stricmp(a,b) == 0) #else # define strcaseeq(a,b) (strcasecmp(a,b) == 0) #endif void termkey_check_version(int major, int minor) { if(major != TERMKEY_VERSION_MAJOR) { fprintf(stderr, "libtermkey major version mismatch; %d (wants) != %d (library)\n", major, TERMKEY_VERSION_MAJOR); exit(1); } if(minor > TERMKEY_VERSION_MINOR) { fprintf(stderr, "libtermkey minor version mismatch; %d (wants) > %d (library)\n", minor, TERMKEY_VERSION_MINOR); exit(1); } // Happy } static struct TermKeyDriver *drivers[] = { &termkey_driver_ti, &termkey_driver_csi, NULL, }; // Forwards for the "protected" methods // static void eat_bytes(TermKey *tk, size_t count); static void emit_codepoint(TermKey *tk, long codepoint, TermKeyKey *key); static TermKeyResult peekkey_simple(TermKey *tk, TermKeyKey *key, int force, size_t *nbytes); static TermKeyResult peekkey_mouse(TermKey *tk, TermKeyKey *key, size_t *nbytes); static TermKeySym register_c0(TermKey *tk, TermKeySym sym, unsigned char ctrl, const char *name); static TermKeySym register_c0_full(TermKey *tk, TermKeySym sym, int modifier_set, int modifier_mask, unsigned char ctrl, const char *name); static struct { TermKeySym sym; const char *name; } keynames[] = { { TERMKEY_SYM_NONE, "NONE" }, { TERMKEY_SYM_BACKSPACE, "Backspace" }, { TERMKEY_SYM_TAB, "Tab" }, { TERMKEY_SYM_ENTER, "Enter" }, { TERMKEY_SYM_ESCAPE, "Escape" }, { TERMKEY_SYM_SPACE, "Space" }, { TERMKEY_SYM_DEL, "DEL" }, { TERMKEY_SYM_UP, "Up" }, { TERMKEY_SYM_DOWN, "Down" }, { TERMKEY_SYM_LEFT, "Left" }, { TERMKEY_SYM_RIGHT, "Right" }, { TERMKEY_SYM_BEGIN, "Begin" }, { TERMKEY_SYM_FIND, "Find" }, { TERMKEY_SYM_INSERT, "Insert" }, { TERMKEY_SYM_DELETE, "Delete" }, { TERMKEY_SYM_SELECT, "Select" }, { TERMKEY_SYM_PAGEUP, "PageUp" }, { TERMKEY_SYM_PAGEDOWN, "PageDown" }, { TERMKEY_SYM_HOME, "Home" }, { TERMKEY_SYM_END, "End" }, { TERMKEY_SYM_CANCEL, "Cancel" }, { TERMKEY_SYM_CLEAR, "Clear" }, { TERMKEY_SYM_CLOSE, "Close" }, { TERMKEY_SYM_COMMAND, "Command" }, { TERMKEY_SYM_COPY, "Copy" }, { TERMKEY_SYM_EXIT, "Exit" }, { TERMKEY_SYM_HELP, "Help" }, { TERMKEY_SYM_MARK, "Mark" }, { TERMKEY_SYM_MESSAGE, "Message" }, { TERMKEY_SYM_MOVE, "Move" }, { TERMKEY_SYM_OPEN, "Open" }, { TERMKEY_SYM_OPTIONS, "Options" }, { TERMKEY_SYM_PRINT, "Print" }, { TERMKEY_SYM_REDO, "Redo" }, { TERMKEY_SYM_REFERENCE, "Reference" }, { TERMKEY_SYM_REFRESH, "Refresh" }, { TERMKEY_SYM_REPLACE, "Replace" }, { TERMKEY_SYM_RESTART, "Restart" }, { TERMKEY_SYM_RESUME, "Resume" }, { TERMKEY_SYM_SAVE, "Save" }, { TERMKEY_SYM_SUSPEND, "Suspend" }, { TERMKEY_SYM_UNDO, "Undo" }, { TERMKEY_SYM_KP0, "KP0" }, { TERMKEY_SYM_KP1, "KP1" }, { TERMKEY_SYM_KP2, "KP2" }, { TERMKEY_SYM_KP3, "KP3" }, { TERMKEY_SYM_KP4, "KP4" }, { TERMKEY_SYM_KP5, "KP5" }, { TERMKEY_SYM_KP6, "KP6" }, { TERMKEY_SYM_KP7, "KP7" }, { TERMKEY_SYM_KP8, "KP8" }, { TERMKEY_SYM_KP9, "KP9" }, { TERMKEY_SYM_KPENTER, "KPEnter" }, { TERMKEY_SYM_KPPLUS, "KPPlus" }, { TERMKEY_SYM_KPMINUS, "KPMinus" }, { TERMKEY_SYM_KPMULT, "KPMult" }, { TERMKEY_SYM_KPDIV, "KPDiv" }, { TERMKEY_SYM_KPCOMMA, "KPComma" }, { TERMKEY_SYM_KPPERIOD, "KPPeriod" }, { TERMKEY_SYM_KPEQUALS, "KPEquals" }, { 0, NULL }, }; // Mouse event names static const char *evnames[] = { "Unknown", "Press", "Drag", "Release" }; #define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) #ifdef DEBUG /* Some internal debugging functions */ static void print_buffer(TermKey *tk) { int i; for(i = 0; i < tk->buffcount && i < 20; i++) fprintf(stderr, "%02x ", CHARAT(i)); if(tk->buffcount > 20) fprintf(stderr, "..."); } static void print_key(TermKey *tk, TermKeyKey *key) { switch(key->type) { case TERMKEY_TYPE_UNICODE: fprintf(stderr, "Unicode codepoint=U+%04lx utf8='%s'", key->code.codepoint, key->utf8); break; case TERMKEY_TYPE_FUNCTION: fprintf(stderr, "Function F%d", key->code.number); break; case TERMKEY_TYPE_KEYSYM: fprintf(stderr, "Keysym sym=%d(%s)", key->code.sym, termkey_get_keyname(tk, key->code.sym)); break; case TERMKEY_TYPE_MOUSE: { TermKeyMouseEvent ev; int button, line, col; termkey_interpret_mouse(tk, key, &ev, &button, &line, &col); fprintf(stderr, "Mouse ev=%d button=%d pos=(%d,%d)\n", ev, button, line, col); } break; case TERMKEY_TYPE_POSITION: { int line, col; termkey_interpret_position(tk, key, &line, &col); fprintf(stderr, "Position report pos=(%d,%d)\n", line, col); } break; case TERMKEY_TYPE_MODEREPORT: { int initial, mode, value; termkey_interpret_modereport(tk, key, &initial, &mode, &value); fprintf(stderr, "Mode report mode=%s %d val=%d\n", initial == '?' ? "DEC" : "ANSI", mode, value); } break; case TERMKEY_TYPE_DCS: fprintf(stderr, "Device Control String"); break; case TERMKEY_TYPE_OSC: fprintf(stderr, "Operating System Control"); break; case TERMKEY_TYPE_UNKNOWN_CSI: fprintf(stderr, "unknown CSI\n"); break; } int m = key->modifiers; fprintf(stderr, " mod=%s%s%s+%02x", (m & TERMKEY_KEYMOD_CTRL ? "C" : ""), (m & TERMKEY_KEYMOD_ALT ? "A" : ""), (m & TERMKEY_KEYMOD_SHIFT ? "S" : ""), m & ~(TERMKEY_KEYMOD_CTRL|TERMKEY_KEYMOD_ALT|TERMKEY_KEYMOD_SHIFT)); } static const char *res2str(TermKeyResult res) { static char errorbuffer[256]; switch(res) { case TERMKEY_RES_KEY: return "TERMKEY_RES_KEY"; case TERMKEY_RES_EOF: return "TERMKEY_RES_EOF"; case TERMKEY_RES_AGAIN: return "TERMKEY_RES_AGAIN"; case TERMKEY_RES_NONE: return "TERMKEY_RES_NONE"; case TERMKEY_RES_ERROR: snprintf(errorbuffer, sizeof errorbuffer, "TERMKEY_RES_ERROR(errno=%d)\n", errno); return (const char*)errorbuffer; } return "unknown"; } #endif /* Similar to snprintf(str, size, "%s", src) except it turns CamelCase into * space separated values */ static int snprint_cameltospaces(char *str, size_t size, const char *src) { int prev_lower = 0; size_t l = 0; while(*src && l < size - 1) { if(isupper(*src) && prev_lower) { if(str) str[l++] = ' '; if(l >= size - 1) break; } prev_lower = islower(*src); str[l++] = tolower(*src++); } str[l] = 0; /* For consistency with snprintf, return the number of bytes that would have * been written, excluding '\0' */ while(*src) { if(isupper(*src) && prev_lower) { l++; } prev_lower = islower(*src); src++; l++; } return l; } /* Similar to strcmp(str, strcamel, n) except that: * it compares CamelCase in strcamel with space separated values in str; * it takes char**s and updates them * n counts bytes of strcamel, not str */ static int strpncmp_camel(const char **strp, const char **strcamelp, size_t n) { const char *str = *strp, *strcamel = *strcamelp; int prev_lower = 0; for( ; (*str || *strcamel) && n; n--) { char b = tolower(*strcamel); if(isupper(*strcamel) && prev_lower) { if(*str != ' ') break; str++; if(*str != b) break; } else if(*str != b) break; prev_lower = islower(*strcamel); str++; strcamel++; } *strp = str; *strcamelp = strcamel; return *str - *strcamel; } static TermKey *termkey_alloc(void) { TermKey *tk = malloc(sizeof(TermKey)); if(!tk) return NULL; /* Default all the object fields but don't allocate anything */ tk->fd = -1; tk->flags = 0; tk->canonflags = 0; tk->buffer = NULL; tk->buffstart = 0; tk->buffcount = 0; tk->buffsize = 256; /* bytes */ tk->hightide = 0; #ifdef HAVE_TERMIOS tk->restore_termios_valid = 0; #endif tk->ti_getstr_hook = NULL; tk->ti_getstr_hook_data = NULL; tk->waittime = 50; /* msec */ tk->is_closed = 0; tk->is_started = 0; tk->nkeynames = 64; tk->keynames = NULL; for(int i = 0; i < 32; i++) tk->c0[i].sym = TERMKEY_SYM_NONE; tk->drivers = NULL; tk->method.emit_codepoint = &emit_codepoint; tk->method.peekkey_simple = &peekkey_simple; tk->method.peekkey_mouse = &peekkey_mouse; return tk; } static int termkey_init(TermKey *tk, const char *term) { tk->buffer = malloc(tk->buffsize); if(!tk->buffer) return 0; tk->keynames = malloc(sizeof(tk->keynames[0]) * tk->nkeynames); if(!tk->keynames) goto abort_free_buffer; int i; for(i = 0; i < tk->nkeynames; i++) tk->keynames[i] = NULL; for(i = 0; keynames[i].name; i++) if(termkey_register_keyname(tk, keynames[i].sym, keynames[i].name) == -1) goto abort_free_keynames; register_c0(tk, TERMKEY_SYM_TAB, 0x09, NULL); register_c0(tk, TERMKEY_SYM_ENTER, 0x0d, NULL); register_c0(tk, TERMKEY_SYM_ESCAPE, 0x1b, NULL); struct TermKeyDriverNode *tail = NULL; for(i = 0; drivers[i]; i++) { void *info = (*drivers[i]->new_driver)(tk, term); if(!info) continue; #ifdef DEBUG fprintf(stderr, "Loading the %s driver...\n", drivers[i]->name); #endif struct TermKeyDriverNode *thisdrv = malloc(sizeof(*thisdrv)); if(!thisdrv) goto abort_free_drivers; thisdrv->driver = drivers[i]; thisdrv->info = info; thisdrv->next = NULL; if(!tail) tk->drivers = thisdrv; else tail->next = thisdrv; tail = thisdrv; #ifdef DEBUG fprintf(stderr, "Loaded %s driver\n", drivers[i]->name); #endif } if(!tk->drivers) { errno = ENOENT; goto abort_free_keynames; } return 1; abort_free_drivers: for(struct TermKeyDriverNode *p = tk->drivers; p; ) { (*p->driver->free_driver)(p->info); struct TermKeyDriverNode *next = p->next; free(p); p = next; } abort_free_keynames: free(tk->keynames); abort_free_buffer: free(tk->buffer); return 0; } TermKey *termkey_new(int fd, int flags) { TermKey *tk = termkey_alloc(); if(!tk) return NULL; tk->fd = fd; if(!(flags & (TERMKEY_FLAG_RAW|TERMKEY_FLAG_UTF8))) { char *e; /* Most OSes will set .UTF-8. Some will set .utf8. Try to be fairly * generous in parsing these */ if(((e = getenv("LANG")) || (e = getenv("LC_MESSAGES")) || (e = getenv("LC_ALL"))) && (e = strchr(e, '.')) && e++ && (strcaseeq(e, "UTF-8") || strcaseeq(e, "UTF8"))) flags |= TERMKEY_FLAG_UTF8; else flags |= TERMKEY_FLAG_RAW; } termkey_set_flags(tk, flags); const char *term = getenv("TERM"); if(!termkey_init(tk, term)) goto abort; if(!(flags & TERMKEY_FLAG_NOSTART) && !termkey_start(tk)) goto abort; return tk; abort: free(tk); return NULL; } TermKey *termkey_new_abstract(const char *term, int flags) { TermKey *tk = termkey_alloc(); if(!tk) return NULL; tk->fd = -1; termkey_set_flags(tk, flags); if(!termkey_init(tk, term)) { free(tk); return NULL; } if(!(flags & TERMKEY_FLAG_NOSTART) && !termkey_start(tk)) goto abort; return tk; abort: free(tk); return NULL; } void termkey_free(TermKey *tk) { free(tk->buffer); tk->buffer = NULL; free(tk->keynames); tk->keynames = NULL; struct TermKeyDriverNode *p; for(p = tk->drivers; p; ) { (*p->driver->free_driver)(p->info); struct TermKeyDriverNode *next = p->next; free(p); p = next; } free(tk); } void termkey_destroy(TermKey *tk) { if(tk->is_started) termkey_stop(tk); termkey_free(tk); } void termkey_hook_terminfo_getstr(TermKey *tk, TermKey_Terminfo_Getstr_Hook *hookfn, void *data) { tk->ti_getstr_hook = hookfn; tk->ti_getstr_hook_data = data; } int termkey_start(TermKey *tk) { if(tk->is_started) return 1; #ifdef HAVE_TERMIOS if(tk->fd != -1 && !(tk->flags & TERMKEY_FLAG_NOTERMIOS)) { struct termios termios; if(tcgetattr(tk->fd, &termios) == 0) { tk->restore_termios = termios; tk->restore_termios_valid = 1; termios.c_iflag &= ~(IXON|INLCR|ICRNL); termios.c_lflag &= ~(ICANON|ECHO #ifdef IEXTEN | IEXTEN #endif ); termios.c_cc[VMIN] = 1; termios.c_cc[VTIME] = 0; if(tk->flags & TERMKEY_FLAG_CTRLC) /* want no signal keys at all, so just disable ISIG */ termios.c_lflag &= ~ISIG; else { /* Disable Ctrl-\==VQUIT and Ctrl-D==VSUSP but leave Ctrl-C as SIGINT */ termios.c_cc[VQUIT] = _POSIX_VDISABLE; termios.c_cc[VSUSP] = _POSIX_VDISABLE; /* Some OSes have Ctrl-Y==VDSUSP */ #ifdef VDSUSP termios.c_cc[VDSUSP] = _POSIX_VDISABLE; #endif } #ifdef DEBUG fprintf(stderr, "Setting termios(3) flags\n"); #endif tcsetattr(tk->fd, TCSANOW, &termios); } } #endif struct TermKeyDriverNode *p; for(p = tk->drivers; p; p = p->next) if(p->driver->start_driver) if(!(*p->driver->start_driver)(tk, p->info)) return 0; #ifdef DEBUG fprintf(stderr, "Drivers started; termkey instance %p is ready\n", tk); #endif tk->is_started = 1; return 1; } int termkey_stop(TermKey *tk) { if(!tk->is_started) return 1; struct TermKeyDriverNode *p; for(p = tk->drivers; p; p = p->next) if(p->driver->stop_driver) (*p->driver->stop_driver)(tk, p->info); #ifdef HAVE_TERMIOS if(tk->restore_termios_valid) tcsetattr(tk->fd, TCSANOW, &tk->restore_termios); #endif tk->is_started = 0; return 1; } int termkey_is_started(TermKey *tk) { return tk->is_started; } int termkey_get_fd(TermKey *tk) { return tk->fd; } int termkey_get_flags(TermKey *tk) { return tk->flags; } void termkey_set_flags(TermKey *tk, int newflags) { tk->flags = newflags; if(tk->flags & TERMKEY_FLAG_SPACESYMBOL) tk->canonflags |= TERMKEY_CANON_SPACESYMBOL; else tk->canonflags &= ~TERMKEY_CANON_SPACESYMBOL; } void termkey_set_waittime(TermKey *tk, int msec) { tk->waittime = msec; } int termkey_get_waittime(TermKey *tk) { return tk->waittime; } int termkey_get_canonflags(TermKey *tk) { return tk->canonflags; } void termkey_set_canonflags(TermKey *tk, int flags) { tk->canonflags = flags; if(tk->canonflags & TERMKEY_CANON_SPACESYMBOL) tk->flags |= TERMKEY_FLAG_SPACESYMBOL; else tk->flags &= ~TERMKEY_FLAG_SPACESYMBOL; } size_t termkey_get_buffer_size(TermKey *tk) { return tk->buffsize; } int termkey_set_buffer_size(TermKey *tk, size_t size) { unsigned char *buffer = realloc(tk->buffer, size); if(!buffer) return 0; tk->buffer = buffer; tk->buffsize = size; return 1; } size_t termkey_get_buffer_remaining(TermKey *tk) { /* Return the total number of free bytes in the buffer, because that's what * is available to the user. */ return tk->buffsize - tk->buffcount; } static void eat_bytes(TermKey *tk, size_t count) { if(count >= tk->buffcount) { tk->buffstart = 0; tk->buffcount = 0; return; } tk->buffstart += count; tk->buffcount -= count; } static inline unsigned int utf8_seqlen(long 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(TermKeyKey *key) { long 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; } } #define UTF8_INVALID 0xFFFD static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, long *cp, size_t *nbytep) { unsigned int nbytes; unsigned char b0 = bytes[0]; if(b0 < 0x80) { // Single byte ASCII *cp = b0; *nbytep = 1; return TERMKEY_RES_KEY; } else if(b0 < 0xc0) { // Starts with a continuation byte - that's not right *cp = UTF8_INVALID; *nbytep = 1; return TERMKEY_RES_KEY; } else if(b0 < 0xe0) { nbytes = 2; *cp = b0 & 0x1f; } else if(b0 < 0xf0) { nbytes = 3; *cp = b0 & 0x0f; } else if(b0 < 0xf8) { nbytes = 4; *cp = b0 & 0x07; } else if(b0 < 0xfc) { nbytes = 5; *cp = b0 & 0x03; } else if(b0 < 0xfe) { nbytes = 6; *cp = b0 & 0x01; } else { *cp = UTF8_INVALID; *nbytep = 1; return TERMKEY_RES_KEY; } for(unsigned int b = 1; b < nbytes; b++) { unsigned char cb; if(b >= len) return TERMKEY_RES_AGAIN; cb = bytes[b]; if(cb < 0x80 || cb >= 0xc0) { *cp = UTF8_INVALID; *nbytep = b; return TERMKEY_RES_KEY; } *cp <<= 6; *cp |= cb & 0x3f; } // Check for overlong sequences if(nbytes > utf8_seqlen(*cp)) *cp = UTF8_INVALID; // Check for UTF-16 surrogates or invalid *cps if((*cp >= 0xD800 && *cp <= 0xDFFF) || *cp == 0xFFFE || *cp == 0xFFFF) *cp = UTF8_INVALID; *nbytep = nbytes; return TERMKEY_RES_KEY; } static void emit_codepoint(TermKey *tk, long codepoint, TermKeyKey *key) { if(codepoint == 0) { // ASCII NUL = Ctrl-Space key->type = TERMKEY_TYPE_KEYSYM; key->code.sym = TERMKEY_SYM_SPACE; key->modifiers = TERMKEY_KEYMOD_CTRL; } else 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; /* Generically modified Unicode ought not report the SHIFT state, or else * we get into complications trying to report Shift-; vs : and so on... * In order to be able to represent Ctrl-Shift-A as CTRL modified * unicode A, we need to call Ctrl-A simply 'a', lowercase */ if(codepoint+0x40 >= 'A' && codepoint+0x40 <= 'Z') // it's a letter - use lowercase instead key->code.codepoint = codepoint + 0x60; else key->code.codepoint = codepoint + 0x40; key->modifiers = TERMKEY_KEYMOD_CTRL; } else { key->type = TERMKEY_TYPE_KEYSYM; } } 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; } termkey_canonicalise(tk, key); if(key->type == TERMKEY_TYPE_UNICODE) fill_utf8(key); } void termkey_canonicalise(TermKey *tk, TermKeyKey *key) { int flags = tk->canonflags; if(flags & TERMKEY_CANON_SPACESYMBOL) { if(key->type == TERMKEY_TYPE_UNICODE && key->code.codepoint == 0x20) { key->type = TERMKEY_TYPE_KEYSYM; key->code.sym = TERMKEY_SYM_SPACE; } } else { if(key->type == TERMKEY_TYPE_KEYSYM && key->code.sym == TERMKEY_SYM_SPACE) { key->type = TERMKEY_TYPE_UNICODE; key->code.codepoint = 0x20; fill_utf8(key); } } if(flags & TERMKEY_CANON_DELBS) { if(key->type == TERMKEY_TYPE_KEYSYM && key->code.sym == TERMKEY_SYM_DEL) { key->code.sym = TERMKEY_SYM_BACKSPACE; } } } static TermKeyResult peekkey(TermKey *tk, TermKeyKey *key, int force, size_t *nbytep) { int again = 0; if(!tk->is_started) { errno = EINVAL; return TERMKEY_RES_ERROR; } #ifdef DEBUG fprintf(stderr, "getkey(force=%d): buffer ", force); print_buffer(tk); fprintf(stderr, "\n"); #endif if(tk->hightide) { tk->buffstart += tk->hightide; tk->buffcount -= tk->hightide; tk->hightide = 0; } TermKeyResult ret; struct TermKeyDriverNode *p; for(p = tk->drivers; p; p = p->next) { ret = (p->driver->peekkey)(tk, p->info, key, force, nbytep); #ifdef DEBUG fprintf(stderr, "Driver %s yields %s\n", p->driver->name, res2str(ret)); #endif switch(ret) { case TERMKEY_RES_KEY: #ifdef DEBUG print_key(tk, key); fprintf(stderr, "\n"); #endif // Slide the data down to stop it running away { size_t halfsize = tk->buffsize / 2; if(tk->buffstart > halfsize) { memcpy(tk->buffer, tk->buffer + halfsize, halfsize); tk->buffstart -= halfsize; } } /* fallthrough */ case TERMKEY_RES_EOF: case TERMKEY_RES_ERROR: return ret; case TERMKEY_RES_AGAIN: if(!force) again = 1; /* fallthrough */ case TERMKEY_RES_NONE: break; } } if(again) return TERMKEY_RES_AGAIN; ret = peekkey_simple(tk, key, force, nbytep); #ifdef DEBUG fprintf(stderr, "getkey_simple(force=%d) yields %s\n", force, res2str(ret)); if(ret == TERMKEY_RES_KEY) { print_key(tk, key); fprintf(stderr, "\n"); } #endif return ret; } static TermKeyResult peekkey_simple(TermKey *tk, TermKeyKey *key, int force, size_t *nbytep) { if(tk->buffcount == 0) return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE; unsigned char b0 = CHARAT(0); if(b0 == 0x1b) { // Escape-prefixed value? Might therefore be Alt+key if(tk->buffcount == 1) { // This might be an press, or it may want to be part of a longer // sequence if(!force) return TERMKEY_RES_AGAIN; (*tk->method.emit_codepoint)(tk, b0, key); *nbytep = 1; return TERMKEY_RES_KEY; } // Try another key there tk->buffstart++; tk->buffcount--; // Run the full driver TermKeyResult metakey_result = peekkey(tk, key, force, nbytep); tk->buffstart--; tk->buffcount++; switch(metakey_result) { case TERMKEY_RES_KEY: key->modifiers |= TERMKEY_KEYMOD_ALT; (*nbytep)++; break; case TERMKEY_RES_NONE: case TERMKEY_RES_EOF: case TERMKEY_RES_AGAIN: case TERMKEY_RES_ERROR: break; } return metakey_result; } else if(b0 < 0xa0) { // Single byte C0, G0 or C1 - C1 is never UTF-8 initial byte (*tk->method.emit_codepoint)(tk, b0, key); *nbytep = 1; return TERMKEY_RES_KEY; } else if(tk->flags & TERMKEY_FLAG_UTF8) { // Some UTF-8 long codepoint; TermKeyResult res = parse_utf8(tk->buffer + tk->buffstart, tk->buffcount, &codepoint, nbytep); if(res == TERMKEY_RES_AGAIN && force) { /* There weren't enough bytes for a complete UTF-8 sequence but caller * demands an answer. About the best thing we can do here is eat as many * bytes as we have, and emit a UTF8_INVALID. If the remaining bytes * arrive later, they'll be invalid too. */ codepoint = UTF8_INVALID; *nbytep = tk->buffcount; res = TERMKEY_RES_KEY; } key->type = TERMKEY_TYPE_UNICODE; key->modifiers = 0; (*tk->method.emit_codepoint)(tk, codepoint, key); return res; } else { // Non UTF-8 case - just report the raw byte key->type = TERMKEY_TYPE_UNICODE; key->code.codepoint = b0; key->modifiers = 0; key->utf8[0] = key->code.codepoint; key->utf8[1] = 0; *nbytep = 1; return TERMKEY_RES_KEY; } } static TermKeyResult peekkey_mouse(TermKey *tk, TermKeyKey *key, size_t *nbytep) { if(tk->buffcount < 3) return TERMKEY_RES_AGAIN; key->type = TERMKEY_TYPE_MOUSE; key->code.mouse[0] = CHARAT(0) - 0x20; key->code.mouse[1] = CHARAT(1) - 0x20; key->code.mouse[2] = CHARAT(2) - 0x20; key->code.mouse[3] = 0; key->modifiers = (key->code.mouse[0] & 0x1c) >> 2; key->code.mouse[0] &= ~0x1c; *nbytep = 3; return TERMKEY_RES_KEY; } TermKeyResult termkey_getkey(TermKey *tk, TermKeyKey *key) { size_t nbytes = 0; TermKeyResult ret = peekkey(tk, key, 0, &nbytes); if(ret == TERMKEY_RES_KEY) eat_bytes(tk, nbytes); if(ret == TERMKEY_RES_AGAIN) /* Call peekkey() again in force mode to obtain whatever it can */ (void)peekkey(tk, key, 1, &nbytes); /* Don't eat it yet though */ return ret; } TermKeyResult termkey_getkey_force(TermKey *tk, TermKeyKey *key) { size_t nbytes = 0; TermKeyResult ret = peekkey(tk, key, 1, &nbytes); if(ret == TERMKEY_RES_KEY) eat_bytes(tk, nbytes); return ret; } #ifndef _WIN32 TermKeyResult termkey_waitkey(TermKey *tk, TermKeyKey *key) { if(tk->fd == -1) { errno = EBADF; return TERMKEY_RES_ERROR; } while(1) { TermKeyResult ret = termkey_getkey(tk, key); switch(ret) { case TERMKEY_RES_KEY: case TERMKEY_RES_EOF: case TERMKEY_RES_ERROR: return ret; case TERMKEY_RES_NONE: ret = termkey_advisereadable(tk); if(ret == TERMKEY_RES_ERROR) return ret; break; case TERMKEY_RES_AGAIN: { if(tk->is_closed) // We're closed now. Never going to get more bytes so just go with // what we have return termkey_getkey_force(tk, key); struct pollfd fd; retry: fd.fd = tk->fd; fd.events = POLLIN; int pollret = poll(&fd, 1, tk->waittime); if(pollret == -1) { if(errno == EINTR && !(tk->flags & TERMKEY_FLAG_EINTR)) goto retry; return TERMKEY_RES_ERROR; } if(fd.revents & (POLLIN|POLLHUP|POLLERR)) ret = termkey_advisereadable(tk); else ret = TERMKEY_RES_NONE; if(ret == TERMKEY_RES_ERROR) return ret; if(ret == TERMKEY_RES_NONE) return termkey_getkey_force(tk, key); } break; } } /* UNREACHABLE */ } #endif TermKeyResult termkey_advisereadable(TermKey *tk) { ssize_t len; if(tk->fd == -1) { errno = EBADF; return TERMKEY_RES_ERROR; } if(tk->buffstart) { memmove(tk->buffer, tk->buffer + tk->buffstart, tk->buffcount); tk->buffstart = 0; } /* Not expecting it ever to be greater but doesn't hurt to handle that */ if(tk->buffcount >= tk->buffsize) { errno = ENOMEM; return TERMKEY_RES_ERROR; } retry: len = read(tk->fd, tk->buffer + tk->buffcount, tk->buffsize - tk->buffcount); if(len == -1) { if(errno == EAGAIN) return TERMKEY_RES_NONE; else if(errno == EINTR && !(tk->flags & TERMKEY_FLAG_EINTR)) goto retry; else return TERMKEY_RES_ERROR; } else if(len < 1) { tk->is_closed = 1; return TERMKEY_RES_NONE; } else { tk->buffcount += len; return TERMKEY_RES_AGAIN; } } size_t termkey_push_bytes(TermKey *tk, const char *bytes, size_t len) { if(tk->buffstart) { memmove(tk->buffer, tk->buffer + tk->buffstart, tk->buffcount); tk->buffstart = 0; } /* Not expecting it ever to be greater but doesn't hurt to handle that */ if(tk->buffcount >= tk->buffsize) { errno = ENOMEM; return (size_t)-1; } if(len > tk->buffsize - tk->buffcount) len = tk->buffsize - tk->buffcount; // memcpy(), not strncpy() in case of null bytes in input memcpy(tk->buffer + tk->buffcount, bytes, len); tk->buffcount += len; return len; } TermKeySym termkey_register_keyname(TermKey *tk, TermKeySym sym, const char *name) { if(!sym) sym = tk->nkeynames; if(sym >= tk->nkeynames) { const char **new_keynames = realloc(tk->keynames, sizeof(new_keynames[0]) * (sym + 1)); if(!new_keynames) return -1; tk->keynames = new_keynames; // Fill in the hole for(int i = tk->nkeynames; i < sym; i++) tk->keynames[i] = NULL; tk->nkeynames = sym + 1; } tk->keynames[sym] = name; return sym; } const char *termkey_get_keyname(TermKey *tk, TermKeySym sym) { if(sym == TERMKEY_SYM_UNKNOWN) return "UNKNOWN"; if(sym < tk->nkeynames) return tk->keynames[sym]; return "UNKNOWN"; } static const char *termkey_lookup_keyname_format(TermKey *tk, const char *str, TermKeySym *sym, TermKeyFormat format) { /* We store an array, so we can't do better than a linear search. Doesn't * matter because user won't be calling this too often */ for(*sym = 0; *sym < tk->nkeynames; (*sym)++) { const char *thiskey = tk->keynames[*sym]; if(!thiskey) continue; size_t len = strlen(thiskey); if(format & TERMKEY_FORMAT_LOWERSPACE) { const char *thisstr = str; if(strpncmp_camel(&thisstr, &thiskey, len) == 0) return thisstr; } else { if(strncmp(str, thiskey, len) == 0) return (char *)str + len; } } return NULL; } const char *termkey_lookup_keyname(TermKey *tk, const char *str, TermKeySym *sym) { return termkey_lookup_keyname_format(tk, str, sym, 0); } TermKeySym termkey_keyname2sym(TermKey *tk, const char *keyname) { TermKeySym sym; const char *endp = termkey_lookup_keyname(tk, keyname, &sym); if(!endp || endp[0]) return TERMKEY_SYM_UNKNOWN; return sym; } static TermKeySym register_c0(TermKey *tk, TermKeySym sym, unsigned char ctrl, const char *name) { return register_c0_full(tk, sym, 0, 0, ctrl, name); } static TermKeySym register_c0_full(TermKey *tk, TermKeySym sym, int modifier_set, int modifier_mask, unsigned char ctrl, const char *name) { if(ctrl >= 0x20) { errno = EINVAL; 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; } /* Previous name for this function * No longer declared in termkey.h but it remains in the compiled library for * backward-compatibility reasons. */ size_t termkey_snprint_key(TermKey *tk, char *buffer, size_t len, TermKeyKey *key, TermKeyFormat format) { return termkey_strfkey(tk, buffer, len, key, format); } static struct modnames { const char *shift, *alt, *ctrl; } modnames[] = { { "S", "A", "C" }, // 0 { "Shift", "Alt", "Ctrl" }, // LONGMOD { "S", "M", "C" }, // ALTISMETA { "Shift", "Meta", "Ctrl" }, // ALTISMETA+LONGMOD { "s", "a", "c" }, // LOWERMOD { "shift", "alt", "ctrl" }, // LOWERMOD+LONGMOD { "s", "m", "c" }, // LOWERMOD+ALTISMETA { "shift", "meta", "ctrl" }, // LOWERMOD+ALTISMETA+LONGMOD }; size_t termkey_strfkey(TermKey *tk, char *buffer, size_t len, TermKeyKey *key, TermKeyFormat format) { size_t pos = 0; size_t l = 0; struct modnames *mods = &modnames[!!(format & TERMKEY_FORMAT_LONGMOD) + !!(format & TERMKEY_FORMAT_ALTISMETA) * 2 + !!(format & TERMKEY_FORMAT_LOWERMOD) * 4]; int wrapbracket = (format & TERMKEY_FORMAT_WRAPBRACKET) && (key->type != TERMKEY_TYPE_UNICODE || key->modifiers != 0); char sep = (format & TERMKEY_FORMAT_SPACEMOD) ? ' ' : '-'; if(format & TERMKEY_FORMAT_CARETCTRL && key->type == TERMKEY_TYPE_UNICODE && key->modifiers == TERMKEY_KEYMOD_CTRL) { long codepoint = key->code.codepoint; // Handle some of the special cases first if(codepoint >= 'a' && codepoint <= 'z') { l = snprintf(buffer + pos, len - pos, wrapbracket ? "<^%c>" : "^%c", (char)codepoint - 0x20); if(l <= 0) return pos; pos += l; return pos; } else if((codepoint >= '@' && codepoint < 'A') || (codepoint > 'Z' && codepoint <= '_')) { l = snprintf(buffer + pos, len - pos, wrapbracket ? "<^%c>" : "^%c", (char)codepoint); if(l <= 0) return pos; pos += l; return pos; } } if(wrapbracket) { l = snprintf(buffer + pos, len - pos, "<"); if(l <= 0) return pos; pos += l; } if(key->modifiers & TERMKEY_KEYMOD_ALT) { l = snprintf(buffer + pos, len - pos, "%s%c", mods->alt, sep); if(l <= 0) return pos; pos += l; } if(key->modifiers & TERMKEY_KEYMOD_CTRL) { l = snprintf(buffer + pos, len - pos, "%s%c", mods->ctrl, sep); if(l <= 0) return pos; pos += l; } if(key->modifiers & TERMKEY_KEYMOD_SHIFT) { l = snprintf(buffer + pos, len - pos, "%s%c", mods->shift, sep); if(l <= 0) return pos; pos += l; } switch(key->type) { case TERMKEY_TYPE_UNICODE: if(!key->utf8[0]) // In case of user-supplied key structures fill_utf8(key); l = snprintf(buffer + pos, len - pos, "%s", key->utf8); break; case TERMKEY_TYPE_KEYSYM: { const char *name = termkey_get_keyname(tk, key->code.sym); if(format & TERMKEY_FORMAT_LOWERSPACE) l = snprint_cameltospaces(buffer + pos, len - pos, name); else l = snprintf(buffer + pos, len - pos, "%s", name); } break; case TERMKEY_TYPE_FUNCTION: l = snprintf(buffer + pos, len - pos, "%c%d", (format & TERMKEY_FORMAT_LOWERSPACE ? 'f' : 'F'), key->code.number); break; case TERMKEY_TYPE_MOUSE: { TermKeyMouseEvent ev; int button; int line, col; termkey_interpret_mouse(tk, key, &ev, &button, &line, &col); l = snprintf(buffer + pos, len - pos, "Mouse%s(%d)", evnames[ev], button); if(format & TERMKEY_FORMAT_MOUSE_POS) { if(l <= 0) return pos; pos += l; l = snprintf(buffer + pos, len - pos, " @ (%u,%u)", col, line); } } break; case TERMKEY_TYPE_POSITION: l = snprintf(buffer + pos, len - pos, "Position"); break; case TERMKEY_TYPE_MODEREPORT: { int initial, mode, value; termkey_interpret_modereport(tk, key, &initial, &mode, &value); if(initial) l = snprintf(buffer + pos, len - pos, "Mode(%c%d=%d)", initial, mode, value); else l = snprintf(buffer + pos, len - pos, "Mode(%d=%d)", mode, value); } case TERMKEY_TYPE_DCS: l = snprintf(buffer + pos, len - pos, "DCS"); break; case TERMKEY_TYPE_OSC: l = snprintf(buffer + pos, len - pos, "OSC"); break; case TERMKEY_TYPE_UNKNOWN_CSI: l = snprintf(buffer + pos, len - pos, "CSI %c", key->code.number & 0xff); break; } if(l <= 0) return pos; pos += l; if(wrapbracket) { l = snprintf(buffer + pos, len - pos, ">"); if(l <= 0) return pos; pos += l; } return pos; } const char *termkey_strpkey(TermKey *tk, const char *str, TermKeyKey *key, TermKeyFormat format) { struct modnames *mods = &modnames[!!(format & TERMKEY_FORMAT_LONGMOD) + !!(format & TERMKEY_FORMAT_ALTISMETA) * 2 + !!(format & TERMKEY_FORMAT_LOWERMOD) * 4]; key->modifiers = 0; if((format & TERMKEY_FORMAT_CARETCTRL) && str[0] == '^' && str[1]) { str = termkey_strpkey(tk, str+1, key, format & ~TERMKEY_FORMAT_CARETCTRL); if(!str || key->type != TERMKEY_TYPE_UNICODE || key->code.codepoint < '@' || key->code.codepoint > '_' || key->modifiers != 0) return NULL; if(key->code.codepoint >= 'A' && key->code.codepoint <= 'Z') key->code.codepoint += 0x20; key->modifiers = TERMKEY_KEYMOD_CTRL; fill_utf8(key); return (char *)str; } const char *sep_at; while((sep_at = strchr(str, (format & TERMKEY_FORMAT_SPACEMOD) ? ' ' : '-'))) { size_t n = sep_at - str; if(n == strlen(mods->alt) && strncmp(mods->alt, str, n) == 0) key->modifiers |= TERMKEY_KEYMOD_ALT; else if(n == strlen(mods->ctrl) && strncmp(mods->ctrl, str, n) == 0) key->modifiers |= TERMKEY_KEYMOD_CTRL; else if(n == strlen(mods->shift) && strncmp(mods->shift, str, n) == 0) key->modifiers |= TERMKEY_KEYMOD_SHIFT; else break; str = sep_at + 1; } size_t nbytes; ssize_t snbytes; const char *endstr; int button; char event_name[32]; if((endstr = termkey_lookup_keyname_format(tk, str, &key->code.sym, format))) { key->type = TERMKEY_TYPE_KEYSYM; str = endstr; } else if(sscanf(str, "F%d%zn", &key->code.number, &snbytes) == 1) { key->type = TERMKEY_TYPE_FUNCTION; str += snbytes; } else if(sscanf(str, "Mouse%31[^(](%d)%zn", event_name, &button, &snbytes) == 2) { str += snbytes; key->type = TERMKEY_TYPE_MOUSE; TermKeyMouseEvent ev = TERMKEY_MOUSE_UNKNOWN; for(size_t i = 0; i < sizeof(evnames)/sizeof(evnames[0]); i++) { if(strcmp(evnames[i], event_name) == 0) { ev = TERMKEY_MOUSE_UNKNOWN + i; break; } } int code; switch(ev) { case TERMKEY_MOUSE_PRESS: case TERMKEY_MOUSE_DRAG: code = button - 1; if(ev == TERMKEY_MOUSE_DRAG) { code |= 0x20; } break; case TERMKEY_MOUSE_RELEASE: code = 3; break; default: code = 128; break; } key->code.mouse[0] = code; unsigned int line = 0, col = 0; if((format & TERMKEY_FORMAT_MOUSE_POS) && sscanf(str, " @ (%u,%u)%zn", &col, &line, &snbytes) == 2) { str += snbytes; } termkey_key_set_linecol(key, col, line); } // Unicode must be last else if(parse_utf8((unsigned const char *)str, strlen(str), &key->code.codepoint, &nbytes) == TERMKEY_RES_KEY) { key->type = TERMKEY_TYPE_UNICODE; fill_utf8(key); str += nbytes; } else return NULL; termkey_canonicalise(tk, key); return (char *)str; } int termkey_keycmp(TermKey *tk, const TermKeyKey *key1p, const TermKeyKey *key2p) { /* Copy the key structs since we'll be modifying them */ TermKeyKey key1 = *key1p, key2 = *key2p; termkey_canonicalise(tk, &key1); termkey_canonicalise(tk, &key2); if(key1.type != key2.type) return key1.type - key2.type; switch(key1.type) { case TERMKEY_TYPE_UNICODE: if(key1.code.codepoint != key2.code.codepoint) return key1.code.codepoint - key2.code.codepoint; break; case TERMKEY_TYPE_KEYSYM: if(key1.code.sym != key2.code.sym) return key1.code.sym - key2.code.sym; break; case TERMKEY_TYPE_FUNCTION: case TERMKEY_TYPE_UNKNOWN_CSI: if(key1.code.number != key2.code.number) return key1.code.number - key2.code.number; break; case TERMKEY_TYPE_MOUSE: { int cmp = strncmp(key1.code.mouse, key2.code.mouse, 4); if(cmp != 0) return cmp; } break; case TERMKEY_TYPE_POSITION: { int line1, col1, line2, col2; termkey_interpret_position(tk, &key1, &line1, &col1); termkey_interpret_position(tk, &key2, &line2, &col2); if(line1 != line2) return line1 - line2; return col1 - col2; } break; case TERMKEY_TYPE_DCS: case TERMKEY_TYPE_OSC: return key1p - key2p; case TERMKEY_TYPE_MODEREPORT: { int initial1, initial2, mode1, mode2, value1, value2; termkey_interpret_modereport(tk, &key1, &initial1, &mode1, &value1); termkey_interpret_modereport(tk, &key2, &initial2, &mode2, &value2); if(initial1 != initial2) return initial1 - initial2; if(mode1 != mode2) return mode1 - mode2; return value1 - value2; } } return key1.modifiers - key2.modifiers; } libtermkey-0.22/driver-ti.c0000644000175000017500000003625313442455536014021 0ustar leoleo// we want strdup() #define _XOPEN_SOURCE 600 #include "termkey.h" #include "termkey-internal.h" #ifdef HAVE_UNIBILIUM # include #else # include # include /* curses.h has just polluted our namespace. We want this back */ # undef buttons #endif #include #include #include #include #include #ifndef _WIN32 # include #endif #include #include #define streq(a,b) (!strcmp(a,b)) #define MAX_FUNCNAME 9 static struct { const char *funcname; TermKeyType type; TermKeySym sym; int mods; } funcs[] = { /* THIS LIST MUST REMAIN SORTED! */ { "backspace", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BACKSPACE, 0 }, { "begin", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BEGIN, 0 }, { "beg", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BEGIN, 0 }, { "btab", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_TAB, TERMKEY_KEYMOD_SHIFT }, { "cancel", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_CANCEL, 0 }, { "clear", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_CLEAR, 0 }, { "close", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_CLOSE, 0 }, { "command", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_COMMAND, 0 }, { "copy", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_COPY, 0 }, { "dc", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DELETE, 0 }, { "down", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DOWN, 0 }, { "end", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_END, 0 }, { "enter", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_ENTER, 0 }, { "exit", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_EXIT, 0 }, { "find", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_FIND, 0 }, { "help", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HELP, 0 }, { "home", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HOME, 0 }, { "ic", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_INSERT, 0 }, { "left", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_LEFT, 0 }, { "mark", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_MARK, 0 }, { "message", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_MESSAGE, 0 }, { "move", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_MOVE, 0 }, { "next", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEDOWN, 0 }, // Not quite, but it's the best we can do { "npage", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEDOWN, 0 }, { "open", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_OPEN, 0 }, { "options", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_OPTIONS, 0 }, { "ppage", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEUP, 0 }, { "previous", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEUP, 0 }, // Not quite, but it's the best we can do { "print", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PRINT, 0 }, { "redo", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REDO, 0 }, { "reference", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REFERENCE, 0 }, { "refresh", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REFRESH, 0 }, { "replace", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REPLACE, 0 }, { "restart", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RESTART, 0 }, { "resume", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RESUME, 0 }, { "right", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RIGHT, 0 }, { "save", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SAVE, 0 }, { "select", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SELECT, 0 }, { "suspend", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SUSPEND, 0 }, { "undo", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_UNDO, 0 }, { "up", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_UP, 0 }, { NULL }, }; #ifdef HAVE_UNIBILIUM static enum unibi_string unibi_lookup_str(const char *name) { for(enum unibi_string ret = unibi_string_begin_+1; ret < unibi_string_end_; ret++) if(streq(unibi_name_str(ret), name)) return ret; return -1; } static const char *unibi_get_str_by_name(const unibi_term *ut, const char *name) { enum unibi_string idx = unibi_lookup_str(name); if(idx == (enum unibi_string)-1) return NULL; return unibi_get_str(ut, idx); } #endif /* To be efficient at lookups, we store the byte sequence => keyinfo mapping * in a trie. This avoids a slow linear search through a flat list of * sequences. Because it is likely most nodes will be very sparse, we optimise * vector to store an extent map after the database is loaded. */ typedef enum { TYPE_KEY, TYPE_ARR, } trie_nodetype; struct trie_node { trie_nodetype type; }; struct trie_node_key { trie_nodetype type; struct keyinfo key; }; struct trie_node_arr { trie_nodetype type; unsigned char min, max; /* INCLUSIVE endpoints of the extent range */ struct trie_node *arr[]; /* dynamic size at allocation time */ }; typedef struct { TermKey *tk; #ifdef HAVE_UNIBILIUM unibi_term *unibi; /* only valid until first 'start' call */ #else char *term; /* only valid until first 'start' call */ #endif struct trie_node *root; char *start_string; char *stop_string; } TermKeyTI; static int insert_seq(TermKeyTI *ti, const char *seq, struct trie_node *node); static struct trie_node *new_node_key(TermKeyType type, TermKeySym sym, int modmask, int modset) { struct trie_node_key *n = malloc(sizeof(*n)); if(!n) return NULL; n->type = TYPE_KEY; n->key.type = type; n->key.sym = sym; n->key.modifier_mask = modmask; n->key.modifier_set = modset; return (struct trie_node*)n; } static struct trie_node *new_node_arr(unsigned char min, unsigned char max) { struct trie_node_arr *n = malloc(sizeof(*n) + ((int)max-min+1) * sizeof(n->arr[0])); if(!n) return NULL; n->type = TYPE_ARR; n->min = min; n->max = max; int i; for(i = min; i <= max; i++) n->arr[i-min] = NULL; return (struct trie_node*)n; } static struct trie_node *lookup_next(struct trie_node *n, unsigned char b) { switch(n->type) { case TYPE_KEY: fprintf(stderr, "ABORT: lookup_next within a TYPE_KEY node\n"); abort(); case TYPE_ARR: { struct trie_node_arr *nar = (struct trie_node_arr*)n; if(b < nar->min || b > nar->max) return NULL; return nar->arr[b - nar->min]; } } return NULL; // Never reached but keeps compiler happy } static void free_trie(struct trie_node *n) { switch(n->type) { case TYPE_KEY: break; case TYPE_ARR: { struct trie_node_arr *nar = (struct trie_node_arr*)n; int i; for(i = nar->min; i <= nar->max; i++) if(nar->arr[i - nar->min]) free_trie(nar->arr[i - nar->min]); break; } } free(n); } static struct trie_node *compress_trie(struct trie_node *n) { if(!n) return NULL; switch(n->type) { case TYPE_KEY: return n; case TYPE_ARR: { struct trie_node_arr *nar = (struct trie_node_arr*)n; unsigned char min, max; // Find the real bounds for(min = 0; !nar->arr[min]; min++) if(min == 255 && !nar->arr[min]) { free(nar); return new_node_arr(1, 0); } for(max = 0xff; !nar->arr[max]; max--) ; struct trie_node_arr *new = (struct trie_node_arr*)new_node_arr(min, max); int i; for(i = min; i <= max; i++) new->arr[i - min] = compress_trie(nar->arr[i]); free(nar); return (struct trie_node*)new; } } return n; } static bool try_load_terminfo_key(TermKeyTI *ti, const char *name, struct keyinfo *info) { const char *value = NULL; #ifdef HAVE_UNIBILIUM if(ti->unibi) value = unibi_get_str_by_name(ti->unibi, name); #else if(ti->term) value = tigetstr(name); #endif if(ti->tk->ti_getstr_hook) value = (ti->tk->ti_getstr_hook)(name, value, ti->tk->ti_getstr_hook_data); if(!value || value == (char*)-1 || !value[0]) return false; struct trie_node *node = new_node_key(info->type, info->sym, info->modifier_mask, info->modifier_set); insert_seq(ti, value, node); return true; } static int load_terminfo(TermKeyTI *ti) { int i; #ifdef HAVE_UNIBILIUM unibi_term *unibi = ti->unibi; #else { int err; /* Have to cast away the const. But it's OK - we know terminfo won't really * modify term */ if(setupterm((char*)ti->term, 1, &err) != OK) return 0; } #endif ti->root = new_node_arr(0, 0xff); if(!ti->root) return 0; /* First the regular key strings */ for(i = 0; funcs[i].funcname; i++) { char name[MAX_FUNCNAME + 5 + 1]; sprintf(name, "key_%s", funcs[i].funcname); if(!try_load_terminfo_key(ti, name, &(struct keyinfo){ .type = funcs[i].type, .sym = funcs[i].sym, .modifier_mask = funcs[i].mods, .modifier_set = funcs[i].mods, })) continue; /* Maybe it has a shifted version */ sprintf(name, "key_s%s", funcs[i].funcname); try_load_terminfo_key(ti, name, &(struct keyinfo){ .type = funcs[i].type, .sym = funcs[i].sym, .modifier_mask = funcs[i].mods | TERMKEY_KEYMOD_SHIFT, .modifier_set = funcs[i].mods | TERMKEY_KEYMOD_SHIFT, }); } /* Now the F keys */ for(i = 1; i < 255; i++) { char name[9]; sprintf(name, "key_f%d", i); if(!try_load_terminfo_key(ti, name, &(struct keyinfo){ .type = TERMKEY_TYPE_FUNCTION, .sym = i, .modifier_mask = 0, .modifier_set = 0, })) break; } /* Finally mouse mode */ try_load_terminfo_key(ti, "key_mouse", &(struct keyinfo){ .type = TERMKEY_TYPE_MOUSE, }); /* Take copies of these terminfo strings, in case we build multiple termkey * instances for multiple different termtypes, and it's different by the * time we want to use it */ #ifdef HAVE_UNIBILIUM const char *keypad_xmit = unibi ? unibi_get_str(unibi, unibi_keypad_xmit) : NULL; #endif if(keypad_xmit) ti->start_string = strdup(keypad_xmit); else ti->start_string = NULL; #ifdef HAVE_UNIBILIUM const char *keypad_local = unibi ? unibi_get_str(unibi, unibi_keypad_local) : NULL; #endif if(keypad_local) ti->stop_string = strdup(keypad_local); else ti->stop_string = NULL; #ifdef HAVE_UNIBILIUM if(unibi) unibi_destroy(unibi); ti->unibi = NULL; #else if(ti->term) free(ti->term); ti->term = NULL; #endif ti->root = compress_trie(ti->root); return 1; } static void *new_driver(TermKey *tk, const char *term) { TermKeyTI *ti = malloc(sizeof *ti); if(!ti) return NULL; ti->tk = tk; ti->root = NULL; ti->start_string = NULL; ti->stop_string = NULL; #ifdef HAVE_UNIBILIUM ti->unibi = unibi_from_term(term); int saved_errno = errno; if(!ti->unibi && saved_errno != ENOENT) { free(ti); return NULL; } /* ti->unibi may be NULL if errno == ENOENT. That means the terminal wasn't * known. Lets keep going because if we get getstr hook that might invent * new strings for us */ #else { int err; ti->term = NULL; /* Have to cast away the const. But it's OK - we know terminfo won't really * modify term */ if(setupterm((char*)term, 1, &err) == OK) ti->term = strdup(term); } #endif return ti; } static int start_driver(TermKey *tk, void *info) { TermKeyTI *ti = info; struct stat statbuf; char *start_string; size_t len; if(!ti->root) load_terminfo(ti); start_string = ti->start_string; if(tk->fd == -1 || !start_string) return 1; /* The terminfo database will contain keys in application cursor key mode. * We may need to enable that mode */ /* There's no point trying to write() to a pipe */ if(fstat(tk->fd, &statbuf) == -1) return 0; #ifndef _WIN32 if(S_ISFIFO(statbuf.st_mode)) return 1; #endif // Can't call putp or tputs because they suck and don't give us fd control len = strlen(start_string); while(len) { size_t written = write(tk->fd, start_string, len); if(written == -1) return 0; start_string += written; len -= written; } return 1; } static int stop_driver(TermKey *tk, void *info) { TermKeyTI *ti = info; struct stat statbuf; char *stop_string = ti->stop_string; size_t len; if(tk->fd == -1 || !stop_string) return 1; /* There's no point trying to write() to a pipe */ if(fstat(tk->fd, &statbuf) == -1) return 0; #ifndef _WIN32 if(S_ISFIFO(statbuf.st_mode)) return 1; #endif /* The terminfo database will contain keys in application cursor key mode. * We may need to enable that mode */ // Can't call putp or tputs because they suck and don't give us fd control len = strlen(stop_string); while(len) { size_t written = write(tk->fd, stop_string, len); if(written == -1) return 0; stop_string += written; len -= written; } return 1; } static void free_driver(void *info) { TermKeyTI *ti = info; free_trie(ti->root); if(ti->start_string) free(ti->start_string); if(ti->stop_string) free(ti->stop_string); #ifdef HAVE_UNIBILIUM if(ti->unibi) unibi_destroy(ti->unibi); #else if(ti->term) free(ti->term); #endif free(ti); } #define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) static TermKeyResult peekkey(TermKey *tk, void *info, TermKeyKey *key, int force, size_t *nbytep) { TermKeyTI *ti = info; if(tk->buffcount == 0) return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE; struct trie_node *p = ti->root; unsigned int pos = 0; while(pos < tk->buffcount) { p = lookup_next(p, CHARAT(pos)); if(!p) break; pos++; if(p->type != TYPE_KEY) continue; struct trie_node_key *nk = (struct trie_node_key*)p; if(nk->key.type == TERMKEY_TYPE_MOUSE) { tk->buffstart += pos; tk->buffcount -= pos; TermKeyResult mouse_result = (*tk->method.peekkey_mouse)(tk, key, nbytep); tk->buffstart -= pos; tk->buffcount += pos; if(mouse_result == TERMKEY_RES_KEY) *nbytep += pos; return mouse_result; } key->type = nk->key.type; key->code.sym = nk->key.sym; key->modifiers = nk->key.modifier_set; *nbytep = pos; return TERMKEY_RES_KEY; } // If p is not NULL then we hadn't walked off the end yet, so we have a // partial match if(p && !force) return TERMKEY_RES_AGAIN; return TERMKEY_RES_NONE; } static int insert_seq(TermKeyTI *ti, const char *seq, struct trie_node *node) { int pos = 0; struct trie_node *p = ti->root; // Unsigned because we'll be using it as an array subscript unsigned char b; while((b = seq[pos])) { struct trie_node *next = lookup_next(p, b); if(!next) break; p = next; pos++; } while((b = seq[pos])) { struct trie_node *next; if(seq[pos+1]) // Intermediate node next = new_node_arr(0, 0xff); else // Final key node next = node; if(!next) return 0; switch(p->type) { case TYPE_ARR: { struct trie_node_arr *nar = (struct trie_node_arr*)p; if(b < nar->min || b > nar->max) { fprintf(stderr, "ASSERT FAIL: Trie insert at 0x%02x is outside of extent bounds (0x%02x..0x%02x)\n", b, nar->min, nar->max); abort(); } nar->arr[b - nar->min] = next; p = next; break; } case TYPE_KEY: fprintf(stderr, "ASSERT FAIL: Tried to insert child node in TYPE_KEY\n"); abort(); } pos++; } return 1; } struct TermKeyDriver termkey_driver_ti = { .name = "terminfo", .new_driver = new_driver, .free_driver = free_driver, .start_driver = start_driver, .stop_driver = stop_driver, .peekkey = peekkey, }; libtermkey-0.22/man/0000755000175000017500000000000013442455536012512 5ustar leoleolibtermkey-0.22/man/termkey_push_bytes.30000644000175000017500000000231013442455536016517 0ustar leoleo.TH TERMKEY_PUSH_BYTES 3 .SH NAME termkey_push_bytes \- supply more bytes to the input buffer .SH SYNOPSIS .nf .B #include .sp .BI "size_t termkey_push_bytes(TermKey *" tk ", const char *" bytes ", size_t " len "); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_push_bytes\fP() allows more bytes of input to be supplied directly into the input buffer of the \fBtermkey\fP(7) instance. If there was no buffer space remaining then -1 is returned with \fIerrno\fP set to \fBENOMEM\fP. .PP This function, along with \fBtermkey_getkey\fP(3), makes it possible to use the \fBtermkey\fP instance with a source of bytes other than from reading a filehandle. .PP For synchronous usage, \fBtermkey_waitkey\fP(3) performs the input blocking task. For use against a regular stream filehandle that supports \fBread\fP(2), see \fBtermkey_advisereadable\fP(3). .SH "RETURN VALUE" \fBtermkey_push_bytes\fP() the number of bytes consumed from the input (which may be smaller than the length provided, if the buffer runs out of space) or -1 cast to \fBsize_t\fP if an error occurs, in which case \fIerrno\fP is set accordingly. .SH "SEE ALSO" .BR termkey_getkey (3), .BR termkey_advisereadable (3), .BR termkey (7) libtermkey-0.22/man/termkey_keycmp.30000644000175000017500000000252713442455536015634 0ustar leoleo.TH TERMKEY_KEYCMP 3 .SH NAME termkey_keycmp \- compare two key events .SH SYNOPSIS .nf .B #include .sp .BI "int termkey_keycmp(TermKey *" tk ", const TermKeyKey *" key1 ", .BI " const TermKeyKey *" key2 ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_keycmp\fP() compares two key structures and applies a total ordering, returning a value that is negative, zero, or positive, to indicate if the given structures are increasing, identical, or decreasing. Before comparison, copies of both referenced structures are taken, and canonicalised according to the rules for \fBtermkey_canonicalise\fP(3). .PP Two structures of differing type are ordered \fBTERMKEY_TYPE_UNICODE\fP, \fBTERMKEY_TYPE_KEYSYM\fP, \fBTERMKEY_TYPE_FUNCTION\fP, \fBTERMKEY_TYPE_MOUSE\fP. Unicode structures are ordered by codepoint, keysym structures are ordered by keysym number, function structures are ordered by function key number, and mouse structures are ordered opaquely by an unspecified but consistent ordering. Within these values, keys different in modifier bits are ordered by the modifiers. .SH "RETURN VALUE" \fBtermkey_keycmp\fP() returns an integer greater than, equal to, or less than zero to indicate the relation between the two given key structures. .SH "SEE ALSO" .BR termkey_strpkey (3), .BR termkey_canonicalise (3), .BR termkey (7) libtermkey-0.22/man/termkey_keyname2sym.30000644000175000017500000000230113442455536016576 0ustar leoleo.TH TERMKEY_KEYNAME2SYM 3 .SH NAME termkey_keyname2sym \- look up a symbolic key value for a string name .SH SYNOPSIS .nf .B #include .sp .BI "TermKeySym termkey_keyname2sym(TermKey *" tk ", const char *" keyname ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_keyname2sym\fP() looks up the symbolic key value represented by the given string name. This is a case-sensitive comparison. If the given name is not found, \fBTERMKEY_SYM_UNKNOWN\fP is returned instead. This function is the inverse of \fBtermkey_get_keyname\fP(3), and is a more specific form of \fBtermkey_lookup_keyname\fP(3) which only recognises names as complete strings. .PP Because the key names are stored in an array indexed by the symbol number, this function has to perform a linear search of the names. Use of this function should be restricted to converting key names into symbolic values during a program's initialisation, so that efficient comparisons can be done while it is running. .SH "RETURN VALUE" \fBtermkey_keyname2sym\fP() returns a symbolic key constant, or \fBTERMKEY_SYM_UNKNOWN\fP. .SH "SEE ALSO" .BR termkey_get_keyname (3), .BR termkey_lookup_keyname (3), .BR termkey_strpkey (3), .BR termkey (7) libtermkey-0.22/man/termkey_strfkey.30000644000175000017500000000537213442455536016034 0ustar leoleo.TH TERMKEY_STRFKEY 3 .SH NAME termkey_strfkey \- format a string representing a key event .SH SYNOPSIS .nf .B #include .sp .BI "size_t termkey_strfkey(TermKey *" tk ", char *" buffer ", size_t " len ", .BI " TermKeyKey *" key ", TermKeyFormat " format ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_strfkey\fP() formats a string buffer to contain a human-readable representation of a key event. It fills the \fIbuffer\fP in a way analogous to the \fBsnprintf\fP(3) or \fBstrftime\fP(3) standard library functions. This function used to be called \fBtermkey_snprint_key\fP() but was renamed after version 0.6. .PP The \fIformat\fP argument specifies the format of the output, as a bitmask of the following constants: .TP .B TERMKEY_FORMAT_LONGMOD Print full modifier names e.g. "\f(CWShift-\fP" instead of abbreviating to "\f(CWS-\fP". .TP .B TERMKEY_FORMAT_CARETCTRL If the only modifier is \fBTERMKEY_MOD_CTRL\fP on a plain letter, render it as "\f(CW^X\fP" rather than "\f(CWCtrl-X\fP". .TP .B TERMKEY_FORMAT_ALTISMETA Use the name "\f(CWMeta\fP" or the letter "\f(CWM\fP" instead of "\f(CWAlt\fP" or "\f(CWA\fP". .TP .B TERMKEY_FORMAT_WRAPBRACKET If the key event is a special key instead of unmodified Unicode, wrap it in "\f(CW\fP". .TP .B TERMKEY_FORMAT_SPACEMOD Use spaces instead of hyphens to separate the modifier name(s) from the base key name. .TP .B TERMKEY_FORMAT_LOWERMOD Use lowercase for the modifier name. .TP .B TERMKEY_FORMAT_LOWERSPACE Use lowercase with spaces in for the key name instead of camelCase (for example "\f(CWpage down\fP" instead of "\f(CWPageDown\fP"). .TP .B TERMKEY_FORMAT_MOUSE_POS If the event is a mouse event, include the position rendered as "\f(CW@ (col,line)\fP". .PP The following shortcuts are provided for common combinations of format bits: .TP .B TERMKEY_FORMAT_VIM Shortcut to set \fBALTISMETA\fP and \fBWRAPBRACKET\fP, to give an output close to the format the \fIvim\fP editor uses. .TP .B TERMKEY_FORMAT_URWID Shortcut to set \fBALTISMETA\fP, \fBLONGMOD\fP, \fBLOWERMOD\fP, \fBSPACEMOD\fP and \fBLOWERSPACE\fP, to give an output close to the format the \fIurwid\fP python library uses. .PP When formatting a \fBTERMKEY_TYPE_UNICODE\fP key structure, this function uses the \fIutf8\fP member. If this member contains an empty string (i.e. its first character is 0) then this member will be prefilled by the function from the \fIcode.number\fP member. This can be convenient when the key structure is being constructed programmatically by user code. .SH "RETURN VALUE" \fBtermkey_strfkey\fP() returns the number of characters written to \fIbuffer\fP. .SH "SEE ALSO" .BR termkey_new (3), .BR termkey_getkey (3), .BR termkey_waitkey (3), .BR termkey_get_keyname (3), .BR termkey_strpkey (3), .BR termkey (7) libtermkey-0.22/man/termkey_get_fd.30000644000175000017500000000106413442455536015567 0ustar leoleo.TH TERMKEY_GET_FD 3 .SH NAME termkey_get_fd \- obtain the file descriptor for the terminal .SH SYNOPSIS .nf .B #include .sp .BI "int termkey_get_fd(TermKey *" tk ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_get_fd\fP() returns the file descriptor that the \fBtermkey\fP(7) instance is using to read bytes from. .SH "RETURN VALUE" \fBtermkey_get_fd\fP() returns the current file descriptor, or -1 if no file descriptor is associated with this instance. .SH "SEE ALSO" .BR termkey_new (3), .BR termkey_get_flags (3), .BR termkey (7) libtermkey-0.22/man/termkey_interpret_mouse.30000644000175000017500000000363413442455536017570 0ustar leoleo.TH TERMKEY_INTERPRET_MOUSE 3 .SH NAME termkey_interpret_mouse \- interpret opaque mouse event data .SH SYNOPSIS .nf .B #include .sp .BI "TermKeyResult termkey_interpret_mouse(TermKey *" tk ", const TermKeyKey *" key ", " .BI " TermKeyMouseEvent *" ev ", int *" button ", int *" line ", int *" col ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_interpret_mouse\fP() fills in variables in the passed pointers according to the mouse event found in \fIkey\fP. It should be called if \fBtermkey_getkey\fP(3) or similar have returned a key event with the type of \fBTERMKEY_TYPE_MOUSE\fP. .PP Any pointer may instead be given as \fBNULL\fP to not return that value. .PP The \fIev\fP variable will take one of the following values: .in .TP .B TERMKEY_MOUSE_UNKNOWN an unknown mouse event. .TP .B TERMKEY_MOUSE_PRESS a mouse button was pressed; \fIbutton\fP will contain its number. .TP .B TERMKEY_MOUSE_DRAG the mouse was moved while holding a button; \fIbutton\fP will contain its number. .TP .B TERMKEY_MOUSE_RELEASE a mouse button was released, or the mouse was moved while no button was pressed. If known, \fIbutton\fP will contain the number of the button released. Not all terminals can report this, so it may be 0 instead. .PP The \fIline\fP and \fIcol\fP variables will be filled in with the mouse position, indexed from 1. Note that due to the limited number of bytes in the \fBTermKeyKey\fP structure, the line and column numbers are limited to 2047 and 4095 respectively. .SH "RETURN VALUE" If passed a \fIkey\fP event of the type \fBTERMKEY_TYPE_MOUSE\fP, this function will return \fBTERMKEY_RES_KEY\fP and will affect the variables whose pointers were passed in, as described above. .PP For other event types it will return \fBTERMKEY_RES_NONE\fP, and its effects on any variables whose pointers were passed in, are undefined. .SH "SEE ALSO" .BR termkey_waitkey (3), .BR termkey_getkey (3), .BR termkey (7) libtermkey-0.22/man/termkey_get_buffer_remaining.30000644000175000017500000000140213442455536020474 0ustar leoleo.TH TERMKEY_GET_BUFFER_REMAINING 3 .SH NAME termkey_get_buffer_remaining \- returns the free buffer space .SH SYNOPSIS .nf .B #include .sp .BI "size_t termkey_get_buffer_remaining(TermKey *" tk "); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_get_buffer_remaining\fP() returns the number of bytes of buffer space currently free in the \fBtermkey\fP(7) instance. These bytes are free to use by \fBtermkey_push_bytes\fP(3), or may be filled by \fBtermkey_advisereadable\fP(3). .PP .SH "RETURN VALUE" \fBtermkey_get_buffer_remaining\fP() returns a size in bytes. .SH "SEE ALSO" .BR termkey_push_bytes (3), .BR termkey_advisereadable (3), .BR termkey_set_buffer_size (3), .BR termkey_get_buffer_size (3), .BR termkey_getkey (3), .BR termkey (7) libtermkey-0.22/man/termkey_interpret_csi.30000644000175000017500000000401713442455536017212 0ustar leoleo.TH TERMKEY_INTERPRET_CSI 3 .SH NAME termkey_interpret_csi \- interpret unrecognised CSI sequence .SH SYNOPSIS .nf .B #include .sp .BI "TermKeyResult termkey_interpret_csi(TermKey *" tk ", const TermKeyKey *" key ", " .BI " long *" args "[], size_t *" nargs ", unsigned long *" cmd ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_interpret_csi\fP() fills in variables in the passed pointers according to the unrecognised CSI sequence event found in \fIkey\fP. It should be called if \fBtermkey_getkey\fP(3) or similar have returned a key event with the type of \fBTERMKEY_TYPE_UNKNOWN_CSI\fP. Note that it is important to call this function as soon as possible after obtaining a \fBTERMKEY_TYPE_CSI\fP key event; specifically, before calling \fBtermkey_getkey\fP() or \fBtermkey_waitkey\fP() again, as a subsequent call will overwrite the buffer space currently containing this sequence. .PP The \fIargs\fP array will be filled with the numerical arguments of the CSI sequence. The number of elements available in this array should be given as the initial value of the value pointed to by \fInargs\fP, which will be adjusted to give the number of arguments actually found when the function returns. The \fIcmd\fP variable will contain the CSI command value. If a leading byte was found (such as '\f(CW?\fP') then it will be bitwise-ored with the command value, shifted up by 8 bits. If an intermediate byte was found (such as '\f(CW$\fP') then it will be bitwise-ored with the command value, shifted up by 16 bits. .nf .sp *cmd = command | (initial << 8) | (intermediate << 16); .fi .SH "RETURN VALUE" If passed a \fIkey\fP event of the type \fBTERMKEY_TYPE_UNKNOWN_CSI\fP, this function will return \fBTERMKEY_RES_KEY\fP and will affect the variables whose pointers were passed in, as described above. .PP For other event types it will return \fBTERMKEY_RES_NONE\fP, and its effects on any variables whose pointers were passed in, are undefined. .SH "SEE ALSO" .BR termkey_waitkey (3), .BR termkey_getkey (3), .BR termkey (7) libtermkey-0.22/man/also0000644000175000017500000000057713442455536013404 0ustar leoleotermkey_destroy.3 = termkey_new.3 termkey_new_abstract.3 = termkey_new.3 termkey_get_flags.3 = termkey_set_flags.3 termkey_get_canonflags.3 = termkey_set_canonflags.3 termkey_get_buffer_size.3 = termkey_set_buffer_size.3 termkey_get_waittime.3 = termkey_set_waittime.3 termkey_getkey_force.3 = termkey_getkey.3 termkey_stop.3 = termkey_start.3 termkey_is_started.3 = termkey_start.3 libtermkey-0.22/man/termkey.70000644000175000017500000003147713442455536014276 0ustar leoleo.TH TERMKEY 7 .SH NAME termkey \- terminal keypress reading library .SH DESCRIPTION \fBtermkey\fP is a library that allows programs to read and interpret keypress and other events from a terminal. It understands encoding schemes used by terminals to encode keypresses, and .SM UTF-8 , allowing it to return events representing key events. .PP \fBtermkey\fP operates in a pseudo object-oriented fashion. It provides one function, \fBtermkey_new\fP(3), that returns a pointer to a newly-allocated structure. All other functions take this pointer as their first argument. A typical use of this library would consist of a call to \fBtermkey_new\fP() to construct a new instance to represent the \fIstdin\fP stream, then use the \fBtermkey_waitkey\fP(3) function to wait for and interpret key press events. The \fBtermkey_destroy\fP(3) function can be used to deallocate resources used by the instance if the program has finished using it. .SS Reading Events Each instance of a \fBtermkey\fP structure may be used in one of three ways by the program. It may be used synchronously, blocking to wait for keypresses from a filehandle. It may be used asynchronously, returning keypresses if they are available, while co-operating with a non-blocking program. Or it may be used abstractly, interpreting key press bytes fed to it directly by the containing program. .PP To obtain the next key event synchronously, a program may call \fBtermkey_waitkey\fP(3). This will either return an event from its internal buffer, or block until a key is available, returning it when it is ready. It behaves similarly to \fBgetc\fP(3), \fBfgetc\fP(3), or similar, except that it understands and returns entire key press events, rather than single bytes. .PP To work with an asynchronous program, two other functions are used. \fBtermkey_advisereadable\fP(3) informs a \fBtermkey\fP instance that more bytes of input may be available from its file handle, so it should call \fBread\fP(2) to obtain them. The program can then call \fBtermkey_getkey\fP(3) to extract key press events out of the internal buffer, in a way similar to \fBtermkey_waitkey\fP(). .PP Finally, bytes of input can be fed into the \fBtermkey\fP instance directly, by calling \fBtermkey_push_bytes\fP(3). This may be useful if the bytes have already been read from the terminal by the application, or even in situations that don't directly involve a terminal filehandle. Because of these situations, it is possible to construct a \fBtermkey\fP instance not associated with a file handle, by passing -1 as the file descriptor. .PP A \fBtermkey\fP instance contains a buffer of pending bytes that have been read but not yet consumed by \fBtermkey_getkey\fP(3). \fBtermkey_get_buffer_remaining\fP(3) returns the number of bytes of buffer space currently free in the instance. \fBtermkey_set_buffer_size\fP(3) and \fBtermkey_get_buffer_size\fP(3) can be used to control and return the total size of this buffer. .SS Key Events Key events are stored in structures. Each structure holds details of one key event. This structure is defined as follows. .PP .in +4n .nf typedef struct { TermKeyType type; union { long codepoint; /* TERMKEY_TYPE_UNICODE */ int number; /* TERMKEY_TYPE_FUNCTION */ TermKeySym sym; /* TERMKEY_TYPE_KEYSYM */ } code; int modifiers; char utf8[7]; } TermKeyKey; .fi .in .PP The \fItype\fP field indicates the type of event, and determines which of the members of the \fIcode\fP union is valid. It will be one of the following constants: .TP .B TERMKEY_TYPE_UNICODE a Unicode codepoint. This value indicates that \fIcode.codepoint\fP is valid, and will contain the codepoint number of the keypress. In Unicode mode (if the \fBTERMKEY_FLAG_UTF8\fP bit is set) this will be its Unicode character number. In raw byte mode, this will contain a single 8-bit byte. .TP .B TERMKEY_TYPE_FUNCTION a numbered function key. This value indicates that \fIcode.number\fP is valid, and contains the number of the numbered function key. .TP .B TERMKEY_TYPE_KEYSYM a symbolic key. This value indicates that \fIcode.sym\fP is valid, and contains the symbolic key value. .TP .B TERMKEY_TYPE_MOUSE a mouse button press, release, or movement. The \fIcode\fP structure should be considered opaque; \fBtermkey_interpret_mouse\fP(3) may be used to interpret it. .TP .B TERMKEY_TYPE_POSITION a cursor position report. The \fIcode\fP structure should be considered opaque; \fBtermkey_interpret_position\fP(3) may be used to interpret it. .TP .B TERMKEY_TYPE_MODEREPORT an ANSI or DEC mode value report. The \fIcode\fP structure should be considered opaque; \fBtermkey_interpret_modereport\fP(3) may be used to interpret it. .TP .B TERMKEY_TYPE_DCS a DCS sequence including its terminator. The \fIcode\fP structure should be considered opaque; \fBtermkey_interpret_string\fP(3) may be used to interpret it. .TP .B TERMKEY_TYPE_OSC a OSC sequence including its terminator. The \fIcode\fP structure should be considered opaque; \fBtermkey_interpret_string\fP(3) may be used to interpret it. .TP .B TERMKEY_TYPE_UNKNOWN_CSI an unrecognised CSI sequence. The \fIcode\fP structure should be considered opaque; \fBtermkey_interpret_csi\fP(3) may be used to interpret it. .PP The \fImodifiers\fP bitmask is composed of a bitwise-or of the constants \fBTERMKEY_KEYMOD_SHIFT\fP, \fBTERMKEY_KEYMOD_CTRL\fP and \fBTERMKEY_KEYMOD_ALT\fP. .PP The \fIutf8\fP field is only set on events whose \fItype\fP is \fBTERMKEY_TYPE_UNICODE\fP. It should not be read for other events. .PP Key events that represent special keys (\fItype\fP is \fBTERMKEY_TYPE_KEYSYM\fP) have with them as symbolic value that identifies the special key, in \fIcode.sym\fP. \fBtermkey_get_keyname\fP(3) may be used to turn this symbolic value into a string, and \fBtermkey_lookup_keyname\fP(3) may be used to turn string names into symbolic values. .PP A pair of functions are also provided to convert between key events and strings. \fBtermkey_strfkey\fP(3) converts a key event into a string, and \fBtermkey_strpkey\fP(3) parses a string turning it into a key event. .PP Key events may be compared for equality or ordering by using \fBtermkey_keycmp\fP(3). .SS Control Flags Details of the behaviour of a \fBtermkey\fP instance are controlled by two bitmasks of flags. \fBtermkey_set_flags\fP(3) and \fBtermkey_get_flags\fP(3) set or return the flags used to control the general behaviour, and \fBtermkey_set_canonflags\fP(3) and \fBtermkey_get_canonflags\fP(3) set or return the flags that control the key value canonicalisation behaviour performed by \fBtermkey_canonicalise\fP(3). .PP The following control flags are recognised. .TP .B TERMKEY_FLAG_NOINTERPRET Do not attempt to interpret \fIC0\fP codes into keysyms. Instead report them as plain \fICtrl-letter\fP events. .TP .B TERMKEY_FLAG_CONVERTKP Convert xterm's alternative keypad symbols into the plain .SM ASCII codes they would represent. .TP .B TERMKEY_FLAG_RAW Ignore locale settings; do not attempt to recombine .SM UTF-8 sequences. Instead report only raw values. .TP .B TERMKEY_FLAG_UTF8 Ignore locale settings; force .SM UTF-8 recombining on. This flag overrides \fBTERMKEY_FLAG_RAW\fP. .TP .B TERMKEY_FLAG_NOTERMIOS Even if the terminal file descriptor \fIfd\fP represents a .SM TTY device, do not call the \fBtcsetattr\fP(3) \fBtermios\fP function on it to set it to canonical input mode. .TP .B TERMKEY_FLAG_SPACESYMBOL Report space as being a symbolic key rather than a Unicode codepoint. Setting or clearing this flag in fact sets or clears the \fBTERMKEY_CANON_SPACESYMBOL\fP canonicalisation flag. .TP .B TERMKEY_FLAG_CTRLC Disable the \fBSIGINT\fP behaviour of \fICtrl-C\fP. If this flag is provided, then \fICtrl-C\fP will be available as a normal keypress, rather than sending the process group a \fBSIGINT\fP. This flag only takes effect without \fBTERMKEY_FLAG_NOTERMIOS\fP; with it, none of the signal keys are disabled anyway. .TP .B TERMKEY_FLAG_EINTR Without this flag, IO operations are retried when interrupted by a signal (\fBEINTR\fP). With this flag the \fBTERMKEY_RES_ERROR\fP result is returned instead. .TP .B TERMKEY_FLAG_NOSTART This flag is only meaningful to the constructor functions \fBtermkey_new\fP(3) and \fBtermkey_new_abstract\fP(3). If set, the constructor will not call \fBtermkey_start\fP(3) as part of the construction process. The user must call that at some future time before the instance will be usable. .PP The following canonicalisation flags are recognised. .TP .B TERMKEY_CANON_SPACESYMBOL If this flag is set then a Unicode space character is represented using the \fBTERMKEY_SYM_SPACE\fP symbol. If this flag is not set, it is represented by the \f(CWU+0020\fP Unicode codepoint. .TP .B TERMKEY_CANON_DELBS If this flag is set then an .SM ASCII .SM DEL character is represented by the \fBTERMKEY_SYM_BACKSPACE\fP symbol. If not, it is represented by \fBTERMKEY_SYM_DEL\fP. An .SM ASCII .SM BS character is always represented by \fBTERMKEY_SYM_BACKSPACE\fP, regardless of this flag. .SS Multi-byte Events Special keys, mouse events, and .SM UTF-8 encoded Unicode text, are all represented by more than one byte. If the start of a multi-byte sequence is seen by \fBtermkey_waitkey\fP() it will wait a short time to see if the remainder of the sequence arrives. If the sequence remains unfinished after this timeout, it will be returned in its incomplete state. Partial escape sequences are returned as an Escape key (\fBTERMKEY_SYM_ESCAPE\fP) followed by the text contained in the sequence. Partial .SM UTF-8 sequences are returned as the Unicode replacement character, \f(CWU+FFFD\fP. .PP The amount of time that the \fBtermkey\fP instance will wait is set by \fBtermkey_set_waittime\fP(3), and is returned by \fBtermkey_get_waittime\fP(3). Initially it will be set to 50 miliseconds. .SS Mouse Events The \fBTERMKEY_TYPE_MOUSE\fP event type indicates a mouse event. The \fIcode\fP field of the event structure should be considered opaque, though \fImodifiers\fP will be valid. In order to obtain the details of the mouse event, call \fBtermkey_interpret_mouse\fP(3) passing the event structure and pointers to integers to store the result in. .PP \fBtermkey\fP recognises three mouse protocols: the original .SM X10 protocol (\f(CWCSI M\fP followed by three bytes), .SM SGR encoding (\f(CWCSI < ... M\fP, as requested by \f(CWCSI ? 1006 h\fP), and rxvt encoding (\f(CWCSI ... M\fP, as requested by \f(CWCSI ? 1015 h\fP). Which encoding is in use is inferred automatically by \fBtermkey\fP, and does not need to be specified explicitly. .SS Position Events The \fBTERMKEY_TYPE_POSITION\fP event type indicates a cursor position report. This is typically sent by a terminal in response to the Report Cursor Position command (\f(CWCSI ? 6 n\fP). The event bytes are opaque, but can be obtained by calling \fBtermkey_interpret_position\fP(3) passing the event structure and pointers to integers to store the result in. Note that only a DEC CPR sequence (\f(CWCSI ? R\fP) is recognised, and not the non-DEC prefixed \f(CWCSI R\fP because the latter could be interpreted as the \f(CWF3\fP function key instead. .SS Mode Reports The \fBTERMKEY_TYPE_MODEREPORT\fP event type indicates an ANSI or DEC mode report. This is typically sent by a terminal in response to the Request Mode command (\f(CWCSI $p\fP or \f(CWCSI ? $p\fP). The event bytes are opaque, but can be obtained by calling \fBtermkey_interpret_modereport\fP(3) passing the event structure and pointers to integers to store the result in. .SS Control Strings The \fBTERMKEY_TYPE_DCS\fP and \fBTERMKEY_TYPE_OSC\fP event types indicate a DCS or OSC control string. These are typically sent by the terminal in response of similar kinds of strings being sent as queries by the application. The event bytes are opaque, but the body of the string itself can be obtained by calling \fBtermkey_interpret_string\fP(3) immediately after this event is received. The underlying \fBtermkey\fP instance itself can only store one pending string, so the application should be sure to call this function in a timely manner soon after the event is received; at the very least, before calling any other functions that will insert bytes into or remove key events from the instance. .SS Unrecognised CSIs The \fBTERMKEY_TYPE_UNKNOWN_CSI\fP event type indicates a CSI sequence that the \fBtermkey\fP does not recognise. It will have been extracted from the stream, but is available to the application to inspect by calling \fBtermkey_interpret_csi\fP(3). It is important that if the application wishes to inspect this sequence it is done immediately, before any other IO operations on the \fBtermkey\fP instance (specifically, before calling \fBtermkey_waitkey\fP() or \fBtermkey_getkey\fP() again), otherwise the buffer space consumed by the sequence will be overwritten. Other types of key event do not suffer this limitation as the \fBTermKeyKey\fP structure is sufficient to contain all the information required. .SH "SEE ALSO" .BR termkey_new (3), .BR termkey_waitkey (3), .BR termkey_getkey (3) libtermkey-0.22/man/termkey_getkey.30000644000175000017500000001001513442455536015623 0ustar leoleo.TH TERMKEY_GETKEY 3 .SH NAME termkey_getkey, termkey_getkey_force \- retrieve the next key event .SH SYNOPSIS .nf .B #include .sp .BI "TermKeyResult termkey_getkey(TermKey *" tk ", TermKeyKey *" key ); .BI "TermKeyResult termkey_getkey_force(TermKey *" tk ", TermKeyKey *" key ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_getkey\fP() attempts to retrieve a single keypress event from the \fBtermkey\fP(7) instance buffer, and put it in the structure referred to by \fIkey\fP. It returns one of the following values: .in .TP .B TERMKEY_RES_KEY a complete keypress was removed from the buffer, and has been placed in the \fIkey\fP structure. .TP .B TERMKEY_RES_AGAIN a partial keypress event was found in the buffer, but it does not yet contain all the bytes required. An indication of what \fBtermkey_getkey_force\fP() would return has been placed in the \fIkey\fP structure. .TP .B TERMKEY_RES_NONE no bytes are waiting in the buffer. .TP .B TERMKEY_RES_EOF no bytes are ready and the input stream is now closed. .TP .B TERMKEY_RES_ERROR called with terminal IO stopped, due to \fBtermkey_stop\fP(3). In this case \fIerrno\fP will be set to \fBEINVAL\fP. .PP \fBtermkey_getkey_force\fP() is similar to \fBtermkey_getkey\fP() but will not return \fBTERMKEY_RES_AGAIN\fP if a partial match is found. Instead, it will force an interpretation of the bytes, even if this means interpreting the start of an Escape-prefixed multi-byte sequence as a literal \fIEscape\fP key followed by normal letters. .PP Neither of these functions will block or perform any IO operations on the underlying filehandle. To use the instance in an asynchronous program, see \fBtermkey_advisereadable\fP(3). For a blocking call suitable for use in a synchronous program, use \fBtermkey_waitkey\fP(3) instead of \fBtermkey_getkey\fP(). For providing input without a readable filehandle, use \fBtermkey_push_bytes\fP(3). .PP Before returning, this function canonicalises the \fIkey\fP structure according to the rules given for \fBtermkey_canonicalise\fP(3). .SH "RETURN VALUE" \fBtermkey_getkey\fP() returns an enumeration of one of \fBTERMKEY_RES_KEY\fP, \fBTEMRKEY_RES_AGAIN\fP, \fBTERMKEY_RES_NONE\fP, \fBTERMKEY_RES_EOF\fP or \fBTERMKEY_RES_ERROR\fP. \fBtermkey_getkey_force\fP() returns one of the above, except for \fBTERMKEY_RES_AGAIN\fP. .SH EXAMPLE The following example program prints details of every keypress until the user presses \fICtrl-C\fP. It demonstrates how to use the \fBtermkey\fP instance in a typical \fBpoll\fP(2)-driven asynchronous program, which may include mixed IO with other file handles. .PP .in +4n .nf // might need this for sigset_t #define _XOPEN_SOURCE 600 #include #include #include "termkey.h" static void on_key(TermKey *tk, TermKeyKey *key) { char buffer[50]; termkey_strfkey(tk, buffer, sizeof buffer, key, TERMKEY_FORMAT_VIM); printf("%s\\n", buffer); } int main(int argc, char *argv[]) { TERMKEY_CHECK_VERSION; TermKey *tk = termkey_new(0, 0); if(!tk) { fprintf(stderr, "Cannot allocate termkey instance\\n"); exit(1); } struct pollfd fd; fd.fd = 0; /* the file descriptor we passed to termkey_new() */ fd.events = POLLIN; TermKeyResult ret; TermKeyKey key; int running = 1; int nextwait = -1; while(running) { if(poll(&fd, 1, nextwait) == 0) { // Timed out if(termkey_getkey_force(tk, &key) == TERMKEY_RES_KEY) on_key(tk, &key); } if(fd.revents & (POLLIN|POLLHUP|POLLERR)) termkey_advisereadable(tk); while((ret = termkey_getkey(tk, &key)) == TERMKEY_RES_KEY) { on_key(tk, &key); if(key.type == TERMKEY_TYPE_UNICODE && key.modifiers & TERMKEY_KEYMOD_CTRL && (key.code.codepoint == 'C' || key.code.codepoint == 'c')) running = 0; } if(ret == TERMKEY_RES_AGAIN) nextwait = termkey_get_waittime(tk); else nextwait = -1; } termkey_destroy(tk); } .in .fi .SH "SEE ALSO" .BR termkey_advisereadable (3), .BR termkey_waitkey (3), .BR termkey_get_waittime (3), .BR termkey (7) libtermkey-0.22/man/termkey_set_canonflags.30000644000175000017500000000155113442455536017326 0ustar leoleo.TH TERMKEY_SET_CANONFLAGS 3 .SH NAME termkey_set_canonflags, termkey_get_canonflags \- control the canonicalisation flags .SH SYNOPSIS .nf .B #include .sp .BI "void termkey_set_canonflags(TermKey *" tk ", int " newflags ); .BI "int termkey_get_canonflags(TermKey *" tk ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_set_canonflags\fP() changes the set of canonicalisation flags in the \fBtermkey\fP(7) instance to those given by \fInewflags\fP. For detail on the available flags and their meaning, see the \fBtermkey\fP manpage. .PP \fBtermkey_get_canonflags\fP() returns the value set by the last call to \fBtermkey_set_canonflags\fP(). .SH "RETURN VALUE" \fBtermkey_set_canonflags\fP() returns no value. \fBtermkey_get_canonflags\fP() returns the current canonicalisation flags. .SH "SEE ALSO" .BR termkey_canonicalise (3), .BR termkey (7) libtermkey-0.22/man/termkey_waitkey.30000644000175000017500000001274713442455536016026 0ustar leoleo.TH TERMKEY_WAITKEY 3 .SH NAME termkey_waitkey \- wait for and retrieve the next key event .SH SYNOPSIS .nf .B #include .sp .BI "TermKeyResult termkey_waitkey(TermKey *" tk ", TermKeyKey *" key ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_waitkey\fP() attempts to retrieve a single keypress event from the \fBtermkey\fP(7) instance buffer, and put it in the structure referred to by \fIkey\fP. If successful it will return \fBTERMKEY_RES_KEY\fP to indicate that the structure now contains a new keypress event. If nothing is in the buffer it will block until one is available. If no events are ready and the input stream is now closed, will return \fBTERMKEY_RES_EOF\fP. If no filehandle is associated with this instance, \fBTERMKEY_RES_ERROR\fP is returned with \fIerrno\fP set to \fBEBADF\fP. .PP Before returning, this function canonicalises the \fIkey\fP structure according to the rules given for \fBtermkey_canonicalise\fP(3). .PP Some keypresses generate multiple bytes from the terminal. Because there may be network or other delays between the terminal and an application using \fBtermkey\fP, \fBtermkey_waitkey\fP() will attempt to wait for the remaining bytes to arrive if it detects the start of a multibyte sequence. If no more bytes arrive within a certain time, then the bytes will be reported as they stand, even if this results in interpreting a partially-complete Escape sequence as a literal Escape key followed by some normal letters or other symbols. The amount of time to wait can be set by \fBtermkey_set_waittime\fP(3). .SH "RETURN VALUE" \fBtermkey_waitkey\fP() returns one of the following constants: .TP .B TERMKEY_RES_KEY A key event as been provided. .TP .B TERMKEY_RES_EOF No key events are ready and the terminal has been closed, so no more will arrive. .TP .B TERMKEY_RES_ERROR An IO error occurred. \fIerrno\fP will be preserved. If the error is \fBEINTR\fP then this will only be returned if \fBTERMKEY_FLAG_EINTR\fP flag is not set; if it is then the IO operation will be retried instead. If this is called with terminal IO stopped, due to \fBtermkey_stop\fP(3) then \fIerrno\fP will be set to \fBEINVAL\fP. .SH EXAMPLE The following example program prints details of every keypress until the user presses \fICtrl-C\fP. .PP .in +4n .nf // we want optarg #define _XOPEN_SOURCE 600 #include #include #include #include "termkey.h" int main(int argc, char *argv[]) { TERMKEY_CHECK_VERSION; int mouse = 0; int mouse_proto = 0; TermKeyFormat format = TERMKEY_FORMAT_VIM; char buffer[50]; TermKey *tk; int opt; while((opt = getopt(argc, argv, "m::p:")) != -1) { switch(opt) { case 'm': if(optarg) mouse = atoi(optarg); else mouse = 1000; break; case 'p': mouse_proto = atoi(optarg); break; default: fprintf(stderr, "Usage: %s [-m]\\n", argv[0]); return 1; } } tk = termkey_new(0, TERMKEY_FLAG_SPACESYMBOL|TERMKEY_FLAG_CTRLC); if(!tk) { fprintf(stderr, "Cannot allocate termkey instance\\n"); exit(1); } if(termkey_get_flags(tk) & TERMKEY_FLAG_UTF8) printf("Termkey in UTF-8 mode\\n"); else if(termkey_get_flags(tk) & TERMKEY_FLAG_RAW) printf("Termkey in RAW mode\\n"); TermKeyResult ret; TermKeyKey key; if(mouse) { printf("\\033[?%dhMouse mode active\\n", mouse); if(mouse_proto) printf("\\033[?%dh", mouse_proto); } while((ret = termkey_waitkey(tk, &key)) != TERMKEY_RES_EOF) { if(ret == TERMKEY_RES_KEY) { termkey_strfkey(tk, buffer, sizeof buffer, &key, format); if(key.type == TERMKEY_TYPE_MOUSE) { int line, col; termkey_interpret_mouse(tk, &key, NULL, NULL, &line, &col); printf("%s at line=%d, col=%d\\n", buffer, line, col); } else if(key.type == TERMKEY_TYPE_POSITION) { int line, col; termkey_interpret_position(tk, &key, &line, &col); printf("Cursor position report at line=%d, col=%d\\n", line, col); } else if(key.type == TERMKEY_TYPE_MODEREPORT) { int initial, mode, value; termkey_interpret_modereport(tk, &key, &initial, &mode, &value); printf("Mode report %s mode %d = %d\\n", initial ? "DEC" : "ANSI", mode, value); } else if(key.type == TERMKEY_TYPE_UNKNOWN_CSI) { long args[16]; size_t nargs = 16; unsigned long command; termkey_interpret_csi(tk, &key, args, &nargs, &command); printf("Unrecognised CSI %c %ld;%ld %c%c\\n", (char)(command >> 8), args[0], args[1], (char)(command >> 16), (char)command); } else { printf("Key %s\\n", buffer); } if(key.type == TERMKEY_TYPE_UNICODE && key.modifiers & TERMKEY_KEYMOD_CTRL && (key.code.codepoint == 'C' || key.code.codepoint == 'c')) break; if(key.type == TERMKEY_TYPE_UNICODE && key.modifiers == 0 && key.code.codepoint == '?') { // printf("\\033[?6n"); // DECDSR 6 == request cursor position printf("\\033[?1$p"); // DECRQM == request mode, DEC origin mode fflush(stdout); } } else if(ret == TERMKEY_RES_ERROR) { if(errno != EINTR) { perror("termkey_waitkey"); break; } printf("Interrupted by signal\\n"); } } if(mouse) printf("\\033[?%dlMouse mode deactivated\\n", mouse); termkey_destroy(tk); } .in .fi .SH COMPATIBILITY This function is not available on Windows. .SH "SEE ALSO" .BR termkey_getkey (3), .BR termkey_set_waittime (3), .BR termkey (7) libtermkey-0.22/man/termkey_canonicalise.30000644000175000017500000000147313442455536016773 0ustar leoleo.TH TERMKEY_CANONICALISE 3 .SH NAME termkey_canonicalise \- canonicalise a key event .SH SYNOPSIS .nf .B #include .sp .BI "void termkey_canonicalise(TermKey *" tk ", TermKeyKey *" key ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_canonicalise\fP(3) modifies the key event structure given by \fIkey\fP according to the canonicalisation flags set on the given \fBtermkey\fP instance. This operation is performed implicitly by \fBtermkey_getkey\fP(3), \fBtermkey_waitkey\fP(3) and \fBtermkey_strpkey\fP(3), and is also provided explicitly by this function. .PP See \fBtermkey\fP(7) for a list of canonicalisation flags. .SH "RETURN VALUE" \fBtermkey_canonicalise\fP() returns no value. .SH "SEE ALSO" .BR termkey_set_canonflags (3), .BR termkey_waitkey (3), .BR termkey_strpkey (3), .BR termkey (7) libtermkey-0.22/man/termkey_lookup_keyname.30000644000175000017500000000254113442455536017362 0ustar leoleo.TH TERMKEY_LOOKUP_KEYNAME 3 .SH NAME termkey_lookup_keyname \- look up a symbolic key value for a string name .SH SYNOPSIS .nf .B #include .sp .BI "char *termkey_lookup_keyname(TermKey *" tk ", const char *" keyname ", .BI " TermKeySym *" sym "); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_lookup_keyname\fP() looks up the symbolic key value represented by the given string name. This is a case-sensitive comparison. The symbolic value is written to the variable addressed by \fIsym\fP. This function is a more general form of \fBtermkey_keyname2sym\fP(3) because it can recognise a symbolic key name within a longer string, returning a pointer to the remainder of the input after the key name. .PP Because the key names are stored in an array indexed by the symbol number, this function has to perform a linear search of the names. Use of this function should be restricted to converting key names into symbolic values during a program's initialisation, so that efficient comparisons can be done while it is running. .SH "RETURN VALUE" \fBtermkey_lookup_keyname\fP() returns a pointer to the first character after a recognised name, or \fBNULL\fP if the string does not begin with the name of a recognised symbolic key. .SH "SEE ALSO" .BR termkey_get_keyname (3), .BR termkey_keyname2sym (3), .BR termkey_strpkey (3), .BR termkey (7) libtermkey-0.22/man/termkey_advisereadable.30000644000175000017500000000331713442455536017275 0ustar leoleo.TH TERMKEY_ADVISEREADABLE 3 .SH NAME termkey_advisereadable \- read more bytes from the underlying terminal .SH SYNOPSIS .nf .B #include .sp .BI "TermKeyResult termkey_advisereadable(TermKey *" tk ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_advisereadable\fP() informs the \fBtermkey\fP(7) instance that new input may be available on the underlying file descriptor and so it should call \fBread\fP(2) to obtain it. If at least one more byte was read it will return \fBTERMKEY_RES_AGAIN\fP to indicate it may be useful to call \fBtermkey_getkey\fP(3) again. If no more input was read then \fBTERMKEY_RES_NONE\fP is returned. If there was no buffer space remaining, then \fBTERMKEY_RES_ERROR\fP is returned with \fIerrno\fP set to \fBENOMEM\fP. If no filehandle is associated with this instance, \fBTERMKEY_RES_ERROR\fP is returned with \fIerrno\fP set to \fBEBADF\fP. .PP This function, along with \fBtermkey_getkey\fP(3) make it possible to use the termkey instance in an asynchronous program. To provide bytes without using a readable file handle, use \fBtermkey_push_bytes\fP(3). .PP For synchronous usage, \fBtermkey_waitkey\fP(3) performs the input blocking task. .SH "RETURN VALUE" \fBtermkey_advisereadable\fP() returns one of the following constants: .TP .B TERMKEY_RES_AGAIN At least one byte was read. .TP .B TERMKEY_RES_NONE No nore bytes were read. .TP .B TERMKEY_RES_ERROR An IO error occurred. \fIerrno\fP will be preserved. If the error is \fBEINTR\fP then this will only be returned if \fBTERMKEY_FLAG_EINTR\fP flag is not set; if it is then the IO operation will be retried instead. .SH "SEE ALSO" .BR termkey_getkey (3), .BR termkey_waitkey (3), .BR termkey_set_waittime (3), .BR termkey (7) libtermkey-0.22/man/termkey_new.30000644000175000017500000000613213442455536015131 0ustar leoleo.TH TERMKEY_NEW 3 .SH NAME termkey_new, termkey_destroy \- create or destroy new termkey instance .SH SYNOPSIS .nf .B #include .sp .BI "TERMKEY_CHECK_VERSION;" .BI "TermKey *termkey_new(int " fd ", int " flags ); .BI "TermKey *termkey_new_abstract(const char *" term ", int " flags ); .BI "void termkey_destroy(TermKey *" tk ); .fi .sp Link with \fI\-ltermkey\fP. .SH DESCRIPTION \fBtermkey_new\fP() creates a new \fBtermkey\fP(7) instance connected to the file handle opened by \fIfd\fP using the \fIflags\fP. The \fITermKey\fP structure should be considered opaque; its contents are not intended for use outside of the library. .PP \fBtermkey_new_abstract\fP() creates a new \fBtermkey\fP() instance with no file handle associated. As this is usually done for handling other sources of terminal byte input, it also takes a string indicating the termtype to use. .PP \fBtermkey_destroy\fP() destroys the given instance and releases any resources controlled by it. It will not close the underlying filehandle given as the \fIfd\fP argument to \fBtermkey_new\fP(). .PP The constructor attempts to detect if the current locale is .SM UTF-8 aware or not, and sets either the \fBTERMKEY_FLAG_UTF8\fP or \fBTERMKEY_FLAG_RAW\fP flag. One of these two bits will always be in effect. The current flags in effect can be obtained by \fBtermkey_get_flags\fP(3). .PP If a file handle is provided, the terminfo driver may send a string to initialise or set the state of the terminal before \fBtermkey_new\fP() returns. This will not be done if no file handle is provided, or if the file handle is a pipe (\fBS_ISFIFO\fP()). In this case it will be the caller's responsibility to ensure the terminal is in the correct mode. Once initialised, the terminal can be stopped by \fBtermkey_stop\fP(3), and started again by \fBtermkey_start\fP(3). .PP This behaviour is modified by the \fBTERMKEY_FLAG_NOSTART\fP flag. If passed in the \fIflags\fP argument then the instance will not be started yet by the constructor; the caller must invoke \fBtermkey_start\fP() at some future point before the instance will be usable. .SH VERSION CHECK MACRO Before calling any functions in the \fBtermkey\fP library, an application should use the \fBTERMKEY_CHECK_VERSION\fP macro to check that the loaded version of the library is compatible with the version it was compiled against. This should be done early on, ideally just after entering its \fBmain\fP() function. .SH "RETURN VALUE" If successful, \fBtermkey_new\fP() returns a pointer to the new instance. On failure, \fBNULL\fP is returned with \fIerrno\fP set to indicate the failure. \fBtermkey_destroy\fP() returns no value. .SH ERRORS .TP .B ENOENT No driver was able to recognise the given terminal type. .TP .B ENOMEM A call to \fBmalloc\fP(3) failed to allocate memory. .PP Additionally, \fBtermkey_new\fP() may fail if \fBfstat\fP(2) or \fBwrite\fP(2) fails on the given file handle. .SH "SEE ALSO" .BR termkey_waitkey (3), .BR termkey_advisereadable (3), .BR termkey_getkey (3), .BR termkey_get_flags (3), .BR termkey_get_fd (3), .BR termkey_get_buffer_remaining (3), .BR termkey_get_buffer_size (3), .BR termkey (7) libtermkey-0.22/man/termkey_get_keyname.30000644000175000017500000000152213442455536016626 0ustar leoleo.TH TERMKEY_GET_KEYNAME 3 .SH NAME termkey_get_keyname \- return a string name for a symbolic key .SH SYNOPSIS .nf .B #include .sp .BI "const char *termkey_get_keyname(TermKey *" tk ", TermKeySym " sym ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_get_keyname\fP() returns a human-readable string name for the symbolic key value given by \fIsym\fP. The returned string is owned by the \fBtermkey\fP(7) instance \fItk\fP so should not be modified or freed. The returned pointer is guaranteed to be valid until the instance is released using \fBtermkey_destroy\fP(3). This function is the inverse of \fBtermkey_keyname2sym\fP(3). .SH "RETURN VALUE" \fBtermkey_get_key\fP() returns a pointer to a string. .SH "SEE ALSO" .BR termkey_lookup_keyname (3), .BR termkey_keyname2sym (3), .BR termkey_strfkey (3), .BR termkey (7) libtermkey-0.22/man/termkey_interpret_modereport.30000644000175000017500000000255713442455536020623 0ustar leoleo.TH TERMKEY_INTERPRET_MODEREPORT 3 .SH NAME termkey_interpret_modereport \- interpret opaque mode report data .SH SYNOPSIS .nf .B #include .sp .BI "TermKeyResult termkey_interpret_modereport(TermKey *" tk ", const TermKeyKey *" key ", " .BI " int *" initial ", int *" mode ", int *" value ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_interpret_modereport\fP() fills in variables in the passed pointers according to the mode report event found in \fIkey\fP. It should be called if \fBtermkey_getkey\fP(3) or similar have returned a key event with the type of \fBTERMKEY_TYPE_MODEREPORT\fP. .PP Any pointer may instead be given as \fBNULL\fP to not return that value. .PP The \fIinitial\fP variable will be filled with 0 for an ANSI mode report, or \f(CW'?'\fP for a DEC mode report. The \fImode\fP variable will be filled with the number of the mode, and \fIvalue\fP will be filled with the value from the report. .SH "RETURN VALUE" If passed a \fIkey\fP event of the type \fBTERMKEY_TYPE_MODEREPORT\fP, this function will return \fBTERMKEY_RES_KEY\fP and will affect the variables whose pointers were passed in, as described above. .PP For other event types it will return \fBTERMKEY_RES_NONE\fP, and its effects on any variables whose pointers were passed in, are undefined. .SH "SEE ALSO" .BR termkey_waitkey (3), .BR termkey_getkey (3), .BR termkey (7) libtermkey-0.22/man/termkey_set_flags.30000644000175000017500000000136013442455536016305 0ustar leoleo.TH TERMKEY_SET_FLAGS 3 .SH NAME termkey_set_flags, termkey_get_flags \- control the operational flags .SH SYNOPSIS .nf .B #include .sp .BI "void termkey_set_flags(TermKey *" tk ", int " newflags ); .BI "int termkey_get_flags(TermKey *" tk ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_set_flags\fP() changes the set of operation flags in the \fBtermkey\fP(7) instance to those given by \fInewflags\fP. .PP \fBtermkey_get_flags\fP() returns the value set by the last call to \fBtermkey_set_flags\fP(), or the value given to the constructor. .SH "RETURN VALUE" \fBtermkey_set_flags\fP() returns no value. \fBtermkey_get_flags\fP() returns the current operational flags. .SH "SEE ALSO" .BR termkey_new (3), .BR termkey (7) libtermkey-0.22/man/termkey_set_waittime.30000644000175000017500000000162413442455536017037 0ustar leoleo.TH TERMKEY_SET_WAITTIME 3 .SH NAME termkey_set_waittime, termkey_get_waittime \- control the wait time for multibyte sequences .SH SYNOPSIS .nf .B #include .sp .BI "void termkey_set_waittime(TermKey *" tk ", int " msec ); .BI "int termkey_get_waittime(TermKey *" tk ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_set_waittime\fP() sets the number of milliseconds that \fBtermkey_waitkey\fP(3) will wait for the remaining bytes of a multibyte sequence if it detects the start of a partially-complete one. .PP \fBtermkey_get_waittime\fP() returns the value set by the last call to \fBtermkey_set_waittime\fP(), or the default value if a different has not been set. .SH "RETURN VALUE" \fBtermkey_set_waittime\fP() returns no value. \fBtermkey_get_waittime\fP() returns the current wait time in milliseconds. .SH "SEE ALSO" .BR termkey_getkey (3), .BR termkey_waitkey (3), .BR termkey (7) libtermkey-0.22/man/termkey_strpkey.30000644000175000017500000000477013442455536016047 0ustar leoleo.TH TERMKEY_STRPKEY 3 .SH NAME termkey_strpkey \- parse a string representing a key event .SH SYNOPSIS .nf .B #include .sp .BI "char *termkey_strpkey(TermKey *" tk ", const char *" str ", .BI " TermKeyKey *" key ", TermKeyFormat " format ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_strpkey\fP(3) parses a string buffer containing a human-readable representation of a key event. It fills the \fIkey\fP structure from the results of this parsing, similar to the \fBstrptime\fP(3) standard library function. .PP The \fIformat\fP argument specifies the format expected in the string, as a bitmask of the following constants: .TP .B TERMKEY_FORMAT_LONGMOD Expect full modifier names e.g. "\f(CWShift-\fP" instead of abbreviating to "\f(CWS-\fP". .TP .B TERMKEY_FORMAT_CARETCTRL If the only modifier is \fBTERMKEY_MOD_CTRL\fP on a plain letter, accept it as "\f(CW^X\fP" rather than "\f(CWCtrl-X\fP". .TP .B TERMKEY_FORMAT_ALTISMETA Expect the name "\f(CWMeta\fP" or the letter "\f(CWM\fP" instead of "\f(CWAlt\fP" or "\f(CWA\fP". .TP .B TERMKEY_FORMAT_SPACEMOD Expect spaces instead of hyphens to separate the modifier name(s) from the base key name. .TP .B TERMKEY_FORMAT_LOWERMOD Expect lowercase for the modifier name. .TP .B TERMKEY_FORMAT_LOWERSPACE Expect lowercase with spaces in for the key name instead of camelCase (for example "\f(CWpage down\fP" instead of "\f(CWPageDown\fP"). .TP .B TERMKEY_FORMAT_MOUSE_POS Expect a mouse event to be followed by its position rendered as "\f(CW@ (col,line)\fP". .PP Before returning, this function canonicalises the \fIkey\fP structure according to the rules given for \fBtermkey_canonicalise\fP(3). .PP The \fBTERMKEY_FORMAT_WRAPBRACKET\fP option is currently not supported by \fBtermkey_strpkey\fP(). When returning a \fBTERMKEY_TYPE_UNICODE\fP key structure, this function will fill in the \fIutf8\fP member. .SH "RETURN VALUE" After a successful parse, \fBtermkey_strpkey\fP() returns a pointer to the first character of the input it did not consume. If the input string contains more characters then this will point at the first character beyond. If the entire input string was consumed, then this will point at a null byte. If \fBtermkey_strpkey\fP() fails to parse, it returns \fBNULL\fP. After a failed parse, the \fIkey\fP structure may contain partial or invalid results. The structure will only be valid if the function returns a non-\fBNULL\fP result. .SH "SEE ALSO" .BR termkey_new (3), .BR termkey_strfkey (3), .BR termkey_keycmp (3), .BR termkey (7) libtermkey-0.22/man/termkey_start.30000644000175000017500000000217113442455536015474 0ustar leoleo.TH TERMKEY_START 3 .SH NAME termkey_start, termkey_stop, termkey_is_started \- enable or disable terminal operations .SH SYNOPSIS .nf .B #include .sp .BI "int termkey_start(TermKey *" tk ); .BI "int termkey_stop(TermKey *" tk ); .sp .BI "int termkey_is_started(TermKey *" tk ); .fi .sp Link with \fI\-ltermkey\fP. .SH DESCRIPTION \fBtermkey_start\fP() enables the terminal IO operations of the given \fBtermkey\fP(7) instance, including sending a terminal control sequence and setting the \fBtermios\fP(3) modes required. .PP \fBtermkey_stop\fP() disables terminal IO operations, by reversing the steps taken by \fBtermkey_start\fP(). A newly-constructed \fBtermkey\fP instance will have terminal IO enabled already. .PP \fBtermkey_is_started\fP() enquires whether terminal IO is currently enabled. .SH "RETURN VALUE" If successful, \fBtermkey_start\fP() and \fBtermkey_stop\fP() return a true value. On failure, zero is returned with \fIerrno\fP set to indicate the failure. \fBtermkey_is_started\fP() returns true or false to indicate whether terminal IO is currently enabled. .SH "SEE ALSO" .BR termkey_new (3), .BR termkey (7) libtermkey-0.22/man/termkey_interpret_string.30000644000175000017500000000311013442455536017733 0ustar leoleo.TH TERMKEY_INTERPRET_STRING 3 .SH NAME termkey_interpret_string \- fetch stored control string .SH SYNOPSIS .nf .B #include .sp .BI "TermKeyResult termkey_interpret_string(TermKey *" tk ", const TermKeyKey *" key ", " .BI " const char **" strp ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_interpret_string\fP() fetches the string stored in the \fBTermKey\fP instance from the most recently received \fBTERMKEY_TYPE_DCS\fP or \fBTERMKEY_TYPE_OSC\fP event. Note that it is important to call this function as soon as possible after obtaining a one of these string key event; specifically, before calling \fBtermkey_getkey\fP() or \fBtermkey_waitkey\fP() again, as a subsequent call will overwrite the buffer space currently containing this string. .PP The string pointer whose address is given by \fIstrp\fP will be set to point at the actual stored string in the instance. The caller is free to read this string (which will be correctly NUL-terminated), but should not modify it. It is not necessary to \fBfree\fP() the pointer; the containing \fBTermKey\fP instance will do that. .SH "RETURN VALUE" If passed the most recent \fIkey\fP event of the type \fBTERMKEY_TYPE_DCS\fP or \fBTERMKEY_TYPE_OSC\fP, this function will return \fBTERMKEY_RES_KEY\fP and will affect the variables whose pointers were passed in, as described above. .PP For other event types, or stale events, it will return \fBTERMKEY_RES_NONE\fP, and its effects on any variables whose pointers were passed in are undefined. .SH "SEE ALSO" .BR termkey_waitkey (3), .BR termkey_getkey (3), .BR termkey (7) libtermkey-0.22/man/termkey_interpret_position.30000644000175000017500000000255313442455536020303 0ustar leoleo.TH TERMKEY_INTERPRET_POSITION 3 .SH NAME termkey_interpret_position \- interpret opaque cursor position event data .SH SYNOPSIS .nf .B #include .sp .BI "TermKeyResult termkey_interpret_position(TermKey *" tk ", const TermKeyKey *" key ", " .BI " int *" line ", int *" col ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_interpret_position\fP() fills in variables in the passed pointers according to the cursor position report event found in \fIkey\fP. It should be called if \fBtermkey_getkey\fP(3) or similar have returned a key event with the type of \fBTERMKEY_TYPE_POSITION\fP. .PP Any pointer may instead be given as \fBNULL\fP to not return that value. .PP The \fIline\fP and \fIcol\fP variables will be filled in with the cursor position, indexed from 1. Note that due to the limited number of bytes in the \fBTermKeyKey\fP structure, the line and column numbers are limited to 2047 and 4095 respectively. .SH "RETURN VALUE" If passed a \fIkey\fP event of the type \fBTERMKEY_TYPE_POSITION\fP, this function will return \fBTERMKEY_RES_KEY\fP and will affect the variables whose pointers were passed in, as described above. .PP For other event types it will return \fBTERMKEY_RES_NONE\fP, and its effects on any variables whose pointers were passed in, are undefined. .SH "SEE ALSO" .BR termkey_waitkey (3), .BR termkey_getkey (3), .BR termkey (7) libtermkey-0.22/man/termkey_set_buffer_size.30000644000175000017500000000204513442455536017515 0ustar leoleo.TH TERMKEY_SET_BUFFER_SIZE 3 .SH NAME termkey_set_buffer_size, termkey_get_buffer_size \- control the buffer size .SH SYNOPSIS .nf .B #include .sp .BI "int termkey_set_buffer_size(TermKey *" tk ", size_t " size ); .BI "size_t termkey_get_buffer_size(TermKey *" tk ); .fi .sp Link with \fI-ltermkey\fP. .SH DESCRIPTION \fBtermkey_set_buffer_size\fP() changes the size of the buffer space in the \fBtermkey\fP(7) instance to that given by \fIsize\fP. Any bytes pending in the buffer will be preserved when resizing, though they will be truncated if the new size is smaller than the total number of bytes in the buffer. .PP \fBtermkey_get_buffer_size\fP() returns the size of the buffer set by the last call to \fBtermkey_set_buffer_size\fP(), or the default initial size of 256 bytes. .SH "RETURN VALUE" \fBtermkey_set_buffer_size\fP() returns a true value, or zero if an error occurs. \fBtermkey_get_buffer_size\fP() returns the current buffer size in bytes. .SH "SEE ALSO" .BR termkey_new (3), .BR termkey_get_buffer_remaining (3), .BR termkey (7) libtermkey-0.22/demo-glib.c0000644000175000017500000000235213442455536013744 0ustar leoleo#include #include #include "termkey.h" static TermKey *tk; static int timeout_id; static void on_key(TermKey *tk, TermKeyKey *key) { char buffer[50]; termkey_strfkey(tk, buffer, sizeof buffer, key, TERMKEY_FORMAT_VIM); printf("%s\n", buffer); } static gboolean key_timer(gpointer data) { TermKeyKey key; if(termkey_getkey_force(tk, &key) == TERMKEY_RES_KEY) on_key(tk, &key); return FALSE; } static gboolean stdin_io(GIOChannel *source, GIOCondition condition, gpointer data) { if(condition && G_IO_IN) { if(timeout_id) g_source_remove(timeout_id); termkey_advisereadable(tk); TermKeyResult ret; TermKeyKey key; while((ret = termkey_getkey(tk, &key)) == TERMKEY_RES_KEY) { on_key(tk, &key); } if(ret == TERMKEY_RES_AGAIN) timeout_id = g_timeout_add(termkey_get_waittime(tk), key_timer, NULL); } return TRUE; } int main(int argc, char *argv[]) { TERMKEY_CHECK_VERSION; tk = termkey_new(0, 0); if(!tk) { fprintf(stderr, "Cannot allocate termkey instance\n"); exit(1); } GMainLoop *loop = g_main_loop_new(NULL, FALSE); g_io_add_watch(g_io_channel_unix_new(0), G_IO_IN, stdin_io, NULL); g_main_loop_run(loop); termkey_destroy(tk); } libtermkey-0.22/termkey-internal.h0000644000175000017500000000537613442455536015415 0ustar leoleo#ifndef GUARD_TERMKEY_INTERNAL_H_ #define GUARD_TERMKEY_INTERNAL_H_ #define HAVE_TERMIOS #ifdef _WIN32 # undef HAVE_TERMIOS #endif #include "termkey.h" #include #ifdef HAVE_TERMIOS # include #endif #ifdef _MSC_VER #include typedef SSIZE_T ssize_t; #endif struct TermKeyDriver { const char *name; void *(*new_driver)(TermKey *tk, const char *term); void (*free_driver)(void *info); int (*start_driver)(TermKey *tk, void *info); int (*stop_driver)(TermKey *tk, void *info); TermKeyResult (*peekkey)(TermKey *tk, void *info, TermKeyKey *key, int force, size_t *nbytes); }; struct keyinfo { TermKeyType type; TermKeySym sym; int modifier_mask; int modifier_set; }; struct TermKeyDriverNode; struct TermKeyDriverNode { struct TermKeyDriver *driver; void *info; struct TermKeyDriverNode *next; }; struct TermKey { int fd; int flags; int canonflags; unsigned char *buffer; size_t buffstart; // First offset in buffer size_t buffcount; // NUMBER of entires valid in buffer size_t buffsize; // Total malloc'ed size size_t hightide; /* Position beyond buffstart at which peekkey() should next start * normally 0, but see also termkey_interpret_csi */ #ifdef HAVE_TERMIOS struct termios restore_termios; char restore_termios_valid; #endif TermKey_Terminfo_Getstr_Hook *ti_getstr_hook; void *ti_getstr_hook_data; int waittime; // msec char is_closed; char is_started; int nkeynames; const char **keynames; // There are 32 C0 codes struct keyinfo c0[32]; struct TermKeyDriverNode *drivers; // Now some "protected" methods for the driver to call but which we don't // want exported as real symbols in the library struct { void (*emit_codepoint)(TermKey *tk, long codepoint, TermKeyKey *key); TermKeyResult (*peekkey_simple)(TermKey *tk, TermKeyKey *key, int force, size_t *nbytes); TermKeyResult (*peekkey_mouse)(TermKey *tk, TermKeyKey *key, size_t *nbytes); } method; }; static inline void termkey_key_get_linecol(const TermKeyKey *key, int *line, int *col) { if(col) *col = (unsigned char)key->code.mouse[1] | ((unsigned char)key->code.mouse[3] & 0x0f) << 8; if(line) *line = (unsigned char)key->code.mouse[2] | ((unsigned char)key->code.mouse[3] & 0x70) << 4; } static inline void termkey_key_set_linecol(TermKeyKey *key, int line, int col) { if(line > 0xfff) line = 0xfff; if(col > 0x7ff) col = 0x7ff; key->code.mouse[1] = (line & 0x0ff); key->code.mouse[2] = (col & 0x0ff); key->code.mouse[3] = (line & 0xf00) >> 8 | (col & 0x300) >> 4; } extern struct TermKeyDriver termkey_driver_csi; extern struct TermKeyDriver termkey_driver_ti; #endif libtermkey-0.22/Makefile0000644000175000017500000000666613442455536013415 0ustar leoleopkgconfig = $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config $(1)) ifeq ($(shell uname),Darwin) LIBTOOL ?= glibtool else LIBTOOL ?= libtool endif ifneq ($(VERBOSE),1) LIBTOOL +=--quiet endif override CFLAGS +=-Wall -std=c99 ifeq ($(DEBUG),1) override CFLAGS +=-ggdb -DDEBUG endif ifeq ($(PROFILE),1) override CFLAGS +=-pg override LDFLAGS+=-pg endif ifeq ($(call pkgconfig, --atleast-version=0.1.0 unibilium && echo 1),1) override CFLAGS +=$(call pkgconfig, --cflags unibilium) -DHAVE_UNIBILIUM override LDFLAGS+=$(call pkgconfig, --libs unibilium) else ifeq ($(call pkgconfig, tinfo && echo 1),1) override CFLAGS +=$(call pkgconfig, --cflags tinfo) override LDFLAGS+=$(call pkgconfig, --libs tinfo) else ifeq ($(call pkgconfig, ncursesw && echo 1),1) override CFLAGS +=$(call pkgconfig, --cflags ncursesw) override LDFLAGS+=$(call pkgconfig, --libs ncursesw) else override LDFLAGS+=-lncurses endif OBJECTS=termkey.lo driver-csi.lo driver-ti.lo LIBRARY=libtermkey.la DEMOS=demo demo-async ifeq ($(call pkgconfig, glib-2.0 && echo 1),1) DEMOS+=demo-glib endif DEMO_OBJECTS=$(DEMOS:=.lo) TESTSOURCES=$(wildcard t/[0-9]*.c) TESTFILES=$(TESTSOURCES:.c=.t) VERSION_MAJOR=0 VERSION_MINOR=22 VERSION=$(VERSION_MAJOR).$(VERSION_MINOR) VERSION_CURRENT=15 VERSION_REVISION=2 VERSION_AGE=14 PREFIX=/usr/local LIBDIR=$(PREFIX)/lib INCDIR=$(PREFIX)/include MANDIR=$(PREFIX)/share/man MAN3DIR=$(MANDIR)/man3 MAN7DIR=$(MANDIR)/man7 all: $(LIBRARY) $(DEMOS) %.lo: %.c termkey.h termkey-internal.h $(LIBTOOL) --mode=compile --tag=CC $(CC) $(CFLAGS) -o $@ -c $< $(LIBRARY): $(OBJECTS) $(LIBTOOL) --mode=link --tag=CC $(CC) -rpath $(LIBDIR) -version-info $(VERSION_CURRENT):$(VERSION_REVISION):$(VERSION_AGE) $(LDFLAGS) -o $@ $^ demo: $(LIBRARY) demo.lo $(LIBTOOL) --mode=link --tag=CC $(CC) -o $@ $^ demo-async: $(LIBRARY) demo-async.lo $(LIBTOOL) --mode=link --tag=CC $(CC) -o $@ $^ demo-glib.lo: demo-glib.c termkey.h $(LIBTOOL) --mode=compile --tag=CC $(CC) -o $@ -c $< $(call pkgconfig, glib-2.0 --cflags) demo-glib: $(LIBRARY) demo-glib.lo $(LIBTOOL) --mode=link --tag=CC $(CC) -o $@ $^ $(call pkgconfig, glib-2.0 --libs) t/%.t: t/%.c $(LIBRARY) t/taplib.lo $(LIBTOOL) --mode=link --tag=CC $(CC) -o $@ $^ t/taplib.lo: t/taplib.c $(LIBTOOL) --mode=compile --tag=CC $(CC) $(CFLAGS) -o $@ -c $^ .PHONY: test test: $(TESTFILES) prove -e "" .PHONY: clean-test clean-test: $(LIBTOOL) --mode=clean rm -f $(TESTFILES) t/taplib.lo .PHONY: clean clean: clean-test $(LIBTOOL) --mode=clean rm -f $(OBJECTS) $(DEMO_OBJECTS) $(LIBTOOL) --mode=clean rm -f $(LIBRARY) $(LIBTOOL) --mode=clean rm -rf $(DEMOS) .PHONY: install install: install-inc install-lib install-man $(LIBTOOL) --mode=finish $(DESTDIR)$(LIBDIR) install-inc: termkey.h install -d $(DESTDIR)$(INCDIR) install -m644 termkey.h $(DESTDIR)$(INCDIR) install -d $(DESTDIR)$(LIBDIR)/pkgconfig LIBDIR=$(LIBDIR) INCDIR=$(INCDIR) VERSION=$(VERSION) sh termkey.pc.sh >$(DESTDIR)$(LIBDIR)/pkgconfig/termkey.pc install-lib: $(LIBRARY) install -d $(DESTDIR)$(LIBDIR) $(LIBTOOL) --mode=install install libtermkey.la $(DESTDIR)$(LIBDIR)/libtermkey.la install-man: install -d $(DESTDIR)$(MAN3DIR) install -d $(DESTDIR)$(MAN7DIR) for F in man/*.3; do \ gzip <$$F >$(DESTDIR)$(MAN3DIR)/$${F#man/}.gz; \ done for F in man/*.7; do \ gzip <$$F >$(DESTDIR)$(MAN7DIR)/$${F#man/}.gz; \ done while read FROM EQ TO; do \ echo ln -sf $$TO.gz $(DESTDIR)$(MAN3DIR)/$$FROM.gz; \ done < man/also libtermkey-0.22/demo-async.c0000644000175000017500000000247313442455536014150 0ustar leoleo// might need this for sigset_t #define _XOPEN_SOURCE 600 #include #include #include "termkey.h" static void on_key(TermKey *tk, TermKeyKey *key) { char buffer[50]; termkey_strfkey(tk, buffer, sizeof buffer, key, TERMKEY_FORMAT_VIM); printf("%s\n", buffer); } int main(int argc, char *argv[]) { TERMKEY_CHECK_VERSION; TermKey *tk = termkey_new(0, 0); if(!tk) { fprintf(stderr, "Cannot allocate termkey instance\n"); exit(1); } struct pollfd fd; fd.fd = 0; /* the file descriptor we passed to termkey_new() */ fd.events = POLLIN; TermKeyResult ret; TermKeyKey key; int running = 1; int nextwait = -1; while(running) { if(poll(&fd, 1, nextwait) == 0) { // Timed out if(termkey_getkey_force(tk, &key) == TERMKEY_RES_KEY) on_key(tk, &key); } if(fd.revents & (POLLIN|POLLHUP|POLLERR)) termkey_advisereadable(tk); while((ret = termkey_getkey(tk, &key)) == TERMKEY_RES_KEY) { on_key(tk, &key); if(key.type == TERMKEY_TYPE_UNICODE && key.modifiers & TERMKEY_KEYMOD_CTRL && (key.code.codepoint == 'C' || key.code.codepoint == 'c')) running = 0; } if(ret == TERMKEY_RES_AGAIN) nextwait = termkey_get_waittime(tk); else nextwait = -1; } termkey_destroy(tk); } libtermkey-0.22/termkey.pc.sh0000644000175000017500000000027013442455536014353 0ustar leoleocat < #include // There are 64 codes 0x40 - 0x7F static int keyinfo_initialised = 0; static struct keyinfo ss3s[64]; static char ss3_kpalts[64]; typedef struct { TermKey *tk; int saved_string_id; char *saved_string; } TermKeyCsi; typedef TermKeyResult CsiHandler(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args); static CsiHandler *csi_handlers[64]; /* * Handler for CSI/SS3 cmd keys */ static struct keyinfo csi_ss3s[64]; static TermKeyResult handle_csi_ss3_full(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) { if(args > 1 && arg[1] != -1) key->modifiers = arg[1] - 1; else key->modifiers = 0; key->type = csi_ss3s[cmd - 0x40].type; key->code.sym = csi_ss3s[cmd - 0x40].sym; key->modifiers &= ~(csi_ss3s[cmd - 0x40].modifier_mask); key->modifiers |= csi_ss3s[cmd - 0x40].modifier_set; if(key->code.sym == TERMKEY_SYM_UNKNOWN) return TERMKEY_RES_NONE; return TERMKEY_RES_KEY; } static void register_csi_ss3_full(TermKeyType type, TermKeySym sym, int modifier_set, int modifier_mask, unsigned char cmd) { if(cmd < 0x40 || cmd >= 0x80) { return; } csi_ss3s[cmd - 0x40].type = type; csi_ss3s[cmd - 0x40].sym = sym; csi_ss3s[cmd - 0x40].modifier_set = modifier_set; csi_ss3s[cmd - 0x40].modifier_mask = modifier_mask; csi_handlers[cmd - 0x40] = &handle_csi_ss3_full; } static void register_csi_ss3(TermKeyType type, TermKeySym sym, unsigned char cmd) { register_csi_ss3_full(type, sym, 0, 0, cmd); } /* * Handler for SS3 keys with kpad alternate representations */ static void register_ss3kpalt(TermKeyType type, TermKeySym sym, unsigned char cmd, char kpalt) { if(cmd < 0x40 || cmd >= 0x80) { return; } ss3s[cmd - 0x40].type = type; ss3s[cmd - 0x40].sym = sym; ss3s[cmd - 0x40].modifier_set = 0; ss3s[cmd - 0x40].modifier_mask = 0; ss3_kpalts[cmd - 0x40] = kpalt; } /* * Handler for CSI number ~ function keys */ static struct keyinfo csifuncs[35]; /* This value must be increased if more CSI function keys are added */ #define NCSIFUNCS (sizeof(csifuncs)/sizeof(csifuncs[0])) static TermKeyResult handle_csifunc(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) { if(args > 1 && arg[1] != -1) key->modifiers = arg[1] - 1; else key->modifiers = 0; key->type = TERMKEY_TYPE_KEYSYM; if(arg[0] == 27) { int mod = key->modifiers; (*tk->method.emit_codepoint)(tk, arg[2], key); key->modifiers |= mod; } else if(arg[0] >= 0 && arg[0] < NCSIFUNCS) { key->type = csifuncs[arg[0]].type; key->code.sym = csifuncs[arg[0]].sym; key->modifiers &= ~(csifuncs[arg[0]].modifier_mask); key->modifiers |= csifuncs[arg[0]].modifier_set; } else key->code.sym = TERMKEY_SYM_UNKNOWN; if(key->code.sym == TERMKEY_SYM_UNKNOWN) { #ifdef DEBUG fprintf(stderr, "CSI: Unknown function key %ld\n", arg[0]); #endif return TERMKEY_RES_NONE; } return TERMKEY_RES_KEY; } static void register_csifunc(TermKeyType type, TermKeySym sym, int number) { if(number >= NCSIFUNCS) { return; } csifuncs[number].type = type; csifuncs[number].sym = sym; csifuncs[number].modifier_set = 0; csifuncs[number].modifier_mask = 0; csi_handlers['~' - 0x40] = &handle_csifunc; } /* * Handler for CSI u extended Unicode keys */ static TermKeyResult handle_csi_u(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) { switch(cmd) { case 'u': { if(args > 1 && arg[1] != -1) key->modifiers = arg[1] - 1; else key->modifiers = 0; int mod = key->modifiers; key->type = TERMKEY_TYPE_KEYSYM; (*tk->method.emit_codepoint)(tk, arg[0], key); key->modifiers |= mod; return TERMKEY_RES_KEY; } default: return TERMKEY_RES_NONE; } } /* * Handler for CSI M / CSI m mouse events in SGR and rxvt encodings * Note: This does not handle X10 encoding */ static TermKeyResult handle_csi_m(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) { int initial = cmd >> 8; cmd &= 0xff; switch(cmd) { case 'M': case 'm': break; default: return TERMKEY_RES_NONE; } if(!initial && args >= 3) { // rxvt protocol key->type = TERMKEY_TYPE_MOUSE; key->code.mouse[0] = arg[0]; key->modifiers = (key->code.mouse[0] & 0x1c) >> 2; key->code.mouse[0] &= ~0x1c; termkey_key_set_linecol(key, arg[1], arg[2]); return TERMKEY_RES_KEY; } if(initial == '<' && args >= 3) { // SGR protocol key->type = TERMKEY_TYPE_MOUSE; key->code.mouse[0] = arg[0]; key->modifiers = (key->code.mouse[0] & 0x1c) >> 2; key->code.mouse[0] &= ~0x1c; termkey_key_set_linecol(key, arg[1], arg[2]); if(cmd == 'm') // release key->code.mouse[3] |= 0x80; return TERMKEY_RES_KEY; } return TERMKEY_RES_NONE; } TermKeyResult termkey_interpret_mouse(TermKey *tk, const TermKeyKey *key, TermKeyMouseEvent *event, int *button, int *line, int *col) { if(key->type != TERMKEY_TYPE_MOUSE) return TERMKEY_RES_NONE; if(button) *button = 0; termkey_key_get_linecol(key, line, col); if(!event) return TERMKEY_RES_KEY; int btn = 0; int code = key->code.mouse[0]; int drag = code & 0x20; code &= ~0x3c; switch(code) { case 0: case 1: case 2: *event = drag ? TERMKEY_MOUSE_DRAG : TERMKEY_MOUSE_PRESS; btn = code + 1; break; case 3: *event = TERMKEY_MOUSE_RELEASE; // no button hint break; case 64: case 65: *event = drag ? TERMKEY_MOUSE_DRAG : TERMKEY_MOUSE_PRESS; btn = code + 4 - 64; break; default: *event = TERMKEY_MOUSE_UNKNOWN; } if(button) *button = btn; if(key->code.mouse[3] & 0x80) *event = TERMKEY_MOUSE_RELEASE; return TERMKEY_RES_KEY; } /* * Handler for CSI ? R position reports * A plain CSI R with no arguments is probably actually */ static TermKeyResult handle_csi_R(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) { switch(cmd) { case 'R'|'?'<<8: if(args < 2) return TERMKEY_RES_NONE; key->type = TERMKEY_TYPE_POSITION; termkey_key_set_linecol(key, arg[1], arg[0]); return TERMKEY_RES_KEY; default: return handle_csi_ss3_full(tk, key, cmd, arg, args); } } TermKeyResult termkey_interpret_position(TermKey *tk, const TermKeyKey *key, int *line, int *col) { if(key->type != TERMKEY_TYPE_POSITION) return TERMKEY_RES_NONE; termkey_key_get_linecol(key, line, col); return TERMKEY_RES_KEY; } /* * Handler for CSI $y mode status reports */ static TermKeyResult handle_csi_y(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) { switch(cmd) { case 'y'|'$'<<16: case 'y'|'$'<<16 | '?'<<8: if(args < 2) return TERMKEY_RES_NONE; key->type = TERMKEY_TYPE_MODEREPORT; key->code.mouse[0] = (cmd >> 8); key->code.mouse[1] = arg[0] >> 8; key->code.mouse[2] = arg[0] & 0xff; key->code.mouse[3] = arg[1]; return TERMKEY_RES_KEY; default: return TERMKEY_RES_NONE; } } TermKeyResult termkey_interpret_modereport(TermKey *tk, const TermKeyKey *key, int *initial, int *mode, int *value) { if(key->type != TERMKEY_TYPE_MODEREPORT) return TERMKEY_RES_NONE; if(initial) *initial = key->code.mouse[0]; if(mode) *mode = (key->code.mouse[1] << 8) | key->code.mouse[2]; if(value) *value = key->code.mouse[3]; return TERMKEY_RES_KEY; } #define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) static TermKeyResult parse_csi(TermKey *tk, size_t introlen, size_t *csi_len, long args[], size_t *nargs, unsigned long *commandp) { size_t csi_end = introlen; while(csi_end < tk->buffcount) { if(CHARAT(csi_end) >= 0x40 && CHARAT(csi_end) < 0x80) break; csi_end++; } if(csi_end >= tk->buffcount) return TERMKEY_RES_AGAIN; unsigned char cmd = CHARAT(csi_end); *commandp = cmd; char present = 0; int argi = 0; size_t p = introlen; // See if there is an initial byte if(CHARAT(p) >= '<' && CHARAT(p) <= '?') { *commandp |= (CHARAT(p) << 8); p++; } // Now attempt to parse out up number;number;... separated values while(p < csi_end) { unsigned char c = CHARAT(p); if(c >= '0' && c <= '9') { if(!present) { args[argi] = c - '0'; present = 1; } else { args[argi] = (args[argi] * 10) + c - '0'; } } else if(c == ';') { if(!present) args[argi] = -1; present = 0; argi++; if(argi > 16) break; } else if(c >= 0x20 && c <= 0x2f) { *commandp |= c << 16; break; } p++; } if(present) argi++; *nargs = argi; *csi_len = csi_end + 1; return TERMKEY_RES_KEY; } TermKeyResult termkey_interpret_csi(TermKey *tk, const TermKeyKey *key, long args[], size_t *nargs, unsigned long *cmd) { size_t dummy; if(tk->hightide == 0) return TERMKEY_RES_NONE; if(key->type != TERMKEY_TYPE_UNKNOWN_CSI) return TERMKEY_RES_NONE; return parse_csi(tk, 0, &dummy, args, nargs, cmd); } static int register_keys(void) { int i; for(i = 0; i < 64; i++) { csi_ss3s[i].sym = TERMKEY_SYM_UNKNOWN; ss3s[i].sym = TERMKEY_SYM_UNKNOWN; ss3_kpalts[i] = 0; } for(i = 0; i < NCSIFUNCS; i++) csifuncs[i].sym = TERMKEY_SYM_UNKNOWN; register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_UP, 'A'); register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DOWN, 'B'); register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RIGHT, 'C'); register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_LEFT, 'D'); register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BEGIN, 'E'); register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_END, 'F'); register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HOME, 'H'); register_csi_ss3(TERMKEY_TYPE_FUNCTION, 1, 'P'); register_csi_ss3(TERMKEY_TYPE_FUNCTION, 2, 'Q'); register_csi_ss3(TERMKEY_TYPE_FUNCTION, 3, 'R'); register_csi_ss3(TERMKEY_TYPE_FUNCTION, 4, 'S'); register_csi_ss3_full(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_TAB, TERMKEY_KEYMOD_SHIFT, TERMKEY_KEYMOD_SHIFT, 'Z'); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPENTER, 'M', 0); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPEQUALS, 'X', '='); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPMULT, 'j', '*'); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPPLUS, 'k', '+'); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPCOMMA, 'l', ','); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPMINUS, 'm', '-'); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPPERIOD, 'n', '.'); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPDIV, 'o', '/'); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP0, 'p', '0'); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP1, 'q', '1'); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP2, 'r', '2'); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP3, 's', '3'); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP4, 't', '4'); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP5, 'u', '5'); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP6, 'v', '6'); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP7, 'w', '7'); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP8, 'x', '8'); register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP9, 'y', '9'); register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_FIND, 1); register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_INSERT, 2); register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DELETE, 3); register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SELECT, 4); register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEUP, 5); register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEDOWN, 6); register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HOME, 7); register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_END, 8); register_csifunc(TERMKEY_TYPE_FUNCTION, 1, 11); register_csifunc(TERMKEY_TYPE_FUNCTION, 2, 12); register_csifunc(TERMKEY_TYPE_FUNCTION, 3, 13); register_csifunc(TERMKEY_TYPE_FUNCTION, 4, 14); register_csifunc(TERMKEY_TYPE_FUNCTION, 5, 15); register_csifunc(TERMKEY_TYPE_FUNCTION, 6, 17); register_csifunc(TERMKEY_TYPE_FUNCTION, 7, 18); register_csifunc(TERMKEY_TYPE_FUNCTION, 8, 19); register_csifunc(TERMKEY_TYPE_FUNCTION, 9, 20); register_csifunc(TERMKEY_TYPE_FUNCTION, 10, 21); register_csifunc(TERMKEY_TYPE_FUNCTION, 11, 23); register_csifunc(TERMKEY_TYPE_FUNCTION, 12, 24); register_csifunc(TERMKEY_TYPE_FUNCTION, 13, 25); register_csifunc(TERMKEY_TYPE_FUNCTION, 14, 26); register_csifunc(TERMKEY_TYPE_FUNCTION, 15, 28); register_csifunc(TERMKEY_TYPE_FUNCTION, 16, 29); register_csifunc(TERMKEY_TYPE_FUNCTION, 17, 31); register_csifunc(TERMKEY_TYPE_FUNCTION, 18, 32); register_csifunc(TERMKEY_TYPE_FUNCTION, 19, 33); register_csifunc(TERMKEY_TYPE_FUNCTION, 20, 34); csi_handlers['u' - 0x40] = &handle_csi_u; csi_handlers['M' - 0x40] = &handle_csi_m; csi_handlers['m' - 0x40] = &handle_csi_m; csi_handlers['R' - 0x40] = &handle_csi_R; csi_handlers['y' - 0x40] = &handle_csi_y; keyinfo_initialised = 1; return 1; } static void *new_driver(TermKey *tk, const char *term) { if(!keyinfo_initialised) if(!register_keys()) return NULL; TermKeyCsi *csi = malloc(sizeof *csi); if(!csi) return NULL; csi->tk = tk; csi->saved_string_id = 0; csi->saved_string = NULL; return csi; } static void free_driver(void *info) { TermKeyCsi *csi = info; if(csi->saved_string) free(csi->saved_string); free(csi); } static TermKeyResult peekkey_csi(TermKey *tk, TermKeyCsi *csi, size_t introlen, TermKeyKey *key, int force, size_t *nbytep) { size_t csi_len; size_t args = 16; long arg[16]; unsigned long cmd; TermKeyResult ret = parse_csi(tk, introlen, &csi_len, arg, &args, &cmd); if(ret == TERMKEY_RES_AGAIN) { if(!force) return TERMKEY_RES_AGAIN; (*tk->method.emit_codepoint)(tk, '[', key); key->modifiers |= TERMKEY_KEYMOD_ALT; *nbytep = introlen; return TERMKEY_RES_KEY; } if(cmd == 'M' && args < 3) { // Mouse in X10 encoding consumes the next 3 bytes also tk->buffstart += csi_len; tk->buffcount -= csi_len; TermKeyResult mouse_result = (*tk->method.peekkey_mouse)(tk, key, nbytep); tk->buffstart -= csi_len; tk->buffcount += csi_len; if(mouse_result == TERMKEY_RES_KEY) *nbytep += csi_len; return mouse_result; } TermKeyResult result = TERMKEY_RES_NONE; // We know from the logic above that cmd must be >= 0x40 and < 0x80 if(csi_handlers[(cmd & 0xff) - 0x40]) result = (*csi_handlers[(cmd & 0xff) - 0x40])(tk, key, cmd, arg, args); if(result == TERMKEY_RES_NONE) { #ifdef DEBUG switch(args) { case 0: fprintf(stderr, "CSI: Unknown cmd=%c\n", (char)cmd); break; case 1: fprintf(stderr, "CSI: Unknown arg1=%ld cmd=%c\n", arg[0], (char)cmd); break; case 2: fprintf(stderr, "CSI: Unknown arg1=%ld arg2=%ld cmd=%c\n", arg[0], arg[1], (char)cmd); break; case 3: fprintf(stderr, "CSI: Unknown arg1=%ld arg2=%ld arg3=%ld cmd=%c\n", arg[0], arg[1], arg[2], (char)cmd); break; default: fprintf(stderr, "CSI: Unknown arg1=%ld arg2=%ld arg3=%ld ... args=%d cmd=%c\n", arg[0], arg[1], arg[2], args, (char)cmd); break; } #endif key->type = TERMKEY_TYPE_UNKNOWN_CSI; key->code.number = cmd; key->modifiers = 0; tk->hightide = csi_len - introlen; *nbytep = introlen; // Do not yet eat the data bytes return TERMKEY_RES_KEY; } *nbytep = csi_len; return result; } static TermKeyResult peekkey_ss3(TermKey *tk, TermKeyCsi *csi, size_t introlen, TermKeyKey *key, int force, size_t *nbytep) { if(tk->buffcount < introlen + 1) { if(!force) return TERMKEY_RES_AGAIN; (*tk->method.emit_codepoint)(tk, 'O', key); key->modifiers |= TERMKEY_KEYMOD_ALT; *nbytep = tk->buffcount; return TERMKEY_RES_KEY; } unsigned char cmd = CHARAT(introlen); if(cmd < 0x40 || cmd >= 0x80) return TERMKEY_RES_NONE; key->type = csi_ss3s[cmd - 0x40].type; key->code.sym = csi_ss3s[cmd - 0x40].sym; key->modifiers = csi_ss3s[cmd - 0x40].modifier_set; if(key->code.sym == TERMKEY_SYM_UNKNOWN) { if(tk->flags & TERMKEY_FLAG_CONVERTKP && ss3_kpalts[cmd - 0x40]) { key->type = TERMKEY_TYPE_UNICODE; key->code.codepoint = ss3_kpalts[cmd - 0x40]; key->modifiers = 0; key->utf8[0] = key->code.codepoint; key->utf8[1] = 0; } else { key->type = ss3s[cmd - 0x40].type; key->code.sym = ss3s[cmd - 0x40].sym; key->modifiers = ss3s[cmd - 0x40].modifier_set; } } if(key->code.sym == TERMKEY_SYM_UNKNOWN) { #ifdef DEBUG fprintf(stderr, "CSI: Unknown SS3 %c (0x%02x)\n", (char)cmd, cmd); #endif return TERMKEY_RES_NONE; } *nbytep = introlen + 1; return TERMKEY_RES_KEY; } static TermKeyResult peekkey_ctrlstring(TermKey *tk, TermKeyCsi *csi, size_t introlen, TermKeyKey *key, int force, size_t *nbytep) { size_t str_end = introlen; while(str_end < tk->buffcount) { if(CHARAT(str_end) == 0x9c) // ST break; if(CHARAT(str_end) == 0x1b && (str_end + 1) < tk->buffcount && CHARAT(str_end+1) == 0x5c) // ESC-prefixed ST break; str_end++; } if(str_end >= tk->buffcount) return TERMKEY_RES_AGAIN; #ifdef DEBUG fprintf(stderr, "Found a control string: %*s", str_end - introlen, tk->buffer + tk->buffstart + introlen); #endif *nbytep = str_end + 1; if(CHARAT(str_end) == 0x1b) (*nbytep)++; if(csi->saved_string) free(csi->saved_string); size_t len = str_end - introlen; csi->saved_string_id++; csi->saved_string = malloc(len + 1); strncpy(csi->saved_string, (char *)tk->buffer + tk->buffstart + introlen, len); csi->saved_string[len] = 0; key->type = (CHARAT(introlen-1) & 0x1f) == 0x10 ? TERMKEY_TYPE_DCS : TERMKEY_TYPE_OSC; key->code.number = csi->saved_string_id; key->modifiers = 0; return TERMKEY_RES_KEY; } static TermKeyResult peekkey(TermKey *tk, void *info, TermKeyKey *key, int force, size_t *nbytep) { if(tk->buffcount == 0) return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE; TermKeyCsi *csi = info; switch(CHARAT(0)) { case 0x1b: if(tk->buffcount < 2) return TERMKEY_RES_NONE; switch(CHARAT(1)) { case 0x4f: // ESC-prefixed SS3 return peekkey_ss3(tk, csi, 2, key, force, nbytep); case 0x50: // ESC-prefixed DCS case 0x5d: // ESC-prefixed OSC return peekkey_ctrlstring(tk, csi, 2, key, force, nbytep); case 0x5b: // ESC-prefixed CSI return peekkey_csi(tk, csi, 2, key, force, nbytep); } return TERMKEY_RES_NONE; case 0x8f: // SS3 return peekkey_ss3(tk, csi, 1, key, force, nbytep); case 0x90: // DCS case 0x9d: // OSC return peekkey_ctrlstring(tk, csi, 1, key, force, nbytep); case 0x9b: // CSI return peekkey_csi(tk, csi, 1, key, force, nbytep); } return TERMKEY_RES_NONE; } struct TermKeyDriver termkey_driver_csi = { .name = "CSI", .new_driver = new_driver, .free_driver = free_driver, .peekkey = peekkey, }; TermKeyResult termkey_interpret_string(TermKey *tk, const TermKeyKey *key, const char **strp) { struct TermKeyDriverNode *p; for(p = tk->drivers; p; p = p->next) if(p->driver == &termkey_driver_csi) break; if(!p) return TERMKEY_RES_NONE; if(key->type != TERMKEY_TYPE_DCS && key->type != TERMKEY_TYPE_OSC) return TERMKEY_RES_NONE; TermKeyCsi *csi = p->info; if(csi->saved_string_id != key->code.number) return TERMKEY_RES_NONE; *strp = csi->saved_string; return TERMKEY_RES_KEY; } libtermkey-0.22/LICENSE0000644000175000017500000000211713442455536012745 0ustar leoleo The MIT License Copyright (c) 2007-2011 Paul Evans Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. libtermkey-0.22/termkey.h0000444000175000017500000001652413442455536013576 0ustar leoleo#ifdef __cplusplus extern "C" { #endif #ifndef GUARD_TERMKEY_H_ #define GUARD_TERMKEY_H_ #include #include #define TERMKEY_VERSION_MAJOR 0 #define TERMKEY_VERSION_MINOR 22 #define TERMKEY_CHECK_VERSION \ termkey_check_version(TERMKEY_VERSION_MAJOR, TERMKEY_VERSION_MINOR) typedef enum { TERMKEY_SYM_UNKNOWN = -1, TERMKEY_SYM_NONE = 0, /* Special names in C0 */ TERMKEY_SYM_BACKSPACE, TERMKEY_SYM_TAB, TERMKEY_SYM_ENTER, TERMKEY_SYM_ESCAPE, /* Special names in G0 */ TERMKEY_SYM_SPACE, TERMKEY_SYM_DEL, /* Special keys */ TERMKEY_SYM_UP, TERMKEY_SYM_DOWN, TERMKEY_SYM_LEFT, TERMKEY_SYM_RIGHT, TERMKEY_SYM_BEGIN, TERMKEY_SYM_FIND, TERMKEY_SYM_INSERT, TERMKEY_SYM_DELETE, TERMKEY_SYM_SELECT, TERMKEY_SYM_PAGEUP, TERMKEY_SYM_PAGEDOWN, TERMKEY_SYM_HOME, TERMKEY_SYM_END, /* Special keys from terminfo */ TERMKEY_SYM_CANCEL, TERMKEY_SYM_CLEAR, TERMKEY_SYM_CLOSE, TERMKEY_SYM_COMMAND, TERMKEY_SYM_COPY, TERMKEY_SYM_EXIT, TERMKEY_SYM_HELP, TERMKEY_SYM_MARK, TERMKEY_SYM_MESSAGE, TERMKEY_SYM_MOVE, TERMKEY_SYM_OPEN, TERMKEY_SYM_OPTIONS, TERMKEY_SYM_PRINT, TERMKEY_SYM_REDO, TERMKEY_SYM_REFERENCE, TERMKEY_SYM_REFRESH, TERMKEY_SYM_REPLACE, TERMKEY_SYM_RESTART, TERMKEY_SYM_RESUME, TERMKEY_SYM_SAVE, TERMKEY_SYM_SUSPEND, TERMKEY_SYM_UNDO, /* Numeric keypad special keys */ TERMKEY_SYM_KP0, TERMKEY_SYM_KP1, TERMKEY_SYM_KP2, TERMKEY_SYM_KP3, TERMKEY_SYM_KP4, TERMKEY_SYM_KP5, TERMKEY_SYM_KP6, TERMKEY_SYM_KP7, TERMKEY_SYM_KP8, TERMKEY_SYM_KP9, TERMKEY_SYM_KPENTER, TERMKEY_SYM_KPPLUS, TERMKEY_SYM_KPMINUS, TERMKEY_SYM_KPMULT, TERMKEY_SYM_KPDIV, TERMKEY_SYM_KPCOMMA, TERMKEY_SYM_KPPERIOD, TERMKEY_SYM_KPEQUALS, /* et cetera ad nauseum */ TERMKEY_N_SYMS } TermKeySym; typedef enum { TERMKEY_TYPE_UNICODE, TERMKEY_TYPE_FUNCTION, TERMKEY_TYPE_KEYSYM, TERMKEY_TYPE_MOUSE, TERMKEY_TYPE_POSITION, TERMKEY_TYPE_MODEREPORT, TERMKEY_TYPE_DCS, TERMKEY_TYPE_OSC, /* add other recognised types here */ TERMKEY_TYPE_UNKNOWN_CSI = -1 } TermKeyType; typedef enum { TERMKEY_RES_NONE, TERMKEY_RES_KEY, TERMKEY_RES_EOF, TERMKEY_RES_AGAIN, TERMKEY_RES_ERROR } TermKeyResult; typedef enum { TERMKEY_MOUSE_UNKNOWN, TERMKEY_MOUSE_PRESS, TERMKEY_MOUSE_DRAG, TERMKEY_MOUSE_RELEASE } TermKeyMouseEvent; enum { TERMKEY_KEYMOD_SHIFT = 1 << 0, TERMKEY_KEYMOD_ALT = 1 << 1, TERMKEY_KEYMOD_CTRL = 1 << 2 }; typedef struct { TermKeyType type; union { long codepoint; /* TERMKEY_TYPE_UNICODE */ int number; /* TERMKEY_TYPE_FUNCTION */ TermKeySym sym; /* TERMKEY_TYPE_KEYSYM */ char mouse[4]; /* TERMKEY_TYPE_MOUSE */ /* opaque. see termkey_interpret_mouse */ } code; int modifiers; /* Any Unicode character can be UTF-8 encoded in no more than 6 bytes, plus * terminating NUL */ char utf8[7]; } TermKeyKey; typedef struct TermKey TermKey; enum { TERMKEY_FLAG_NOINTERPRET = 1 << 0, /* Do not interpret C0//DEL codes if possible */ TERMKEY_FLAG_CONVERTKP = 1 << 1, /* Convert KP codes to regular keypresses */ TERMKEY_FLAG_RAW = 1 << 2, /* Input is raw bytes, not UTF-8 */ TERMKEY_FLAG_UTF8 = 1 << 3, /* Input is definitely UTF-8 */ TERMKEY_FLAG_NOTERMIOS = 1 << 4, /* Do not make initial termios calls on construction */ TERMKEY_FLAG_SPACESYMBOL = 1 << 5, /* Sets TERMKEY_CANON_SPACESYMBOL */ TERMKEY_FLAG_CTRLC = 1 << 6, /* Allow Ctrl-C to be read as normal, disabling SIGINT */ TERMKEY_FLAG_EINTR = 1 << 7, /* Return ERROR on signal (EINTR) rather than retry */ TERMKEY_FLAG_NOSTART = 1 << 8 /* Do not call termkey_start() in constructor */ }; enum { TERMKEY_CANON_SPACESYMBOL = 1 << 0, /* Space is symbolic rather than Unicode */ TERMKEY_CANON_DELBS = 1 << 1 /* Del is converted to Backspace */ }; void termkey_check_version(int major, int minor); TermKey *termkey_new(int fd, int flags); TermKey *termkey_new_abstract(const char *term, int flags); void termkey_free(TermKey *tk); void termkey_destroy(TermKey *tk); /* Mostly-undocumented hooks for doing evil evil things */ typedef const char *TermKey_Terminfo_Getstr_Hook(const char *name, const char *value, void *data); void termkey_hook_terminfo_getstr(TermKey *tk, TermKey_Terminfo_Getstr_Hook *hookfn, void *data); int termkey_start(TermKey *tk); int termkey_stop(TermKey *tk); int termkey_is_started(TermKey *tk); int termkey_get_fd(TermKey *tk); int termkey_get_flags(TermKey *tk); void termkey_set_flags(TermKey *tk, int newflags); int termkey_get_waittime(TermKey *tk); void termkey_set_waittime(TermKey *tk, int msec); int termkey_get_canonflags(TermKey *tk); void termkey_set_canonflags(TermKey *tk, int); size_t termkey_get_buffer_size(TermKey *tk); int termkey_set_buffer_size(TermKey *tk, size_t size); size_t termkey_get_buffer_remaining(TermKey *tk); void termkey_canonicalise(TermKey *tk, TermKeyKey *key); TermKeyResult termkey_getkey(TermKey *tk, TermKeyKey *key); TermKeyResult termkey_getkey_force(TermKey *tk, TermKeyKey *key); TermKeyResult termkey_waitkey(TermKey *tk, TermKeyKey *key); TermKeyResult termkey_advisereadable(TermKey *tk); size_t termkey_push_bytes(TermKey *tk, const char *bytes, size_t len); TermKeySym termkey_register_keyname(TermKey *tk, TermKeySym sym, const char *name); const char *termkey_get_keyname(TermKey *tk, TermKeySym sym); const char *termkey_lookup_keyname(TermKey *tk, const char *str, TermKeySym *sym); TermKeySym termkey_keyname2sym(TermKey *tk, const char *keyname); TermKeyResult termkey_interpret_mouse(TermKey *tk, const TermKeyKey *key, TermKeyMouseEvent *event, int *button, int *line, int *col); TermKeyResult termkey_interpret_position(TermKey *tk, const TermKeyKey *key, int *line, int *col); TermKeyResult termkey_interpret_modereport(TermKey *tk, const TermKeyKey *key, int *initial, int *mode, int *value); TermKeyResult termkey_interpret_csi(TermKey *tk, const TermKeyKey *key, long args[], size_t *nargs, unsigned long *cmd); TermKeyResult termkey_interpret_string(TermKey *tk, const TermKeyKey *key, const char **strp); typedef enum { TERMKEY_FORMAT_LONGMOD = 1 << 0, /* Shift-... instead of S-... */ TERMKEY_FORMAT_CARETCTRL = 1 << 1, /* ^X instead of C-X */ TERMKEY_FORMAT_ALTISMETA = 1 << 2, /* Meta- or M- instead of Alt- or A- */ TERMKEY_FORMAT_WRAPBRACKET = 1 << 3, /* Wrap special keys in brackets like */ TERMKEY_FORMAT_SPACEMOD = 1 << 4, /* M Foo instead of M-Foo */ TERMKEY_FORMAT_LOWERMOD = 1 << 5, /* meta or m instead of Meta or M */ TERMKEY_FORMAT_LOWERSPACE = 1 << 6, /* page down instead of PageDown */ TERMKEY_FORMAT_MOUSE_POS = 1 << 8 /* Include mouse position if relevant; @ col,line */ } TermKeyFormat; /* Some useful combinations */ #define TERMKEY_FORMAT_VIM (TermKeyFormat)(TERMKEY_FORMAT_ALTISMETA|TERMKEY_FORMAT_WRAPBRACKET) #define TERMKEY_FORMAT_URWID (TermKeyFormat)(TERMKEY_FORMAT_LONGMOD|TERMKEY_FORMAT_ALTISMETA| \ TERMKEY_FORMAT_LOWERMOD|TERMKEY_FORMAT_SPACEMOD|TERMKEY_FORMAT_LOWERSPACE) size_t termkey_strfkey(TermKey *tk, char *buffer, size_t len, TermKeyKey *key, TermKeyFormat format); const char *termkey_strpkey(TermKey *tk, const char *str, TermKeyKey *key, TermKeyFormat format); int termkey_keycmp(TermKey *tk, const TermKeyKey *key1, const TermKeyKey *key2); #endif #ifdef __cplusplus } #endif libtermkey-0.22/demo.c0000644000175000017500000000607513442455536013037 0ustar leoleo// we want optarg #define _XOPEN_SOURCE 600 #include #include #include #include "termkey.h" int main(int argc, char *argv[]) { TERMKEY_CHECK_VERSION; int mouse = 0; int mouse_proto = 0; TermKeyFormat format = TERMKEY_FORMAT_VIM; char buffer[50]; TermKey *tk; int opt; while((opt = getopt(argc, argv, "m::p:")) != -1) { switch(opt) { case 'm': if(optarg) mouse = atoi(optarg); else mouse = 1000; break; case 'p': mouse_proto = atoi(optarg); break; default: fprintf(stderr, "Usage: %s [-m]\n", argv[0]); return 1; } } tk = termkey_new(0, TERMKEY_FLAG_SPACESYMBOL|TERMKEY_FLAG_CTRLC); if(!tk) { fprintf(stderr, "Cannot allocate termkey instance\n"); exit(1); } if(termkey_get_flags(tk) & TERMKEY_FLAG_UTF8) printf("Termkey in UTF-8 mode\n"); else if(termkey_get_flags(tk) & TERMKEY_FLAG_RAW) printf("Termkey in RAW mode\n"); TermKeyResult ret; TermKeyKey key; if(mouse) { printf("\033[?%dhMouse mode active\n", mouse); if(mouse_proto) printf("\033[?%dh", mouse_proto); } while((ret = termkey_waitkey(tk, &key)) != TERMKEY_RES_EOF) { if(ret == TERMKEY_RES_KEY) { termkey_strfkey(tk, buffer, sizeof buffer, &key, format); if(key.type == TERMKEY_TYPE_MOUSE) { int line, col; termkey_interpret_mouse(tk, &key, NULL, NULL, &line, &col); printf("%s at line=%d, col=%d\n", buffer, line, col); } else if(key.type == TERMKEY_TYPE_POSITION) { int line, col; termkey_interpret_position(tk, &key, &line, &col); printf("Cursor position report at line=%d, col=%d\n", line, col); } else if(key.type == TERMKEY_TYPE_MODEREPORT) { int initial, mode, value; termkey_interpret_modereport(tk, &key, &initial, &mode, &value); printf("Mode report %s mode %d = %d\n", initial ? "DEC" : "ANSI", mode, value); } else if(key.type == TERMKEY_TYPE_UNKNOWN_CSI) { long args[16]; size_t nargs = 16; unsigned long command; termkey_interpret_csi(tk, &key, args, &nargs, &command); printf("Unrecognised CSI %c %ld;%ld %c%c\n", (char)(command >> 8), args[0], args[1], (char)(command >> 16), (char)command); } else { printf("Key %s\n", buffer); } if(key.type == TERMKEY_TYPE_UNICODE && key.modifiers & TERMKEY_KEYMOD_CTRL && (key.code.codepoint == 'C' || key.code.codepoint == 'c')) break; if(key.type == TERMKEY_TYPE_UNICODE && key.modifiers == 0 && key.code.codepoint == '?') { // printf("\033[?6n"); // DECDSR 6 == request cursor position printf("\033[?1$p"); // DECRQM == request mode, DEC origin mode fflush(stdout); } } else if(ret == TERMKEY_RES_ERROR) { if(errno != EINTR) { perror("termkey_waitkey"); break; } printf("Interrupted by signal\n"); } } if(mouse) printf("\033[?%dlMouse mode deactivated\n", mouse); termkey_destroy(tk); } libtermkey-0.22/t/0000755000175000017500000000000013442455536012202 5ustar leoleolibtermkey-0.22/t/02getkey.c0000644000175000017500000000640113442455536014001 0ustar leoleo#include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { TermKey *tk; TermKeyKey key; plan_tests(35); tk = termkey_new_abstract("vt100", 0); is_int(termkey_get_buffer_remaining(tk), 256, "buffer free initially 256"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_NONE, "getkey yields RES_NONE when empty"); is_int(termkey_push_bytes(tk, "h", 1), 1, "push_bytes returns 1"); is_int(termkey_get_buffer_remaining(tk), 255, "buffer free 255 after push_bytes"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY after h"); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type after h"); is_int(key.code.codepoint, 'h', "key.code.codepoint after h"); is_int(key.modifiers, 0, "key.modifiers after h"); is_str(key.utf8, "h", "key.utf8 after h"); is_int(termkey_get_buffer_remaining(tk), 256, "buffer free 256 after getkey"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_NONE, "getkey yields RES_NONE a second time"); termkey_push_bytes(tk, "\x01", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY after C-a"); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type after C-a"); is_int(key.code.codepoint, 'a', "key.code.codepoint after C-a"); is_int(key.modifiers, TERMKEY_KEYMOD_CTRL, "key.modifiers after C-a"); termkey_push_bytes(tk, "\033OA", 3); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY after Up"); is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type after Up"); is_int(key.code.sym, TERMKEY_SYM_UP, "key.code.sym after Up"); is_int(key.modifiers, 0, "key.modifiers after Up"); is_int(termkey_push_bytes(tk, "\033O", 2), 2, "push_bytes returns 2"); is_int(termkey_get_buffer_remaining(tk), 254, "buffer free 254 after partial write"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_AGAIN, "getkey yields RES_AGAIN after partial write"); termkey_push_bytes(tk, "C", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY after Right completion"); is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type after Right"); is_int(key.code.sym, TERMKEY_SYM_RIGHT, "key.code.sym after Right"); is_int(key.modifiers, 0, "key.modifiers after Right"); is_int(termkey_get_buffer_remaining(tk), 256, "buffer free 256 after completion"); termkey_push_bytes(tk, "\033[27;5u", 7); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY after Ctrl-Escape"); is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type after Ctrl-Escape"); is_int(key.code.sym, TERMKEY_SYM_ESCAPE, "key.code.sym after Ctrl-Escape"); is_int(key.modifiers, TERMKEY_KEYMOD_CTRL, "key.modifiers after Ctrl-Escape"); termkey_push_bytes(tk, "\0", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY after Ctrl-Space"); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type after Ctrl-Space"); is_int(key.code.codepoint, ' ', "key.code.codepoint after Ctrl-Space"); is_int(key.modifiers, TERMKEY_KEYMOD_CTRL, "key.modifiers after Ctrl-Space"); termkey_destroy(tk); return exit_status(); } libtermkey-0.22/t/taplib.c0000644000175000017500000000176213442455536013627 0ustar leoleo#include "taplib.h" #include #include #include static int nexttest = 1; static int _exit_status = 0; void plan_tests(int n) { printf("1..%d\n", n); } void pass(char *name) { printf("ok %d - %s\n", nexttest++, name); } void fail(char *name) { printf("not ok %d - %s\n", nexttest++, name); _exit_status = 1; } void ok(int cmp, char *name) { if(cmp) pass(name); else fail(name); } void diag(char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "# "); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); } void is_int(int got, int expect, char *name) { if(got == expect) ok(1, name); else { ok(0, name); diag("got %d expected %d", got, expect); } } void is_str(const char *got, const char *expect, char *name) { if(strcmp(got, expect) == 0) ok(1, name); else { ok(0, name); diag("got '%s' expected '%s'", got, expect); } } int exit_status(void) { return _exit_status; } libtermkey-0.22/t/06buffer.c0000644000175000017500000000172013442455536013765 0ustar leoleo#include #include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { TermKey *tk; TermKeyKey key; plan_tests(9); tk = termkey_new_abstract("vt100", 0); is_int(termkey_get_buffer_remaining(tk), 256, "buffer free initially 256"); is_int(termkey_get_buffer_size(tk), 256, "buffer size initially 256"); is_int(termkey_push_bytes(tk, "h", 1), 1, "push_bytes returns 1"); is_int(termkey_get_buffer_remaining(tk), 255, "buffer free 255 after push_bytes"); is_int(termkey_get_buffer_size(tk), 256, "buffer size 256 after push_bytes"); ok(!!termkey_set_buffer_size(tk, 512), "buffer set size OK"); is_int(termkey_get_buffer_remaining(tk), 511, "buffer free 511 after push_bytes"); is_int(termkey_get_buffer_size(tk), 512, "buffer size 512 after push_bytes"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "buffered key still useable after resize"); termkey_destroy(tk); return exit_status(); } libtermkey-0.22/t/30mouse.c0000644000175000017500000001443413442455536013647 0ustar leoleo#include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { TermKey *tk; TermKeyKey key; TermKeyMouseEvent ev; int button, line, col; char buffer[32]; size_t len; plan_tests(60); tk = termkey_new_abstract("vt100", 0); termkey_push_bytes(tk, "\x1b[M !!", 6); key.type = -1; is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for mouse press"); is_int(key.type, TERMKEY_TYPE_MOUSE, "key.type for mouse press"); ev = -1; button = -1; line = -1; col = -1; is_int(termkey_interpret_mouse(tk, &key, &ev, &button, &line, &col), TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); is_int(ev, TERMKEY_MOUSE_PRESS, "mouse event for press"); is_int(button, 1, "mouse button for press"); is_int(line, 1, "mouse line for press"); is_int(col, 1, "mouse column for press"); is_int(key.modifiers, 0, "modifiers for press"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, 0); is_int(len, 13, "string length for press"); is_str(buffer, "MousePress(1)", "string buffer for press"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_MOUSE_POS); is_int(len, 21, "string length for press"); is_str(buffer, "MousePress(1) @ (1,1)", "string buffer for press"); termkey_push_bytes(tk, "\x1b[M@\"!", 6); key.type = -1; ev = -1; button = -1; line = -1; col = -1; termkey_getkey(tk, &key); is_int(termkey_interpret_mouse(tk, &key, &ev, &button, &line, &col), TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); is_int(ev, TERMKEY_MOUSE_DRAG, "mouse event for drag"); is_int(button, 1, "mouse button for drag"); is_int(line, 1, "mouse line for drag"); is_int(col, 2, "mouse column for drag"); is_int(key.modifiers, 0, "modifiers for press"); termkey_push_bytes(tk, "\x1b[M##!", 6); key.type = -1; ev = -1; button = -1; line = -1; col = -1; termkey_getkey(tk, &key); is_int(termkey_interpret_mouse(tk, &key, &ev, &button, &line, &col), TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); is_int(ev, TERMKEY_MOUSE_RELEASE, "mouse event for release"); is_int(line, 1, "mouse line for release"); is_int(col, 3, "mouse column for release"); is_int(key.modifiers, 0, "modifiers for press"); termkey_push_bytes(tk, "\x1b[M0++", 6); key.type = -1; ev = -1; button = -1; line = -1; col = -1; termkey_getkey(tk, &key); is_int(termkey_interpret_mouse(tk, &key, &ev, &button, &line, &col), TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); is_int(ev, TERMKEY_MOUSE_PRESS, "mouse event for Ctrl-press"); is_int(button, 1, "mouse button for Ctrl-press"); is_int(line, 11, "mouse line for Ctrl-press"); is_int(col, 11, "mouse column for Ctrl-press"); is_int(key.modifiers, TERMKEY_KEYMOD_CTRL, "modifiers for Ctrl-press"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, 0); is_int(len, 15, "string length for Ctrl-press"); is_str(buffer, "C-MousePress(1)", "string buffer for Ctrl-press"); // rxvt protocol termkey_push_bytes(tk, "\x1b[0;20;20M", 10); key.type = -1; is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for mouse press rxvt protocol"); is_int(key.type, TERMKEY_TYPE_MOUSE, "key.type for mouse press rxvt protocol"); is_int(termkey_interpret_mouse(tk, &key, &ev, &button, &line, &col), TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); is_int(ev, TERMKEY_MOUSE_PRESS, "mouse event for press rxvt protocol"); is_int(button, 1, "mouse button for press rxvt protocol"); is_int(line, 20, "mouse line for press rxvt protocol"); is_int(col, 20, "mouse column for press rxvt protocol"); is_int(key.modifiers, 0, "modifiers for press rxvt protocol"); termkey_push_bytes(tk, "\x1b[3;20;20M", 10); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for mouse release rxvt protocol"); is_int(key.type, TERMKEY_TYPE_MOUSE, "key.type for mouse release rxvt protocol"); ev = -1; button = -1; line = -1; col = -1; is_int(termkey_interpret_mouse(tk, &key, &ev, &button, &line, &col), TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); is_int(ev, TERMKEY_MOUSE_RELEASE, "mouse event for release rxvt protocol"); is_int(line, 20, "mouse line for release rxvt protocol"); is_int(col, 20, "mouse column for release rxvt protocol"); is_int(key.modifiers, 0, "modifiers for release rxvt protocol"); // SGR protocol termkey_push_bytes(tk, "\x1b[<0;30;30M", 11); key.type = -1; is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for mouse press SGR encoding"); is_int(key.type, TERMKEY_TYPE_MOUSE, "key.type for mouse press SGR encoding"); ev = -1; button = -1; line = -1; col = -1; is_int(termkey_interpret_mouse(tk, &key, &ev, &button, &line, &col), TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); is_int(ev, TERMKEY_MOUSE_PRESS, "mouse event for press SGR"); is_int(button, 1, "mouse button for press SGR"); is_int(line, 30, "mouse line for press SGR"); is_int(col, 30, "mouse column for press SGR"); is_int(key.modifiers, 0, "modifiers for press SGR"); termkey_push_bytes(tk, "\x1b[<0;30;30m", 11); key.type = -1; is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for mouse release SGR encoding"); is_int(key.type, TERMKEY_TYPE_MOUSE, "key.type for mouse release SGR encoding"); ev = -1; button = -1; line = -1; col = -1; is_int(termkey_interpret_mouse(tk, &key, &ev, &button, &line, &col), TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); is_int(ev, TERMKEY_MOUSE_RELEASE, "mouse event for release SGR"); termkey_push_bytes(tk, "\x1b[<0;500;300M", 13); key.type = -1; ev = -1; button = -1; line = -1; col = -1; termkey_getkey(tk, &key); termkey_interpret_mouse(tk, &key, &ev, &button, &line, &col); is_int(line, 300, "mouse line for press SGR wide"); is_int(col, 500, "mouse column for press SGR wide"); termkey_destroy(tk); return exit_status(); } libtermkey-0.22/t/11strfkey.c0000644000175000017500000001233613442455536014204 0ustar leoleo#include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { TermKey *tk; TermKeyKey key; char buffer[16]; size_t len; plan_tests(44); tk = termkey_new_abstract("vt100", 0); key.type = TERMKEY_TYPE_UNICODE; key.code.codepoint = 'A'; key.modifiers = 0; key.utf8[0] = 0; len = termkey_strfkey(tk, buffer, sizeof buffer, &key, 0); is_int(len, 1, "length for unicode/A/0"); is_str(buffer, "A", "buffer for unicode/A/0"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_WRAPBRACKET); is_int(len, 1, "length for unicode/A/0 wrapbracket"); is_str(buffer, "A", "buffer for unicode/A/0 wrapbracket"); key.type = TERMKEY_TYPE_UNICODE; key.code.codepoint = 'b'; key.modifiers = TERMKEY_KEYMOD_CTRL; key.utf8[0] = 0; len = termkey_strfkey(tk, buffer, sizeof buffer, &key, 0); is_int(len, 3, "length for unicode/b/CTRL"); is_str(buffer, "C-b", "buffer for unicode/b/CTRL"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_LONGMOD); is_int(len, 6, "length for unicode/b/CTRL longmod"); is_str(buffer, "Ctrl-b", "buffer for unicode/b/CTRL longmod"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_LONGMOD|TERMKEY_FORMAT_SPACEMOD); is_int(len, 6, "length for unicode/b/CTRL longmod|spacemod"); is_str(buffer, "Ctrl b", "buffer for unicode/b/CTRL longmod|spacemod"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_LONGMOD|TERMKEY_FORMAT_LOWERMOD); is_int(len, 6, "length for unicode/b/CTRL longmod|lowermod"); is_str(buffer, "ctrl-b", "buffer for unicode/b/CTRL longmod|lowermod"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_LONGMOD|TERMKEY_FORMAT_SPACEMOD|TERMKEY_FORMAT_LOWERMOD); is_int(len, 6, "length for unicode/b/CTRL longmod|spacemod|lowermode"); is_str(buffer, "ctrl b", "buffer for unicode/b/CTRL longmod|spacemod|lowermode"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_CARETCTRL); is_int(len, 2, "length for unicode/b/CTRL caretctrl"); is_str(buffer, "^B", "buffer for unicode/b/CTRL caretctrl"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_WRAPBRACKET); is_int(len, 5, "length for unicode/b/CTRL wrapbracket"); is_str(buffer, "", "buffer for unicode/b/CTRL wrapbracket"); key.type = TERMKEY_TYPE_UNICODE; key.code.codepoint = 'c'; key.modifiers = TERMKEY_KEYMOD_ALT; key.utf8[0] = 0; len = termkey_strfkey(tk, buffer, sizeof buffer, &key, 0); is_int(len, 3, "length for unicode/c/ALT"); is_str(buffer, "A-c", "buffer for unicode/c/ALT"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_LONGMOD); is_int(len, 5, "length for unicode/c/ALT longmod"); is_str(buffer, "Alt-c", "buffer for unicode/c/ALT longmod"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_ALTISMETA); is_int(len, 3, "length for unicode/c/ALT altismeta"); is_str(buffer, "M-c", "buffer for unicode/c/ALT altismeta"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_LONGMOD|TERMKEY_FORMAT_ALTISMETA); is_int(len, 6, "length for unicode/c/ALT longmod|altismeta"); is_str(buffer, "Meta-c", "buffer for unicode/c/ALT longmod|altismeta"); key.type = TERMKEY_TYPE_KEYSYM; key.code.sym = TERMKEY_SYM_UP; key.modifiers = 0; len = termkey_strfkey(tk, buffer, sizeof buffer, &key, 0); is_int(len, 2, "length for sym/Up/0"); is_str(buffer, "Up", "buffer for sym/Up/0"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_WRAPBRACKET); is_int(len, 4, "length for sym/Up/0 wrapbracket"); is_str(buffer, "", "buffer for sym/Up/0 wrapbracket"); key.type = TERMKEY_TYPE_KEYSYM; key.code.sym = TERMKEY_SYM_PAGEUP; key.modifiers = 0; len = termkey_strfkey(tk, buffer, sizeof buffer, &key, 0); is_int(len, 6, "length for sym/PageUp/0"); is_str(buffer, "PageUp", "buffer for sym/PageUp/0"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_LOWERSPACE); is_int(len, 7, "length for sym/PageUp/0 lowerspace"); is_str(buffer, "page up", "buffer for sym/PageUp/0 lowerspace"); /* If size of buffer is too small, strfkey should return something consistent */ len = termkey_strfkey(tk, buffer, 4, &key, 0); is_int(len, 6, "length for sym/PageUp/0"); is_str(buffer, "Pag", "buffer of len 4 for sym/PageUp/0"); len = termkey_strfkey(tk, buffer, 4, &key, TERMKEY_FORMAT_LOWERSPACE); is_int(len, 7, "length for sym/PageUp/0 lowerspace"); is_str(buffer, "pag", "buffer of len 4 for sym/PageUp/0 lowerspace"); key.type = TERMKEY_TYPE_FUNCTION; key.code.number = 5; key.modifiers = 0; len = termkey_strfkey(tk, buffer, sizeof buffer, &key, 0); is_int(len, 2, "length for func/5/0"); is_str(buffer, "F5", "buffer for func/5/0"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_WRAPBRACKET); is_int(len, 4, "length for func/5/0 wrapbracket"); is_str(buffer, "", "buffer for func/5/0 wrapbracket"); len = termkey_strfkey(tk, buffer, sizeof buffer, &key, TERMKEY_FORMAT_LOWERSPACE); is_int(len, 2, "length for func/5/0 lowerspace"); is_str(buffer, "f5", "buffer for func/5/0 lowerspace"); termkey_destroy(tk); return exit_status(); } libtermkey-0.22/t/31position.c0000644000175000017500000000176513442455536014367 0ustar leoleo#include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { TermKey *tk; TermKeyKey key; int line, col; plan_tests(8); tk = termkey_new_abstract("vt100", 0); termkey_push_bytes(tk, "\x1b[?15;7R", 8); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for position report"); is_int(key.type, TERMKEY_TYPE_POSITION, "key.type for position report"); is_int(termkey_interpret_position(tk, &key, &line, &col), TERMKEY_RES_KEY, "interpret_position yields RES_KEY"); is_int(line, 15, "line for position report"); is_int(col, 7, "column for position report"); /* A plain CSI R is likely to be though. * This is tricky :/ */ termkey_push_bytes(tk, "\x1b[R", 3); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for "); is_int(key.type, TERMKEY_TYPE_FUNCTION, "key.type for "); is_int(key.code.number, 3, "key.code.number for "); termkey_destroy(tk); return exit_status(); } libtermkey-0.22/t/13cmpkey.c0000644000175000017500000000357113442455536014010 0ustar leoleo#include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { TermKey *tk; TermKeyKey key1, key2; plan_tests(12); tk = termkey_new_abstract("vt100", 0); key1.type = TERMKEY_TYPE_UNICODE; key1.code.codepoint = 'A'; key1.modifiers = 0; is_int(termkey_keycmp(tk, &key1, &key1), 0, "cmpkey same structure"); key2.type = TERMKEY_TYPE_UNICODE; key2.code.codepoint = 'A'; key2.modifiers = 0; is_int(termkey_keycmp(tk, &key1, &key2), 0, "cmpkey identical structure"); key2.modifiers = TERMKEY_KEYMOD_CTRL; ok(termkey_keycmp(tk, &key1, &key2) < 0, "cmpkey orders CTRL after nomod"); ok(termkey_keycmp(tk, &key2, &key1) > 0, "cmpkey orders nomod before CTRL"); key2.code.codepoint = 'B'; key2.modifiers = 0; ok(termkey_keycmp(tk, &key1, &key2) < 0, "cmpkey orders 'B' after 'A'"); ok(termkey_keycmp(tk, &key2, &key1) > 0, "cmpkey orders 'A' before 'B'"); key1.modifiers = TERMKEY_KEYMOD_CTRL; ok(termkey_keycmp(tk, &key1, &key2) < 0, "cmpkey orders nomod 'B' after CTRL 'A'"); ok(termkey_keycmp(tk, &key2, &key1) > 0, "cmpkey orders CTRL 'A' before nomod 'B'"); key2.type = TERMKEY_TYPE_KEYSYM; key2.code.sym = TERMKEY_SYM_UP; ok(termkey_keycmp(tk, &key1, &key2) < 0, "cmpkey orders KEYSYM after UNICODE"); ok(termkey_keycmp(tk, &key2, &key1) > 0, "cmpkey orders UNICODE before KEYSYM"); key1.type = TERMKEY_TYPE_KEYSYM; key1.code.sym = TERMKEY_SYM_SPACE; key1.modifiers = 0; key2.type = TERMKEY_TYPE_UNICODE; key2.code.codepoint = ' '; key2.modifiers = 0; is_int(termkey_keycmp(tk, &key1, &key2), 0, "cmpkey considers KEYSYM/SPACE and UNICODE/SP identical"); termkey_set_canonflags(tk, termkey_get_canonflags(tk) | TERMKEY_CANON_SPACESYMBOL); is_int(termkey_keycmp(tk, &key1, &key2), 0, "cmpkey considers KEYSYM/SPACE and UNICODE/SP identical under SPACESYMBOL"); termkey_destroy(tk); return exit_status(); } libtermkey-0.22/t/05read.c0000644000175000017500000000457513442455536013441 0ustar leoleo#include #include #include #include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { int fd[2]; TermKey *tk; TermKeyKey key; plan_tests(21); /* We'll need a real filehandle we can write/read. * pipe() can make us one */ pipe(fd); /* Sanitise this just in case */ putenv("TERM=vt100"); tk = termkey_new(fd[0], TERMKEY_FLAG_NOTERMIOS); is_int(termkey_get_buffer_remaining(tk), 256, "buffer free initially 256"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_NONE, "getkey yields RES_NONE when empty"); write(fd[1], "h", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_NONE, "getkey yields RES_NONE before advisereadable"); is_int(termkey_advisereadable(tk), TERMKEY_RES_AGAIN, "advisereadable yields RES_AGAIN after h"); is_int(termkey_get_buffer_remaining(tk), 255, "buffer free 255 after advisereadable"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY after h"); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type after h"); is_int(key.code.codepoint, 'h', "key.code.codepoint after h"); is_int(key.modifiers, 0, "key.modifiers after h"); is_str(key.utf8, "h", "key.utf8 after h"); is_int(termkey_get_buffer_remaining(tk), 256, "buffer free 256 after getkey"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_NONE, "getkey yields RES_NONE a second time"); write(fd[1], "\033O", 2); termkey_advisereadable(tk); is_int(termkey_get_buffer_remaining(tk), 254, "buffer free 254 after partial write"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_AGAIN, "getkey yields RES_AGAIN after partial write"); write(fd[1], "C", 1); termkey_advisereadable(tk); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY after Right completion"); is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type after Right"); is_int(key.code.sym, TERMKEY_SYM_RIGHT, "key.code.sym after Right"); is_int(key.modifiers, 0, "key.modifiers after Right"); is_int(termkey_get_buffer_remaining(tk), 256, "buffer free 256 after completion"); termkey_stop(tk); is_int(termkey_getkey(tk, &key), TERMKEY_RES_ERROR, "getkey yields RES_ERROR after termkey_stop()"); is_int(errno, EINVAL, "getkey error is EINVAL"); termkey_destroy(tk); return exit_status(); } libtermkey-0.22/t/12strpkey.c0000644000175000017500000002235413442455536014220 0ustar leoleo#include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { TermKey *tk; TermKeyKey key; TermKeyMouseEvent mouse; int button, line, col; const char *endp; #define CLEAR_KEY do { key.type = -1; key.code.codepoint = -1; key.modifiers = -1; key.utf8[0] = 0; } while(0) #define CLEAR_MOUSE do { CLEAR_KEY; mouse = -1; button = -1, line = -1; col = -1; } while(0) plan_tests(84); tk = termkey_new_abstract("vt100", 0); CLEAR_KEY; endp = termkey_strpkey(tk, "A", &key, 0); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type for unicode/A/0"); is_int(key.code.codepoint, 'A', "key.code.codepoint for unicode/A/0"); is_int(key.modifiers, 0, "key.modifiers for unicode/A/0"); is_str(key.utf8, "A", "key.utf8 for unicode/A/0"); is_str(endp, "", "consumed entire input for unicode/A/0"); CLEAR_KEY; endp = termkey_strpkey(tk, "A and more", &key, 0); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type for unicode/A/0 trailing"); is_int(key.code.codepoint, 'A', "key.code.codepoint for unicode/A/0 trailing"); is_int(key.modifiers, 0, "key.modifiers for unicode/A/0 trailing"); is_str(key.utf8, "A", "key.utf8 for unicode/A/0 trailing"); is_str(endp, " and more", "points at string tail for unicode/A/0 trailing"); CLEAR_KEY; endp = termkey_strpkey(tk, "C-b", &key, 0); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type for unicode/b/CTRL"); is_int(key.code.codepoint, 'b', "key.code.codepoint for unicode/b/CTRL"); is_int(key.modifiers, TERMKEY_KEYMOD_CTRL, "key.modifiers for unicode/b/CTRL"); is_str(key.utf8, "b", "key.utf8 for unicode/b/CTRL"); is_str(endp, "", "consumed entire input for unicode/b/CTRL"); CLEAR_KEY; endp = termkey_strpkey(tk, "Ctrl-b", &key, TERMKEY_FORMAT_LONGMOD); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type for unicode/b/CTRL longmod"); is_int(key.code.codepoint, 'b', "key.code.codepoint for unicode/b/CTRL longmod"); is_int(key.modifiers, TERMKEY_KEYMOD_CTRL, "key.modifiers for unicode/b/CTRL longmod"); is_str(key.utf8, "b", "key.utf8 for unicode/b/CTRL longmod"); is_str(endp, "", "consumed entire input for unicode/b/CTRL longmod"); CLEAR_KEY; endp = termkey_strpkey(tk, "^B", &key, TERMKEY_FORMAT_CARETCTRL); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type for unicode/b/CTRL caretctrl"); is_int(key.code.codepoint, 'b', "key.code.codepoint for unicode/b/CTRL caretctrl"); is_int(key.modifiers, TERMKEY_KEYMOD_CTRL, "key.modifiers for unicode/b/CTRL caretctrl"); is_str(key.utf8, "b", "key.utf8 for unicode/b/CTRL caretctrl"); is_str(endp, "", "consumed entire input for unicode/b/CTRL caretctrl"); CLEAR_KEY; endp = termkey_strpkey(tk, "A-c", &key, 0); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type for unicode/c/ALT"); is_int(key.code.codepoint, 'c', "key.code.codepoint for unicode/c/ALT"); is_int(key.modifiers, TERMKEY_KEYMOD_ALT, "key.modifiers for unicode/c/ALT"); is_str(key.utf8, "c", "key.utf8 for unicode/c/ALT"); is_str(endp, "", "consumed entire input for unicode/c/ALT"); CLEAR_KEY; endp = termkey_strpkey(tk, "Alt-c", &key, TERMKEY_FORMAT_LONGMOD); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type for unicode/c/ALT longmod"); is_int(key.code.codepoint, 'c', "key.code.codepoint for unicode/c/ALT longmod"); is_int(key.modifiers, TERMKEY_KEYMOD_ALT, "key.modifiers for unicode/c/ALT longmod"); is_str(key.utf8, "c", "key.utf8 for unicode/c/ALT longmod"); is_str(endp, "", "consumed entire input for unicode/c/ALT longmod"); CLEAR_KEY; endp = termkey_strpkey(tk, "M-c", &key, TERMKEY_FORMAT_ALTISMETA); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type for unicode/c/ALT altismeta"); is_int(key.code.codepoint, 'c', "key.code.codepoint for unicode/c/ALT altismeta"); is_int(key.modifiers, TERMKEY_KEYMOD_ALT, "key.modifiers for unicode/c/ALT altismeta"); is_str(key.utf8, "c", "key.utf8 for unicode/c/ALT altismeta"); is_str(endp, "", "consumed entire input for unicode/c/ALT altismeta"); CLEAR_KEY; endp = termkey_strpkey(tk, "Meta-c", &key, TERMKEY_FORMAT_ALTISMETA|TERMKEY_FORMAT_LONGMOD); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type for unicode/c/ALT altismeta+longmod"); is_int(key.code.codepoint, 'c', "key.code.codepoint for unicode/c/ALT altismeta+longmod"); is_int(key.modifiers, TERMKEY_KEYMOD_ALT, "key.modifiers for unicode/c/ALT altismeta+longmod"); is_str(key.utf8, "c", "key.utf8 for unicode/c/ALT altismeta+longmod"); is_str(endp, "", "consumed entire input for unicode/c/ALT altismeta+longmod"); CLEAR_KEY; endp = termkey_strpkey(tk, "meta c", &key, TERMKEY_FORMAT_ALTISMETA|TERMKEY_FORMAT_LONGMOD|TERMKEY_FORMAT_SPACEMOD|TERMKEY_FORMAT_LOWERMOD); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type for unicode/c/ALT altismeta+long/space+lowermod"); is_int(key.code.codepoint, 'c', "key.code.codepoint for unicode/c/ALT altismeta+long/space+lowermod"); is_int(key.modifiers, TERMKEY_KEYMOD_ALT, "key.modifiers for unicode/c/ALT altismeta+long/space+lowermod"); is_str(key.utf8, "c", "key.utf8 for unicode/c/ALT altismeta+long/space_lowermod"); is_str(endp, "", "consumed entire input for unicode/c/ALT altismeta+long/space+lowermod"); CLEAR_KEY; endp = termkey_strpkey(tk, "ctrl alt page up", &key, TERMKEY_FORMAT_LONGMOD|TERMKEY_FORMAT_SPACEMOD|TERMKEY_FORMAT_LOWERMOD|TERMKEY_FORMAT_LOWERSPACE); is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type for sym/PageUp/CTRL+ALT long/space/lowermod+lowerspace"); is_int(key.code.sym, TERMKEY_SYM_PAGEUP, "key.code.codepoint for sym/PageUp/CTRL+ALT long/space/lowermod+lowerspace"); is_int(key.modifiers, TERMKEY_KEYMOD_ALT | TERMKEY_KEYMOD_CTRL, "key.modifiers for sym/PageUp/CTRL+ALT long/space/lowermod+lowerspace"); is_str(endp, "", "consumed entire input for sym/PageUp/CTRL+ALT long/space/lowermod+lowerspace"); CLEAR_KEY; endp = termkey_strpkey(tk, "Up", &key, 0); is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type for sym/Up/0"); is_int(key.code.sym, TERMKEY_SYM_UP, "key.code.codepoint for sym/Up/0"); is_int(key.modifiers, 0, "key.modifiers for sym/Up/0"); is_str(endp, "", "consumed entire input for sym/Up/0"); CLEAR_KEY; endp = termkey_strpkey(tk, "F5", &key, 0); is_int(key.type, TERMKEY_TYPE_FUNCTION, "key.type for func/5/0"); is_int(key.code.number, 5, "key.code.number for func/5/0"); is_int(key.modifiers, 0, "key.modifiers for func/5/0"); is_str(endp, "", "consumed entire input for func/5/0"); CLEAR_MOUSE; endp = termkey_strpkey(tk, "MousePress(1)", &key, 0); termkey_interpret_mouse(tk, &key, &mouse, &button, &line, &col); is_int(key.type, TERMKEY_TYPE_MOUSE, "key.type for mouse"); is_int(mouse, TERMKEY_MOUSE_PRESS, "mouse press event"); is_int(button, 1, "mouse button 1"); is_str(endp, "", "consumed entire input for MousePress(1)"); CLEAR_MOUSE; endp = termkey_strpkey(tk, "MousePress(2) @ (3, 5)", &key, TERMKEY_FORMAT_MOUSE_POS); termkey_interpret_mouse(tk, &key, &mouse, &button, &line, &col); is_int(key.type, TERMKEY_TYPE_MOUSE, "key.type for mouse"); is_int(mouse, TERMKEY_MOUSE_PRESS, "mouse press event"); is_int(button, 2, "mouse button 2"); is_int(col, 3, "mouse column 3"); is_int(line, 5, "mouse line 5"); is_str(endp, "", "consumed entire input for MousePress(2) @ (3, 5)"); CLEAR_MOUSE; endp = termkey_strpkey(tk, "MouseRelease(1)", &key, 0); termkey_interpret_mouse(tk, &key, &mouse, &button, &line, &col); is_int(key.type, TERMKEY_TYPE_MOUSE, "key.type for mouse"); is_int(mouse, TERMKEY_MOUSE_RELEASE, "mouse release event"); is_int(button, 0, "mouse button 1 lost"); // TODO: can we recover it? is_str(endp, "", "consumed entire input for MouseRelease(1)"); CLEAR_MOUSE; endp = termkey_strpkey(tk, "MouseDrag(1)", &key, 0); termkey_interpret_mouse(tk, &key, &mouse, &button, &line, &col); is_int(key.type, TERMKEY_TYPE_MOUSE, "key.type for mouse"); is_int(mouse, TERMKEY_MOUSE_DRAG, "mouse drap event"); is_int(button, 1, "mouse button 1"); is_str(endp, "", "consumed entire input for MouseDrag(1)"); CLEAR_MOUSE; endp = termkey_strpkey(tk, "MouseUnknown(1)", &key, 0); termkey_interpret_mouse(tk, &key, &mouse, &button, &line, &col); is_int(key.type, TERMKEY_TYPE_MOUSE, "key.type for mouse"); is_int(mouse, TERMKEY_MOUSE_UNKNOWN, "mouse unknown event"); is_int(button, 0, "mouse button 1 lost"); is_str(endp, "", "consumed entire input for MouseUnknown(1)"); termkey_destroy(tk); return exit_status(); } libtermkey-0.22/t/40ti-override.c0000644000175000017500000000177413442455536014754 0ustar leoleo#include "../termkey.h" #include "taplib.h" #include #include #define streq(a,b) (!strcmp(a,b)) static const char *backspace_is_X(const char *name, const char *val, void *_) { if(streq(name, "key_backspace")) return "X"; return val; } int main(int argc, char *argv[]) { TermKey *tk; TermKeyKey key; plan_tests(3); /* There was never a VT750. We've just made this string up. * This test ensures that the hooked function can invent TI strings for new * terminal types that don't exist in the TI database yet. */ tk = termkey_new_abstract("vt750", TERMKEY_FLAG_NOSTART); termkey_hook_terminfo_getstr(tk, &backspace_is_X, NULL); termkey_start(tk); termkey_push_bytes(tk, "X", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY after X"); is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type after X"); is_int(key.code.sym, TERMKEY_SYM_BACKSPACE, "key.code.sym after X"); termkey_destroy(tk); return exit_status(); } libtermkey-0.22/t/39dcs.c0000644000175000017500000000463613442455536013304 0ustar leoleo#include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { TermKey *tk; TermKeyKey key; const char *str; plan_tests(23); tk = termkey_new_abstract("xterm", 0); // 7bit DCS termkey_push_bytes(tk, "\x1bP1$r1 q\x1b\\", 10); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for DCS"); is_int(key.type, TERMKEY_TYPE_DCS, "key.type for DCS"); is_int(key.modifiers, 0, "key.modifiers for DCS"); is_int(termkey_interpret_string(tk, &key, &str), TERMKEY_RES_KEY, "termkey_interpret_string() gives string"); is_str(str, "1$r1 q", "termkey_interpret_string() yields correct string"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_NONE, "getkey again yields RES_NONE"); // 8bit DCS termkey_push_bytes(tk, "\x90""1$r2 q""\x9c", 8); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for DCS"); is_int(key.type, TERMKEY_TYPE_DCS, "key.type for DCS"); is_int(key.modifiers, 0, "key.modifiers for DCS"); is_int(termkey_interpret_string(tk, &key, &str), TERMKEY_RES_KEY, "termkey_interpret_string() gives string"); is_str(str, "1$r2 q", "termkey_interpret_string() yields correct string"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_NONE, "getkey again yields RES_NONE"); // 7bit OSC termkey_push_bytes(tk, "\x1b]15;abc\x1b\\", 10); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for OSC"); is_int(key.type, TERMKEY_TYPE_OSC, "key.type for OSC"); is_int(key.modifiers, 0, "key.modifiers for OSC"); is_int(termkey_interpret_string(tk, &key, &str), TERMKEY_RES_KEY, "termkey_interpret_string() gives string"); is_str(str, "15;abc", "termkey_interpret_string() yields correct string"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_NONE, "getkey again yields RES_NONE"); // False alarm termkey_push_bytes(tk, "\x1bP", 2); is_int(termkey_getkey(tk, &key), TERMKEY_RES_AGAIN, "getkey yields RES_AGAIN for false alarm"); is_int(termkey_getkey_force(tk, &key), TERMKEY_RES_KEY, "getkey_force yields RES_KEY for false alarm"); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type for false alarm"); is_int(key.code.codepoint, 'P', "key.code.codepoint for false alarm"); is_int(key.modifiers, TERMKEY_KEYMOD_ALT, "key.modifiers for false alarm"); termkey_destroy(tk); return exit_status(); } libtermkey-0.22/t/38csi.c0000644000175000017500000000310413442455536013275 0ustar leoleo#include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { TermKey *tk; TermKeyKey key; long args[16]; size_t nargs = 16; unsigned long command; plan_tests(15); tk = termkey_new_abstract("vt100", 0); termkey_push_bytes(tk, "\x1b[5;25v", 7); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for CSI v"); is_int(key.type, TERMKEY_TYPE_UNKNOWN_CSI, "key.type for unknown CSI"); is_int(termkey_interpret_csi(tk, &key, args, &nargs, &command), TERMKEY_RES_KEY, "interpret_csi yields RES_KEY"); is_int(nargs, 2, "nargs for unknown CSI"); is_int(args[0], 5, "args[0] for unknown CSI"); is_int(args[1], 25, "args[1] for unknown CSI"); is_int(command, 'v', "command for unknown CSI"); termkey_push_bytes(tk, "\x1b[?w", 4); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for CSI ? w"); is_int(key.type, TERMKEY_TYPE_UNKNOWN_CSI, "key.type for unknown CSI"); is_int(termkey_interpret_csi(tk, &key, args, &nargs, &command), TERMKEY_RES_KEY, "interpret_csi yields RES_KEY"); is_int(command, '?'<<8 | 'w', "command for unknown CSI"); termkey_push_bytes(tk, "\x1b[?$x", 5); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for CSI ? $x"); is_int(key.type, TERMKEY_TYPE_UNKNOWN_CSI, "key.type for unknown CSI"); is_int(termkey_interpret_csi(tk, &key, args, &nargs, &command), TERMKEY_RES_KEY, "interpret_csi yields RES_KEY"); is_int(command, '$'<<16 | '?'<<8 | 'x', "command for unknown CSI"); termkey_destroy(tk); return exit_status(); } libtermkey-0.22/t/taplib.h0000644000175000017500000000034713442455536013632 0ustar leoleovoid plan_tests(int n); void ok(int cmp, char *name); void pass(char *name); void fail(char *name); void is_int(int got, int expect, char *name); void is_str(const char *got, const char *expect, char *name); int exit_status(void); libtermkey-0.22/t/32modereport.c0000644000175000017500000000241713442455536014677 0ustar leoleo#include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { TermKey *tk; TermKeyKey key; int initial, mode, value; plan_tests(12); tk = termkey_new_abstract("vt100", 0); termkey_push_bytes(tk, "\x1b[?1;2$y", 8); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for mode report"); is_int(key.type, TERMKEY_TYPE_MODEREPORT, "key.type for mode report"); is_int(termkey_interpret_modereport(tk, &key, &initial, &mode, &value), TERMKEY_RES_KEY, "interpret_modereoprt yields RES_KEY"); is_int(initial, '?', "initial indicator from mode report"); is_int(mode, 1, "mode number from mode report"); is_int(value, 2, "mode value from mode report"); termkey_push_bytes(tk, "\x1b[4;1$y", 7); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for mode report"); is_int(key.type, TERMKEY_TYPE_MODEREPORT, "key.type for mode report"); is_int(termkey_interpret_modereport(tk, &key, &initial, &mode, &value), TERMKEY_RES_KEY, "interpret_modereoprt yields RES_KEY"); is_int(initial, 0, "initial indicator from mode report"); is_int(mode, 4, "mode number from mode report"); is_int(value, 1, "mode value from mode report"); termkey_destroy(tk); return exit_status(); } libtermkey-0.22/t/10keyname.c0000644000175000017500000000226213442455536014142 0ustar leoleo#include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { TermKey *tk; TermKeySym sym; const char *end; plan_tests(10); tk = termkey_new_abstract("vt100", 0); sym = termkey_keyname2sym(tk, "Space"); is_int(sym, TERMKEY_SYM_SPACE, "keyname2sym Space"); sym = termkey_keyname2sym(tk, "SomeUnknownKey"); is_int(sym, TERMKEY_SYM_UNKNOWN, "keyname2sym SomeUnknownKey"); end = termkey_lookup_keyname(tk, "Up", &sym); ok(!!end, "termkey_get_keyname Up returns non-NULL"); is_str(end, "", "termkey_get_keyname Up return points at endofstring"); is_int(sym, TERMKEY_SYM_UP, "termkey_get_keyname Up yields Up symbol"); end = termkey_lookup_keyname(tk, "DownMore", &sym); ok(!!end, "termkey_get_keyname DownMore returns non-NULL"); is_str(end, "More", "termkey_get_keyname DownMore return points at More"); is_int(sym, TERMKEY_SYM_DOWN, "termkey_get_keyname DownMore yields Down symbol"); end = termkey_lookup_keyname(tk, "SomeUnknownKey", &sym); ok(!end, "termkey_get_keyname SomeUnknownKey returns NULL"); is_str(termkey_get_keyname(tk, TERMKEY_SYM_SPACE), "Space", "get_keyname SPACE"); termkey_destroy(tk); return exit_status(); } libtermkey-0.22/t/04flags.c0000644000175000017500000000206713442455536013613 0ustar leoleo#include #include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { TermKey *tk; TermKeyKey key; plan_tests(8); tk = termkey_new_abstract("vt100", 0); termkey_push_bytes(tk, " ", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY after space"); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type after space"); is_int(key.code.codepoint, ' ', "key.code.codepoint after space"); is_int(key.modifiers, 0, "key.modifiers after space"); termkey_set_flags(tk, TERMKEY_FLAG_SPACESYMBOL); termkey_push_bytes(tk, " ", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY after space"); is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type after space with FLAG_SPACESYMBOL"); is_int(key.code.sym, TERMKEY_SYM_SPACE, "key.code.sym after space with FLAG_SPACESYMBOL"); is_int(key.modifiers, 0, "key.modifiers after space with FLAG_SPACESYMBOL"); termkey_destroy(tk); return exit_status(); } libtermkey-0.22/t/01base.c0000644000175000017500000000120213442455536013414 0ustar leoleo#include #include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { TermKey *tk; plan_tests(6); tk = termkey_new_abstract("vt100", 0); ok(!!tk, "termkey_new_abstract"); is_int(termkey_get_buffer_size(tk), 256, "termkey_get_buffer_size"); ok(termkey_is_started(tk), "termkey_is_started true after construction"); termkey_stop(tk); ok(!termkey_is_started(tk), "termkey_is_started false after termkey_stop()"); termkey_start(tk); ok(termkey_is_started(tk), "termkey_is_started true after termkey_start()"); termkey_destroy(tk); ok(1, "termkey_free"); return exit_status(); } libtermkey-0.22/t/03utf8.c0000644000175000017500000001464113442455536013405 0ustar leoleo#include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { TermKey *tk; TermKeyKey key; plan_tests(57); tk = termkey_new_abstract("vt100", TERMKEY_FLAG_UTF8); termkey_push_bytes(tk, "a", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY low ASCII"); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type low ASCII"); is_int(key.code.codepoint, 'a', "key.code.codepoint low ASCII"); /* 2-byte UTF-8 range is U+0080 to U+07FF (0xDF 0xBF) */ /* However, we'd best avoid the C1 range, so we'll start at U+00A0 (0xC2 0xA0) */ termkey_push_bytes(tk, "\xC2\xA0", 2); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 2 low"); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type UTF-8 2 low"); is_int(key.code.codepoint, 0x00A0, "key.code.codepoint UTF-8 2 low"); termkey_push_bytes(tk, "\xDF\xBF", 2); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 2 high"); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type UTF-8 2 high"); is_int(key.code.codepoint, 0x07FF, "key.code.codepoint UTF-8 2 high"); /* 3-byte UTF-8 range is U+0800 (0xE0 0xA0 0x80) to U+FFFD (0xEF 0xBF 0xBD) */ termkey_push_bytes(tk, "\xE0\xA0\x80", 3); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 3 low"); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type UTF-8 3 low"); is_int(key.code.codepoint, 0x0800, "key.code.codepoint UTF-8 3 low"); termkey_push_bytes(tk, "\xEF\xBF\xBD", 3); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 3 high"); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type UTF-8 3 high"); is_int(key.code.codepoint, 0xFFFD, "key.code.codepoint UTF-8 3 high"); /* 4-byte UTF-8 range is U+10000 (0xF0 0x90 0x80 0x80) to U+10FFFF (0xF4 0x8F 0xBF 0xBF) */ termkey_push_bytes(tk, "\xF0\x90\x80\x80", 4); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 4 low"); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type UTF-8 4 low"); is_int(key.code.codepoint, 0x10000, "key.code.codepoint UTF-8 4 low"); termkey_push_bytes(tk, "\xF4\x8F\xBF\xBF", 4); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 4 high"); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type UTF-8 4 high"); is_int(key.code.codepoint, 0x10FFFF, "key.code.codepoint UTF-8 4 high"); /* Invalid continuations */ termkey_push_bytes(tk, "\xC2!", 2); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 2 invalid cont"); is_int(key.code.codepoint, 0xFFFD, "key.code.codepoint UTF-8 2 invalid cont"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 2 invalid after"); is_int(key.code.codepoint, '!', "key.code.codepoint UTF-8 2 invalid after"); termkey_push_bytes(tk, "\xE0!", 2); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 3 invalid cont"); is_int(key.code.codepoint, 0xFFFD, "key.code.codepoint UTF-8 3 invalid cont"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 3 invalid after"); is_int(key.code.codepoint, '!', "key.code.codepoint UTF-8 3 invalid after"); termkey_push_bytes(tk, "\xE0\xA0!", 3); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 3 invalid cont 2"); is_int(key.code.codepoint, 0xFFFD, "key.code.codepoint UTF-8 3 invalid cont 2"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 3 invalid after"); is_int(key.code.codepoint, '!', "key.code.codepoint UTF-8 3 invalid after"); termkey_push_bytes(tk, "\xF0!", 2); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 4 invalid cont"); is_int(key.code.codepoint, 0xFFFD, "key.code.codepoint UTF-8 4 invalid cont"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 4 invalid after"); is_int(key.code.codepoint, '!', "key.code.codepoint UTF-8 4 invalid after"); termkey_push_bytes(tk, "\xF0\x90!", 3); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 4 invalid cont 2"); is_int(key.code.codepoint, 0xFFFD, "key.code.codepoint UTF-8 4 invalid cont 2"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 4 invalid after"); is_int(key.code.codepoint, '!', "key.code.codepoint UTF-8 4 invalid after"); termkey_push_bytes(tk, "\xF0\x90\x80!", 4); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 4 invalid cont 3"); is_int(key.code.codepoint, 0xFFFD, "key.code.codepoint UTF-8 4 invalid cont 3"); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 4 invalid after"); is_int(key.code.codepoint, '!', "key.code.codepoint UTF-8 4 invalid after"); /* Partials */ termkey_push_bytes(tk, "\xC2", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_AGAIN, "getkey yields RES_AGAIN UTF-8 2 partial"); termkey_push_bytes(tk, "\xA0", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 2 partial"); is_int(key.code.codepoint, 0x00A0, "key.code.codepoint UTF-8 2 partial"); termkey_push_bytes(tk, "\xE0", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_AGAIN, "getkey yields RES_AGAIN UTF-8 3 partial"); termkey_push_bytes(tk, "\xA0", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_AGAIN, "getkey yields RES_AGAIN UTF-8 3 partial"); termkey_push_bytes(tk, "\x80", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 3 partial"); is_int(key.code.codepoint, 0x0800, "key.code.codepoint UTF-8 3 partial"); termkey_push_bytes(tk, "\xF0", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_AGAIN, "getkey yields RES_AGAIN UTF-8 4 partial"); termkey_push_bytes(tk, "\x90", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_AGAIN, "getkey yields RES_AGAIN UTF-8 4 partial"); termkey_push_bytes(tk, "\x80", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_AGAIN, "getkey yields RES_AGAIN UTF-8 4 partial"); termkey_push_bytes(tk, "\x80", 1); is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY UTF-8 4 partial"); is_int(key.code.codepoint, 0x10000, "key.code.codepoint UTF-8 4 partial"); termkey_destroy(tk); return exit_status(); } libtermkey-0.22/t/20canon.c0000644000175000017500000000552213442455536013612 0ustar leoleo#include "../termkey.h" #include "taplib.h" int main(int argc, char *argv[]) { TermKey *tk; TermKeyKey key; const char *endp; #define CLEAR_KEY do { key.type = -1; key.code.codepoint = -1; key.modifiers = -1; key.utf8[0] = 0; } while(0) plan_tests(26); tk = termkey_new_abstract("vt100", 0); CLEAR_KEY; endp = termkey_strpkey(tk, " ", &key, 0); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type for SP/unicode"); is_int(key.code.codepoint, ' ', "key.code.codepoint for SP/unicode"); is_int(key.modifiers, 0, "key.modifiers for SP/unicode"); is_str(key.utf8, " ", "key.utf8 for SP/unicode"); is_str(endp, "", "consumed entire input for SP/unicode"); CLEAR_KEY; endp = termkey_strpkey(tk, "Space", &key, 0); is_int(key.type, TERMKEY_TYPE_UNICODE, "key.type for Space/unicode"); is_int(key.code.codepoint, ' ', "key.code.codepoint for Space/unicode"); is_int(key.modifiers, 0, "key.modifiers for Space/unicode"); is_str(key.utf8, " ", "key.utf8 for Space/unicode"); is_str(endp, "", "consumed entire input for Space/unicode"); termkey_set_canonflags(tk, termkey_get_canonflags(tk) | TERMKEY_CANON_SPACESYMBOL); CLEAR_KEY; endp = termkey_strpkey(tk, " ", &key, 0); is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type for SP/symbol"); is_int(key.code.sym, TERMKEY_SYM_SPACE, "key.code.codepoint for SP/symbol"); is_int(key.modifiers, 0, "key.modifiers for SP/symbol"); is_str(endp, "", "consumed entire input for SP/symbol"); CLEAR_KEY; endp = termkey_strpkey(tk, "Space", &key, 0); is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type for Space/symbol"); is_int(key.code.sym, TERMKEY_SYM_SPACE, "key.code.codepoint for Space/symbol"); is_int(key.modifiers, 0, "key.modifiers for Space/symbol"); is_str(endp, "", "consumed entire input for Space/symbol"); CLEAR_KEY; endp = termkey_strpkey(tk, "DEL", &key, 0); is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type for Del/unconverted"); is_int(key.code.sym, TERMKEY_SYM_DEL, "key.code.codepoint for Del/unconverted"); is_int(key.modifiers, 0, "key.modifiers for Del/unconverted"); is_str(endp, "", "consumed entire input for Del/unconverted"); termkey_set_canonflags(tk, termkey_get_canonflags(tk) | TERMKEY_CANON_DELBS); CLEAR_KEY; endp = termkey_strpkey(tk, "DEL", &key, 0); is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type for Del/as-backspace"); is_int(key.code.sym, TERMKEY_SYM_BACKSPACE, "key.code.codepoint for Del/as-backspace"); is_int(key.modifiers, 0, "key.modifiers for Del/as-backspace"); is_str(endp, "", "consumed entire input for Del/as-backspace"); termkey_destroy(tk); return exit_status(); }