aboutsummaryrefslogtreecommitdiff
path: root/termkey.c
diff options
context:
space:
mode:
Diffstat (limited to 'termkey.c')
-rw-r--r--termkey.c1556
1 files changed, 0 insertions, 1556 deletions
diff --git a/termkey.c b/termkey.c
deleted file mode 100644
index ad80657..0000000
--- a/termkey.c
+++ /dev/null
@@ -1,1556 +0,0 @@
-#include "termkey.h"
-#include "termkey-internal.h"
-
-#include <ctype.h>
-#include <errno.h>
-#include <poll.h>
-#include <unistd.h>
-#include <string.h>
-#include <strings.h>
-#include <langinfo.h>
-
-#include <stdio.h>
-
-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);
- else if (minor > TERMKEY_VERSION_MINOR)
- fprintf (stderr, "libtermkey minor version mismatch;"
- " %d (wants) > %d (library)\n",
- minor, TERMKEY_VERSION_MINOR);
- else
- return;
- exit (1);
-}
-
-static termkey_driver_t *drivers[] =
-{
- &termkey_driver_ti,
- &termkey_driver_csi,
- NULL,
-};
-
-// Forwards for the "protected" methods
-static void emit_codepoint (termkey_t *tk, uint32_t codepoint, termkey_key_t *key);
-static termkey_result_t peekkey_simple (termkey_t *tk,
- termkey_key_t *key, int force, size_t *nbytes);
-static termkey_result_t peekkey_mouse (termkey_t *tk,
- termkey_key_t *key, size_t *nbytes);
-
-static termkey_sym_t register_c0 (termkey_t *tk, termkey_sym_t sym,
- unsigned char ctrl, const char *name);
-static termkey_sym_t register_c0_full (termkey_t *tk, termkey_sym_t sym,
- int modifier_set, int modifier_mask, unsigned char ctrl, const char *name);
-
-static struct
-{
- termkey_sym_t 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 },
-};
-
-#define CHARAT(i) (tk->buffer[tk->buffstart + (i)])
-
-#ifdef DEBUG
-/* Some internal deubgging functions */
-
-static void
-print_buffer (termkey_t *tk)
-{
- size_t 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_t *tk, termkey_key_t *key)
-{
- switch (key->type)
- {
- case TERMKEY_TYPE_KEY:
- fprintf (stderr, "Unicode codepoint=U+%04lx multibyte='%s'",
- (long) key->code.codepoint, key->multibyte);
- 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:
- {
- termkey_mouse_event_t 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_UNKNOWN_CSI:
- fprintf (stderr, "unknown CSI\n");
- }
-
- 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 (termkey_result_t 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' */
- for (; *src; src++)
- {
- if (isupper (*src) && prev_lower)
- l++;
- prev_lower = islower (*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_t *
-termkey_alloc (void)
-{
- termkey_t *tk = malloc (sizeof *tk);
- 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;
-
- tk->restore_termios_valid = false;
-
- tk->waittime = 50; /* msec */
-
- tk->is_closed = false;
- tk->is_started = false;
-
- 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_t *tk, const char *term, const char *encoding)
-{
- if (!encoding)
- encoding = nl_langinfo (CODESET);
-
- static const uint16_t endianity = 0x0102;
- const char *utf32 = (*(uint8_t *) &endianity == 0x01)
- ? "UTF-32BE" : "UTF-32LE";
-
- if ((tk->to_utf32_conv = iconv_open (utf32, encoding)) == (iconv_t) -1)
- return 0;
- if ((tk->from_utf32_conv = iconv_open (encoding, utf32)) == (iconv_t) -1)
- goto abort_free_to_utf32;
-
- tk->buffer = malloc (tk->buffsize);
- if (!tk->buffer)
- goto abort_free_from_utf32;
-
- 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_BACKSPACE, 0x08, NULL);
- register_c0 (tk, TERMKEY_SYM_TAB, 0x09, NULL);
- register_c0 (tk, TERMKEY_SYM_ENTER, 0x0d, NULL);
- register_c0 (tk, TERMKEY_SYM_ESCAPE, 0x1b, NULL);
-
- termkey_driver_node_t **tail = &tk->drivers;
- 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
-
- termkey_driver_node_t *thisdrv = malloc (sizeof *thisdrv);
- if (!thisdrv)
- goto abort_free_drivers;
-
- thisdrv->driver = drivers[i];
- thisdrv->info = info;
- thisdrv->next = NULL;
-
- *tail = thisdrv;
- tail = &thisdrv->next;
-
-#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 (termkey_driver_node_t *p = tk->drivers; p; )
- {
- (*p->driver->free_driver) (p->info);
- termkey_driver_node_t *next = p->next;
- free (p);
- p = next;
- }
-
-abort_free_keynames:
- free (tk->keynames);
-abort_free_buffer:
- free (tk->buffer);
-abort_free_from_utf32:
- iconv_close (tk->from_utf32_conv);
-abort_free_to_utf32:
- iconv_close (tk->to_utf32_conv);
- return 0;
-}
-
-termkey_t *
-termkey_new (int fd, const char *encoding, int flags)
-{
- termkey_t *tk = termkey_alloc ();
- if (!tk)
- return NULL;
-
- tk->fd = fd;
- termkey_set_flags (tk, flags);
-
- const char *term = getenv ("TERM");
- if (termkey_init (tk, term, encoding)
- && termkey_start (tk))
- return tk;
-
- free (tk);
- return NULL;
-}
-
-termkey_t *
-termkey_new_abstract (const char *term, const char *encoding, int flags)
-{
- termkey_t *tk = termkey_alloc ();
- if (!tk)
- return NULL;
-
- tk->fd = -1;
- termkey_set_flags (tk, flags);
-
- if (!termkey_init (tk, term, encoding))
- {
- free (tk);
- return NULL;
- }
-
- termkey_start (tk);
- return tk;
-}
-
-void
-termkey_free (termkey_t *tk)
-{
- free (tk->buffer); tk->buffer = NULL;
- free (tk->keynames); tk->keynames = NULL;
-
- iconv_close (tk->to_utf32_conv);
- tk->to_utf32_conv = (iconv_t) -1;
- iconv_close (tk->from_utf32_conv);
- tk->from_utf32_conv = (iconv_t) -1;
-
- termkey_driver_node_t *p, *next;
- for (p = tk->drivers; p; p = next)
- {
- (*p->driver->free_driver) (p->info);
- next = p->next;
- free (p);
- }
- free (tk);
-}
-
-void
-termkey_destroy (termkey_t *tk)
-{
- if (tk->is_started)
- termkey_stop (tk);
-
- termkey_free (tk);
-}
-
-int
-termkey_start (termkey_t *tk)
-{
- if (tk->is_started)
- return 1;
-
- 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 = true;
-
- termios.c_iflag &= ~(IXON|INLCR|ICRNL);
- termios.c_lflag &= ~(ICANON|ECHO);
- 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 ^\ == VQUIT and ^D == VSUSP but leave ^C as SIGINT */
- termios.c_cc[VQUIT] = _POSIX_VDISABLE;
- termios.c_cc[VSUSP] = _POSIX_VDISABLE;
- /* Some OSes have ^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);
- }
- }
-
- termkey_driver_node_t *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_t *tk)
-{
- if (!tk->is_started)
- return 1;
-
- struct termkey_driver_node *p;
- for (p = tk->drivers; p; p = p->next)
- if (p->driver->stop_driver)
- (*p->driver->stop_driver) (tk, p->info);
-
- if (tk->restore_termios_valid)
- tcsetattr (tk->fd, TCSANOW, &tk->restore_termios);
-
- tk->is_started = false;
- return 1;
-}
-
-int
-termkey_is_started (termkey_t *tk)
-{
- return tk->is_started;
-}
-
-int
-termkey_get_fd (termkey_t *tk)
-{
- return tk->fd;
-}
-
-int
-termkey_get_flags (termkey_t *tk)
-{
- return tk->flags;
-}
-
-void
-termkey_set_flags (termkey_t *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_t *tk, int msec)
-{
- tk->waittime = msec;
-}
-
-int
-termkey_get_waittime (termkey_t *tk)
-{
- return tk->waittime;
-}
-
-int
-termkey_get_canonflags (termkey_t *tk)
-{
- return tk->canonflags;
-}
-
-void
-termkey_set_canonflags (termkey_t *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_t *tk)
-{
- return tk->buffsize;
-}
-
-int
-termkey_set_buffer_size (termkey_t *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_t *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_t *tk, size_t count)
-{
- if (count >= tk->buffcount)
- {
- tk->buffstart = 0;
- tk->buffcount = 0;
- return;
- }
-
- tk->buffstart += count;
- tk->buffcount -= count;
-}
-
-#define MULTIBYTE_INVALID '?'
-
-static void
-fill_multibyte (termkey_t *tk, termkey_key_t *key)
-{
- size_t codepoint_len = sizeof key->code.codepoint;
- char *codepoint_ptr = (char *) &key->code.codepoint;
- size_t multibyte_len = sizeof key->multibyte;
- char *multibyte_ptr = (char *) key->multibyte;
-
- size_t result = iconv (tk->from_utf32_conv,
- &codepoint_ptr, &codepoint_len, &multibyte_ptr, &multibyte_len);
- size_t output = sizeof key->multibyte - multibyte_len;
-
- // Something broke
- if (result == (size_t) -1 || output == 0)
- {
- key->multibyte[0] = MULTIBYTE_INVALID;
- key->multibyte[1] = 0;
- return;
- }
-
- // Append a null character, as it wasn't port of the input
- key->multibyte[output] = 0;
-}
-
-static termkey_result_t
-parse_multibyte (termkey_t *tk, const unsigned char *bytes, size_t len,
- uint32_t *cp, size_t *nbytep)
-{
- size_t multibyte_len = len;
- char *multibyte_ptr = (char *) bytes;
- size_t codepoint_len = sizeof *cp;
- char *codepoint_ptr = (char *) cp;
-
- // Fingers crossed...
- errno = 0;
- iconv (tk->to_utf32_conv,
- &multibyte_ptr, &multibyte_len, &codepoint_ptr, &codepoint_len);
-
- // Only one Unicode character could have been processed at maximum,
- // so let's just set the number of processed bytes to the difference
- *nbytep = len - multibyte_len;
-
- // Nothing has been converted, let's examine what happened
- if (codepoint_ptr == (char *) cp)
- {
- if (errno == 0)
- // The input was probably a shift sequence
- return TERMKEY_RES_AGAIN;
- if (errno == EINVAL)
- // Incomplete character or shift sequence
- return TERMKEY_RES_AGAIN;
- if (errno == EILSEQ)
- {
- // Invalid multibyte sequence in the input, let's try going
- // byte after byte in hope we skip it completely
- *cp = MULTIBYTE_INVALID;
- *nbytep = 1;
- return TERMKEY_RES_KEY;
- }
-
- // We can't really get E2BIG so what the fuck is going on here
- abort ();
- }
- return TERMKEY_RES_KEY;
-}
-
-static void
-emit_codepoint (termkey_t *tk, uint32_t codepoint, termkey_key_t *key)
-{
- if (codepoint < 0x20)
- {
- // C0 range
- key->code.codepoint = 0;
- key->modifiers = 0;
-
- if (!(tk->flags & TERMKEY_FLAG_NOINTERPRET)
- && tk->c0[codepoint].sym != TERMKEY_SYM_UNKNOWN)
- {
- key->code.sym = tk->c0[codepoint].sym;
- key->modifiers |= tk->c0[codepoint].modifier_set;
- }
-
- if (!key->code.sym)
- {
- key->type = TERMKEY_TYPE_KEY;
- /* 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 lowecase 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
- {
- key->type = TERMKEY_TYPE_KEY;
- key->code.codepoint = codepoint;
- key->modifiers = 0;
- }
-
- termkey_canonicalise (tk, key);
-
- if (key->type == TERMKEY_TYPE_KEY)
- fill_multibyte (tk, key);
-}
-
-void
-termkey_canonicalise (termkey_t *tk, termkey_key_t *key)
-{
- int flags = tk->canonflags;
-
- if (flags & TERMKEY_CANON_SPACESYMBOL)
- {
- if (key->type == TERMKEY_TYPE_KEY && 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_KEY;
- key->code.codepoint = 0x20;
- fill_multibyte (tk, 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 termkey_result_t
-peekkey (termkey_t *tk, termkey_key_t *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;
- }
-
- termkey_result_t ret;
- termkey_driver_node_t *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;
- 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 termkey_result_t
-peekkey_simple (termkey_t *tk, termkey_key_t *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 <Esc> 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
- termkey_result_t 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 (!(tk->flags & TERMKEY_FLAG_RAW))
- {
- uint32_t codepoint;
- termkey_result_t res = parse_multibyte
- (tk, tk->buffer + tk->buffstart, tk->buffcount, &codepoint, nbytep);
-
- if (res == TERMKEY_RES_AGAIN && force)
- {
- /* There weren't enough bytes for a complete character but
- * caller demands an answer. About the best thing we can do here
- * is eat as many bytes as we have, and emit a MULTIBYTE_INVALID.
- * If the remaining bytes arrive later, they'll be invalid too.
- */
- codepoint = MULTIBYTE_INVALID;
- *nbytep = tk->buffcount;
- res = TERMKEY_RES_KEY;
- }
-
- key->type = TERMKEY_TYPE_KEY;
- key->modifiers = 0;
- (*tk->method.emit_codepoint) (tk, codepoint, key);
- return res;
- }
- else
- {
- // Non multibyte case - just report the raw byte
- key->type = TERMKEY_TYPE_KEY;
- key->code.codepoint = b0;
- key->modifiers = 0;
-
- key->multibyte[0] = b0;
- key->multibyte[1] = 0;
-
- *nbytep = 1;
- return TERMKEY_RES_KEY;
- }
-}
-
-static termkey_result_t
-peekkey_mouse (termkey_t *tk, termkey_key_t *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;
-}
-
-termkey_result_t
-termkey_getkey (termkey_t *tk, termkey_key_t *key)
-{
- size_t nbytes = 0;
- termkey_result_t 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;
-}
-
-termkey_result_t
-termkey_getkey_force (termkey_t *tk, termkey_key_t *key)
-{
- size_t nbytes = 0;
- termkey_result_t ret = peekkey (tk, key, 1, &nbytes);
-
- if (ret == TERMKEY_RES_KEY)
- eat_bytes (tk, nbytes);
-
- return ret;
-}
-
-termkey_result_t
-termkey_waitkey (termkey_t *tk, termkey_key_t *key)
-{
- if (tk->fd == -1)
- {
- errno = EBADF;
- return TERMKEY_RES_ERROR;
- }
-
- while (1)
- {
- termkey_result_t 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);
- }
- }
- }
-
- /* UNREACHABLE */
-}
-
-termkey_result_t
-termkey_advisereadable (termkey_t *tk)
-{
- 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;
- }
-
- ssize_t len;
-retry:
- len = read (tk->fd, tk->buffer + tk->buffcount,
- tk->buffsize - tk->buffcount);
-
- if (len == -1)
- {
- if (errno == EAGAIN)
- return TERMKEY_RES_NONE;
- if (errno == EINTR && !(tk->flags & TERMKEY_FLAG_EINTR))
- goto retry;
- return TERMKEY_RES_ERROR;
- }
- if (len < 1)
- {
- tk->is_closed = true;
- return TERMKEY_RES_NONE;
- }
- tk->buffcount += len;
- return TERMKEY_RES_AGAIN;
-}
-
-size_t
-termkey_push_bytes (termkey_t *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;
-}
-
-termkey_sym_t
-termkey_register_keyname (termkey_t *tk, termkey_sym_t 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_t *tk, termkey_sym_t 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_t *tk,
- const char *str, termkey_sym_t *sym, termkey_format_t 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))
- return (char *) str + len;
- }
- return NULL;
-}
-
-const char *
-termkey_lookup_keyname (termkey_t *tk, const char *str, termkey_sym_t *sym)
-{
- return termkey_lookup_keyname_format (tk, str, sym, 0);
-}
-
-termkey_sym_t
-termkey_keyname2sym (termkey_t *tk, const char *keyname)
-{
- termkey_sym_t sym;
- const char *endp = termkey_lookup_keyname (tk, keyname, &sym);
- if (!endp || endp[0])
- return TERMKEY_SYM_UNKNOWN;
- return sym;
-}
-
-static termkey_sym_t
-register_c0 (termkey_t *tk,
- termkey_sym_t sym, unsigned char ctrl, const char *name)
-{
- return register_c0_full (tk, sym, 0, 0, ctrl, name);
-}
-
-static termkey_sym_t
-register_c0_full (termkey_t *tk, termkey_sym_t 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;
-}
-
-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_t *tk, char *buffer, size_t len,
- termkey_key_t *key, termkey_format_t 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_KEY || key->modifiers != 0);
-
- char sep = (format & TERMKEY_FORMAT_SPACEMOD) ? ' ' : '-';
-
- if (format & TERMKEY_FORMAT_CARETCTRL &&
- key->type == TERMKEY_TYPE_KEY &&
- key->modifiers == TERMKEY_KEYMOD_CTRL)
- {
- uint32_t codepoint = key->code.codepoint;
-
- // Handle some of the special casesfirst
- 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_KEY:
- if (!key->multibyte[0]) // In case of user-supplied key structures
- fill_multibyte (tk, key);
- l = snprintf (buffer + pos, len - pos, "%s", key->multibyte);
- 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:
- {
- termkey_mouse_event_t ev;
- int button;
- int line, col;
- termkey_interpret_mouse (tk, key, &ev, &button, &line, &col);
-
- static const char *evnames[] =
- { "Unknown", "Press", "Drag", "Release" };
- 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);
- 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_t *tk,
- const char *str, termkey_key_t *key, termkey_format_t 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_KEY
- || 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_multibyte (tk, 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))
- key->modifiers |= TERMKEY_KEYMOD_ALT;
- else if (n == strlen (mods->ctrl) && !strncmp (mods->ctrl, str, n))
- key->modifiers |= TERMKEY_KEYMOD_CTRL;
- else if (n == strlen (mods->shift) && !strncmp (mods->shift, str, n))
- key->modifiers |= TERMKEY_KEYMOD_SHIFT;
- else
- break;
-
- str = sep_at + 1;
- }
-
- size_t nbytes;
- ssize_t snbytes;
- const char *endstr;
-
- 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;
- }
- // Multibyte must be last
- else if (parse_multibyte (tk, (unsigned const char *) str, strlen (str),
- &key->code.codepoint, &nbytes) == TERMKEY_RES_KEY)
- {
- key->type = TERMKEY_TYPE_KEY;
- fill_multibyte (tk, key);
- str += nbytes;
- }
- // TODO: Consider mouse events?
- else
- return NULL;
-
- termkey_canonicalise (tk, key);
- return (char *) str;
-}
-
-int
-termkey_keycmp (termkey_t *tk,
- const termkey_key_t *key1p, const termkey_key_t *key2p)
-{
- /* Copy the key structs since we'll be modifying them */
- termkey_key_t 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_KEY:
- 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;
- }
- 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;
-}
-