From e98d9c0fd1a148adc844046d568d40de135fb366 Mon Sep 17 00:00:00 2001 From: Přemysl Janouch Date: Tue, 14 Oct 2014 00:08:15 +0200 Subject: Rename to termo --- CMakeLists.txt | 42 +- README | 18 +- config.cmake.in | 2 +- demo-async.c | 34 +- demo-glib.c | 32 +- demo.c | 54 +- driver-csi.c | 408 ++++++------- driver-ti.c | 164 ++--- termkey2-config.h.in | 8 - termkey2-internal.h | 112 ---- termkey2.c | 1656 -------------------------------------------------- termkey2.h | 277 --------- termo-config.h.in | 8 + termo-internal.h | 112 ++++ termo.c | 1656 ++++++++++++++++++++++++++++++++++++++++++++++++++ termo.h | 277 +++++++++ 16 files changed, 2430 insertions(+), 2430 deletions(-) delete mode 100644 termkey2-config.h.in delete mode 100644 termkey2-internal.h delete mode 100644 termkey2.c delete mode 100644 termkey2.h create mode 100644 termo-config.h.in create mode 100644 termo-internal.h create mode 100644 termo.c create mode 100644 termo.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 81dbb2c..10665f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -project (termkey2 C) +project (termo C) cmake_minimum_required (VERSION 2.8.5) if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC) @@ -18,9 +18,9 @@ set (project_VERSION ${project_VERSION}.${project_VERSION_PATCH}) set (project_API_VERSION ${project_VERSION_MAJOR}) # Names -set (project_LIB_NAME "termkey2-${project_API_VERSION}") -set (project_INCLUDE_NAME "termkey2-${project_API_VERSION}") -set (project_CMAKE_NAME "TermKey2") +set (project_LIB_NAME "termo-${project_API_VERSION}") +set (project_INCLUDE_NAME "termo-${project_API_VERSION}") +set (project_CMAKE_NAME "Termo") # Dependecies find_package (Curses) @@ -29,19 +29,19 @@ pkg_check_modules (glib glib-2.0 gio-2.0) pkg_check_modules (unibilium unibilium>=0.1.0) # Header files with configuration -configure_file (${PROJECT_SOURCE_DIR}/termkey2-config.h.in - ${PROJECT_BINARY_DIR}/termkey2-config.h) +configure_file (${PROJECT_SOURCE_DIR}/termo-config.h.in + ${PROJECT_BINARY_DIR}/termo-config.h) include_directories (${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR}) # Project source files set (lib_sources - termkey2.c + termo.c driver-csi.c driver-ti.c) set (lib_headers - termkey2.h - termkey2-internal.h - ${PROJECT_BINARY_DIR}/termkey2-config.h) + termo.h + termo-internal.h + ${PROJECT_BINARY_DIR}/termo-config.h) # Project libraries if (unibilium_FOUND) @@ -56,33 +56,33 @@ else (CURSES_FOUND) endif (unibilium_FOUND) # Create the library targets -add_library (termkey2 SHARED ${lib_sources} ${lib_headers}) -target_link_libraries (termkey2 ${lib_libraries}) -set_target_properties (termkey2 PROPERTIES +add_library (termo SHARED ${lib_sources} ${lib_headers}) +target_link_libraries (termo ${lib_libraries}) +set_target_properties (termo PROPERTIES OUTPUT_NAME ${project_LIB_NAME} VERSION ${project_VERSION} SOVERSION ${project_API_VERSION}) -add_library (termkey2-static STATIC ${lib_sources} ${lib_headers}) -target_link_libraries (termkey2-static ${lib_libraries}) -set_target_properties (termkey2-static PROPERTIES +add_library (termo-static STATIC ${lib_sources} ${lib_headers}) +target_link_libraries (termo-static ${lib_libraries}) +set_target_properties (termo-static PROPERTIES OUTPUT_NAME ${project_LIB_NAME} VERSION ${project_VERSION} SOVERSION ${project_API_VERSION}) # Demos add_executable (demo-async EXCLUDE_FROM_ALL demo-async.c) -target_link_libraries (demo-async termkey2-static ${lib_libraries}) +target_link_libraries (demo-async termo-static ${lib_libraries}) add_executable (demo EXCLUDE_FROM_ALL demo.c) -target_link_libraries (demo termkey2-static ${lib_libraries}) +target_link_libraries (demo termo-static ${lib_libraries}) set (demos demo demo-async) if (glib_FOUND) include_directories (${glib_INCLUDE_DIRS}) add_executable (demo-glib EXCLUDE_FROM_ALL demo-glib.c) target_link_libraries (demo - termkey2-static ${lib_libraries} ${glib_LIBRARIES}) + termo-static ${lib_libraries} ${glib_LIBRARIES}) list (APPEND demos demo-glib) endif (glib_FOUND) @@ -90,9 +90,9 @@ add_custom_target (demos DEPENDS ${demos}) # The files to be installed include (GNUInstallDirs) -install (TARGETS termkey2 termkey2-static DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install (TARGETS termo termo-static DESTINATION ${CMAKE_INSTALL_LIBDIR}) install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR}) -install (FILES termkey2.h ${PROJECT_BINARY_DIR}/termkey2-config.h +install (FILES termo.h ${PROJECT_BINARY_DIR}/termo-config.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${project_INCLUDE_NAME}) # Configuration for other CMake projects diff --git a/README b/README index 3a1c2ee..bb776a5 100644 --- a/README +++ b/README @@ -1,9 +1,9 @@ -termkey2 -======== +termo +===== -`termkey2' is a library providing an alternative to ncurses' handling of -terminal input. ncurses does a really terrible job at that, mainly wrt. mouse -support which seems to be utterly broken. If you can drag things in a terminal +`termo' is a library providing an alternative to ncurses' handling of terminal +input. ncurses does a really terrible job at that, mainly wrt. mouse support +which seems to be utterly broken. If you can drag things in a terminal application, such as in VIM, I can assure you it's not using ncurses for that. Since terminal I/O is really complicated and full of special cases, this project @@ -17,7 +17,7 @@ Building and Installing Build dependencies: GCC/Clang, pkg-config, cmake >= 2.8.5 Optional dependencies: Unibilium (alternative for curses), GLib (for the demos) - $ git clone https://github.com/pjanouch/termkey2.git + $ git clone https://github.com/pjanouch/termo.git $ mkdir build $ cd build $ cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local @@ -27,7 +27,7 @@ To install the library, you can do either the usual: Or you can try telling CMake to make a package for you. For Debian it is: $ cpack -G DEB - # dpkg -i termkey2-*.deb + # dpkg -i termo-*.deb To see the library in action, you can try running the demos, which are statically linked against the library, and hence they can be run as they are: @@ -38,7 +38,7 @@ What's Different From the Original termkey? ------------------------------------------- The main change is throwing away any UTF-8 dependent code, making the library capable of handling all unibyte and multibyte encodings supported by iconv on -your system. The characters are still presented as Unicode at the end, however, +your system. The characters are still presented as Unicode in the end, however, as the other sensible option is wchar_t and that doesn't really work well, see http://gnu.org/software/libunistring/manual/libunistring.html#The-wchar_005ft-mess @@ -52,7 +52,7 @@ Oh, and I've deleted the manpages. It needs more Doxygen. :) TBD License ------- -`termkey2' is based on the `termkey' library originally written by Paul Evans +`termo' is based on the `termkey' library originally written by Paul Evans , with additional changes made by Přemysl Janouch . diff --git a/config.cmake.in b/config.cmake.in index 9a19a6f..aa0209c 100644 --- a/config.cmake.in +++ b/config.cmake.in @@ -4,5 +4,5 @@ # @project_CMAKE_NAME@_LIBRARIES set (@project_CMAKE_NAME@_INCLUDE_DIRS @CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_INCLUDEDIR@/@project_INCLUDE_NAME@) -set (@project_CMAKE_NAME@_LIBRARIES termkey2) +set (@project_CMAKE_NAME@_LIBRARIES termo) diff --git a/demo-async.c b/demo-async.c index 88d68a5..c59b8f6 100644 --- a/demo-async.c +++ b/demo-async.c @@ -6,13 +6,13 @@ #include #include -#include "termkey2.h" +#include "termo.h" static void -on_key (termkey_t *tk, termkey_key_t *key) +on_key (termo_t *tk, termo_key_t *key) { char buffer[50]; - termkey_strfkey (tk, buffer, sizeof buffer, key, TERMKEY_FORMAT_VIM); + termo_strfkey (tk, buffer, sizeof buffer, key, TERMO_FORMAT_VIM); printf ("%s\n", buffer); } @@ -22,23 +22,23 @@ main (int argc, char *argv[]) (void) argc; (void) argv; - TERMKEY_CHECK_VERSION; + TERMO_CHECK_VERSION; setlocale (LC_CTYPE, ""); - termkey_t *tk = termkey_new (STDIN_FILENO, NULL, 0); + termo_t *tk = termo_new (STDIN_FILENO, NULL, 0); if (!tk) { - fprintf (stderr, "Cannot allocate termkey instance\n"); + fprintf (stderr, "Cannot allocate termo instance\n"); exit (1); } struct pollfd fd; - fd.fd = STDIN_FILENO; /* the file descriptor we passed to termkey_new() */ + fd.fd = STDIN_FILENO; /* the file descriptor we passed to termo_new() */ fd.events = POLLIN; - termkey_result_t ret; - termkey_key_t key; + termo_result_t ret; + termo_key_t key; int running = 1; int nextwait = -1; @@ -47,27 +47,27 @@ main (int argc, char *argv[]) { if (poll (&fd, 1, nextwait) == 0) // Timed out - if (termkey_getkey_force (tk, &key) == TERMKEY_RES_KEY) + if (termo_getkey_force (tk, &key) == TERMO_RES_KEY) on_key (tk, &key); if (fd.revents & (POLLIN | POLLHUP | POLLERR)) - termkey_advisereadable (tk); + termo_advisereadable (tk); - while ((ret = termkey_getkey (tk, &key)) == TERMKEY_RES_KEY) + while ((ret = termo_getkey (tk, &key)) == TERMO_RES_KEY) { on_key (tk, &key); - if (key.type == TERMKEY_TYPE_KEY - && (key.modifiers & TERMKEY_KEYMOD_CTRL) + if (key.type == TERMO_TYPE_KEY + && (key.modifiers & TERMO_KEYMOD_CTRL) && (key.code.codepoint == 'C' || key.code.codepoint == 'c')) running = 0; } - if (ret == TERMKEY_RES_AGAIN) - nextwait = termkey_get_waittime (tk); + if (ret == TERMO_RES_AGAIN) + nextwait = termo_get_waittime (tk); else nextwait = -1; } - termkey_destroy (tk); + termo_destroy (tk); } diff --git a/demo-glib.c b/demo-glib.c index dfe5cd4..80f3a7b 100644 --- a/demo-glib.c +++ b/demo-glib.c @@ -3,24 +3,24 @@ #include #include -#include "termkey2.h" +#include "termo.h" -static termkey_t *tk; +static termo_t *tk; static int timeout_id; static void -on_key (termkey_t *tk, termkey_key_t *key) +on_key (termo_t *tk, termo_key_t *key) { char buffer[50]; - termkey_strfkey (tk, buffer, sizeof buffer, key, TERMKEY_FORMAT_VIM); + termo_strfkey (tk, buffer, sizeof buffer, key, TERMO_FORMAT_VIM); printf ("%s\n", buffer); } static gboolean key_timer (gpointer data) { - termkey_key_t key; - if (termkey_getkey_force (tk, &key) == TERMKEY_RES_KEY) + termo_key_t key; + if (termo_getkey_force (tk, &key) == TERMO_RES_KEY) on_key (tk, &key); return FALSE; } @@ -33,16 +33,16 @@ stdin_io (GIOChannel *source, GIOCondition condition, gpointer data) if (timeout_id) g_source_remove (timeout_id); - termkey_advisereadable (tk); + termo_advisereadable (tk); - termkey_result_t ret; - termkey_key_t key; - while ((ret = termkey_getkey (tk, &key)) == TERMKEY_RES_KEY) + termo_result_t ret; + termo_key_t key; + while ((ret = termo_getkey (tk, &key)) == TERMO_RES_KEY) on_key (tk, &key); - if (ret == TERMKEY_RES_AGAIN) + if (ret == TERMO_RES_AGAIN) timeout_id = g_timeout_add - (termkey_get_waittime (tk), key_timer, NULL); + (termo_get_waittime (tk), key_timer, NULL); } return TRUE; @@ -54,13 +54,13 @@ main (int argc, char *argv[]) (void) argc; (void) argv; - TERMKEY_CHECK_VERSION; + TERMO_CHECK_VERSION; setlocale (LC_CTYPE, ""); - tk = termkey_new (STDIN_FILENO, NULL, 0); + tk = termo_new (STDIN_FILENO, NULL, 0); if (!tk) { - fprintf (stderr, "Cannot allocate termkey instance\n"); + fprintf (stderr, "Cannot allocate termo instance\n"); exit (1); } @@ -68,5 +68,5 @@ main (int argc, char *argv[]) g_io_add_watch (g_io_channel_unix_new (STDIN_FILENO), G_IO_IN, stdin_io, NULL); g_main_loop_run (loop); - termkey_destroy (tk); + termo_destroy (tk); } diff --git a/demo.c b/demo.c index 5bb9fc2..d74892c 100644 --- a/demo.c +++ b/demo.c @@ -6,20 +6,20 @@ #include #include -#include "termkey2.h" +#include "termo.h" int main(int argc, char *argv[]) { - TERMKEY_CHECK_VERSION; + TERMO_CHECK_VERSION; setlocale (LC_CTYPE, ""); int mouse = 0; int mouse_proto = 0; - termkey_format_t format = TERMKEY_FORMAT_VIM; + termo_format_t format = TERMO_FORMAT_VIM; char buffer[50]; - termkey_t *tk; + termo_t *tk; int opt; while ((opt = getopt (argc, argv, "m::p:")) != -1) @@ -43,21 +43,21 @@ main(int argc, char *argv[]) } } - tk = termkey_new (STDIN_FILENO, NULL, - TERMKEY_FLAG_SPACESYMBOL | TERMKEY_FLAG_CTRLC); + tk = termo_new (STDIN_FILENO, NULL, + TERMO_FLAG_SPACESYMBOL | TERMO_FLAG_CTRLC); if (!tk) { - fprintf (stderr, "Cannot allocate termkey instance\n"); + fprintf (stderr, "Cannot allocate termo instance\n"); exit (1); } - if (termkey_get_flags (tk) & TERMKEY_FLAG_RAW) + if (termo_get_flags (tk) & TERMO_FLAG_RAW) printf ("Termkey in RAW mode\n"); else printf ("Termkey in multibyte mode\n"); - termkey_result_t ret; - termkey_key_t key; + termo_result_t ret; + termo_key_t key; if (mouse) { @@ -66,38 +66,38 @@ main(int argc, char *argv[]) printf ("\033[?%dh", mouse_proto); } - while ((ret = termkey_waitkey (tk, &key)) != TERMKEY_RES_EOF) + while ((ret = termo_waitkey (tk, &key)) != TERMO_RES_EOF) { - if (ret == TERMKEY_RES_KEY) + if (ret == TERMO_RES_KEY) { - termkey_strfkey (tk, buffer, sizeof buffer, &key, format); - if (key.type == TERMKEY_TYPE_MOUSE) + termo_strfkey (tk, buffer, sizeof buffer, &key, format); + if (key.type == TERMO_TYPE_MOUSE) { int line, col; - termkey_interpret_mouse (tk, &key, NULL, NULL, &line, &col); + termo_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) + else if (key.type == TERMO_TYPE_POSITION) { int line, col; - termkey_interpret_position (tk, &key, &line, &col); + termo_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) + else if (key.type == TERMO_TYPE_MODEREPORT) { int initial, mode, value; - termkey_interpret_modereport + termo_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) + else if (key.type == TERMO_TYPE_UNKNOWN_CSI) { long args[16]; size_t nargs = 16; unsigned long command; - termkey_interpret_csi (tk, &key, args, &nargs, &command); + termo_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); @@ -105,12 +105,12 @@ main(int argc, char *argv[]) else printf ("Key %s\n", buffer); - if (key.type == TERMKEY_TYPE_KEY - && key.modifiers & TERMKEY_KEYMOD_CTRL + if (key.type == TERMO_TYPE_KEY + && key.modifiers & TERMO_KEYMOD_CTRL && (key.code.codepoint == 'C' || key.code.codepoint == 'c')) break; - if (key.type == TERMKEY_TYPE_KEY + if (key.type == TERMO_TYPE_KEY && key.modifiers == 0 && key.code.codepoint == '?') { @@ -119,11 +119,11 @@ main(int argc, char *argv[]) fflush (stdout); } } - else if (ret == TERMKEY_RES_ERROR) + else if (ret == TERMO_RES_ERROR) { if (errno != EINTR) { - perror ("termkey_waitkey"); + perror ("termo_waitkey"); break; } printf ("Interrupted by signal\n"); @@ -133,5 +133,5 @@ main(int argc, char *argv[]) if (mouse) printf ("\033[?%dlMouse mode deactivated\n", mouse); - termkey_destroy (tk); + termo_destroy (tk); } diff --git a/driver-csi.c b/driver-csi.c index a7fcc52..4a954fc 100644 --- a/driver-csi.c +++ b/driver-csi.c @@ -1,5 +1,5 @@ -#include "termkey2.h" -#include "termkey2-internal.h" +#include "termo.h" +#include "termo-internal.h" #include #include @@ -11,12 +11,12 @@ static char ss3_kpalts[64]; typedef struct { - termkey_t *tk; + termo_t *tk; } -termkey_csi_t; +termo_csi_t; -typedef termkey_result_t (*csi_handler_fn) - (termkey_t *tk, termkey_key_t *key, int cmd, long *arg, int args); +typedef termo_result_t (*csi_handler_fn) + (termo_t *tk, termo_key_t *key, int cmd, long *arg, int args); static csi_handler_fn csi_handlers[64]; /* @@ -25,9 +25,9 @@ static csi_handler_fn csi_handlers[64]; static struct keyinfo csi_ss3s[64]; -static termkey_result_t -handle_csi_ss3_full (termkey_t *tk, - termkey_key_t *key, int cmd, long *arg, int args) +static termo_result_t +handle_csi_ss3_full (termo_t *tk, + termo_key_t *key, int cmd, long *arg, int args) { (void) tk; @@ -41,13 +41,13 @@ handle_csi_ss3_full (termkey_t *tk, 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; + if (key->code.sym == TERMO_SYM_UNKNOWN) + return TERMO_RES_NONE; + return TERMO_RES_KEY; } static void -register_csi_ss3_full (termkey_type_t type, termkey_sym_t sym, +register_csi_ss3_full (termo_type_t type, termo_sym_t sym, int modifier_set, int modifier_mask, unsigned char cmd) { if (cmd < 0x40 || cmd >= 0x80) @@ -62,7 +62,7 @@ register_csi_ss3_full (termkey_type_t type, termkey_sym_t sym, } static void -register_csi_ss3 (termkey_type_t type, termkey_sym_t sym, unsigned char cmd) +register_csi_ss3 (termo_type_t type, termo_sym_t sym, unsigned char cmd) { register_csi_ss3_full (type, sym, 0, 0, cmd); } @@ -72,7 +72,7 @@ register_csi_ss3 (termkey_type_t type, termkey_sym_t sym, unsigned char cmd) */ static void -register_ss3kpalt (termkey_type_t type, termkey_sym_t sym, +register_ss3kpalt (termo_type_t type, termo_sym_t sym, unsigned char cmd, char kpalt) { if (cmd < 0x40 || cmd >= 0x80) @@ -94,8 +94,8 @@ register_ss3kpalt (termkey_type_t type, termkey_sym_t sym, static struct keyinfo csifuncs[35]; #define NCSIFUNCS ((long) (sizeof csifuncs / sizeof csifuncs[0])) -static termkey_result_t -handle_csifunc (termkey_t *tk, termkey_key_t *key, int cmd, long *arg, int args) +static termo_result_t +handle_csifunc (termo_t *tk, termo_key_t *key, int cmd, long *arg, int args) { (void) cmd; @@ -103,7 +103,7 @@ handle_csifunc (termkey_t *tk, termkey_key_t *key, int cmd, long *arg, int args) key->modifiers = arg[1] - 1; else key->modifiers = 0; - key->type = TERMKEY_TYPE_KEYSYM; + key->type = TERMO_TYPE_KEYSYM; if (arg[0] == 27) { @@ -119,21 +119,21 @@ handle_csifunc (termkey_t *tk, termkey_key_t *key, int cmd, long *arg, int args) key->modifiers |= csifuncs[arg[0]].modifier_set; } else - key->code.sym = TERMKEY_SYM_UNKNOWN; + key->code.sym = TERMO_SYM_UNKNOWN; - if (key->code.sym == TERMKEY_SYM_UNKNOWN) + if (key->code.sym == TERMO_SYM_UNKNOWN) { #ifdef DEBUG fprintf (stderr, "CSI: Unknown function key %ld\n", arg[0]); #endif - return TERMKEY_RES_NONE; + return TERMO_RES_NONE; } - return TERMKEY_RES_KEY; + return TERMO_RES_KEY; } static void -register_csifunc (termkey_type_t type, termkey_sym_t sym, int number) +register_csifunc (termo_type_t type, termo_sym_t sym, int number) { if (number >= NCSIFUNCS) return; @@ -150,21 +150,21 @@ register_csifunc (termkey_type_t type, termkey_sym_t sym, int number) * URxvt seems to emit this instead of ~ when holding Ctrl */ -static termkey_result_t -handle_csi_caret (termkey_t *tk, - termkey_key_t *key, int cmd, long *arg, int args) +static termo_result_t +handle_csi_caret (termo_t *tk, + termo_key_t *key, int cmd, long *arg, int args) { switch (cmd) { case '^': { - termkey_result_t res = handle_csifunc (tk, key, cmd, arg, args); - if (res == TERMKEY_RES_KEY) - key->modifiers |= TERMKEY_KEYMOD_CTRL; + termo_result_t res = handle_csifunc (tk, key, cmd, arg, args); + if (res == TERMO_RES_KEY) + key->modifiers |= TERMO_KEYMOD_CTRL; return res; } default: - return TERMKEY_RES_NONE; + return TERMO_RES_NONE; } } @@ -172,8 +172,8 @@ handle_csi_caret (termkey_t *tk, * Handler for CSI u extended Unicode keys */ -static termkey_result_t -handle_csi_u (termkey_t *tk, termkey_key_t *key, int cmd, long *arg, int args) +static termo_result_t +handle_csi_u (termo_t *tk, termo_key_t *key, int cmd, long *arg, int args) { switch (cmd) { @@ -185,13 +185,13 @@ handle_csi_u (termkey_t *tk, termkey_key_t *key, int cmd, long *arg, int args) key->modifiers = 0; int mod = key->modifiers; - key->type = TERMKEY_TYPE_KEYSYM; + key->type = TERMO_TYPE_KEYSYM; (*tk->method.emit_codepoint) (tk, arg[0], key); key->modifiers |= mod; - return TERMKEY_RES_KEY; + return TERMO_RES_KEY; } default: - return TERMKEY_RES_NONE; + return TERMO_RES_NONE; } } @@ -200,8 +200,8 @@ handle_csi_u (termkey_t *tk, termkey_key_t *key, int cmd, long *arg, int args) * Note: This does not handle X10 encoding */ -static termkey_result_t -handle_csi_m (termkey_t *tk, termkey_key_t *key, int cmd, long *arg, int args) +static termo_result_t +handle_csi_m (termo_t *tk, termo_key_t *key, int cmd, long *arg, int args) { (void) tk; @@ -214,57 +214,57 @@ handle_csi_m (termkey_t *tk, termkey_key_t *key, int cmd, long *arg, int args) case 'm': break; default: - return TERMKEY_RES_NONE; + return TERMO_RES_NONE; } if (!initial && args >= 3) { // rxvt protocol - key->type = TERMKEY_TYPE_MOUSE; + key->type = TERMO_TYPE_MOUSE; key->code.mouse.info = arg[0] - 0x20; key->modifiers = (key->code.mouse.info & 0x1c) >> 2; key->code.mouse.info &= ~0x1c; - termkey_key_set_linecol (key, arg[2] - 1, arg[1] - 1); - return TERMKEY_RES_KEY; + termo_key_set_linecol (key, arg[2] - 1, arg[1] - 1); + return TERMO_RES_KEY; } if (initial == '<' && args >= 3) { // SGR protocol - key->type = TERMKEY_TYPE_MOUSE; + key->type = TERMO_TYPE_MOUSE; key->code.mouse.info = arg[0]; key->modifiers = (key->code.mouse.info & 0x1c) >> 2; key->code.mouse.info &= ~0x1c; - termkey_key_set_linecol (key, arg[2] - 1, arg[1] - 1); + termo_key_set_linecol (key, arg[2] - 1, arg[1] - 1); if (cmd == 'm') // release key->code.mouse.info |= 0x8000; - return TERMKEY_RES_KEY; + return TERMO_RES_KEY; } - return TERMKEY_RES_NONE; + return TERMO_RES_NONE; } -termkey_result_t -termkey_interpret_mouse (termkey_t *tk, const termkey_key_t *key, - termkey_mouse_event_t *event, int *button, int *line, int *col) +termo_result_t +termo_interpret_mouse (termo_t *tk, const termo_key_t *key, + termo_mouse_event_t *event, int *button, int *line, int *col) { (void) tk; - if (key->type != TERMKEY_TYPE_MOUSE) - return TERMKEY_RES_NONE; + if (key->type != TERMO_TYPE_MOUSE) + return TERMO_RES_NONE; if (button) *button = 0; - termkey_key_get_linecol (key, line, col); + termo_key_get_linecol (key, line, col); // XXX: WTF is this logic? if (!event) - return TERMKEY_RES_KEY; + return TERMO_RES_KEY; int btn = 0; int code = key->code.mouse.info; @@ -276,30 +276,30 @@ termkey_interpret_mouse (termkey_t *tk, const termkey_key_t *key, case 0: case 1: case 2: - *event = drag ? TERMKEY_MOUSE_DRAG : TERMKEY_MOUSE_PRESS; + *event = drag ? TERMO_MOUSE_DRAG : TERMO_MOUSE_PRESS; btn = code + 1; break; case 3: - *event = TERMKEY_MOUSE_RELEASE; + *event = TERMO_MOUSE_RELEASE; // no button hint break; case 64: case 65: - *event = drag ? TERMKEY_MOUSE_DRAG : TERMKEY_MOUSE_PRESS; + *event = drag ? TERMO_MOUSE_DRAG : TERMO_MOUSE_PRESS; btn = code + 4 - 64; break; default: - *event = TERMKEY_MOUSE_UNKNOWN; + *event = TERMO_MOUSE_UNKNOWN; } if (button) *button = btn; if (key->code.mouse.info & 0x8000) - *event = TERMKEY_MOUSE_RELEASE; - return TERMKEY_RES_KEY; + *event = TERMO_MOUSE_RELEASE; + return TERMO_RES_KEY; } /* @@ -307,43 +307,43 @@ termkey_interpret_mouse (termkey_t *tk, const termkey_key_t *key, * A plain CSI R with no arguments is probably actually */ -static termkey_result_t -handle_csi_R (termkey_t *tk, termkey_key_t *key, int cmd, long *arg, int args) +static termo_result_t +handle_csi_R (termo_t *tk, termo_key_t *key, int cmd, long *arg, int args) { switch (cmd) { case 'R' | '?' << 8: if (args < 2) - return TERMKEY_RES_NONE; + return TERMO_RES_NONE; - key->type = TERMKEY_TYPE_POSITION; - termkey_key_set_linecol (key, arg[1], arg[0]); - return TERMKEY_RES_KEY; + key->type = TERMO_TYPE_POSITION; + termo_key_set_linecol (key, arg[1], arg[0]); + return TERMO_RES_KEY; default: return handle_csi_ss3_full (tk, key, cmd, arg, args); } } -termkey_result_t -termkey_interpret_position (termkey_t *tk, - const termkey_key_t *key, int *line, int *col) +termo_result_t +termo_interpret_position (termo_t *tk, + const termo_key_t *key, int *line, int *col) { (void) tk; - if (key->type != TERMKEY_TYPE_POSITION) - return TERMKEY_RES_NONE; + if (key->type != TERMO_TYPE_POSITION) + return TERMO_RES_NONE; - termkey_key_get_linecol (key, line, col); - return TERMKEY_RES_KEY; + termo_key_get_linecol (key, line, col); + return TERMO_RES_KEY; } /* * Handler for CSI $y mode status reports */ -static termkey_result_t -handle_csi_y (termkey_t *tk, termkey_key_t *key, int cmd, long *arg, int args) +static termo_result_t +handle_csi_y (termo_t *tk, termo_key_t *key, int cmd, long *arg, int args) { (void) tk; @@ -352,27 +352,27 @@ handle_csi_y (termkey_t *tk, termkey_key_t *key, int cmd, long *arg, int args) case 'y' | '$' << 16: case 'y' | '$' << 16 | '?' << 8: if (args < 2) - return TERMKEY_RES_NONE; + return TERMO_RES_NONE; - key->type = TERMKEY_TYPE_MODEREPORT; + key->type = TERMO_TYPE_MODEREPORT; key->code.mode.initial = (cmd >> 8); key->code.mode.mode = arg[0]; key->code.mode.value = arg[1]; - return TERMKEY_RES_KEY; + return TERMO_RES_KEY; default: - return TERMKEY_RES_NONE; + return TERMO_RES_NONE; } } -termkey_result_t -termkey_interpret_modereport (termkey_t *tk, - const termkey_key_t *key, int *initial, int *mode, int *value) +termo_result_t +termo_interpret_modereport (termo_t *tk, + const termo_key_t *key, int *initial, int *mode, int *value) { (void) tk; - if (key->type != TERMKEY_TYPE_MODEREPORT) - return TERMKEY_RES_NONE; + if (key->type != TERMO_TYPE_MODEREPORT) + return TERMO_RES_NONE; if (initial) *initial = key->code.mode.initial; @@ -380,13 +380,13 @@ termkey_interpret_modereport (termkey_t *tk, *mode = key->code.mode.mode; if (value) *value = key->code.mode.value; - return TERMKEY_RES_KEY; + return TERMO_RES_KEY; } #define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) -static termkey_result_t -parse_csi (termkey_t *tk, size_t introlen, size_t *csi_len, +static termo_result_t +parse_csi (termo_t *tk, size_t introlen, size_t *csi_len, long args[], size_t *nargs, unsigned long *commandp) { size_t csi_end = introlen; @@ -398,7 +398,7 @@ parse_csi (termkey_t *tk, size_t introlen, size_t *csi_len, } if (csi_end >= tk->buffcount) - return TERMKEY_RES_AGAIN; + return TERMO_RES_AGAIN; unsigned char cmd = CHARAT (csi_end); *commandp = cmd; @@ -451,17 +451,17 @@ parse_csi (termkey_t *tk, size_t introlen, size_t *csi_len, *nargs = argi; *csi_len = csi_end + 1; - return TERMKEY_RES_KEY; + return TERMO_RES_KEY; } -termkey_result_t -termkey_interpret_csi (termkey_t *tk, const termkey_key_t *key, +termo_result_t +termo_interpret_csi (termo_t *tk, const termo_key_t *key, long args[], size_t *nargs, unsigned long *cmd) { if (tk->hightide == 0) - return TERMKEY_RES_NONE; - if (key->type != TERMKEY_TYPE_UNKNOWN_CSI) - return TERMKEY_RES_NONE; + return TERMO_RES_NONE; + if (key->type != TERMO_TYPE_UNKNOWN_CSI) + return TERMO_RES_NONE; size_t dummy; return parse_csi (tk, 0, &dummy, args, nargs, cmd); @@ -473,76 +473,76 @@ register_keys (void) int i; for (i = 0; i < 64; i++) { - csi_ss3s[i].sym = TERMKEY_SYM_UNKNOWN; - ss3s[i].sym = TERMKEY_SYM_UNKNOWN; + csi_ss3s[i].sym = TERMO_SYM_UNKNOWN; + ss3s[i].sym = TERMO_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); + csifuncs[i].sym = TERMO_SYM_UNKNOWN; + + register_csi_ss3 (TERMO_TYPE_KEYSYM, TERMO_SYM_UP, 'A'); + register_csi_ss3 (TERMO_TYPE_KEYSYM, TERMO_SYM_DOWN, 'B'); + register_csi_ss3 (TERMO_TYPE_KEYSYM, TERMO_SYM_RIGHT, 'C'); + register_csi_ss3 (TERMO_TYPE_KEYSYM, TERMO_SYM_LEFT, 'D'); + register_csi_ss3 (TERMO_TYPE_KEYSYM, TERMO_SYM_BEGIN, 'E'); + register_csi_ss3 (TERMO_TYPE_KEYSYM, TERMO_SYM_END, 'F'); + register_csi_ss3 (TERMO_TYPE_KEYSYM, TERMO_SYM_HOME, 'H'); + register_csi_ss3 (TERMO_TYPE_FUNCTION, 1, 'P'); + register_csi_ss3 (TERMO_TYPE_FUNCTION, 2, 'Q'); + register_csi_ss3 (TERMO_TYPE_FUNCTION, 3, 'R'); + register_csi_ss3 (TERMO_TYPE_FUNCTION, 4, 'S'); + + register_csi_ss3_full (TERMO_TYPE_KEYSYM, TERMO_SYM_TAB, + TERMO_KEYMOD_SHIFT, TERMO_KEYMOD_SHIFT, 'Z'); + + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KPENTER, 'M', 0); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KPEQUALS, 'X', '='); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KPMULT, 'j', '*'); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KPPLUS, 'k', '+'); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KPCOMMA, 'l', ','); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KPMINUS, 'm', '-'); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KPPERIOD, 'n', '.'); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KPDIV, 'o', '/'); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KP0, 'p', '0'); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KP1, 'q', '1'); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KP2, 'r', '2'); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KP3, 's', '3'); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KP4, 't', '4'); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KP5, 'u', '5'); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KP6, 'v', '6'); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KP7, 'w', '7'); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KP8, 'x', '8'); + register_ss3kpalt (TERMO_TYPE_KEYSYM, TERMO_SYM_KP9, 'y', '9'); + + register_csifunc (TERMO_TYPE_KEYSYM, TERMO_SYM_FIND, 1); + register_csifunc (TERMO_TYPE_KEYSYM, TERMO_SYM_INSERT, 2); + register_csifunc (TERMO_TYPE_KEYSYM, TERMO_SYM_DELETE, 3); + register_csifunc (TERMO_TYPE_KEYSYM, TERMO_SYM_SELECT, 4); + register_csifunc (TERMO_TYPE_KEYSYM, TERMO_SYM_PAGEUP, 5); + register_csifunc (TERMO_TYPE_KEYSYM, TERMO_SYM_PAGEDOWN, 6); + register_csifunc (TERMO_TYPE_KEYSYM, TERMO_SYM_HOME, 7); + register_csifunc (TERMO_TYPE_KEYSYM, TERMO_SYM_END, 8); + + register_csifunc (TERMO_TYPE_FUNCTION, 1, 11); + register_csifunc (TERMO_TYPE_FUNCTION, 2, 12); + register_csifunc (TERMO_TYPE_FUNCTION, 3, 13); + register_csifunc (TERMO_TYPE_FUNCTION, 4, 14); + register_csifunc (TERMO_TYPE_FUNCTION, 5, 15); + register_csifunc (TERMO_TYPE_FUNCTION, 6, 17); + register_csifunc (TERMO_TYPE_FUNCTION, 7, 18); + register_csifunc (TERMO_TYPE_FUNCTION, 8, 19); + register_csifunc (TERMO_TYPE_FUNCTION, 9, 20); + register_csifunc (TERMO_TYPE_FUNCTION, 10, 21); + register_csifunc (TERMO_TYPE_FUNCTION, 11, 23); + register_csifunc (TERMO_TYPE_FUNCTION, 12, 24); + register_csifunc (TERMO_TYPE_FUNCTION, 13, 25); + register_csifunc (TERMO_TYPE_FUNCTION, 14, 26); + register_csifunc (TERMO_TYPE_FUNCTION, 15, 28); + register_csifunc (TERMO_TYPE_FUNCTION, 16, 29); + register_csifunc (TERMO_TYPE_FUNCTION, 17, 31); + register_csifunc (TERMO_TYPE_FUNCTION, 18, 32); + register_csifunc (TERMO_TYPE_FUNCTION, 19, 33); + register_csifunc (TERMO_TYPE_FUNCTION, 20, 34); csi_handlers['u' - 0x40] = &handle_csi_u; @@ -554,14 +554,14 @@ register_keys (void) csi_handlers['y' - 0x40] = &handle_csi_y; // URxvt - register_csi_ss3_full (TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_UP, - TERMKEY_KEYMOD_CTRL, TERMKEY_KEYMOD_CTRL, 'a'); - register_csi_ss3_full (TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DOWN, - TERMKEY_KEYMOD_CTRL, TERMKEY_KEYMOD_CTRL, 'b'); - register_csi_ss3_full (TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RIGHT, - TERMKEY_KEYMOD_CTRL, TERMKEY_KEYMOD_CTRL, 'c'); - register_csi_ss3_full (TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_LEFT, - TERMKEY_KEYMOD_CTRL, TERMKEY_KEYMOD_CTRL, 'd'); + register_csi_ss3_full (TERMO_TYPE_KEYSYM, TERMO_SYM_UP, + TERMO_KEYMOD_CTRL, TERMO_KEYMOD_CTRL, 'a'); + register_csi_ss3_full (TERMO_TYPE_KEYSYM, TERMO_SYM_DOWN, + TERMO_KEYMOD_CTRL, TERMO_KEYMOD_CTRL, 'b'); + register_csi_ss3_full (TERMO_TYPE_KEYSYM, TERMO_SYM_RIGHT, + TERMO_KEYMOD_CTRL, TERMO_KEYMOD_CTRL, 'c'); + register_csi_ss3_full (TERMO_TYPE_KEYSYM, TERMO_SYM_LEFT, + TERMO_KEYMOD_CTRL, TERMO_KEYMOD_CTRL, 'd'); csi_handlers['^' - 0x40] = &handle_csi_caret; @@ -570,14 +570,14 @@ register_keys (void) } static void * -new_driver (termkey_t *tk, const char *term) +new_driver (termo_t *tk, const char *term) { (void) term; if (!keyinfo_initialised && !register_keys ()) return NULL; - termkey_csi_t *csi = malloc (sizeof *csi); + termo_csi_t *csi = malloc (sizeof *csi); if (!csi) return NULL; @@ -588,13 +588,13 @@ new_driver (termkey_t *tk, const char *term) static void free_driver (void *info) { - termkey_csi_t *csi = info; + termo_csi_t *csi = info; free (csi); } -static termkey_result_t -peekkey_csi (termkey_t *tk, termkey_csi_t *csi, - size_t introlen, termkey_key_t *key, int force, size_t *nbytep) +static termo_result_t +peekkey_csi (termo_t *tk, termo_csi_t *csi, + size_t introlen, termo_key_t *key, int force, size_t *nbytep) { (void) csi; @@ -603,16 +603,16 @@ peekkey_csi (termkey_t *tk, termkey_csi_t *csi, long arg[16]; unsigned long cmd; - termkey_result_t ret = parse_csi (tk, introlen, &csi_len, arg, &args, &cmd); - if (ret == TERMKEY_RES_AGAIN) + termo_result_t ret = parse_csi (tk, introlen, &csi_len, arg, &args, &cmd); + if (ret == TERMO_RES_AGAIN) { if (!force) - return TERMKEY_RES_AGAIN; + return TERMO_RES_AGAIN; (*tk->method.emit_codepoint) (tk, '[', key); - key->modifiers |= TERMKEY_KEYMOD_ALT; + key->modifiers |= TERMO_KEYMOD_ALT; *nbytep = introlen; - return TERMKEY_RES_KEY; + return TERMO_RES_KEY; } // Mouse in X10 encoding consumes the next 3 bytes also (or more with 1005) @@ -621,24 +621,24 @@ peekkey_csi (termkey_t *tk, termkey_csi_t *csi, tk->buffstart += csi_len; tk->buffcount -= csi_len; - termkey_result_t mouse_result = + termo_result_t mouse_result = (*tk->method.peekkey_mouse) (tk, key, nbytep); tk->buffstart -= csi_len; tk->buffcount += csi_len; - if (mouse_result == TERMKEY_RES_KEY) + if (mouse_result == TERMO_RES_KEY) *nbytep += csi_len; return mouse_result; } - termkey_result_t result = TERMKEY_RES_NONE; + termo_result_t result = TERMO_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) + if (result == TERMO_RES_NONE) { #ifdef DEBUG switch (args) @@ -661,49 +661,49 @@ peekkey_csi (termkey_t *tk, termkey_csi_t *csi, break; } #endif - key->type = TERMKEY_TYPE_UNKNOWN_CSI; + key->type = TERMO_TYPE_UNKNOWN_CSI; key->code.number = cmd; tk->hightide = csi_len - introlen; *nbytep = introlen; // Do not yet eat the data bytes - return TERMKEY_RES_KEY; + return TERMO_RES_KEY; } *nbytep = csi_len; return result; } -static termkey_result_t -peekkey_ss3 (termkey_t *tk, termkey_csi_t *csi, size_t introlen, - termkey_key_t *key, int force, size_t *nbytep) +static termo_result_t +peekkey_ss3 (termo_t *tk, termo_csi_t *csi, size_t introlen, + termo_key_t *key, int force, size_t *nbytep) { (void) csi; if (tk->buffcount < introlen + 1) { if (!force) - return TERMKEY_RES_AGAIN; + return TERMO_RES_AGAIN; (*tk->method.emit_codepoint) (tk, 'O', key); - key->modifiers |= TERMKEY_KEYMOD_ALT; + key->modifiers |= TERMO_KEYMOD_ALT; *nbytep = tk->buffcount; - return TERMKEY_RES_KEY; + return TERMO_RES_KEY; } unsigned char cmd = CHARAT (introlen); if (cmd < 0x40 || cmd >= 0x80) - return TERMKEY_RES_NONE; + return TERMO_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 (key->code.sym == TERMO_SYM_UNKNOWN) { - if (tk->flags & TERMKEY_FLAG_CONVERTKP && ss3_kpalts[cmd - 0x40]) + if (tk->flags & TERMO_FLAG_CONVERTKP && ss3_kpalts[cmd - 0x40]) { - key->type = TERMKEY_TYPE_KEY; + key->type = TERMO_TYPE_KEY; key->code.codepoint = ss3_kpalts[cmd - 0x40]; key->modifiers = 0; @@ -718,26 +718,26 @@ peekkey_ss3 (termkey_t *tk, termkey_csi_t *csi, size_t introlen, } } - if (key->code.sym == TERMKEY_SYM_UNKNOWN) + if (key->code.sym == TERMO_SYM_UNKNOWN) { #ifdef DEBUG fprintf (stderr, "CSI: Unknown SS3 %c (0x%02x)\n", (char) cmd, cmd); #endif - return TERMKEY_RES_NONE; + return TERMO_RES_NONE; } *nbytep = introlen + 1; - return TERMKEY_RES_KEY; + return TERMO_RES_KEY; } -static termkey_result_t -peekkey (termkey_t *tk, void *info, - termkey_key_t *key, int force, size_t *nbytep) +static termo_result_t +peekkey (termo_t *tk, void *info, + termo_key_t *key, int force, size_t *nbytep) { if (tk->buffcount == 0) - return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE; + return tk->is_closed ? TERMO_RES_EOF : TERMO_RES_NONE; - termkey_csi_t *csi = info; + termo_csi_t *csi = info; // Now we're sure at least 1 byte is valid unsigned char b0 = CHARAT (0); @@ -750,10 +750,10 @@ peekkey (termkey_t *tk, void *info, return peekkey_ss3 (tk, csi, 1, key, force, nbytep); if (b0 == 0x9b) return peekkey_csi (tk, csi, 1, key, force, nbytep); - return TERMKEY_RES_NONE; + return TERMO_RES_NONE; } -termkey_driver_t termkey_driver_csi = +termo_driver_t termo_driver_csi = { .name = "CSI", .new_driver = new_driver, diff --git a/driver-ti.c b/driver-ti.c index 1b026fa..80b5d98 100644 --- a/driver-ti.c +++ b/driver-ti.c @@ -1,8 +1,8 @@ // we want strdup() #define _XOPEN_SOURCE 600 -#include "termkey2.h" -#include "termkey2-internal.h" +#include "termo.h" +#include "termo-internal.h" #ifdef HAVE_UNIBILIUM # include @@ -53,7 +53,7 @@ trie_node_array_t; typedef struct { - termkey_t *tk; + termo_t *tk; trie_node_t *root; char *start_string; @@ -62,14 +62,14 @@ typedef struct bool have_mouse; char *set_mouse_string; } -termkey_ti_t; +termo_ti_t; -static int funcname2keysym (const char *funcname, termkey_type_t *typep, - termkey_sym_t *symp, int *modmask, int *modsetp); -static int insert_seq (termkey_ti_t *ti, const char *seq, trie_node_t *node); +static int funcname2keysym (const char *funcname, termo_type_t *typep, + termo_sym_t *symp, int *modmask, int *modsetp); +static int insert_seq (termo_ti_t *ti, const char *seq, trie_node_t *node); static trie_node_t * -new_node_key (termkey_type_t type, termkey_sym_t sym, int modmask, int modset) +new_node_key (termo_type_t type, termo_sym_t sym, int modmask, int modset) { trie_node_key_t *n = malloc (sizeof *n); if (!n) @@ -176,7 +176,7 @@ compress_trie (struct trie_node *n) } static bool -load_terminfo (termkey_ti_t *ti, const char *term) +load_terminfo (termo_ti_t *ti, const char *term) { const char *mouse_report_string = NULL; @@ -212,15 +212,15 @@ load_terminfo (termkey_ti_t *ti, const char *term) mouse_report_string = value; else { - termkey_type_t type; - termkey_sym_t sym; + termo_type_t type; + termo_sym_t sym; int mask = 0; int set = 0; if (!funcname2keysym (name + 4, &type, &sym, &mask, &set)) continue; - if (sym == TERMKEY_SYM_NONE) + if (sym == TERMO_SYM_NONE) continue; node = new_node_key (type, sym, mask, set); @@ -262,7 +262,7 @@ load_terminfo (termkey_ti_t *ti, const char *term) } } - /* Take copies of these terminfo strings, in case we build multiple termkey + /* Take copies of these terminfo strings, in case we build multiple termo * instances for multiple different termtypes, and it's different by the * time we want to use it */ @@ -292,9 +292,9 @@ load_terminfo (termkey_ti_t *ti, const char *term) } static void * -new_driver (termkey_t *tk, const char *term) +new_driver (termo_t *tk, const char *term) { - termkey_ti_t *ti = calloc (1, sizeof *ti); + termo_ti_t *ti = calloc (1, sizeof *ti); if (!ti) return NULL; @@ -318,7 +318,7 @@ abort_free_ti: } static bool -write_string (termkey_t *tk, char *string) +write_string (termo_t *tk, char *string) { if (tk->fd == -1 || !isatty (tk->fd) || !string) return true; @@ -340,7 +340,7 @@ write_string (termkey_t *tk, char *string) } static bool -set_mouse (termkey_ti_t *ti, bool enable) +set_mouse (termo_ti_t *ti, bool enable) { #ifdef HAVE_UNIBILIUM unibi_var_t params[9] = { enable, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -355,9 +355,9 @@ set_mouse (termkey_ti_t *ti, bool enable) } static int -start_driver (termkey_t *tk, void *info) +start_driver (termo_t *tk, void *info) { - termkey_ti_t *ti = info; + termo_ti_t *ti = info; // TODO: Don't start the mouse automatically, find a nice place to put // a public function to be called by users. // TODO: Try to autodetect rxvt and use its protocol instead of mode 1000 @@ -368,9 +368,9 @@ start_driver (termkey_t *tk, void *info) } static int -stop_driver (termkey_t *tk, void *info) +stop_driver (termo_t *tk, void *info) { - termkey_ti_t *ti = info; + termo_ti_t *ti = info; if (ti->have_mouse && !set_mouse (ti, false)) return false; return write_string (tk, ti->stop_string); @@ -379,7 +379,7 @@ stop_driver (termkey_t *tk, void *info) static void free_driver (void *info) { - termkey_ti_t *ti = info; + termo_ti_t *ti = info; free_trie (ti->root); free (ti->set_mouse_string); free (ti->start_string); @@ -389,14 +389,14 @@ free_driver (void *info) #define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) -static termkey_result_t -peekkey (termkey_t *tk, void *info, - termkey_key_t *key, int force, size_t *nbytep) +static termo_result_t +peekkey (termo_t *tk, void *info, + termo_key_t *key, int force, size_t *nbytep) { - termkey_ti_t *ti = info; + termo_ti_t *ti = info; if (tk->buffcount == 0) - return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE; + return tk->is_closed ? TERMO_RES_EOF : TERMO_RES_NONE; trie_node_t *p = ti->root; unsigned int pos = 0; @@ -415,20 +415,20 @@ peekkey (termkey_t *tk, void *info, key->code.sym = nk->key.sym; key->modifiers = nk->key.modifier_set; *nbytep = pos; - return TERMKEY_RES_KEY; + return TERMO_RES_KEY; } else if (p->type == TYPE_MOUSE) { tk->buffstart += pos; tk->buffcount -= pos; - termkey_result_t mouse_result = + termo_result_t mouse_result = (*tk->method.peekkey_mouse) (tk, key, nbytep); tk->buffstart -= pos; tk->buffcount += pos; - if (mouse_result == TERMKEY_RES_KEY) + if (mouse_result == TERMO_RES_KEY) *nbytep += pos; return mouse_result; @@ -438,65 +438,65 @@ peekkey (termkey_t *tk, void *info, // 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 TERMO_RES_AGAIN; - return TERMKEY_RES_NONE; + return TERMO_RES_NONE; } static struct func { const char *funcname; - termkey_type_t type; - termkey_sym_t sym; + termo_type_t type; + termo_sym_t 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 }, - { "mouse", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_NONE, 0 }, - { "move", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_MOVE, 0 }, + { "backspace", TERMO_TYPE_KEYSYM, TERMO_SYM_BACKSPACE, 0 }, + { "begin", TERMO_TYPE_KEYSYM, TERMO_SYM_BEGIN, 0 }, + { "beg", TERMO_TYPE_KEYSYM, TERMO_SYM_BEGIN, 0 }, + { "btab", TERMO_TYPE_KEYSYM, TERMO_SYM_TAB, TERMO_KEYMOD_SHIFT }, + { "cancel", TERMO_TYPE_KEYSYM, TERMO_SYM_CANCEL, 0 }, + { "clear", TERMO_TYPE_KEYSYM, TERMO_SYM_CLEAR, 0 }, + { "close", TERMO_TYPE_KEYSYM, TERMO_SYM_CLOSE, 0 }, + { "command", TERMO_TYPE_KEYSYM, TERMO_SYM_COMMAND, 0 }, + { "copy", TERMO_TYPE_KEYSYM, TERMO_SYM_COPY, 0 }, + { "dc", TERMO_TYPE_KEYSYM, TERMO_SYM_DELETE, 0 }, + { "down", TERMO_TYPE_KEYSYM, TERMO_SYM_DOWN, 0 }, + { "end", TERMO_TYPE_KEYSYM, TERMO_SYM_END, 0 }, + { "enter", TERMO_TYPE_KEYSYM, TERMO_SYM_ENTER, 0 }, + { "exit", TERMO_TYPE_KEYSYM, TERMO_SYM_EXIT, 0 }, + { "find", TERMO_TYPE_KEYSYM, TERMO_SYM_FIND, 0 }, + { "help", TERMO_TYPE_KEYSYM, TERMO_SYM_HELP, 0 }, + { "home", TERMO_TYPE_KEYSYM, TERMO_SYM_HOME, 0 }, + { "ic", TERMO_TYPE_KEYSYM, TERMO_SYM_INSERT, 0 }, + { "left", TERMO_TYPE_KEYSYM, TERMO_SYM_LEFT, 0 }, + { "mark", TERMO_TYPE_KEYSYM, TERMO_SYM_MARK, 0 }, + { "message", TERMO_TYPE_KEYSYM, TERMO_SYM_MESSAGE, 0 }, + { "mouse", TERMO_TYPE_KEYSYM, TERMO_SYM_NONE, 0 }, + { "move", TERMO_TYPE_KEYSYM, TERMO_SYM_MOVE, 0 }, // Not quite, but it's the best we can do - { "next", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEDOWN, 0 }, - { "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 }, + { "next", TERMO_TYPE_KEYSYM, TERMO_SYM_PAGEDOWN, 0 }, + { "npage", TERMO_TYPE_KEYSYM, TERMO_SYM_PAGEDOWN, 0 }, + { "open", TERMO_TYPE_KEYSYM, TERMO_SYM_OPEN, 0 }, + { "options", TERMO_TYPE_KEYSYM, TERMO_SYM_OPTIONS, 0 }, + { "ppage", TERMO_TYPE_KEYSYM, TERMO_SYM_PAGEUP, 0 }, // Not quite, but it's the best we can do - { "previous", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEUP, 0 }, - { "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 }, + { "previous", TERMO_TYPE_KEYSYM, TERMO_SYM_PAGEUP, 0 }, + { "print", TERMO_TYPE_KEYSYM, TERMO_SYM_PRINT, 0 }, + { "redo", TERMO_TYPE_KEYSYM, TERMO_SYM_REDO, 0 }, + { "reference", TERMO_TYPE_KEYSYM, TERMO_SYM_REFERENCE, 0 }, + { "refresh", TERMO_TYPE_KEYSYM, TERMO_SYM_REFRESH, 0 }, + { "replace", TERMO_TYPE_KEYSYM, TERMO_SYM_REPLACE, 0 }, + { "restart", TERMO_TYPE_KEYSYM, TERMO_SYM_RESTART, 0 }, + { "resume", TERMO_TYPE_KEYSYM, TERMO_SYM_RESUME, 0 }, + { "right", TERMO_TYPE_KEYSYM, TERMO_SYM_RIGHT, 0 }, + { "save", TERMO_TYPE_KEYSYM, TERMO_SYM_SAVE, 0 }, + { "select", TERMO_TYPE_KEYSYM, TERMO_SYM_SELECT, 0 }, + { "suspend", TERMO_TYPE_KEYSYM, TERMO_SYM_SUSPEND, 0 }, + { "undo", TERMO_TYPE_KEYSYM, TERMO_SYM_UNDO, 0 }, + { "up", TERMO_TYPE_KEYSYM, TERMO_SYM_UP, 0 }, { NULL }, }; @@ -508,7 +508,7 @@ func_compare (const void *key, const void *element) static int funcname2keysym (const char *funcname, - termkey_type_t *typep, termkey_sym_t *symp, int *modmaskp, int *modsetp) + termo_type_t *typep, termo_sym_t *symp, int *modmaskp, int *modsetp) { struct func *func = bsearch (funcname, funcs, sizeof funcs / sizeof funcs[0], sizeof funcs[0], func_compare); @@ -523,7 +523,7 @@ funcname2keysym (const char *funcname, if (funcname[0] == 'f' && isdigit (funcname[1])) { - *typep = TERMKEY_TYPE_FUNCTION; + *typep = TERMO_TYPE_FUNCTION; *symp = atoi (funcname + 1); return 1; } @@ -532,8 +532,8 @@ funcname2keysym (const char *funcname, if (funcname[0] == 's' && funcname2keysym (funcname + 1, typep, symp, modmaskp, modsetp)) { - *modmaskp |= TERMKEY_KEYMOD_SHIFT; - *modsetp |= TERMKEY_KEYMOD_SHIFT; + *modmaskp |= TERMO_KEYMOD_SHIFT; + *modsetp |= TERMO_KEYMOD_SHIFT; return 1; } @@ -546,7 +546,7 @@ funcname2keysym (const char *funcname, } static int -insert_seq (termkey_ti_t *ti, const char *seq, trie_node_t *node) +insert_seq (termo_ti_t *ti, const char *seq, trie_node_t *node) { int pos = 0; trie_node_t *p = ti->root; @@ -603,7 +603,7 @@ insert_seq (termkey_ti_t *ti, const char *seq, trie_node_t *node) return 1; } -termkey_driver_t termkey_driver_ti = +termo_driver_t termo_driver_ti = { .name = "terminfo", .new_driver = new_driver, diff --git a/termkey2-config.h.in b/termkey2-config.h.in deleted file mode 100644 index 676a2c0..0000000 --- a/termkey2-config.h.in +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef TERMKEY2_CONFIG_H -#define TERMKEY2_CONFIG_H - -#define TERMKEY_VERSION_MAJOR @project_VERSION_MAJOR@ -#define TERMKEY_VERSION_MINOR @project_VERSION_MINOR@ - -#endif // ! TERMKEY2_CONFIG_H - diff --git a/termkey2-internal.h b/termkey2-internal.h deleted file mode 100644 index 5da33ff..0000000 --- a/termkey2-internal.h +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef TERMKEY2_INTERNAL_H -#define TERMKEY2_INTERNAL_H - -#include "termkey2.h" - -#include -#include -#include -#include - -typedef struct termkey_driver termkey_driver_t; -struct termkey_driver -{ - const char *name; - void *(*new_driver) (termkey_t *tk, const char *term); - void (*free_driver) (void *info); - int (*start_driver) (termkey_t *tk, void *info); - int (*stop_driver) (termkey_t *tk, void *info); - termkey_result_t (*peekkey) (termkey_t *tk, - void *info, termkey_key_t *key, int force, size_t *nbytes); -}; - -typedef struct keyinfo keyinfo_t; -struct keyinfo -{ - termkey_type_t type; - termkey_sym_t sym; - int modifier_mask; - int modifier_set; -}; - -typedef struct termkey_driver_node termkey_driver_node_t; -struct termkey_driver_node -{ - termkey_driver_t *driver; - void *info; - termkey_driver_node_t *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 - - // Position beyond buffstart at which peekkey() should next start. - // Normally 0, but see also termkey_interpret_csi(). - size_t hightide; - - struct termios restore_termios; - bool restore_termios_valid; - - int waittime; // In milliseconds - - bool is_closed; // We've received EOF - bool is_started; - - int nkeynames; - const char **keynames; - - keyinfo_t c0[32]; // There are 32 C0 codes - iconv_t to_utf32_conv; - iconv_t from_utf32_conv; - termkey_driver_node_t *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_t *tk, - uint32_t codepoint, termkey_key_t *key); - termkey_result_t (*peekkey_simple) (termkey_t *tk, - termkey_key_t *key, int force, size_t *nbytes); - termkey_result_t (*peekkey_mouse) (termkey_t *tk, - termkey_key_t *key, size_t *nbytes); - } - method; -}; - -static inline void -termkey_key_get_linecol (const termkey_key_t *key, int *line, int *col) -{ - if (col) - *col = key->code.mouse.x; - - if (line) - *line = key->code.mouse.y; -} - -static inline void -termkey_key_set_linecol (termkey_key_t *key, int line, int col) -{ - if (line > UINT16_MAX) - line = UINT16_MAX; - - if (col > UINT16_MAX) - col = UINT16_MAX; - - key->code.mouse.x = col; - key->code.mouse.y = line; -} - -extern termkey_driver_t termkey_driver_csi; -extern termkey_driver_t termkey_driver_ti; - -#endif // ! TERMKEY2_INTERNAL_H - diff --git a/termkey2.c b/termkey2.c deleted file mode 100644 index 37831b8..0000000 --- a/termkey2.c +++ /dev/null @@ -1,1656 +0,0 @@ -#include "termkey2.h" -#include "termkey2-internal.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -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 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 - { - 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 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; - } -} - -// XXX: With the current infrastructure I'm not sure how to properly handle -// this. peekkey() isn't made for skipping invalid inputs. -#define INVALID_1005 0x20 - -static termkey_result_t -parse_1005_value (const unsigned char **bytes, size_t *len, uint32_t *cp) -{ - unsigned int nbytes; - unsigned char b0 = (*bytes)[0]; - if (b0 < 0x80) - { - // Single byte ASCII - *cp = b0; - nbytes = 1; - goto end; - } - else if (b0 < 0xc0) - { - // Starts with a continuation byte - that's not right - *cp = INVALID_1005; - nbytes = 1; - goto end; - } - 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 = INVALID_1005; - nbytes = 1; - goto end; - } - - for (unsigned int b = 1; b < nbytes; b++) - { - if (b >= *len) - return TERMKEY_RES_AGAIN; - - unsigned char cb = (*bytes)[b]; - if (cb < 0x80 || cb >= 0xc0) - { - *cp = INVALID_1005; - nbytes = b; - goto end; - } - *cp <<= 6; - *cp |= cb & 0x3f; - } - -end: - (*bytes) += nbytes; - (*len) -= nbytes; - return TERMKEY_RES_KEY; -} - -static termkey_result_t -peekkey_mouse (termkey_t *tk, termkey_key_t *key, size_t *nbytep) -{ - uint32_t b, x, y; - - // TODO: Add some API to switch on 1005 mode support - if (false) - { - const unsigned char *buff = tk->buffer + tk->buffstart; - size_t len = tk->buffcount; - - if (parse_1005_value (&buff, &len, &b) == TERMKEY_RES_AGAIN - || parse_1005_value (&buff, &len, &x) == TERMKEY_RES_AGAIN - || parse_1005_value (&buff, &len, &y) == TERMKEY_RES_AGAIN) - return TERMKEY_RES_AGAIN; - - *nbytep = tk->buffcount - len; - } - else - { - if (tk->buffcount < 3) - return TERMKEY_RES_AGAIN; - - b = CHARAT (0); - x = CHARAT (1); - y = CHARAT (2); - - *nbytep = 3; - } - - key->type = TERMKEY_TYPE_MOUSE; - key->code.mouse.info = b - 0x20; - key->code.mouse.x = x - 0x20 - 1; - key->code.mouse.y = y - 0x20 - 1; - - key->modifiers = (key->code.mouse.info & 0x1c) >> 2; - key->code.mouse.info &= ~0x1c; - - 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 = memcmp (&key1.code.mouse, &key2.code.mouse, - sizeof key1.code.mouse); - 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; -} - diff --git a/termkey2.h b/termkey2.h deleted file mode 100644 index 0fa0635..0000000 --- a/termkey2.h +++ /dev/null @@ -1,277 +0,0 @@ -#ifndef TERMKEY2_H -#define TERMKEY2_H - -#include -#include -#include - -#include "termkey2-config.h" - -#define TERMKEY_CHECK_VERSION \ - termkey_check_version (TERMKEY_VERSION_MAJOR, TERMKEY_VERSION_MINOR) - -typedef enum termkey_sym termkey_sym_t; -enum termkey_sym -{ - 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, - - TERMKEY_N_SYMS -}; - -typedef enum termkey_type termkey_type_t; -enum termkey_type -{ - TERMKEY_TYPE_KEY, - TERMKEY_TYPE_FUNCTION, - TERMKEY_TYPE_KEYSYM, - TERMKEY_TYPE_MOUSE, - TERMKEY_TYPE_POSITION, - TERMKEY_TYPE_MODEREPORT, - /* add other recognised types here */ - - TERMKEY_TYPE_UNKNOWN_CSI = -1 -}; - -typedef enum termkey_result termkey_result_t; -enum termkey_result -{ - TERMKEY_RES_NONE, - TERMKEY_RES_KEY, - TERMKEY_RES_EOF, - TERMKEY_RES_AGAIN, - TERMKEY_RES_ERROR -}; - -typedef enum termkey_mouse_event termkey_mouse_event_t; -enum termkey_mouse_event -{ - TERMKEY_MOUSE_UNKNOWN, - TERMKEY_MOUSE_PRESS, - TERMKEY_MOUSE_DRAG, - TERMKEY_MOUSE_RELEASE -}; - -enum -{ - TERMKEY_KEYMOD_SHIFT = 1 << 0, - TERMKEY_KEYMOD_ALT = 1 << 1, - TERMKEY_KEYMOD_CTRL = 1 << 2 -}; - -typedef struct termkey_key termkey_key_t; -struct termkey_key -{ - termkey_type_t type; - union - { - uint32_t codepoint; /* TERMKEY_TYPE_KEY */ - int number; /* TERMKEY_TYPE_FUNCTION */ - termkey_sym_t sym; /* TERMKEY_TYPE_KEYSYM */ - - /* TERMKEY_TYPE_MODEREPORT */ - /* opaque, see termkey_interpret_modereport() */ - struct { char initial; int mode, value; } mode; - - /* TERMKEY_TYPE_MOUSE */ - /* opaque, see termkey_interpret_mouse() */ - struct { uint16_t x, y, info; } mouse; - } code; - - int modifiers; - - /* The raw multibyte sequence for the key */ - char multibyte[MB_LEN_MAX + 1]; -}; - -typedef struct termkey termkey_t; - -enum -{ - /* Do not interpret C0//DEL codes if possible */ - TERMKEY_FLAG_NOINTERPRET = 1 << 0, - /* Convert KP codes to regular keypresses */ - TERMKEY_FLAG_CONVERTKP = 1 << 1, - /* Don't try to decode the input characters */ - TERMKEY_FLAG_RAW = 1 << 2, - /* Do not make initial termios calls on construction */ - TERMKEY_FLAG_NOTERMIOS = 1 << 4, - /* Sets TERMKEY_CANON_SPACESYMBOL */ - TERMKEY_FLAG_SPACESYMBOL = 1 << 5, - /* Allow Ctrl-C to be read as normal, disabling SIGINT */ - TERMKEY_FLAG_CTRLC = 1 << 6, - /* Return ERROR on signal (EINTR) rather than retry */ - TERMKEY_FLAG_EINTR = 1 << 7 -}; - -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_t *termkey_new (int fd, const char *encoding, int flags); -termkey_t *termkey_new_abstract (const char *term, - const char *encoding, int flags); -void termkey_free (termkey_t *tk); -void termkey_destroy (termkey_t *tk); - -int termkey_start (termkey_t *tk); -int termkey_stop (termkey_t *tk); -int termkey_is_started (termkey_t *tk); - -int termkey_get_fd (termkey_t *tk); - -int termkey_get_flags (termkey_t *tk); -void termkey_set_flags (termkey_t *tk, int newflags); - -int termkey_get_waittime (termkey_t *tk); -void termkey_set_waittime (termkey_t *tk, int msec); - -int termkey_get_canonflags (termkey_t *tk); -void termkey_set_canonflags (termkey_t *tk, int flags); - -size_t termkey_get_buffer_size (termkey_t *tk); -int termkey_set_buffer_size (termkey_t *tk, size_t size); - -size_t termkey_get_buffer_remaining (termkey_t *tk); - -void termkey_canonicalise (termkey_t *tk, termkey_key_t *key); - -termkey_result_t termkey_getkey (termkey_t *tk, termkey_key_t *key); -termkey_result_t termkey_getkey_force (termkey_t *tk, termkey_key_t *key); -termkey_result_t termkey_waitkey (termkey_t *tk, termkey_key_t *key); - -termkey_result_t termkey_advisereadable (termkey_t *tk); - -size_t termkey_push_bytes (termkey_t *tk, const char *bytes, size_t len); - -termkey_sym_t termkey_register_keyname (termkey_t *tk, - termkey_sym_t sym, const char *name); -const char *termkey_get_keyname (termkey_t *tk, termkey_sym_t sym); -const char *termkey_lookup_keyname (termkey_t *tk, - const char *str, termkey_sym_t *sym); - -termkey_sym_t termkey_keyname2sym (termkey_t *tk, const char *keyname); - -termkey_result_t termkey_interpret_mouse (termkey_t *tk, - const termkey_key_t *key, termkey_mouse_event_t *event, - int *button, int *line, int *col); -termkey_result_t termkey_interpret_position (termkey_t *tk, - const termkey_key_t *key, int *line, int *col); -termkey_result_t termkey_interpret_modereport (termkey_t *tk, - const termkey_key_t *key, int *initial, int *mode, int *value); -termkey_result_t termkey_interpret_csi (termkey_t *tk, - const termkey_key_t *key, long args[], size_t *nargs, unsigned long *cmd); - -typedef enum termkey_format termkey_format_t; -enum termkey_format -{ - /* Shift-... instead of S-... */ - TERMKEY_FORMAT_LONGMOD = 1 << 0, - /* ^X instead of C-X */ - TERMKEY_FORMAT_CARETCTRL = 1 << 1, - /* Meta- or M- instead of Alt- or A- */ - TERMKEY_FORMAT_ALTISMETA = 1 << 2, - /* Wrap special keys in brackets like */ - TERMKEY_FORMAT_WRAPBRACKET = 1 << 3, - /* M Foo instead of M-Foo */ - TERMKEY_FORMAT_SPACEMOD = 1 << 4, - /* meta or m instead of Meta or M */ - TERMKEY_FORMAT_LOWERMOD = 1 << 5, - /* page down instead of PageDown */ - TERMKEY_FORMAT_LOWERSPACE = 1 << 6, - /* Include mouse position if relevant; @ col,line */ - TERMKEY_FORMAT_MOUSE_POS = 1 << 8 -}; - -/* Some useful combinations */ - -#define TERMKEY_FORMAT_VIM (termkey_format_t) \ - (TERMKEY_FORMAT_ALTISMETA | TERMKEY_FORMAT_WRAPBRACKET) -#define TERMKEY_FORMAT_URWID (termkey_format_t) \ - (TERMKEY_FORMAT_LONGMOD | TERMKEY_FORMAT_ALTISMETA | \ - TERMKEY_FORMAT_LOWERMOD | TERMKEY_FORMAT_SPACEMOD | \ - TERMKEY_FORMAT_LOWERSPACE) - -size_t termkey_strfkey (termkey_t *tk, char *buffer, size_t len, - termkey_key_t *key, termkey_format_t format); -const char *termkey_strpkey (termkey_t *tk, const char *str, - termkey_key_t *key, termkey_format_t format); - -int termkey_keycmp (termkey_t *tk, - const termkey_key_t *key1, const termkey_key_t *key2); - -#endif // ! TERMKEY2_H - diff --git a/termo-config.h.in b/termo-config.h.in new file mode 100644 index 0000000..780927a --- /dev/null +++ b/termo-config.h.in @@ -0,0 +1,8 @@ +#ifndef TERMO_CONFIG_H +#define TERMO_CONFIG_H + +#define TERMO_VERSION_MAJOR @project_VERSION_MAJOR@ +#define TERMO_VERSION_MINOR @project_VERSION_MINOR@ + +#endif // ! TERMO_CONFIG_H + diff --git a/termo-internal.h b/termo-internal.h new file mode 100644 index 0000000..b9a27c2 --- /dev/null +++ b/termo-internal.h @@ -0,0 +1,112 @@ +#ifndef TERMO_INTERNAL_H +#define TERMO_INTERNAL_H + +#include "termo.h" + +#include +#include +#include +#include + +typedef struct termo_driver termo_driver_t; +struct termo_driver +{ + const char *name; + void *(*new_driver) (termo_t *tk, const char *term); + void (*free_driver) (void *info); + int (*start_driver) (termo_t *tk, void *info); + int (*stop_driver) (termo_t *tk, void *info); + termo_result_t (*peekkey) (termo_t *tk, + void *info, termo_key_t *key, int force, size_t *nbytes); +}; + +typedef struct keyinfo keyinfo_t; +struct keyinfo +{ + termo_type_t type; + termo_sym_t sym; + int modifier_mask; + int modifier_set; +}; + +typedef struct termo_driver_node termo_driver_node_t; +struct termo_driver_node +{ + termo_driver_t *driver; + void *info; + termo_driver_node_t *next; +}; + +struct termo +{ + 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 + + // Position beyond buffstart at which peekkey() should next start. + // Normally 0, but see also termo_interpret_csi(). + size_t hightide; + + struct termios restore_termios; + bool restore_termios_valid; + + int waittime; // In milliseconds + + bool is_closed; // We've received EOF + bool is_started; + + int nkeynames; + const char **keynames; + + keyinfo_t c0[32]; // There are 32 C0 codes + iconv_t to_utf32_conv; + iconv_t from_utf32_conv; + termo_driver_node_t *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) (termo_t *tk, + uint32_t codepoint, termo_key_t *key); + termo_result_t (*peekkey_simple) (termo_t *tk, + termo_key_t *key, int force, size_t *nbytes); + termo_result_t (*peekkey_mouse) (termo_t *tk, + termo_key_t *key, size_t *nbytes); + } + method; +}; + +static inline void +termo_key_get_linecol (const termo_key_t *key, int *line, int *col) +{ + if (col) + *col = key->code.mouse.x; + + if (line) + *line = key->code.mouse.y; +} + +static inline void +termo_key_set_linecol (termo_key_t *key, int line, int col) +{ + if (line > UINT16_MAX) + line = UINT16_MAX; + + if (col > UINT16_MAX) + col = UINT16_MAX; + + key->code.mouse.x = col; + key->code.mouse.y = line; +} + +extern termo_driver_t termo_driver_csi; +extern termo_driver_t termo_driver_ti; + +#endif // ! TERMO_INTERNAL_H + diff --git a/termo.c b/termo.c new file mode 100644 index 0000000..9f23158 --- /dev/null +++ b/termo.c @@ -0,0 +1,1656 @@ +#include "termo.h" +#include "termo-internal.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +void +termo_check_version (int major, int minor) +{ + if (major != TERMO_VERSION_MAJOR) + fprintf (stderr, "libtermo major version mismatch;" + " %d (wants) != %d (library)\n", + major, TERMO_VERSION_MAJOR); + else if (minor > TERMO_VERSION_MINOR) + fprintf (stderr, "libtermo minor version mismatch;" + " %d (wants) > %d (library)\n", + minor, TERMO_VERSION_MINOR); + else + return; + exit (1); +} + +static termo_driver_t *drivers[] = +{ + &termo_driver_ti, + &termo_driver_csi, + NULL, +}; + +// Forwards for the "protected" methods +static void emit_codepoint (termo_t *tk, uint32_t codepoint, termo_key_t *key); +static termo_result_t peekkey_simple (termo_t *tk, + termo_key_t *key, int force, size_t *nbytes); +static termo_result_t peekkey_mouse (termo_t *tk, + termo_key_t *key, size_t *nbytes); + +static termo_sym_t register_c0 (termo_t *tk, termo_sym_t sym, + unsigned char ctrl, const char *name); +static termo_sym_t register_c0_full (termo_t *tk, termo_sym_t sym, + int modifier_set, int modifier_mask, unsigned char ctrl, const char *name); + +static struct +{ + termo_sym_t sym; + const char *name; +} +keynames[] = +{ + { TERMO_SYM_NONE, "NONE" }, + { TERMO_SYM_BACKSPACE, "Backspace" }, + { TERMO_SYM_TAB, "Tab" }, + { TERMO_SYM_ENTER, "Enter" }, + { TERMO_SYM_ESCAPE, "Escape" }, + { TERMO_SYM_SPACE, "Space" }, + { TERMO_SYM_DEL, "DEL" }, + { TERMO_SYM_UP, "Up" }, + { TERMO_SYM_DOWN, "Down" }, + { TERMO_SYM_LEFT, "Left" }, + { TERMO_SYM_RIGHT, "Right" }, + { TERMO_SYM_BEGIN, "Begin" }, + { TERMO_SYM_FIND, "Find" }, + { TERMO_SYM_INSERT, "Insert" }, + { TERMO_SYM_DELETE, "Delete" }, + { TERMO_SYM_SELECT, "Select" }, + { TERMO_SYM_PAGEUP, "PageUp" }, + { TERMO_SYM_PAGEDOWN, "PageDown" }, + { TERMO_SYM_HOME, "Home" }, + { TERMO_SYM_END, "End" }, + { TERMO_SYM_CANCEL, "Cancel" }, + { TERMO_SYM_CLEAR, "Clear" }, + { TERMO_SYM_CLOSE, "Close" }, + { TERMO_SYM_COMMAND, "Command" }, + { TERMO_SYM_COPY, "Copy" }, + { TERMO_SYM_EXIT, "Exit" }, + { TERMO_SYM_HELP, "Help" }, + { TERMO_SYM_MARK, "Mark" }, + { TERMO_SYM_MESSAGE, "Message" }, + { TERMO_SYM_MOVE, "Move" }, + { TERMO_SYM_OPEN, "Open" }, + { TERMO_SYM_OPTIONS, "Options" }, + { TERMO_SYM_PRINT, "Print" }, + { TERMO_SYM_REDO, "Redo" }, + { TERMO_SYM_REFERENCE, "Reference" }, + { TERMO_SYM_REFRESH, "Refresh" }, + { TERMO_SYM_REPLACE, "Replace" }, + { TERMO_SYM_RESTART, "Restart" }, + { TERMO_SYM_RESUME, "Resume" }, + { TERMO_SYM_SAVE, "Save" }, + { TERMO_SYM_SUSPEND, "Suspend" }, + { TERMO_SYM_UNDO, "Undo" }, + { TERMO_SYM_KP0, "KP0" }, + { TERMO_SYM_KP1, "KP1" }, + { TERMO_SYM_KP2, "KP2" }, + { TERMO_SYM_KP3, "KP3" }, + { TERMO_SYM_KP4, "KP4" }, + { TERMO_SYM_KP5, "KP5" }, + { TERMO_SYM_KP6, "KP6" }, + { TERMO_SYM_KP7, "KP7" }, + { TERMO_SYM_KP8, "KP8" }, + { TERMO_SYM_KP9, "KP9" }, + { TERMO_SYM_KPENTER, "KPEnter" }, + { TERMO_SYM_KPPLUS, "KPPlus" }, + { TERMO_SYM_KPMINUS, "KPMinus" }, + { TERMO_SYM_KPMULT, "KPMult" }, + { TERMO_SYM_KPDIV, "KPDiv" }, + { TERMO_SYM_KPCOMMA, "KPComma" }, + { TERMO_SYM_KPPERIOD, "KPPeriod" }, + { TERMO_SYM_KPEQUALS, "KPEquals" }, + { 0, NULL }, +}; + +#define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) + +#ifdef DEBUG +/* Some internal deubgging functions */ + +static void +print_buffer (termo_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 (termo_t *tk, termo_key_t *key) +{ + switch (key->type) + { + case TERMO_TYPE_KEY: + fprintf (stderr, "Unicode codepoint=U+%04lx multibyte='%s'", + (long) key->code.codepoint, key->multibyte); + break; + case TERMO_TYPE_FUNCTION: + fprintf (stderr, "Function F%d", key->code.number); + break; + case TERMO_TYPE_KEYSYM: + fprintf (stderr, "Keysym sym=%d(%s)", + key->code.sym, termo_get_keyname (tk, key->code.sym)); + break; + case TERMO_TYPE_MOUSE: + { + termo_mouse_event_t ev; + int button, line, col; + termo_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 TERMO_TYPE_POSITION: + { + int line, col; + termo_interpret_position (tk, key, &line, &col); + fprintf (stderr, "Position report pos=(%d,%d)\n", line, col); + break; + } + case TERMO_TYPE_MODEREPORT: + { + int initial, mode, value; + termo_interpret_modereport (tk, key, &initial, &mode, &value); + fprintf (stderr, "Mode report mode=%s %d val=%d\n", + initial == '?' ? "DEC" : "ANSI", mode, value); + break; + } + case TERMO_TYPE_UNKNOWN_CSI: + fprintf (stderr, "unknown CSI\n"); + } + + int m = key->modifiers; + fprintf (stderr, " mod=%s%s%s+%02x", + (m & TERMO_KEYMOD_CTRL ? "C" : ""), + (m & TERMO_KEYMOD_ALT ? "A" : ""), + (m & TERMO_KEYMOD_SHIFT ? "S" : ""), + m & ~(TERMO_KEYMOD_CTRL | TERMO_KEYMOD_ALT | TERMO_KEYMOD_SHIFT)); +} + +static const char * +res2str (termo_result_t res) +{ + static char errorbuffer[256]; + + switch (res) + { + case TERMO_RES_KEY: + return "TERMO_RES_KEY"; + case TERMO_RES_EOF: + return "TERMO_RES_EOF"; + case TERMO_RES_AGAIN: + return "TERMO_RES_AGAIN"; + case TERMO_RES_NONE: + return "TERMO_RES_NONE"; + case TERMO_RES_ERROR: + snprintf (errorbuffer, sizeof errorbuffer, + "TERMO_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 termo_t * +termo_alloc (void) +{ + termo_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 = TERMO_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 +termo_init (termo_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 (termo_register_keyname (tk, + keynames[i].sym, keynames[i].name) == -1) + goto abort_free_keynames; + + register_c0 (tk, TERMO_SYM_BACKSPACE, 0x08, NULL); + register_c0 (tk, TERMO_SYM_TAB, 0x09, NULL); + register_c0 (tk, TERMO_SYM_ENTER, 0x0d, NULL); + register_c0 (tk, TERMO_SYM_ESCAPE, 0x1b, NULL); + + termo_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 + + termo_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 (termo_driver_node_t *p = tk->drivers; p; ) + { + (*p->driver->free_driver) (p->info); + termo_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; +} + +termo_t * +termo_new (int fd, const char *encoding, int flags) +{ + termo_t *tk = termo_alloc (); + if (!tk) + return NULL; + + tk->fd = fd; + termo_set_flags (tk, flags); + + const char *term = getenv ("TERM"); + if (termo_init (tk, term, encoding) + && termo_start (tk)) + return tk; + + free (tk); + return NULL; +} + +termo_t * +termo_new_abstract (const char *term, const char *encoding, int flags) +{ + termo_t *tk = termo_alloc (); + if (!tk) + return NULL; + + tk->fd = -1; + termo_set_flags (tk, flags); + + if (!termo_init (tk, term, encoding)) + { + free (tk); + return NULL; + } + + termo_start (tk); + return tk; +} + +void +termo_free (termo_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; + + termo_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 +termo_destroy (termo_t *tk) +{ + if (tk->is_started) + termo_stop (tk); + + termo_free (tk); +} + +int +termo_start (termo_t *tk) +{ + if (tk->is_started) + return 1; + + if (tk->fd != -1 && !(tk->flags & TERMO_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 & TERMO_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); + } + } + + termo_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; termo instance %p is ready\n", tk); +#endif + + tk->is_started = 1; + return 1; +} + +int +termo_stop (termo_t *tk) +{ + if (!tk->is_started) + return 1; + + struct termo_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 +termo_is_started (termo_t *tk) +{ + return tk->is_started; +} + +int +termo_get_fd (termo_t *tk) +{ + return tk->fd; +} + +int +termo_get_flags (termo_t *tk) +{ + return tk->flags; +} + +void +termo_set_flags (termo_t *tk, int newflags) +{ + tk->flags = newflags; + if (tk->flags & TERMO_FLAG_SPACESYMBOL) + tk->canonflags |= TERMO_CANON_SPACESYMBOL; + else + tk->canonflags &= ~TERMO_CANON_SPACESYMBOL; +} + +void +termo_set_waittime (termo_t *tk, int msec) +{ + tk->waittime = msec; +} + +int +termo_get_waittime (termo_t *tk) +{ + return tk->waittime; +} + +int +termo_get_canonflags (termo_t *tk) +{ + return tk->canonflags; +} + +void +termo_set_canonflags (termo_t *tk, int flags) +{ + tk->canonflags = flags; + if (tk->canonflags & TERMO_CANON_SPACESYMBOL) + tk->flags |= TERMO_FLAG_SPACESYMBOL; + else + tk->flags &= ~TERMO_FLAG_SPACESYMBOL; +} + +size_t +termo_get_buffer_size (termo_t *tk) +{ + return tk->buffsize; +} + +int +termo_set_buffer_size (termo_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 +termo_get_buffer_remaining (termo_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 (termo_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 (termo_t *tk, termo_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 termo_result_t +parse_multibyte (termo_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 TERMO_RES_AGAIN; + if (errno == EINVAL) + // Incomplete character or shift sequence + return TERMO_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 TERMO_RES_KEY; + } + + // We can't really get E2BIG so what the fuck is going on here + abort (); + } + return TERMO_RES_KEY; +} + +static void +emit_codepoint (termo_t *tk, uint32_t codepoint, termo_key_t *key) +{ + if (codepoint < 0x20) + { + // C0 range + key->code.codepoint = 0; + key->modifiers = 0; + + if (!(tk->flags & TERMO_FLAG_NOINTERPRET) + && tk->c0[codepoint].sym != TERMO_SYM_UNKNOWN) + { + key->code.sym = tk->c0[codepoint].sym; + key->modifiers |= tk->c0[codepoint].modifier_set; + } + + if (!key->code.sym) + { + key->type = TERMO_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 lowercase instead + key->code.codepoint = codepoint + 0x60; + else + key->code.codepoint = codepoint + 0x40; + key->modifiers = TERMO_KEYMOD_CTRL; + } + else + key->type = TERMO_TYPE_KEYSYM; + } + else if (codepoint == 0x7f && !(tk->flags & TERMO_FLAG_NOINTERPRET)) + { + // ASCII DEL + key->type = TERMO_TYPE_KEYSYM; + key->code.sym = TERMO_SYM_DEL; + key->modifiers = 0; + } + else + { + key->type = TERMO_TYPE_KEY; + key->code.codepoint = codepoint; + key->modifiers = 0; + } + + termo_canonicalise (tk, key); + + if (key->type == TERMO_TYPE_KEY) + fill_multibyte (tk, key); +} + +void +termo_canonicalise (termo_t *tk, termo_key_t *key) +{ + int flags = tk->canonflags; + + if (flags & TERMO_CANON_SPACESYMBOL) + { + if (key->type == TERMO_TYPE_KEY && key->code.codepoint == 0x20) + { + key->type = TERMO_TYPE_KEYSYM; + key->code.sym = TERMO_SYM_SPACE; + } + } + else + { + if (key->type == TERMO_TYPE_KEYSYM + && key->code.sym == TERMO_SYM_SPACE) + { + key->type = TERMO_TYPE_KEY; + key->code.codepoint = 0x20; + fill_multibyte (tk, key); + } + } + + if (flags & TERMO_CANON_DELBS) + if (key->type == TERMO_TYPE_KEYSYM + && key->code.sym == TERMO_SYM_DEL) + key->code.sym = TERMO_SYM_BACKSPACE; +} + +static termo_result_t +peekkey (termo_t *tk, termo_key_t *key, int force, size_t *nbytep) +{ + int again = 0; + + if (!tk->is_started) + { + errno = EINVAL; + return TERMO_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; + } + + termo_result_t ret; + termo_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 TERMO_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 TERMO_RES_EOF: + case TERMO_RES_ERROR: + return ret; + + case TERMO_RES_AGAIN: + if (!force) + again = 1; + case TERMO_RES_NONE: + break; + } + } + + if (again) + return TERMO_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 == TERMO_RES_KEY) + { + print_key (tk, key); + fprintf (stderr, "\n"); + } +#endif + + return ret; +} + +static termo_result_t +peekkey_simple (termo_t *tk, termo_key_t *key, int force, size_t *nbytep) +{ + if (tk->buffcount == 0) + return tk->is_closed ? TERMO_RES_EOF : TERMO_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 TERMO_RES_AGAIN; + + (*tk->method.emit_codepoint) (tk, b0, key); + *nbytep = 1; + return TERMO_RES_KEY; + } + + // Try another key there + tk->buffstart++; + tk->buffcount--; + + // Run the full driver + termo_result_t metakey_result = peekkey (tk, key, force, nbytep); + + tk->buffstart--; + tk->buffcount++; + + switch (metakey_result) + { + case TERMO_RES_KEY: + key->modifiers |= TERMO_KEYMOD_ALT; + (*nbytep)++; + break; + + case TERMO_RES_NONE: + case TERMO_RES_EOF: + case TERMO_RES_AGAIN: + case TERMO_RES_ERROR: + break; + } + + return metakey_result; + } + else if (!(tk->flags & TERMO_FLAG_RAW)) + { + uint32_t codepoint; + termo_result_t res = parse_multibyte + (tk, tk->buffer + tk->buffstart, tk->buffcount, &codepoint, nbytep); + + if (res == TERMO_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 = TERMO_RES_KEY; + } + + key->type = TERMO_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 = TERMO_TYPE_KEY; + key->code.codepoint = b0; + key->modifiers = 0; + + key->multibyte[0] = b0; + key->multibyte[1] = 0; + + *nbytep = 1; + return TERMO_RES_KEY; + } +} + +// XXX: With the current infrastructure I'm not sure how to properly handle +// this. peekkey() isn't made for skipping invalid inputs. +#define INVALID_1005 0x20 + +static termo_result_t +parse_1005_value (const unsigned char **bytes, size_t *len, uint32_t *cp) +{ + unsigned int nbytes; + unsigned char b0 = (*bytes)[0]; + if (b0 < 0x80) + { + // Single byte ASCII + *cp = b0; + nbytes = 1; + goto end; + } + else if (b0 < 0xc0) + { + // Starts with a continuation byte - that's not right + *cp = INVALID_1005; + nbytes = 1; + goto end; + } + 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 = INVALID_1005; + nbytes = 1; + goto end; + } + + for (unsigned int b = 1; b < nbytes; b++) + { + if (b >= *len) + return TERMO_RES_AGAIN; + + unsigned char cb = (*bytes)[b]; + if (cb < 0x80 || cb >= 0xc0) + { + *cp = INVALID_1005; + nbytes = b; + goto end; + } + *cp <<= 6; + *cp |= cb & 0x3f; + } + +end: + (*bytes) += nbytes; + (*len) -= nbytes; + return TERMO_RES_KEY; +} + +static termo_result_t +peekkey_mouse (termo_t *tk, termo_key_t *key, size_t *nbytep) +{ + uint32_t b, x, y; + + // TODO: Add some API to switch on 1005 mode support + if (false) + { + const unsigned char *buff = tk->buffer + tk->buffstart; + size_t len = tk->buffcount; + + if (parse_1005_value (&buff, &len, &b) == TERMO_RES_AGAIN + || parse_1005_value (&buff, &len, &x) == TERMO_RES_AGAIN + || parse_1005_value (&buff, &len, &y) == TERMO_RES_AGAIN) + return TERMO_RES_AGAIN; + + *nbytep = tk->buffcount - len; + } + else + { + if (tk->buffcount < 3) + return TERMO_RES_AGAIN; + + b = CHARAT (0); + x = CHARAT (1); + y = CHARAT (2); + + *nbytep = 3; + } + + key->type = TERMO_TYPE_MOUSE; + key->code.mouse.info = b - 0x20; + key->code.mouse.x = x - 0x20 - 1; + key->code.mouse.y = y - 0x20 - 1; + + key->modifiers = (key->code.mouse.info & 0x1c) >> 2; + key->code.mouse.info &= ~0x1c; + + return TERMO_RES_KEY; +} + +termo_result_t +termo_getkey (termo_t *tk, termo_key_t *key) +{ + size_t nbytes = 0; + termo_result_t ret = peekkey (tk, key, 0, &nbytes); + + if (ret == TERMO_RES_KEY) + eat_bytes (tk, nbytes); + + if (ret == TERMO_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; +} + +termo_result_t +termo_getkey_force (termo_t *tk, termo_key_t *key) +{ + size_t nbytes = 0; + termo_result_t ret = peekkey (tk, key, 1, &nbytes); + + if (ret == TERMO_RES_KEY) + eat_bytes (tk, nbytes); + + return ret; +} + +termo_result_t +termo_waitkey (termo_t *tk, termo_key_t *key) +{ + if (tk->fd == -1) + { + errno = EBADF; + return TERMO_RES_ERROR; + } + + while (1) + { + termo_result_t ret = termo_getkey (tk, key); + + switch (ret) + { + case TERMO_RES_KEY: + case TERMO_RES_EOF: + case TERMO_RES_ERROR: + return ret; + + case TERMO_RES_NONE: + ret = termo_advisereadable (tk); + if (ret == TERMO_RES_ERROR) + return ret; + break; + + case TERMO_RES_AGAIN: + { + if (tk->is_closed) + // We're closed now. Never going to get more bytes + // so just go with what we have + return termo_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 & TERMO_FLAG_EINTR)) + goto retry; + + return TERMO_RES_ERROR; + } + + if (fd.revents & (POLLIN | POLLHUP | POLLERR)) + ret = termo_advisereadable (tk); + else + ret = TERMO_RES_NONE; + + if (ret == TERMO_RES_ERROR) + return ret; + if (ret == TERMO_RES_NONE) + return termo_getkey_force (tk, key); + } + } + } + + /* UNREACHABLE */ +} + +termo_result_t +termo_advisereadable (termo_t *tk) +{ + if (tk->fd == -1) + { + errno = EBADF; + return TERMO_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 TERMO_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 TERMO_RES_NONE; + if (errno == EINTR && !(tk->flags & TERMO_FLAG_EINTR)) + goto retry; + return TERMO_RES_ERROR; + } + if (len < 1) + { + tk->is_closed = true; + return TERMO_RES_NONE; + } + tk->buffcount += len; + return TERMO_RES_AGAIN; +} + +size_t +termo_push_bytes (termo_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; +} + +termo_sym_t +termo_register_keyname (termo_t *tk, termo_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 * +termo_get_keyname (termo_t *tk, termo_sym_t sym) +{ + if (sym == TERMO_SYM_UNKNOWN) + return "UNKNOWN"; + if (sym < tk->nkeynames) + return tk->keynames[sym]; + return "UNKNOWN"; +} + +static const char * +termo_lookup_keyname_format (termo_t *tk, + const char *str, termo_sym_t *sym, termo_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 & TERMO_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 * +termo_lookup_keyname (termo_t *tk, const char *str, termo_sym_t *sym) +{ + return termo_lookup_keyname_format (tk, str, sym, 0); +} + +termo_sym_t +termo_keyname2sym (termo_t *tk, const char *keyname) +{ + termo_sym_t sym; + const char *endp = termo_lookup_keyname (tk, keyname, &sym); + if (!endp || endp[0]) + return TERMO_SYM_UNKNOWN; + return sym; +} + +static termo_sym_t +register_c0 (termo_t *tk, + termo_sym_t sym, unsigned char ctrl, const char *name) +{ + return register_c0_full (tk, sym, 0, 0, ctrl, name); +} + +static termo_sym_t +register_c0_full (termo_t *tk, termo_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 = termo_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 +termo_strfkey (termo_t *tk, char *buffer, size_t len, + termo_key_t *key, termo_format_t format) +{ + size_t pos = 0; + size_t l = 0; + + struct modnames *mods = &modnames[ + !!(format & TERMO_FORMAT_LONGMOD) + + !!(format & TERMO_FORMAT_ALTISMETA) * 2 + + !!(format & TERMO_FORMAT_LOWERMOD) * 4]; + + int wrapbracket = (format & TERMO_FORMAT_WRAPBRACKET) && + (key->type != TERMO_TYPE_KEY || key->modifiers != 0); + + char sep = (format & TERMO_FORMAT_SPACEMOD) ? ' ' : '-'; + + if (format & TERMO_FORMAT_CARETCTRL && + key->type == TERMO_TYPE_KEY && + key->modifiers == TERMO_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 & TERMO_KEYMOD_ALT) + { + l = snprintf (buffer + pos, len - pos, "%s%c", mods->alt, sep); + if (l <= 0) + return pos; + pos += l; + } + if (key->modifiers & TERMO_KEYMOD_CTRL) + { + l = snprintf (buffer + pos, len - pos, "%s%c", mods->ctrl, sep); + if (l <= 0) + return pos; + pos += l; + } + if (key->modifiers & TERMO_KEYMOD_SHIFT) + { + l = snprintf (buffer + pos, len - pos, "%s%c", mods->shift, sep); + if (l <= 0) + return pos; + pos += l; + } + + switch (key->type) + { + case TERMO_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 TERMO_TYPE_KEYSYM: + { + const char *name = termo_get_keyname (tk, key->code.sym); + if (format & TERMO_FORMAT_LOWERSPACE) + l = snprint_cameltospaces (buffer + pos, len - pos, name); + else + l = snprintf (buffer + pos, len - pos, "%s", name); + break; + } + case TERMO_TYPE_FUNCTION: + l = snprintf (buffer + pos, len - pos, "%c%d", + (format & TERMO_FORMAT_LOWERSPACE ? 'f' : 'F'), key->code.number); + break; + case TERMO_TYPE_MOUSE: + { + termo_mouse_event_t ev; + int button; + int line, col; + termo_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 & TERMO_FORMAT_MOUSE_POS) + { + if (l <= 0) + return pos; + pos += l; + l = snprintf (buffer + pos, len - pos, " @ (%u,%u)", col, line); + } + break; + } + case TERMO_TYPE_POSITION: + l = snprintf (buffer + pos, len - pos, "Position"); + break; + case TERMO_TYPE_MODEREPORT: + { + int initial, mode, value; + termo_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 TERMO_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 * +termo_strpkey (termo_t *tk, + const char *str, termo_key_t *key, termo_format_t format) +{ + struct modnames *mods = &modnames[ + !!(format & TERMO_FORMAT_LONGMOD) + + !!(format & TERMO_FORMAT_ALTISMETA) * 2 + + !!(format & TERMO_FORMAT_LOWERMOD) * 4]; + + key->modifiers = 0; + + if ((format & TERMO_FORMAT_CARETCTRL) && str[0] == '^' && str[1]) + { + str = termo_strpkey (tk, + str + 1, key, format & ~TERMO_FORMAT_CARETCTRL); + + if (!str + || key->type != TERMO_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 = TERMO_KEYMOD_CTRL; + fill_multibyte (tk, key); + return (char *) str; + } + + const char *sep_at; + while ((sep_at = strchr (str, + (format & TERMO_FORMAT_SPACEMOD) ? ' ' : '-'))) + { + size_t n = sep_at - str; + if (n == strlen (mods->alt) && !strncmp (mods->alt, str, n)) + key->modifiers |= TERMO_KEYMOD_ALT; + else if (n == strlen (mods->ctrl) && !strncmp (mods->ctrl, str, n)) + key->modifiers |= TERMO_KEYMOD_CTRL; + else if (n == strlen (mods->shift) && !strncmp (mods->shift, str, n)) + key->modifiers |= TERMO_KEYMOD_SHIFT; + else + break; + + str = sep_at + 1; + } + + size_t nbytes; + ssize_t snbytes; + const char *endstr; + + if ((endstr = termo_lookup_keyname_format + (tk, str, &key->code.sym, format))) + { + key->type = TERMO_TYPE_KEYSYM; + str = endstr; + } + else if (sscanf(str, "F%d%zn", &key->code.number, &snbytes) == 1) + { + key->type = TERMO_TYPE_FUNCTION; + str += snbytes; + } + // Multibyte must be last + else if (parse_multibyte (tk, (unsigned const char *) str, strlen (str), + &key->code.codepoint, &nbytes) == TERMO_RES_KEY) + { + key->type = TERMO_TYPE_KEY; + fill_multibyte (tk, key); + str += nbytes; + } + // TODO: Consider mouse events? + else + return NULL; + + termo_canonicalise (tk, key); + return (char *) str; +} + +int +termo_keycmp (termo_t *tk, + const termo_key_t *key1p, const termo_key_t *key2p) +{ + /* Copy the key structs since we'll be modifying them */ + termo_key_t key1 = *key1p, key2 = *key2p; + + termo_canonicalise (tk, &key1); + termo_canonicalise (tk, &key2); + + if (key1.type != key2.type) + return key1.type - key2.type; + + switch (key1.type) + { + case TERMO_TYPE_KEY: + if (key1.code.codepoint != key2.code.codepoint) + return key1.code.codepoint - key2.code.codepoint; + break; + case TERMO_TYPE_KEYSYM: + if (key1.code.sym != key2.code.sym) + return key1.code.sym - key2.code.sym; + break; + case TERMO_TYPE_FUNCTION: + case TERMO_TYPE_UNKNOWN_CSI: + if (key1.code.number != key2.code.number) + return key1.code.number - key2.code.number; + break; + case TERMO_TYPE_MOUSE: + { + int cmp = memcmp (&key1.code.mouse, &key2.code.mouse, + sizeof key1.code.mouse); + if (cmp != 0) + return cmp; + break; + } + case TERMO_TYPE_POSITION: + { + int line1, col1, line2, col2; + termo_interpret_position (tk, &key1, &line1, &col1); + termo_interpret_position (tk, &key2, &line2, &col2); + if (line1 != line2) + return line1 - line2; + return col1 - col2; + } + case TERMO_TYPE_MODEREPORT: + { + int initial1, initial2, mode1, mode2, value1, value2; + termo_interpret_modereport (tk, &key1, &initial1, &mode1, &value1); + termo_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; +} + diff --git a/termo.h b/termo.h new file mode 100644 index 0000000..3bd1a00 --- /dev/null +++ b/termo.h @@ -0,0 +1,277 @@ +#ifndef TERMO_H +#define TERMO_H + +#include +#include +#include + +#include "termo-config.h" + +#define TERMO_CHECK_VERSION \ + termo_check_version (TERMO_VERSION_MAJOR, TERMO_VERSION_MINOR) + +typedef enum termo_sym termo_sym_t; +enum termo_sym +{ + TERMO_SYM_UNKNOWN = -1, + TERMO_SYM_NONE = 0, + + /* Special names in C0 */ + TERMO_SYM_BACKSPACE, + TERMO_SYM_TAB, + TERMO_SYM_ENTER, + TERMO_SYM_ESCAPE, + + /* Special names in G0 */ + TERMO_SYM_SPACE, + TERMO_SYM_DEL, + + /* Special keys */ + TERMO_SYM_UP, + TERMO_SYM_DOWN, + TERMO_SYM_LEFT, + TERMO_SYM_RIGHT, + TERMO_SYM_BEGIN, + TERMO_SYM_FIND, + TERMO_SYM_INSERT, + TERMO_SYM_DELETE, + TERMO_SYM_SELECT, + TERMO_SYM_PAGEUP, + TERMO_SYM_PAGEDOWN, + TERMO_SYM_HOME, + TERMO_SYM_END, + + /* Special keys from terminfo */ + TERMO_SYM_CANCEL, + TERMO_SYM_CLEAR, + TERMO_SYM_CLOSE, + TERMO_SYM_COMMAND, + TERMO_SYM_COPY, + TERMO_SYM_EXIT, + TERMO_SYM_HELP, + TERMO_SYM_MARK, + TERMO_SYM_MESSAGE, + TERMO_SYM_MOVE, + TERMO_SYM_OPEN, + TERMO_SYM_OPTIONS, + TERMO_SYM_PRINT, + TERMO_SYM_REDO, + TERMO_SYM_REFERENCE, + TERMO_SYM_REFRESH, + TERMO_SYM_REPLACE, + TERMO_SYM_RESTART, + TERMO_SYM_RESUME, + TERMO_SYM_SAVE, + TERMO_SYM_SUSPEND, + TERMO_SYM_UNDO, + + /* Numeric keypad special keys */ + TERMO_SYM_KP0, + TERMO_SYM_KP1, + TERMO_SYM_KP2, + TERMO_SYM_KP3, + TERMO_SYM_KP4, + TERMO_SYM_KP5, + TERMO_SYM_KP6, + TERMO_SYM_KP7, + TERMO_SYM_KP8, + TERMO_SYM_KP9, + TERMO_SYM_KPENTER, + TERMO_SYM_KPPLUS, + TERMO_SYM_KPMINUS, + TERMO_SYM_KPMULT, + TERMO_SYM_KPDIV, + TERMO_SYM_KPCOMMA, + TERMO_SYM_KPPERIOD, + TERMO_SYM_KPEQUALS, + + TERMO_N_SYMS +}; + +typedef enum termo_type termo_type_t; +enum termo_type +{ + TERMO_TYPE_KEY, + TERMO_TYPE_FUNCTION, + TERMO_TYPE_KEYSYM, + TERMO_TYPE_MOUSE, + TERMO_TYPE_POSITION, + TERMO_TYPE_MODEREPORT, + /* add other recognised types here */ + + TERMO_TYPE_UNKNOWN_CSI = -1 +}; + +typedef enum termo_result termo_result_t; +enum termo_result +{ + TERMO_RES_NONE, + TERMO_RES_KEY, + TERMO_RES_EOF, + TERMO_RES_AGAIN, + TERMO_RES_ERROR +}; + +typedef enum termo_mouse_event termo_mouse_event_t; +enum termo_mouse_event +{ + TERMO_MOUSE_UNKNOWN, + TERMO_MOUSE_PRESS, + TERMO_MOUSE_DRAG, + TERMO_MOUSE_RELEASE +}; + +enum +{ + TERMO_KEYMOD_SHIFT = 1 << 0, + TERMO_KEYMOD_ALT = 1 << 1, + TERMO_KEYMOD_CTRL = 1 << 2 +}; + +typedef struct termo_key termo_key_t; +struct termo_key +{ + termo_type_t type; + union + { + uint32_t codepoint; /* TERMO_TYPE_KEY */ + int number; /* TERMO_TYPE_FUNCTION */ + termo_sym_t sym; /* TERMO_TYPE_KEYSYM */ + + /* TERMO_TYPE_MODEREPORT */ + /* opaque, see termo_interpret_modereport() */ + struct { char initial; int mode, value; } mode; + + /* TERMO_TYPE_MOUSE */ + /* opaque, see termo_interpret_mouse() */ + struct { uint16_t x, y, info; } mouse; + } code; + + int modifiers; + + /* The raw multibyte sequence for the key */ + char multibyte[MB_LEN_MAX + 1]; +}; + +typedef struct termo termo_t; + +enum +{ + /* Do not interpret C0//DEL codes if possible */ + TERMO_FLAG_NOINTERPRET = 1 << 0, + /* Convert KP codes to regular keypresses */ + TERMO_FLAG_CONVERTKP = 1 << 1, + /* Don't try to decode the input characters */ + TERMO_FLAG_RAW = 1 << 2, + /* Do not make initial termios calls on construction */ + TERMO_FLAG_NOTERMIOS = 1 << 4, + /* Sets TERMO_CANON_SPACESYMBOL */ + TERMO_FLAG_SPACESYMBOL = 1 << 5, + /* Allow Ctrl-C to be read as normal, disabling SIGINT */ + TERMO_FLAG_CTRLC = 1 << 6, + /* Return ERROR on signal (EINTR) rather than retry */ + TERMO_FLAG_EINTR = 1 << 7 +}; + +enum +{ + TERMO_CANON_SPACESYMBOL = 1 << 0, /* Space is symbolic rather than Unicode */ + TERMO_CANON_DELBS = 1 << 1 /* Del is converted to Backspace */ +}; + +void termo_check_version (int major, int minor); + +termo_t *termo_new (int fd, const char *encoding, int flags); +termo_t *termo_new_abstract (const char *term, + const char *encoding, int flags); +void termo_free (termo_t *tk); +void termo_destroy (termo_t *tk); + +int termo_start (termo_t *tk); +int termo_stop (termo_t *tk); +int termo_is_started (termo_t *tk); + +int termo_get_fd (termo_t *tk); + +int termo_get_flags (termo_t *tk); +void termo_set_flags (termo_t *tk, int newflags); + +int termo_get_waittime (termo_t *tk); +void termo_set_waittime (termo_t *tk, int msec); + +int termo_get_canonflags (termo_t *tk); +void termo_set_canonflags (termo_t *tk, int flags); + +size_t termo_get_buffer_size (termo_t *tk); +int termo_set_buffer_size (termo_t *tk, size_t size); + +size_t termo_get_buffer_remaining (termo_t *tk); + +void termo_canonicalise (termo_t *tk, termo_key_t *key); + +termo_result_t termo_getkey (termo_t *tk, termo_key_t *key); +termo_result_t termo_getkey_force (termo_t *tk, termo_key_t *key); +termo_result_t termo_waitkey (termo_t *tk, termo_key_t *key); + +termo_result_t termo_advisereadable (termo_t *tk); + +size_t termo_push_bytes (termo_t *tk, const char *bytes, size_t len); + +termo_sym_t termo_register_keyname (termo_t *tk, + termo_sym_t sym, const char *name); +const char *termo_get_keyname (termo_t *tk, termo_sym_t sym); +const char *termo_lookup_keyname (termo_t *tk, + const char *str, termo_sym_t *sym); + +termo_sym_t termo_keyname2sym (termo_t *tk, const char *keyname); + +termo_result_t termo_interpret_mouse (termo_t *tk, + const termo_key_t *key, termo_mouse_event_t *event, + int *button, int *line, int *col); +termo_result_t termo_interpret_position (termo_t *tk, + const termo_key_t *key, int *line, int *col); +termo_result_t termo_interpret_modereport (termo_t *tk, + const termo_key_t *key, int *initial, int *mode, int *value); +termo_result_t termo_interpret_csi (termo_t *tk, + const termo_key_t *key, long args[], size_t *nargs, unsigned long *cmd); + +typedef enum termo_format termo_format_t; +enum termo_format +{ + /* Shift-... instead of S-... */ + TERMO_FORMAT_LONGMOD = 1 << 0, + /* ^X instead of C-X */ + TERMO_FORMAT_CARETCTRL = 1 << 1, + /* Meta- or M- instead of Alt- or A- */ + TERMO_FORMAT_ALTISMETA = 1 << 2, + /* Wrap special keys in brackets like */ + TERMO_FORMAT_WRAPBRACKET = 1 << 3, + /* M Foo instead of M-Foo */ + TERMO_FORMAT_SPACEMOD = 1 << 4, + /* meta or m instead of Meta or M */ + TERMO_FORMAT_LOWERMOD = 1 << 5, + /* page down instead of PageDown */ + TERMO_FORMAT_LOWERSPACE = 1 << 6, + /* Include mouse position if relevant; @ col,line */ + TERMO_FORMAT_MOUSE_POS = 1 << 8 +}; + +/* Some useful combinations */ + +#define TERMO_FORMAT_VIM (termo_format_t) \ + (TERMO_FORMAT_ALTISMETA | TERMO_FORMAT_WRAPBRACKET) +#define TERMO_FORMAT_URWID (termo_format_t) \ + (TERMO_FORMAT_LONGMOD | TERMO_FORMAT_ALTISMETA | \ + TERMO_FORMAT_LOWERMOD | TERMO_FORMAT_SPACEMOD | \ + TERMO_FORMAT_LOWERSPACE) + +size_t termo_strfkey (termo_t *tk, char *buffer, size_t len, + termo_key_t *key, termo_format_t format); +const char *termo_strpkey (termo_t *tk, const char *str, + termo_key_t *key, termo_format_t format); + +int termo_keycmp (termo_t *tk, + const termo_key_t *key1, const termo_key_t *key2); + +#endif // ! TERMO_H + -- cgit v1.2.3-70-g09d2