mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	build: vendor libtermkey
This is a proof of concept/WIP to evaluate the viability of vendoring libtermkey as it's been deprecated.
This commit is contained in:
		
				
					committed by
					
						
						Gregory Anders
					
				
			
			
				
	
			
			
			
						parent
						
							7feed6ccb7
						
					
				
				
					commit
					404043e74c
				
			@@ -35,7 +35,6 @@ set(DEPS_INCLUDE_FLAGS "-I${DEPS_INSTALL_DIR}/include -I${DEPS_INSTALL_DIR}/incl
 | 
			
		||||
option(USE_BUNDLED "Use bundled dependencies." ON)
 | 
			
		||||
 | 
			
		||||
option(USE_BUNDLED_UNIBILIUM "Use the bundled unibilium." ${USE_BUNDLED})
 | 
			
		||||
option(USE_BUNDLED_LIBTERMKEY "Use the bundled libtermkey." ${USE_BUNDLED})
 | 
			
		||||
option(USE_BUNDLED_LIBVTERM "Use the bundled libvterm." ${USE_BUNDLED})
 | 
			
		||||
option(USE_BUNDLED_LIBUV "Use the bundled libuv." ${USE_BUNDLED})
 | 
			
		||||
option(USE_BUNDLED_MSGPACK "Use the bundled msgpack." ${USE_BUNDLED})
 | 
			
		||||
@@ -93,13 +92,6 @@ if(USE_BUNDLED_UNIBILIUM)
 | 
			
		||||
  include(BuildUnibilium)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(USE_BUNDLED_LIBTERMKEY)
 | 
			
		||||
  include(BuildLibtermkey)
 | 
			
		||||
  if(USE_BUNDLED_UNIBILIUM)
 | 
			
		||||
    add_dependencies(libtermkey unibilium)
 | 
			
		||||
  endif()
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(USE_BUNDLED_LIBVTERM)
 | 
			
		||||
  include(BuildLibvterm)
 | 
			
		||||
endif()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
ExternalProject_Add(libtermkey
 | 
			
		||||
  URL ${LIBTERMKEY_URL}
 | 
			
		||||
  URL_HASH SHA256=${LIBTERMKEY_SHA256}
 | 
			
		||||
  DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libtermkey
 | 
			
		||||
  PATCH_COMMAND ${CMAKE_COMMAND} -E copy
 | 
			
		||||
    ${CMAKE_CURRENT_SOURCE_DIR}/cmake/LibtermkeyCMakeLists.txt
 | 
			
		||||
    ${DEPS_BUILD_DIR}/src/libtermkey/CMakeLists.txt
 | 
			
		||||
  CMAKE_ARGS ${DEPS_CMAKE_ARGS}
 | 
			
		||||
    -D CMAKE_SHARED_LIBRARY_LINK_C_FLAGS="" # Hack to avoid -rdynamic in Mingw
 | 
			
		||||
    -D UNIBILIUM_INCLUDE_DIRS=${DEPS_INSTALL_DIR}/include
 | 
			
		||||
    -D UNIBILIUM_LIBRARIES=${DEPS_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}unibilium${CMAKE_STATIC_LIBRARY_SUFFIX}
 | 
			
		||||
  CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS}
 | 
			
		||||
  ${EXTERNALPROJECT_OPTIONS})
 | 
			
		||||
@@ -1,37 +0,0 @@
 | 
			
		||||
cmake_minimum_required(VERSION 3.10)
 | 
			
		||||
# Can be removed once minimum version is at least 3.15
 | 
			
		||||
if(POLICY CMP0092)
 | 
			
		||||
  cmake_policy(SET CMP0092 NEW)
 | 
			
		||||
endif()
 | 
			
		||||
project(libtermkey C)
 | 
			
		||||
 | 
			
		||||
add_compile_options(-w)
 | 
			
		||||
 | 
			
		||||
if(EXISTS ${PROJECT_SOURCE_DIR}/termkey.h.in)
 | 
			
		||||
  file(STRINGS Makefile TERMKEY_VERSION_MAJOR REGEX "VERSION_MAJOR")
 | 
			
		||||
  string(REGEX MATCH "[0-9]+" TERMKEY_VERSION_MAJOR ${TERMKEY_VERSION_MAJOR})
 | 
			
		||||
 | 
			
		||||
  file(STRINGS Makefile TERMKEY_VERSION_MINOR REGEX "VERSION_MINOR")
 | 
			
		||||
  string(REGEX MATCH "[0-9]+" TERMKEY_VERSION_MINOR ${TERMKEY_VERSION_MINOR})
 | 
			
		||||
 | 
			
		||||
  file(READ termkey.h.in TERMKEY_TEXT)
 | 
			
		||||
  string(REPLACE "@@VERSION_MAJOR@@" "${TERMKEY_VERSION_MAJOR}" TERMKEY_TEXT "${TERMKEY_TEXT}")
 | 
			
		||||
  string(REPLACE "@@VERSION_MINOR@@" "${TERMKEY_VERSION_MINOR}" TERMKEY_TEXT "${TERMKEY_TEXT}")
 | 
			
		||||
  file(WRITE termkey.h "${TERMKEY_TEXT}")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
add_library(termkey termkey.c driver-csi.c driver-ti.c)
 | 
			
		||||
 | 
			
		||||
target_compile_definitions(termkey PRIVATE HAVE_UNIBILIUM)
 | 
			
		||||
target_include_directories(termkey PRIVATE SYSTEM ${UNIBILIUM_INCLUDE_DIRS})
 | 
			
		||||
 | 
			
		||||
set_target_properties(termkey PROPERTIES
 | 
			
		||||
  PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/termkey.h)
 | 
			
		||||
target_link_libraries(termkey PRIVATE ${UNIBILIUM_LIBRARIES})
 | 
			
		||||
 | 
			
		||||
include(GNUInstallDirs)
 | 
			
		||||
install(TARGETS termkey
 | 
			
		||||
  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
 | 
			
		||||
  PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
 | 
			
		||||
 | 
			
		||||
# vim: set ft=cmake:
 | 
			
		||||
@@ -13,9 +13,6 @@ LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333
 | 
			
		||||
UNIBILIUM_URL https://github.com/neovim/unibilium/archive/d72c3598e7ac5d1ebf86ee268b8b4ed95c0fa628.tar.gz
 | 
			
		||||
UNIBILIUM_SHA256 9c4747c862ab5e3076dcf8fa8f0ea7a6b50f20ec5905618b9536655596797487
 | 
			
		||||
 | 
			
		||||
LIBTERMKEY_URL https://github.com/neovim/libtermkey/archive/dcb198a85c520ce38450a5970c97f8ff03b50f0e.tar.gz
 | 
			
		||||
LIBTERMKEY_SHA256 5eb3e50af54312817bd56fa63b9f8dbd12ec11cedbcf8a7aa0fd79950cc83259
 | 
			
		||||
 | 
			
		||||
LIBVTERM_URL https://github.com/neovim/libvterm/archive/v0.3.3.tar.gz
 | 
			
		||||
LIBVTERM_SHA256 0babe3ab42c354925dadede90d352f054aa9c4ae6842ea803a20c9741e172e56
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
find_path2(LIBTERMKEY_INCLUDE_DIR termkey.h)
 | 
			
		||||
find_library2(LIBTERMKEY_LIBRARY NAMES termkey)
 | 
			
		||||
find_package_handle_standard_args(Libtermkey DEFAULT_MSG
 | 
			
		||||
  LIBTERMKEY_LIBRARY LIBTERMKEY_INCLUDE_DIR)
 | 
			
		||||
mark_as_advanced(LIBTERMKEY_INCLUDE_DIR LIBTERMKEY_LIBRARY)
 | 
			
		||||
 | 
			
		||||
add_library(libtermkey INTERFACE)
 | 
			
		||||
target_include_directories(libtermkey SYSTEM BEFORE INTERFACE ${LIBTERMKEY_INCLUDE_DIR})
 | 
			
		||||
target_link_libraries(libtermkey INTERFACE ${LIBTERMKEY_LIBRARY})
 | 
			
		||||
@@ -37,7 +37,6 @@ target_link_libraries(main_lib INTERFACE ${LUV_LIBRARY})
 | 
			
		||||
 | 
			
		||||
find_package(Iconv REQUIRED)
 | 
			
		||||
find_package(Lpeg REQUIRED)
 | 
			
		||||
find_package(Libtermkey 0.22 REQUIRED)
 | 
			
		||||
find_package(Libvterm 0.3.3 REQUIRED)
 | 
			
		||||
find_package(Msgpack 1.0.0 REQUIRED)
 | 
			
		||||
find_package(Treesitter 0.20.8 REQUIRED)
 | 
			
		||||
@@ -45,7 +44,6 @@ find_package(Unibilium 2.0 REQUIRED)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(main_lib INTERFACE
 | 
			
		||||
  iconv
 | 
			
		||||
  libtermkey
 | 
			
		||||
  libvterm
 | 
			
		||||
  msgpack
 | 
			
		||||
  treesitter
 | 
			
		||||
@@ -60,6 +58,8 @@ if (LIBINTL_LIBRARY)
 | 
			
		||||
  target_link_libraries(main_lib INTERFACE ${LIBINTL_LIBRARY})
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
target_compile_definitions(main_lib INTERFACE HAVE_UNIBILIUM)
 | 
			
		||||
 | 
			
		||||
# The unit test lib requires LuaJIT; it will be skipped if LuaJIT is missing.
 | 
			
		||||
option(PREFER_LUA "Prefer Lua over LuaJIT in the nvim executable." OFF)
 | 
			
		||||
if(PREFER_LUA)
 | 
			
		||||
@@ -376,8 +376,8 @@ file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR})
 | 
			
		||||
 | 
			
		||||
glob_wrapper(NVIM_SOURCES *.c)
 | 
			
		||||
glob_wrapper(NVIM_HEADERS *.h)
 | 
			
		||||
glob_wrapper(EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c ../cjson/*.c ../klib/*.c)
 | 
			
		||||
glob_wrapper(EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h ../cjson/*.h ../klib/*.h)
 | 
			
		||||
glob_wrapper(EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c ../cjson/*.c ../klib/*.c ../termkey/*.c)
 | 
			
		||||
glob_wrapper(EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h ../cjson/*.h ../klib/*.h ../termkey/*.h)
 | 
			
		||||
 | 
			
		||||
glob_wrapper(NLUA0_SOURCES ../mpack/*.c)
 | 
			
		||||
 | 
			
		||||
@@ -436,13 +436,13 @@ endforeach()
 | 
			
		||||
 | 
			
		||||
list(REMOVE_ITEM NVIM_SOURCES ${to_remove})
 | 
			
		||||
 | 
			
		||||
# xdiff, mpack, lua-cjson: inlined external project, we don't maintain it. #9306
 | 
			
		||||
# xdiff, mpack, lua-cjson, termkey: inlined external project, we don't maintain it. #9306
 | 
			
		||||
if(MSVC)
 | 
			
		||||
  set_source_files_properties(
 | 
			
		||||
    ${EXTERNAL_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -wd4090 -wd4244 -wd4267")
 | 
			
		||||
else()
 | 
			
		||||
  set_source_files_properties(
 | 
			
		||||
    ${EXTERNAL_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion -Wno-missing-noreturn -Wno-missing-format-attribute -Wno-double-promotion -Wno-strict-prototypes -Wno-misleading-indentation")
 | 
			
		||||
    ${EXTERNAL_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion -Wno-missing-noreturn -Wno-missing-format-attribute -Wno-double-promotion -Wno-strict-prototypes -Wno-misleading-indentation -Wno-sign-compare -Wno-implicit-fallthrough -Wno-missing-prototypes -Wno-missing-field-initializers")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Log level (NVIM_LOG_DEBUG in log.h)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <termkey.h>
 | 
			
		||||
#include <uv.h>
 | 
			
		||||
 | 
			
		||||
#include "nvim/event/loop.h"
 | 
			
		||||
@@ -12,6 +11,7 @@
 | 
			
		||||
#include "nvim/tui/input_defs.h"  // IWYU pragma: export
 | 
			
		||||
#include "nvim/tui/tui.h"
 | 
			
		||||
#include "nvim/types_defs.h"
 | 
			
		||||
#include "termkey/termkey.h"
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  kKeyEncodingLegacy,  ///< Legacy key encoding
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										768
									
								
								src/termkey/driver-csi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										768
									
								
								src/termkey/driver-csi.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,768 @@
 | 
			
		||||
#include "termkey.h"
 | 
			
		||||
#include "termkey-internal.h"
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
// There are 64 codes 0x40 - 0x7F
 | 
			
		||||
static int keyinfo_initialised = 0;
 | 
			
		||||
static struct keyinfo ss3s[64];
 | 
			
		||||
static char ss3_kpalts[64];
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  TermKey *tk;
 | 
			
		||||
  int saved_string_id;
 | 
			
		||||
  char *saved_string;
 | 
			
		||||
} TermKeyCsi;
 | 
			
		||||
 | 
			
		||||
typedef TermKeyResult CsiHandler(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args);
 | 
			
		||||
static CsiHandler *csi_handlers[64];
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Handler for CSI/SS3 cmd keys
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static struct keyinfo csi_ss3s[64];
 | 
			
		||||
 | 
			
		||||
static TermKeyResult handle_csi_ss3_full(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args)
 | 
			
		||||
{
 | 
			
		||||
  if(args > 1 && arg[1] != -1)
 | 
			
		||||
    key->modifiers = arg[1] - 1;
 | 
			
		||||
  else
 | 
			
		||||
    key->modifiers = 0;
 | 
			
		||||
 | 
			
		||||
  key->type = csi_ss3s[cmd - 0x40].type;
 | 
			
		||||
  key->code.sym = csi_ss3s[cmd - 0x40].sym;
 | 
			
		||||
  key->modifiers &= ~(csi_ss3s[cmd - 0x40].modifier_mask);
 | 
			
		||||
  key->modifiers |= csi_ss3s[cmd - 0x40].modifier_set;
 | 
			
		||||
 | 
			
		||||
  if(key->code.sym == TERMKEY_SYM_UNKNOWN)
 | 
			
		||||
    return TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
  return TERMKEY_RES_KEY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void register_csi_ss3_full(TermKeyType type, TermKeySym sym, int modifier_set, int modifier_mask, unsigned char cmd)
 | 
			
		||||
{
 | 
			
		||||
  if(cmd < 0x40 || cmd >= 0x80) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  csi_ss3s[cmd - 0x40].type = type;
 | 
			
		||||
  csi_ss3s[cmd - 0x40].sym = sym;
 | 
			
		||||
  csi_ss3s[cmd - 0x40].modifier_set = modifier_set;
 | 
			
		||||
  csi_ss3s[cmd - 0x40].modifier_mask = modifier_mask;
 | 
			
		||||
 | 
			
		||||
  csi_handlers[cmd - 0x40] = &handle_csi_ss3_full;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void register_csi_ss3(TermKeyType type, TermKeySym sym, unsigned char cmd)
 | 
			
		||||
{
 | 
			
		||||
  register_csi_ss3_full(type, sym, 0, 0, cmd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Handler for SS3 keys with kpad alternate representations
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static void register_ss3kpalt(TermKeyType type, TermKeySym sym, unsigned char cmd, char kpalt)
 | 
			
		||||
{
 | 
			
		||||
  if(cmd < 0x40 || cmd >= 0x80) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ss3s[cmd - 0x40].type = type;
 | 
			
		||||
  ss3s[cmd - 0x40].sym = sym;
 | 
			
		||||
  ss3s[cmd - 0x40].modifier_set = 0;
 | 
			
		||||
  ss3s[cmd - 0x40].modifier_mask = 0;
 | 
			
		||||
  ss3_kpalts[cmd - 0x40] = kpalt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Handler for CSI number ~ function keys
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static struct keyinfo csifuncs[35]; /* This value must be increased if more CSI function keys are added */
 | 
			
		||||
#define NCSIFUNCS (sizeof(csifuncs)/sizeof(csifuncs[0]))
 | 
			
		||||
 | 
			
		||||
static TermKeyResult handle_csifunc(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args)
 | 
			
		||||
{
 | 
			
		||||
  if(args > 1 && arg[1] != -1)
 | 
			
		||||
    key->modifiers = arg[1] - 1;
 | 
			
		||||
  else
 | 
			
		||||
    key->modifiers = 0;
 | 
			
		||||
 | 
			
		||||
  key->type = TERMKEY_TYPE_KEYSYM;
 | 
			
		||||
 | 
			
		||||
  if(arg[0] == 27) {
 | 
			
		||||
    int mod = key->modifiers;
 | 
			
		||||
    (*tk->method.emit_codepoint)(tk, arg[2], key);
 | 
			
		||||
    key->modifiers |= mod;
 | 
			
		||||
  }
 | 
			
		||||
  else if(arg[0] >= 0 && arg[0] < NCSIFUNCS) {
 | 
			
		||||
    key->type = csifuncs[arg[0]].type;
 | 
			
		||||
    key->code.sym = csifuncs[arg[0]].sym;
 | 
			
		||||
    key->modifiers &= ~(csifuncs[arg[0]].modifier_mask);
 | 
			
		||||
    key->modifiers |= csifuncs[arg[0]].modifier_set;
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
    key->code.sym = TERMKEY_SYM_UNKNOWN;
 | 
			
		||||
 | 
			
		||||
  if(key->code.sym == TERMKEY_SYM_UNKNOWN) {
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
    fprintf(stderr, "CSI: Unknown function key %ld\n", arg[0]);
 | 
			
		||||
#endif
 | 
			
		||||
    return TERMKEY_RES_NONE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return TERMKEY_RES_KEY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void register_csifunc(TermKeyType type, TermKeySym sym, int number)
 | 
			
		||||
{
 | 
			
		||||
  if(number >= NCSIFUNCS) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  csifuncs[number].type = type;
 | 
			
		||||
  csifuncs[number].sym = sym;
 | 
			
		||||
  csifuncs[number].modifier_set = 0;
 | 
			
		||||
  csifuncs[number].modifier_mask = 0;
 | 
			
		||||
 | 
			
		||||
  csi_handlers['~' - 0x40] = &handle_csifunc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Handler for CSI u extended Unicode keys
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static TermKeyResult handle_csi_u(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args)
 | 
			
		||||
{
 | 
			
		||||
  switch(cmd) {
 | 
			
		||||
    case 'u': {
 | 
			
		||||
      if(args > 1 && arg[1] != -1)
 | 
			
		||||
        key->modifiers = arg[1] - 1;
 | 
			
		||||
      else
 | 
			
		||||
        key->modifiers = 0;
 | 
			
		||||
 | 
			
		||||
      int mod = key->modifiers;
 | 
			
		||||
      key->type = TERMKEY_TYPE_KEYSYM;
 | 
			
		||||
      (*tk->method.emit_codepoint)(tk, arg[0], key);
 | 
			
		||||
      key->modifiers |= mod;
 | 
			
		||||
 | 
			
		||||
      return TERMKEY_RES_KEY;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return TERMKEY_RES_NONE;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Handler for CSI M / CSI m mouse events in SGR and rxvt encodings
 | 
			
		||||
 * Note: This does not handle X10 encoding
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static TermKeyResult handle_csi_m(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args)
 | 
			
		||||
{
 | 
			
		||||
  int initial = cmd >> 8;
 | 
			
		||||
  cmd &= 0xff;
 | 
			
		||||
 | 
			
		||||
  switch(cmd) {
 | 
			
		||||
    case 'M':
 | 
			
		||||
    case 'm':
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      return TERMKEY_RES_NONE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(!initial && args >= 3) { // rxvt protocol
 | 
			
		||||
    key->type = TERMKEY_TYPE_MOUSE;
 | 
			
		||||
    key->code.mouse[0] = arg[0];
 | 
			
		||||
 | 
			
		||||
    key->modifiers     = (key->code.mouse[0] & 0x1c) >> 2;
 | 
			
		||||
    key->code.mouse[0] &= ~0x1c;
 | 
			
		||||
 | 
			
		||||
    termkey_key_set_linecol(key, arg[1], arg[2]);
 | 
			
		||||
 | 
			
		||||
    return TERMKEY_RES_KEY;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(initial == '<' && args >= 3) { // SGR protocol
 | 
			
		||||
    key->type = TERMKEY_TYPE_MOUSE;
 | 
			
		||||
    key->code.mouse[0] = arg[0];
 | 
			
		||||
 | 
			
		||||
    key->modifiers     = (key->code.mouse[0] & 0x1c) >> 2;
 | 
			
		||||
    key->code.mouse[0] &= ~0x1c;
 | 
			
		||||
 | 
			
		||||
    termkey_key_set_linecol(key, arg[1], arg[2]);
 | 
			
		||||
 | 
			
		||||
    if(cmd == 'm') // release
 | 
			
		||||
      key->code.mouse[3] |= 0x80;
 | 
			
		||||
 | 
			
		||||
    return TERMKEY_RES_KEY;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return TERMKEY_RES_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TermKeyResult termkey_interpret_mouse(TermKey *tk, const TermKeyKey *key, TermKeyMouseEvent *event, int *button, int *line, int *col)
 | 
			
		||||
{
 | 
			
		||||
  if(key->type != TERMKEY_TYPE_MOUSE)
 | 
			
		||||
    return TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
  if(button)
 | 
			
		||||
    *button = 0;
 | 
			
		||||
 | 
			
		||||
  termkey_key_get_linecol(key, line, col);
 | 
			
		||||
 | 
			
		||||
  if(!event)
 | 
			
		||||
    return TERMKEY_RES_KEY;
 | 
			
		||||
 | 
			
		||||
  int btn = 0;
 | 
			
		||||
 | 
			
		||||
  int code = key->code.mouse[0];
 | 
			
		||||
 | 
			
		||||
  int drag = code & 0x20;
 | 
			
		||||
 | 
			
		||||
  code &= ~0x3c;
 | 
			
		||||
 | 
			
		||||
  switch(code) {
 | 
			
		||||
  case 0:
 | 
			
		||||
  case 1:
 | 
			
		||||
  case 2:
 | 
			
		||||
    *event = drag ? TERMKEY_MOUSE_DRAG : TERMKEY_MOUSE_PRESS;
 | 
			
		||||
    btn = code + 1;
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case 3:
 | 
			
		||||
    *event = TERMKEY_MOUSE_RELEASE;
 | 
			
		||||
    // no button hint
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case 64:
 | 
			
		||||
  case 65:
 | 
			
		||||
    *event = drag ? TERMKEY_MOUSE_DRAG : TERMKEY_MOUSE_PRESS;
 | 
			
		||||
    btn = code + 4 - 64;
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  default:
 | 
			
		||||
    *event = TERMKEY_MOUSE_UNKNOWN;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(button)
 | 
			
		||||
    *button = btn;
 | 
			
		||||
 | 
			
		||||
  if(key->code.mouse[3] & 0x80)
 | 
			
		||||
    *event = TERMKEY_MOUSE_RELEASE;
 | 
			
		||||
 | 
			
		||||
  return TERMKEY_RES_KEY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Handler for CSI ? R position reports
 | 
			
		||||
 * A plain CSI R with no arguments is probably actually <F3>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static TermKeyResult handle_csi_R(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args)
 | 
			
		||||
{
 | 
			
		||||
  switch(cmd) {
 | 
			
		||||
    case 'R'|'?'<<8:
 | 
			
		||||
      if(args < 2)
 | 
			
		||||
        return TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
      key->type = TERMKEY_TYPE_POSITION;
 | 
			
		||||
      termkey_key_set_linecol(key, arg[1], arg[0]);
 | 
			
		||||
      return TERMKEY_RES_KEY;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      return handle_csi_ss3_full(tk, key, cmd, arg, args);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TermKeyResult termkey_interpret_position(TermKey *tk, const TermKeyKey *key, int *line, int *col)
 | 
			
		||||
{
 | 
			
		||||
  if(key->type != TERMKEY_TYPE_POSITION)
 | 
			
		||||
    return TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
  termkey_key_get_linecol(key, line, col);
 | 
			
		||||
 | 
			
		||||
  return TERMKEY_RES_KEY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Handler for CSI $y mode status reports
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static TermKeyResult handle_csi_y(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args)
 | 
			
		||||
{
 | 
			
		||||
  switch(cmd) {
 | 
			
		||||
    case 'y'|'$'<<16:
 | 
			
		||||
    case 'y'|'$'<<16 | '?'<<8:
 | 
			
		||||
      if(args < 2)
 | 
			
		||||
        return TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
      key->type = TERMKEY_TYPE_MODEREPORT;
 | 
			
		||||
      key->code.mouse[0] = (cmd >> 8);
 | 
			
		||||
      key->code.mouse[1] = arg[0] >> 8;
 | 
			
		||||
      key->code.mouse[2] = arg[0] & 0xff;
 | 
			
		||||
      key->code.mouse[3] = arg[1];
 | 
			
		||||
      return TERMKEY_RES_KEY;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      return TERMKEY_RES_NONE;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TermKeyResult termkey_interpret_modereport(TermKey *tk, const TermKeyKey *key, int *initial, int *mode, int *value)
 | 
			
		||||
{
 | 
			
		||||
  if(key->type != TERMKEY_TYPE_MODEREPORT)
 | 
			
		||||
    return TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
  if(initial)
 | 
			
		||||
    *initial = key->code.mouse[0];
 | 
			
		||||
 | 
			
		||||
  if(mode)
 | 
			
		||||
    *mode = (key->code.mouse[1] << 8) | key->code.mouse[2];
 | 
			
		||||
 | 
			
		||||
  if(value)
 | 
			
		||||
    *value = key->code.mouse[3];
 | 
			
		||||
 | 
			
		||||
  return TERMKEY_RES_KEY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CHARAT(i) (tk->buffer[tk->buffstart + (i)])
 | 
			
		||||
 | 
			
		||||
static TermKeyResult parse_csi(TermKey *tk, size_t introlen, size_t *csi_len, long args[], size_t *nargs, unsigned long *commandp)
 | 
			
		||||
{
 | 
			
		||||
  size_t csi_end = introlen;
 | 
			
		||||
 | 
			
		||||
  while(csi_end < tk->buffcount) {
 | 
			
		||||
    if(CHARAT(csi_end) >= 0x40 && CHARAT(csi_end) < 0x80)
 | 
			
		||||
      break;
 | 
			
		||||
    csi_end++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(csi_end >= tk->buffcount)
 | 
			
		||||
    return TERMKEY_RES_AGAIN;
 | 
			
		||||
 | 
			
		||||
  unsigned char cmd = CHARAT(csi_end);
 | 
			
		||||
  *commandp = cmd;
 | 
			
		||||
 | 
			
		||||
  char present = 0;
 | 
			
		||||
  int argi = 0;
 | 
			
		||||
 | 
			
		||||
  size_t p = introlen;
 | 
			
		||||
 | 
			
		||||
  // See if there is an initial byte
 | 
			
		||||
  if(CHARAT(p) >= '<' && CHARAT(p) <= '?') {
 | 
			
		||||
    *commandp |= (CHARAT(p) << 8);
 | 
			
		||||
    p++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Now attempt to parse out up number;number;... separated values
 | 
			
		||||
  while(p < csi_end) {
 | 
			
		||||
    unsigned char c = CHARAT(p);
 | 
			
		||||
 | 
			
		||||
    if(c >= '0' && c <= '9') {
 | 
			
		||||
      if(!present) {
 | 
			
		||||
        args[argi] = c - '0';
 | 
			
		||||
        present = 1;
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        args[argi] = (args[argi] * 10) + c - '0';
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    else if(c == ';') {
 | 
			
		||||
      if(!present)
 | 
			
		||||
        args[argi] = -1;
 | 
			
		||||
      present = 0;
 | 
			
		||||
      argi++;
 | 
			
		||||
 | 
			
		||||
      if(argi > 16)
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    else if(c >= 0x20 && c <= 0x2f) {
 | 
			
		||||
      *commandp |= c << 16;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    p++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(present)
 | 
			
		||||
    argi++;
 | 
			
		||||
 | 
			
		||||
  *nargs = argi;
 | 
			
		||||
  *csi_len = csi_end + 1;
 | 
			
		||||
 | 
			
		||||
  return TERMKEY_RES_KEY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TermKeyResult termkey_interpret_csi(TermKey *tk, const TermKeyKey *key, long args[], size_t *nargs, unsigned long *cmd)
 | 
			
		||||
{
 | 
			
		||||
  size_t dummy;
 | 
			
		||||
 | 
			
		||||
  if(tk->hightide == 0)
 | 
			
		||||
    return TERMKEY_RES_NONE;
 | 
			
		||||
  if(key->type != TERMKEY_TYPE_UNKNOWN_CSI)
 | 
			
		||||
    return TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
  return parse_csi(tk, 0, &dummy, args, nargs, cmd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int register_keys(void)
 | 
			
		||||
{
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  for(i = 0; i < 64; i++) {
 | 
			
		||||
    csi_ss3s[i].sym = TERMKEY_SYM_UNKNOWN;
 | 
			
		||||
    ss3s[i].sym     = TERMKEY_SYM_UNKNOWN;
 | 
			
		||||
    ss3_kpalts[i] = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for(i = 0; i < NCSIFUNCS; i++)
 | 
			
		||||
    csifuncs[i].sym = TERMKEY_SYM_UNKNOWN;
 | 
			
		||||
 | 
			
		||||
  register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_UP,    'A');
 | 
			
		||||
  register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DOWN,  'B');
 | 
			
		||||
  register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RIGHT, 'C');
 | 
			
		||||
  register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_LEFT,  'D');
 | 
			
		||||
  register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BEGIN, 'E');
 | 
			
		||||
  register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_END,   'F');
 | 
			
		||||
  register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HOME,  'H');
 | 
			
		||||
  register_csi_ss3(TERMKEY_TYPE_FUNCTION, 1, 'P');
 | 
			
		||||
  register_csi_ss3(TERMKEY_TYPE_FUNCTION, 2, 'Q');
 | 
			
		||||
  register_csi_ss3(TERMKEY_TYPE_FUNCTION, 3, 'R');
 | 
			
		||||
  register_csi_ss3(TERMKEY_TYPE_FUNCTION, 4, 'S');
 | 
			
		||||
 | 
			
		||||
  register_csi_ss3_full(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_TAB, TERMKEY_KEYMOD_SHIFT, TERMKEY_KEYMOD_SHIFT, 'Z');
 | 
			
		||||
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPENTER,  'M', 0);
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPEQUALS, 'X', '=');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPMULT,   'j', '*');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPPLUS,   'k', '+');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPCOMMA,  'l', ',');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPMINUS,  'm', '-');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPPERIOD, 'n', '.');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPDIV,    'o', '/');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP0,      'p', '0');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP1,      'q', '1');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP2,      'r', '2');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP3,      's', '3');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP4,      't', '4');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP5,      'u', '5');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP6,      'v', '6');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP7,      'w', '7');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP8,      'x', '8');
 | 
			
		||||
  register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP9,      'y', '9');
 | 
			
		||||
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_FIND,      1);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_INSERT,    2);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DELETE,    3);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SELECT,    4);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEUP,    5);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEDOWN,  6);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HOME,      7);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_END,       8);
 | 
			
		||||
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 1,  11);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 2,  12);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 3,  13);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 4,  14);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 5,  15);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 6,  17);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 7,  18);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 8,  19);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 9,  20);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 10, 21);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 11, 23);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 12, 24);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 13, 25);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 14, 26);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 15, 28);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 16, 29);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 17, 31);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 18, 32);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 19, 33);
 | 
			
		||||
  register_csifunc(TERMKEY_TYPE_FUNCTION, 20, 34);
 | 
			
		||||
 | 
			
		||||
  csi_handlers['u' - 0x40] = &handle_csi_u;
 | 
			
		||||
 | 
			
		||||
  csi_handlers['M' - 0x40] = &handle_csi_m;
 | 
			
		||||
  csi_handlers['m' - 0x40] = &handle_csi_m;
 | 
			
		||||
 | 
			
		||||
  csi_handlers['R' - 0x40] = &handle_csi_R;
 | 
			
		||||
 | 
			
		||||
  csi_handlers['y' - 0x40] = &handle_csi_y;
 | 
			
		||||
 | 
			
		||||
  keyinfo_initialised = 1;
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *new_driver(TermKey *tk, const char *term)
 | 
			
		||||
{
 | 
			
		||||
  if(!keyinfo_initialised)
 | 
			
		||||
    if(!register_keys())
 | 
			
		||||
      return NULL;
 | 
			
		||||
 | 
			
		||||
  TermKeyCsi *csi = malloc(sizeof *csi);
 | 
			
		||||
  if(!csi)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  csi->tk = tk;
 | 
			
		||||
  csi->saved_string_id = 0;
 | 
			
		||||
  csi->saved_string = NULL;
 | 
			
		||||
 | 
			
		||||
  return csi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void free_driver(void *info)
 | 
			
		||||
{
 | 
			
		||||
  TermKeyCsi *csi = info;
 | 
			
		||||
 | 
			
		||||
  if(csi->saved_string)
 | 
			
		||||
    free(csi->saved_string);
 | 
			
		||||
 | 
			
		||||
  free(csi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static TermKeyResult peekkey_csi(TermKey *tk, TermKeyCsi *csi, size_t introlen, TermKeyKey *key, int force, size_t *nbytep)
 | 
			
		||||
{
 | 
			
		||||
  size_t csi_len;
 | 
			
		||||
  size_t args = 16;
 | 
			
		||||
  long arg[16];
 | 
			
		||||
  unsigned long cmd;
 | 
			
		||||
 | 
			
		||||
  TermKeyResult ret = parse_csi(tk, introlen, &csi_len, arg, &args, &cmd);
 | 
			
		||||
 | 
			
		||||
  if(ret == TERMKEY_RES_AGAIN) {
 | 
			
		||||
    if(!force)
 | 
			
		||||
      return TERMKEY_RES_AGAIN;
 | 
			
		||||
 | 
			
		||||
    (*tk->method.emit_codepoint)(tk, '[', key);
 | 
			
		||||
    key->modifiers |= TERMKEY_KEYMOD_ALT;
 | 
			
		||||
    *nbytep = introlen;
 | 
			
		||||
    return TERMKEY_RES_KEY;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(cmd == 'M' && args < 3) { // Mouse in X10 encoding consumes the next 3 bytes also
 | 
			
		||||
    tk->buffstart += csi_len;
 | 
			
		||||
    tk->buffcount -= csi_len;
 | 
			
		||||
 | 
			
		||||
    TermKeyResult mouse_result = (*tk->method.peekkey_mouse)(tk, key, nbytep);
 | 
			
		||||
 | 
			
		||||
    tk->buffstart -= csi_len;
 | 
			
		||||
    tk->buffcount += csi_len;
 | 
			
		||||
 | 
			
		||||
    if(mouse_result == TERMKEY_RES_KEY)
 | 
			
		||||
      *nbytep += csi_len;
 | 
			
		||||
 | 
			
		||||
    return mouse_result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  TermKeyResult result = TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
  // We know from the logic above that cmd must be >= 0x40 and < 0x80
 | 
			
		||||
  if(csi_handlers[(cmd & 0xff) - 0x40])
 | 
			
		||||
    result = (*csi_handlers[(cmd & 0xff) - 0x40])(tk, key, cmd, arg, args);
 | 
			
		||||
 | 
			
		||||
  if(result == TERMKEY_RES_NONE) {
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
    switch(args) {
 | 
			
		||||
      case 0:
 | 
			
		||||
        fprintf(stderr, "CSI: Unknown cmd=%c\n", (char)cmd);
 | 
			
		||||
        break;
 | 
			
		||||
      case 1:
 | 
			
		||||
        fprintf(stderr, "CSI: Unknown arg1=%ld cmd=%c\n", arg[0], (char)cmd);
 | 
			
		||||
        break;
 | 
			
		||||
      case 2:
 | 
			
		||||
        fprintf(stderr, "CSI: Unknown arg1=%ld arg2=%ld cmd=%c\n", arg[0], arg[1], (char)cmd);
 | 
			
		||||
        break;
 | 
			
		||||
      case 3:
 | 
			
		||||
        fprintf(stderr, "CSI: Unknown arg1=%ld arg2=%ld arg3=%ld cmd=%c\n", arg[0], arg[1], arg[2], (char)cmd);
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        fprintf(stderr, "CSI: Unknown arg1=%ld arg2=%ld arg3=%ld ... args=%d cmd=%c\n", arg[0], arg[1], arg[2], args, (char)cmd);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    key->type = TERMKEY_TYPE_UNKNOWN_CSI;
 | 
			
		||||
    key->code.number = cmd;
 | 
			
		||||
    key->modifiers = 0;
 | 
			
		||||
 | 
			
		||||
    tk->hightide = csi_len - introlen;
 | 
			
		||||
    *nbytep = introlen; // Do not yet eat the data bytes
 | 
			
		||||
    return TERMKEY_RES_KEY;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  *nbytep = csi_len;
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static TermKeyResult peekkey_ss3(TermKey *tk, TermKeyCsi *csi, size_t introlen, TermKeyKey *key, int force, size_t *nbytep)
 | 
			
		||||
{
 | 
			
		||||
  if(tk->buffcount < introlen + 1) {
 | 
			
		||||
    if(!force)
 | 
			
		||||
      return TERMKEY_RES_AGAIN;
 | 
			
		||||
 | 
			
		||||
    (*tk->method.emit_codepoint)(tk, 'O', key);
 | 
			
		||||
    key->modifiers |= TERMKEY_KEYMOD_ALT;
 | 
			
		||||
    *nbytep = tk->buffcount;
 | 
			
		||||
    return TERMKEY_RES_KEY;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  unsigned char cmd = CHARAT(introlen);
 | 
			
		||||
 | 
			
		||||
  if(cmd < 0x40 || cmd >= 0x80)
 | 
			
		||||
    return TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
  key->type = csi_ss3s[cmd - 0x40].type;
 | 
			
		||||
  key->code.sym = csi_ss3s[cmd - 0x40].sym;
 | 
			
		||||
  key->modifiers = csi_ss3s[cmd - 0x40].modifier_set;
 | 
			
		||||
 | 
			
		||||
  if(key->code.sym == TERMKEY_SYM_UNKNOWN) {
 | 
			
		||||
    if(tk->flags & TERMKEY_FLAG_CONVERTKP && ss3_kpalts[cmd - 0x40]) {
 | 
			
		||||
      key->type = TERMKEY_TYPE_UNICODE;
 | 
			
		||||
      key->code.codepoint = ss3_kpalts[cmd - 0x40];
 | 
			
		||||
      key->modifiers = 0;
 | 
			
		||||
 | 
			
		||||
      key->utf8[0] = key->code.codepoint;
 | 
			
		||||
      key->utf8[1] = 0;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      key->type = ss3s[cmd - 0x40].type;
 | 
			
		||||
      key->code.sym = ss3s[cmd - 0x40].sym;
 | 
			
		||||
      key->modifiers = ss3s[cmd - 0x40].modifier_set;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(key->code.sym == TERMKEY_SYM_UNKNOWN) {
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
    fprintf(stderr, "CSI: Unknown SS3 %c (0x%02x)\n", (char)cmd, cmd);
 | 
			
		||||
#endif
 | 
			
		||||
    return TERMKEY_RES_NONE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  *nbytep = introlen + 1;
 | 
			
		||||
 | 
			
		||||
  return TERMKEY_RES_KEY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static TermKeyResult peekkey_ctrlstring(TermKey *tk, TermKeyCsi *csi, size_t introlen, TermKeyKey *key, int force, size_t *nbytep)
 | 
			
		||||
{
 | 
			
		||||
  size_t str_end = introlen;
 | 
			
		||||
 | 
			
		||||
  while(str_end < tk->buffcount) {
 | 
			
		||||
    if(CHARAT(str_end) == 0x9c) // ST
 | 
			
		||||
      break;
 | 
			
		||||
    if(CHARAT(str_end) == 0x1b &&
 | 
			
		||||
       (str_end + 1) < tk->buffcount &&
 | 
			
		||||
       CHARAT(str_end+1) == 0x5c) // ESC-prefixed ST
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    str_end++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(str_end >= tk->buffcount)
 | 
			
		||||
    return TERMKEY_RES_AGAIN;
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
  fprintf(stderr, "Found a control string: %*s",
 | 
			
		||||
      str_end - introlen, tk->buffer + tk->buffstart + introlen);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  *nbytep = str_end + 1;
 | 
			
		||||
  if(CHARAT(str_end) == 0x1b)
 | 
			
		||||
    (*nbytep)++;
 | 
			
		||||
 | 
			
		||||
  if(csi->saved_string)
 | 
			
		||||
    free(csi->saved_string);
 | 
			
		||||
 | 
			
		||||
  size_t len = str_end - introlen;
 | 
			
		||||
 | 
			
		||||
  csi->saved_string_id++;
 | 
			
		||||
  csi->saved_string = malloc(len + 1);
 | 
			
		||||
 | 
			
		||||
  strncpy(csi->saved_string, (char *)tk->buffer + tk->buffstart + introlen, len);
 | 
			
		||||
  csi->saved_string[len] = 0;
 | 
			
		||||
 | 
			
		||||
  key->type = (CHARAT(introlen-1) & 0x1f) == 0x10 ?
 | 
			
		||||
    TERMKEY_TYPE_DCS : TERMKEY_TYPE_OSC;
 | 
			
		||||
  key->code.number = csi->saved_string_id;
 | 
			
		||||
  key->modifiers = 0;
 | 
			
		||||
 | 
			
		||||
  return TERMKEY_RES_KEY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static TermKeyResult peekkey(TermKey *tk, void *info, TermKeyKey *key, int force, size_t *nbytep)
 | 
			
		||||
{
 | 
			
		||||
  if(tk->buffcount == 0)
 | 
			
		||||
    return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
  TermKeyCsi *csi = info;
 | 
			
		||||
 | 
			
		||||
  switch(CHARAT(0)) {
 | 
			
		||||
    case 0x1b:
 | 
			
		||||
      if(tk->buffcount < 2)
 | 
			
		||||
        return TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
      switch(CHARAT(1)) {
 | 
			
		||||
        case 0x4f: // ESC-prefixed SS3
 | 
			
		||||
          return peekkey_ss3(tk, csi, 2, key, force, nbytep);
 | 
			
		||||
 | 
			
		||||
        case 0x50: // ESC-prefixed DCS
 | 
			
		||||
        case 0x5d: // ESC-prefixed OSC
 | 
			
		||||
          return peekkey_ctrlstring(tk, csi, 2, key, force, nbytep);
 | 
			
		||||
 | 
			
		||||
        case 0x5b: // ESC-prefixed CSI
 | 
			
		||||
          return peekkey_csi(tk, csi, 2, key, force, nbytep);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
    case 0x8f: // SS3
 | 
			
		||||
      return peekkey_ss3(tk, csi, 1, key, force, nbytep);
 | 
			
		||||
 | 
			
		||||
    case 0x90: // DCS
 | 
			
		||||
    case 0x9d: // OSC
 | 
			
		||||
      return peekkey_ctrlstring(tk, csi, 1, key, force, nbytep);
 | 
			
		||||
 | 
			
		||||
    case 0x9b: // CSI
 | 
			
		||||
      return peekkey_csi(tk, csi, 1, key, force, nbytep);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return TERMKEY_RES_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct TermKeyDriver termkey_driver_csi = {
 | 
			
		||||
  .name        = "CSI",
 | 
			
		||||
 | 
			
		||||
  .new_driver  = new_driver,
 | 
			
		||||
  .free_driver = free_driver,
 | 
			
		||||
 | 
			
		||||
  .peekkey = peekkey,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TermKeyResult termkey_interpret_string(TermKey *tk, const TermKeyKey *key, const char **strp)
 | 
			
		||||
{
 | 
			
		||||
  struct TermKeyDriverNode *p;
 | 
			
		||||
  for(p = tk->drivers; p; p = p->next)
 | 
			
		||||
    if(p->driver == &termkey_driver_csi)
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
  if(!p)
 | 
			
		||||
    return TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
  if(key->type != TERMKEY_TYPE_DCS &&
 | 
			
		||||
     key->type != TERMKEY_TYPE_OSC)
 | 
			
		||||
    return TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
  TermKeyCsi *csi = p->info;
 | 
			
		||||
 | 
			
		||||
  if(csi->saved_string_id != key->code.number)
 | 
			
		||||
    return TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
  *strp = csi->saved_string;
 | 
			
		||||
 | 
			
		||||
  return TERMKEY_RES_KEY;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										633
									
								
								src/termkey/driver-ti.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										633
									
								
								src/termkey/driver-ti.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,633 @@
 | 
			
		||||
// we want strdup()
 | 
			
		||||
#define _XOPEN_SOURCE 600
 | 
			
		||||
 | 
			
		||||
#include "termkey.h"
 | 
			
		||||
#include "termkey-internal.h"
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_UNIBILIUM
 | 
			
		||||
# include <unibilium.h>
 | 
			
		||||
#else
 | 
			
		||||
# include <curses.h>
 | 
			
		||||
# include <term.h>
 | 
			
		||||
 | 
			
		||||
/* curses.h has just polluted our namespace. We want this back */
 | 
			
		||||
# undef buttons
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
# include <unistd.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
 | 
			
		||||
#define streq(a,b) (!strcmp(a,b))
 | 
			
		||||
 | 
			
		||||
#define MAX_FUNCNAME 9
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
  const char *funcname;
 | 
			
		||||
  TermKeyType type;
 | 
			
		||||
  TermKeySym sym;
 | 
			
		||||
  int mods;
 | 
			
		||||
} funcs[] =
 | 
			
		||||
{
 | 
			
		||||
  /* THIS LIST MUST REMAIN SORTED! */
 | 
			
		||||
  { "backspace", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BACKSPACE, 0 },
 | 
			
		||||
  { "begin",     TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BEGIN,     0 },
 | 
			
		||||
  { "beg",       TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BEGIN,     0 },
 | 
			
		||||
  { "btab",      TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_TAB,       TERMKEY_KEYMOD_SHIFT },
 | 
			
		||||
  { "cancel",    TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_CANCEL,    0 },
 | 
			
		||||
  { "clear",     TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_CLEAR,     0 },
 | 
			
		||||
  { "close",     TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_CLOSE,     0 },
 | 
			
		||||
  { "command",   TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_COMMAND,   0 },
 | 
			
		||||
  { "copy",      TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_COPY,      0 },
 | 
			
		||||
  { "dc",        TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DELETE,    0 },
 | 
			
		||||
  { "down",      TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DOWN,      0 },
 | 
			
		||||
  { "end",       TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_END,       0 },
 | 
			
		||||
  { "enter",     TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_ENTER,     0 },
 | 
			
		||||
  { "exit",      TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_EXIT,      0 },
 | 
			
		||||
  { "find",      TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_FIND,      0 },
 | 
			
		||||
  { "help",      TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HELP,      0 },
 | 
			
		||||
  { "home",      TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HOME,      0 },
 | 
			
		||||
  { "ic",        TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_INSERT,    0 },
 | 
			
		||||
  { "left",      TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_LEFT,      0 },
 | 
			
		||||
  { "mark",      TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_MARK,      0 },
 | 
			
		||||
  { "message",   TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_MESSAGE,   0 },
 | 
			
		||||
  { "move",      TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_MOVE,      0 },
 | 
			
		||||
  { "next",      TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEDOWN,  0 }, // Not quite, but it's the best we can do
 | 
			
		||||
  { "npage",     TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEDOWN,  0 },
 | 
			
		||||
  { "open",      TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_OPEN,      0 },
 | 
			
		||||
  { "options",   TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_OPTIONS,   0 },
 | 
			
		||||
  { "ppage",     TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEUP,    0 },
 | 
			
		||||
  { "previous",  TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEUP,    0 }, // Not quite, but it's the best we can do
 | 
			
		||||
  { "print",     TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PRINT,     0 },
 | 
			
		||||
  { "redo",      TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REDO,      0 },
 | 
			
		||||
  { "reference", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REFERENCE, 0 },
 | 
			
		||||
  { "refresh",   TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REFRESH,   0 },
 | 
			
		||||
  { "replace",   TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REPLACE,   0 },
 | 
			
		||||
  { "restart",   TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RESTART,   0 },
 | 
			
		||||
  { "resume",    TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RESUME,    0 },
 | 
			
		||||
  { "right",     TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RIGHT,     0 },
 | 
			
		||||
  { "save",      TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SAVE,      0 },
 | 
			
		||||
  { "select",    TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SELECT,    0 },
 | 
			
		||||
  { "suspend",   TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SUSPEND,   0 },
 | 
			
		||||
  { "undo",      TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_UNDO,      0 },
 | 
			
		||||
  { "up",        TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_UP,        0 },
 | 
			
		||||
  { NULL },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_UNIBILIUM
 | 
			
		||||
static enum unibi_string unibi_lookup_str(const char *name)
 | 
			
		||||
{
 | 
			
		||||
  for(enum unibi_string ret = unibi_string_begin_+1; ret < unibi_string_end_; ret++)
 | 
			
		||||
    if(streq(unibi_name_str(ret), name))
 | 
			
		||||
      return ret;
 | 
			
		||||
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *unibi_get_str_by_name(const unibi_term *ut, const char *name)
 | 
			
		||||
{
 | 
			
		||||
  enum unibi_string idx = unibi_lookup_str(name);
 | 
			
		||||
  if(idx == (enum unibi_string)-1)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  return unibi_get_str(ut, idx);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* To be efficient at lookups, we store the byte sequence => keyinfo mapping
 | 
			
		||||
 * in a trie. This avoids a slow linear search through a flat list of
 | 
			
		||||
 * sequences. Because it is likely most nodes will be very sparse, we optimise
 | 
			
		||||
 * vector to store an extent map after the database is loaded.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  TYPE_KEY,
 | 
			
		||||
  TYPE_ARR,
 | 
			
		||||
} trie_nodetype;
 | 
			
		||||
 | 
			
		||||
struct trie_node {
 | 
			
		||||
  trie_nodetype type;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct trie_node_key {
 | 
			
		||||
  trie_nodetype type;
 | 
			
		||||
  struct keyinfo key;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct trie_node_arr {
 | 
			
		||||
  trie_nodetype type;
 | 
			
		||||
  unsigned char min, max; /* INCLUSIVE endpoints of the extent range */
 | 
			
		||||
  struct trie_node *arr[]; /* dynamic size at allocation time */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  TermKey *tk;
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_UNIBILIUM
 | 
			
		||||
  unibi_term *unibi;  /* only valid until first 'start' call */
 | 
			
		||||
#else
 | 
			
		||||
  char *term; /* only valid until first 'start' call */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  struct trie_node *root;
 | 
			
		||||
 | 
			
		||||
  char *start_string;
 | 
			
		||||
  char *stop_string;
 | 
			
		||||
} TermKeyTI;
 | 
			
		||||
 | 
			
		||||
static int insert_seq(TermKeyTI *ti, const char *seq, struct trie_node *node);
 | 
			
		||||
 | 
			
		||||
static struct trie_node *new_node_key(TermKeyType type, TermKeySym sym, int modmask, int modset)
 | 
			
		||||
{
 | 
			
		||||
  struct trie_node_key *n = malloc(sizeof(*n));
 | 
			
		||||
  if(!n)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  n->type = TYPE_KEY;
 | 
			
		||||
 | 
			
		||||
  n->key.type = type;
 | 
			
		||||
  n->key.sym  = sym;
 | 
			
		||||
  n->key.modifier_mask = modmask;
 | 
			
		||||
  n->key.modifier_set  = modset;
 | 
			
		||||
 | 
			
		||||
  return (struct trie_node*)n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct trie_node *new_node_arr(unsigned char min, unsigned char max)
 | 
			
		||||
{
 | 
			
		||||
  struct trie_node_arr *n = malloc(sizeof(*n) + ((int)max-min+1) * sizeof(n->arr[0]));
 | 
			
		||||
  if(!n)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  n->type = TYPE_ARR;
 | 
			
		||||
  n->min = min; n->max = max;
 | 
			
		||||
 | 
			
		||||
  int i;
 | 
			
		||||
  for(i = min; i <= max; i++)
 | 
			
		||||
    n->arr[i-min] = NULL;
 | 
			
		||||
 | 
			
		||||
  return (struct trie_node*)n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct trie_node *lookup_next(struct trie_node *n, unsigned char b)
 | 
			
		||||
{
 | 
			
		||||
  switch(n->type) {
 | 
			
		||||
  case TYPE_KEY:
 | 
			
		||||
    fprintf(stderr, "ABORT: lookup_next within a TYPE_KEY node\n");
 | 
			
		||||
    abort();
 | 
			
		||||
  case TYPE_ARR:
 | 
			
		||||
    {
 | 
			
		||||
      struct trie_node_arr *nar = (struct trie_node_arr*)n;
 | 
			
		||||
      if(b < nar->min || b > nar->max)
 | 
			
		||||
        return NULL;
 | 
			
		||||
      return nar->arr[b - nar->min];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return NULL; // Never reached but keeps compiler happy
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void free_trie(struct trie_node *n)
 | 
			
		||||
{
 | 
			
		||||
  switch(n->type) {
 | 
			
		||||
  case TYPE_KEY:
 | 
			
		||||
    break;
 | 
			
		||||
  case TYPE_ARR:
 | 
			
		||||
    {
 | 
			
		||||
      struct trie_node_arr *nar = (struct trie_node_arr*)n;
 | 
			
		||||
      int i;
 | 
			
		||||
      for(i = nar->min; i <= nar->max; i++)
 | 
			
		||||
        if(nar->arr[i - nar->min])
 | 
			
		||||
          free_trie(nar->arr[i - nar->min]);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  free(n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct trie_node *compress_trie(struct trie_node *n)
 | 
			
		||||
{
 | 
			
		||||
  if(!n)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  switch(n->type) {
 | 
			
		||||
  case TYPE_KEY:
 | 
			
		||||
    return n;
 | 
			
		||||
  case TYPE_ARR:
 | 
			
		||||
    {
 | 
			
		||||
      struct trie_node_arr *nar = (struct trie_node_arr*)n;
 | 
			
		||||
      unsigned char min, max;
 | 
			
		||||
      // Find the real bounds
 | 
			
		||||
      for(min = 0; !nar->arr[min]; min++)
 | 
			
		||||
        if(min == 255 && !nar->arr[min]) {
 | 
			
		||||
          free(nar);
 | 
			
		||||
          return new_node_arr(1, 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      for(max = 0xff; !nar->arr[max]; max--)
 | 
			
		||||
        ;
 | 
			
		||||
 | 
			
		||||
      struct trie_node_arr *new = (struct trie_node_arr*)new_node_arr(min, max);
 | 
			
		||||
      int i;
 | 
			
		||||
      for(i = min; i <= max; i++)
 | 
			
		||||
        new->arr[i - min] = compress_trie(nar->arr[i]);
 | 
			
		||||
 | 
			
		||||
      free(nar);
 | 
			
		||||
      return (struct trie_node*)new;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool try_load_terminfo_key(TermKeyTI *ti, const char *name, struct keyinfo *info)
 | 
			
		||||
{
 | 
			
		||||
  const char *value = NULL;
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_UNIBILIUM
 | 
			
		||||
  if(ti->unibi)
 | 
			
		||||
    value = unibi_get_str_by_name(ti->unibi, name);
 | 
			
		||||
#else
 | 
			
		||||
  if(ti->term)
 | 
			
		||||
    value = tigetstr(name);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  if(ti->tk->ti_getstr_hook)
 | 
			
		||||
    value = (ti->tk->ti_getstr_hook)(name, value, ti->tk->ti_getstr_hook_data);
 | 
			
		||||
 | 
			
		||||
  if(!value || value == (char*)-1 || !value[0])
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  struct trie_node *node = new_node_key(info->type, info->sym, info->modifier_mask, info->modifier_set);
 | 
			
		||||
  insert_seq(ti, value, node);
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int load_terminfo(TermKeyTI *ti)
 | 
			
		||||
{
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_UNIBILIUM
 | 
			
		||||
  unibi_term *unibi = ti->unibi;
 | 
			
		||||
#else
 | 
			
		||||
  {
 | 
			
		||||
    int err;
 | 
			
		||||
 | 
			
		||||
    /* Have to cast away the const. But it's OK - we know terminfo won't really
 | 
			
		||||
    * modify term */
 | 
			
		||||
    if(setupterm((char*)ti->term, 1, &err) != OK)
 | 
			
		||||
      return 0;
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  ti->root = new_node_arr(0, 0xff);
 | 
			
		||||
  if(!ti->root)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  /* First the regular key strings
 | 
			
		||||
   */
 | 
			
		||||
  for(i = 0; funcs[i].funcname; i++) {
 | 
			
		||||
    char name[MAX_FUNCNAME + 5 + 1];
 | 
			
		||||
 | 
			
		||||
    sprintf(name, "key_%s", funcs[i].funcname);
 | 
			
		||||
    if(!try_load_terminfo_key(ti, name, &(struct keyinfo){
 | 
			
		||||
          .type = funcs[i].type,
 | 
			
		||||
          .sym  = funcs[i].sym,
 | 
			
		||||
          .modifier_mask = funcs[i].mods,
 | 
			
		||||
          .modifier_set  = funcs[i].mods,
 | 
			
		||||
      }))
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    /* Maybe it has a shifted version */
 | 
			
		||||
    sprintf(name, "key_s%s", funcs[i].funcname);
 | 
			
		||||
    try_load_terminfo_key(ti, name, &(struct keyinfo){
 | 
			
		||||
        .type = funcs[i].type,
 | 
			
		||||
        .sym  = funcs[i].sym,
 | 
			
		||||
        .modifier_mask = funcs[i].mods | TERMKEY_KEYMOD_SHIFT,
 | 
			
		||||
        .modifier_set  = funcs[i].mods | TERMKEY_KEYMOD_SHIFT,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Now the F<digit> keys
 | 
			
		||||
   */
 | 
			
		||||
  for(i = 1; i < 255; i++) {
 | 
			
		||||
    char name[9];
 | 
			
		||||
    sprintf(name, "key_f%d", i);
 | 
			
		||||
    if(!try_load_terminfo_key(ti, name, &(struct keyinfo){
 | 
			
		||||
          .type = TERMKEY_TYPE_FUNCTION,
 | 
			
		||||
          .sym  = i,
 | 
			
		||||
          .modifier_mask = 0,
 | 
			
		||||
          .modifier_set  = 0,
 | 
			
		||||
      }))
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Finally mouse mode */
 | 
			
		||||
  try_load_terminfo_key(ti, "key_mouse", &(struct keyinfo){
 | 
			
		||||
      .type = TERMKEY_TYPE_MOUSE,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  /* Take copies of these terminfo strings, in case we build multiple termkey
 | 
			
		||||
   * instances for multiple different termtypes, and it's different by the
 | 
			
		||||
   * time we want to use it
 | 
			
		||||
   */
 | 
			
		||||
#ifdef HAVE_UNIBILIUM
 | 
			
		||||
  const char *keypad_xmit = unibi ?
 | 
			
		||||
    unibi_get_str(unibi, unibi_keypad_xmit) :
 | 
			
		||||
    NULL;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  if(keypad_xmit)
 | 
			
		||||
    ti->start_string = strdup(keypad_xmit);
 | 
			
		||||
  else
 | 
			
		||||
    ti->start_string = NULL;
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_UNIBILIUM
 | 
			
		||||
  const char *keypad_local = unibi ?
 | 
			
		||||
    unibi_get_str(unibi, unibi_keypad_local) :
 | 
			
		||||
    NULL;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  if(keypad_local)
 | 
			
		||||
    ti->stop_string = strdup(keypad_local);
 | 
			
		||||
  else
 | 
			
		||||
    ti->stop_string = NULL;
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_UNIBILIUM
 | 
			
		||||
  if(unibi)
 | 
			
		||||
    unibi_destroy(unibi);
 | 
			
		||||
 | 
			
		||||
  ti->unibi = NULL;
 | 
			
		||||
#else
 | 
			
		||||
  if(ti->term)
 | 
			
		||||
    free(ti->term);
 | 
			
		||||
 | 
			
		||||
  ti->term = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  ti->root = compress_trie(ti->root);
 | 
			
		||||
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *new_driver(TermKey *tk, const char *term)
 | 
			
		||||
{
 | 
			
		||||
  TermKeyTI *ti = malloc(sizeof *ti);
 | 
			
		||||
  if(!ti)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  ti->tk = tk;
 | 
			
		||||
  ti->root = NULL;
 | 
			
		||||
  ti->start_string = NULL;
 | 
			
		||||
  ti->stop_string = NULL;
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_UNIBILIUM
 | 
			
		||||
  ti->unibi = unibi_from_term(term);
 | 
			
		||||
  int saved_errno = errno;
 | 
			
		||||
  if(!ti->unibi && saved_errno != ENOENT) {
 | 
			
		||||
    free(ti);
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
  /* ti->unibi may be NULL if errno == ENOENT. That means the terminal wasn't
 | 
			
		||||
   * known. Lets keep going because if we get getstr hook that might invent
 | 
			
		||||
   * new strings for us
 | 
			
		||||
   */
 | 
			
		||||
#else
 | 
			
		||||
  {
 | 
			
		||||
    int err;
 | 
			
		||||
 | 
			
		||||
    ti->term = NULL;
 | 
			
		||||
 | 
			
		||||
    /* Have to cast away the const. But it's OK - we know terminfo won't really
 | 
			
		||||
    * modify term */
 | 
			
		||||
    if(setupterm((char*)term, 1, &err) == OK)
 | 
			
		||||
      ti->term = strdup(term);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  return ti;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int start_driver(TermKey *tk, void *info)
 | 
			
		||||
{
 | 
			
		||||
  TermKeyTI *ti = info;
 | 
			
		||||
  struct stat statbuf;
 | 
			
		||||
  char *start_string;
 | 
			
		||||
  size_t len;
 | 
			
		||||
 | 
			
		||||
  if(!ti->root)
 | 
			
		||||
    load_terminfo(ti);
 | 
			
		||||
 | 
			
		||||
  start_string = ti->start_string;
 | 
			
		||||
 | 
			
		||||
  if(tk->fd == -1 || !start_string)
 | 
			
		||||
    return 1;
 | 
			
		||||
 | 
			
		||||
  /* The terminfo database will contain keys in application cursor key mode.
 | 
			
		||||
   * We may need to enable that mode
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  /* There's no point trying to write() to a pipe */
 | 
			
		||||
  if(fstat(tk->fd, &statbuf) == -1)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
  if(S_ISFIFO(statbuf.st_mode))
 | 
			
		||||
    return 1;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  // Can't call putp or tputs because they suck and don't give us fd control
 | 
			
		||||
  len = strlen(start_string);
 | 
			
		||||
  while(len) {
 | 
			
		||||
    size_t written = write(tk->fd, start_string, len);
 | 
			
		||||
    if(written == -1)
 | 
			
		||||
      return 0;
 | 
			
		||||
    start_string += written;
 | 
			
		||||
    len -= written;
 | 
			
		||||
  }
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int stop_driver(TermKey *tk, void *info)
 | 
			
		||||
{
 | 
			
		||||
  TermKeyTI *ti = info;
 | 
			
		||||
  struct stat statbuf;
 | 
			
		||||
  char *stop_string = ti->stop_string;
 | 
			
		||||
  size_t len;
 | 
			
		||||
 | 
			
		||||
  if(tk->fd == -1 || !stop_string)
 | 
			
		||||
    return 1;
 | 
			
		||||
 | 
			
		||||
  /* There's no point trying to write() to a pipe */
 | 
			
		||||
  if(fstat(tk->fd, &statbuf) == -1)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
  if(S_ISFIFO(statbuf.st_mode))
 | 
			
		||||
    return 1;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  /* The terminfo database will contain keys in application cursor key mode.
 | 
			
		||||
   * We may need to enable that mode
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  // Can't call putp or tputs because they suck and don't give us fd control
 | 
			
		||||
  len = strlen(stop_string);
 | 
			
		||||
  while(len) {
 | 
			
		||||
    size_t written = write(tk->fd, stop_string, len);
 | 
			
		||||
    if(written == -1)
 | 
			
		||||
      return 0;
 | 
			
		||||
    stop_string += written;
 | 
			
		||||
    len -= written;
 | 
			
		||||
  }
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void free_driver(void *info)
 | 
			
		||||
{
 | 
			
		||||
  TermKeyTI *ti = info;
 | 
			
		||||
 | 
			
		||||
  free_trie(ti->root);
 | 
			
		||||
 | 
			
		||||
  if(ti->start_string)
 | 
			
		||||
    free(ti->start_string);
 | 
			
		||||
 | 
			
		||||
  if(ti->stop_string)
 | 
			
		||||
    free(ti->stop_string);
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_UNIBILIUM
 | 
			
		||||
  if(ti->unibi)
 | 
			
		||||
    unibi_destroy(ti->unibi);
 | 
			
		||||
#else
 | 
			
		||||
  if(ti->term)
 | 
			
		||||
    free(ti->term);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  free(ti);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CHARAT(i) (tk->buffer[tk->buffstart + (i)])
 | 
			
		||||
 | 
			
		||||
static TermKeyResult peekkey(TermKey *tk, void *info, TermKeyKey *key, int force, size_t *nbytep)
 | 
			
		||||
{
 | 
			
		||||
  TermKeyTI *ti = info;
 | 
			
		||||
 | 
			
		||||
  if(tk->buffcount == 0)
 | 
			
		||||
    return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE;
 | 
			
		||||
 | 
			
		||||
  struct trie_node *p = ti->root;
 | 
			
		||||
 | 
			
		||||
  unsigned int pos = 0;
 | 
			
		||||
  while(pos < tk->buffcount) {
 | 
			
		||||
    p = lookup_next(p, CHARAT(pos));
 | 
			
		||||
    if(!p)
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    pos++;
 | 
			
		||||
 | 
			
		||||
    if(p->type != TYPE_KEY)
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    struct trie_node_key *nk = (struct trie_node_key*)p;
 | 
			
		||||
    if(nk->key.type == TERMKEY_TYPE_MOUSE) {
 | 
			
		||||
      tk->buffstart += pos;
 | 
			
		||||
      tk->buffcount -= pos;
 | 
			
		||||
 | 
			
		||||
      TermKeyResult mouse_result = (*tk->method.peekkey_mouse)(tk, key, nbytep);
 | 
			
		||||
 | 
			
		||||
      tk->buffstart -= pos;
 | 
			
		||||
      tk->buffcount += pos;
 | 
			
		||||
 | 
			
		||||
      if(mouse_result == TERMKEY_RES_KEY)
 | 
			
		||||
        *nbytep += pos;
 | 
			
		||||
 | 
			
		||||
      return mouse_result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    key->type      = nk->key.type;
 | 
			
		||||
    key->code.sym  = nk->key.sym;
 | 
			
		||||
    key->modifiers = nk->key.modifier_set;
 | 
			
		||||
    *nbytep = pos;
 | 
			
		||||
    return TERMKEY_RES_KEY;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If p is not NULL then we hadn't walked off the end yet, so we have a
 | 
			
		||||
  // partial match
 | 
			
		||||
  if(p && !force)
 | 
			
		||||
    return TERMKEY_RES_AGAIN;
 | 
			
		||||
 | 
			
		||||
  return TERMKEY_RES_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int insert_seq(TermKeyTI *ti, const char *seq, struct trie_node *node)
 | 
			
		||||
{
 | 
			
		||||
  int pos = 0;
 | 
			
		||||
  struct trie_node *p = ti->root;
 | 
			
		||||
 | 
			
		||||
  // Unsigned because we'll be using it as an array subscript
 | 
			
		||||
  unsigned char b;
 | 
			
		||||
 | 
			
		||||
  while((b = seq[pos])) {
 | 
			
		||||
    struct trie_node *next = lookup_next(p, b);
 | 
			
		||||
    if(!next)
 | 
			
		||||
      break;
 | 
			
		||||
    p = next;
 | 
			
		||||
    pos++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  while((b = seq[pos])) {
 | 
			
		||||
    struct trie_node *next;
 | 
			
		||||
    if(seq[pos+1])
 | 
			
		||||
      // Intermediate node
 | 
			
		||||
      next = new_node_arr(0, 0xff);
 | 
			
		||||
    else
 | 
			
		||||
      // Final key node
 | 
			
		||||
      next = node;
 | 
			
		||||
 | 
			
		||||
    if(!next)
 | 
			
		||||
      return 0;
 | 
			
		||||
 | 
			
		||||
    switch(p->type) {
 | 
			
		||||
    case TYPE_ARR:
 | 
			
		||||
      {
 | 
			
		||||
        struct trie_node_arr *nar = (struct trie_node_arr*)p;
 | 
			
		||||
        if(b < nar->min || b > nar->max) {
 | 
			
		||||
          fprintf(stderr, "ASSERT FAIL: Trie insert at 0x%02x is outside of extent bounds (0x%02x..0x%02x)\n",
 | 
			
		||||
              b, nar->min, nar->max);
 | 
			
		||||
          abort();
 | 
			
		||||
        }
 | 
			
		||||
        nar->arr[b - nar->min] = next;
 | 
			
		||||
        p = next;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    case TYPE_KEY:
 | 
			
		||||
      fprintf(stderr, "ASSERT FAIL: Tried to insert child node in TYPE_KEY\n");
 | 
			
		||||
      abort();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pos++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct TermKeyDriver termkey_driver_ti = {
 | 
			
		||||
  .name        = "terminfo",
 | 
			
		||||
 | 
			
		||||
  .new_driver  = new_driver,
 | 
			
		||||
  .free_driver = free_driver,
 | 
			
		||||
 | 
			
		||||
  .start_driver = start_driver,
 | 
			
		||||
  .stop_driver  = stop_driver,
 | 
			
		||||
 | 
			
		||||
  .peekkey = peekkey,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										112
									
								
								src/termkey/termkey-internal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/termkey/termkey-internal.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
			
		||||
#ifndef GUARD_TERMKEY_INTERNAL_H_
 | 
			
		||||
#define GUARD_TERMKEY_INTERNAL_H_
 | 
			
		||||
 | 
			
		||||
#define HAVE_TERMIOS
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
# undef HAVE_TERMIOS
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "termkey.h"
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#ifdef HAVE_TERMIOS
 | 
			
		||||
# include <termios.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef _MSC_VER
 | 
			
		||||
#include <BaseTsd.h>
 | 
			
		||||
typedef SSIZE_T ssize_t;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct TermKeyDriver
 | 
			
		||||
{
 | 
			
		||||
  const char      *name;
 | 
			
		||||
  void          *(*new_driver)(TermKey *tk, const char *term);
 | 
			
		||||
  void           (*free_driver)(void *info);
 | 
			
		||||
  int            (*start_driver)(TermKey *tk, void *info);
 | 
			
		||||
  int            (*stop_driver)(TermKey *tk, void *info);
 | 
			
		||||
  TermKeyResult (*peekkey)(TermKey *tk, void *info, TermKeyKey *key, int force, size_t *nbytes);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct keyinfo {
 | 
			
		||||
  TermKeyType type;
 | 
			
		||||
  TermKeySym sym;
 | 
			
		||||
  int modifier_mask;
 | 
			
		||||
  int modifier_set;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct TermKeyDriverNode;
 | 
			
		||||
struct TermKeyDriverNode {
 | 
			
		||||
  struct TermKeyDriver     *driver;
 | 
			
		||||
  void                      *info;
 | 
			
		||||
  struct TermKeyDriverNode *next;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct TermKey {
 | 
			
		||||
  int    fd;
 | 
			
		||||
  int    flags;
 | 
			
		||||
  int    canonflags;
 | 
			
		||||
  unsigned char *buffer;
 | 
			
		||||
  size_t buffstart; // First offset in buffer
 | 
			
		||||
  size_t buffcount; // NUMBER of entires valid in buffer
 | 
			
		||||
  size_t buffsize; // Total malloc'ed size
 | 
			
		||||
  size_t hightide; /* Position beyond buffstart at which peekkey() should next start
 | 
			
		||||
                    * normally 0, but see also termkey_interpret_csi */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_TERMIOS
 | 
			
		||||
  struct termios restore_termios;
 | 
			
		||||
  char restore_termios_valid;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  TermKey_Terminfo_Getstr_Hook *ti_getstr_hook;
 | 
			
		||||
  void *ti_getstr_hook_data;
 | 
			
		||||
 | 
			
		||||
  int waittime; // msec
 | 
			
		||||
 | 
			
		||||
  char   is_closed;
 | 
			
		||||
  char   is_started;
 | 
			
		||||
 | 
			
		||||
  int  nkeynames;
 | 
			
		||||
  const char **keynames;
 | 
			
		||||
 | 
			
		||||
  // There are 32 C0 codes
 | 
			
		||||
  struct keyinfo c0[32];
 | 
			
		||||
 | 
			
		||||
  struct TermKeyDriverNode *drivers;
 | 
			
		||||
 | 
			
		||||
  // Now some "protected" methods for the driver to call but which we don't
 | 
			
		||||
  // want exported as real symbols in the library
 | 
			
		||||
  struct {
 | 
			
		||||
    void (*emit_codepoint)(TermKey *tk, long codepoint, TermKeyKey *key);
 | 
			
		||||
    TermKeyResult (*peekkey_simple)(TermKey *tk, TermKeyKey *key, int force, size_t *nbytes);
 | 
			
		||||
    TermKeyResult (*peekkey_mouse)(TermKey *tk, TermKeyKey *key, size_t *nbytes);
 | 
			
		||||
  } method;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline void termkey_key_get_linecol(const TermKeyKey *key, int *line, int *col)
 | 
			
		||||
{
 | 
			
		||||
  if(col)
 | 
			
		||||
    *col  = (unsigned char)key->code.mouse[1] | ((unsigned char)key->code.mouse[3] & 0x0f) << 8;
 | 
			
		||||
 | 
			
		||||
  if(line)
 | 
			
		||||
    *line = (unsigned char)key->code.mouse[2] | ((unsigned char)key->code.mouse[3] & 0x70) << 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void termkey_key_set_linecol(TermKeyKey *key, int line, int col)
 | 
			
		||||
{
 | 
			
		||||
  if(line > 0xfff)
 | 
			
		||||
    line = 0xfff;
 | 
			
		||||
 | 
			
		||||
  if(col > 0x7ff)
 | 
			
		||||
    col = 0x7ff;
 | 
			
		||||
 | 
			
		||||
  key->code.mouse[1] = (line & 0x0ff);
 | 
			
		||||
  key->code.mouse[2] = (col & 0x0ff);
 | 
			
		||||
  key->code.mouse[3] = (line & 0xf00) >> 8 | (col & 0x300) >> 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern struct TermKeyDriver termkey_driver_csi;
 | 
			
		||||
extern struct TermKeyDriver termkey_driver_ti;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										1604
									
								
								src/termkey/termkey.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1604
									
								
								src/termkey/termkey.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										249
									
								
								src/termkey/termkey.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								src/termkey/termkey.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,249 @@
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef GUARD_TERMKEY_H_
 | 
			
		||||
#define GUARD_TERMKEY_H_
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
#define TERMKEY_VERSION_MAJOR 0
 | 
			
		||||
#define TERMKEY_VERSION_MINOR 22
 | 
			
		||||
 | 
			
		||||
#define TERMKEY_CHECK_VERSION \
 | 
			
		||||
        termkey_check_version(TERMKEY_VERSION_MAJOR, TERMKEY_VERSION_MINOR)
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  TERMKEY_SYM_UNKNOWN = -1,
 | 
			
		||||
  TERMKEY_SYM_NONE = 0,
 | 
			
		||||
 | 
			
		||||
  /* Special names in C0 */
 | 
			
		||||
  TERMKEY_SYM_BACKSPACE,
 | 
			
		||||
  TERMKEY_SYM_TAB,
 | 
			
		||||
  TERMKEY_SYM_ENTER,
 | 
			
		||||
  TERMKEY_SYM_ESCAPE,
 | 
			
		||||
 | 
			
		||||
  /* Special names in G0 */
 | 
			
		||||
  TERMKEY_SYM_SPACE,
 | 
			
		||||
  TERMKEY_SYM_DEL,
 | 
			
		||||
 | 
			
		||||
  /* Special keys */
 | 
			
		||||
  TERMKEY_SYM_UP,
 | 
			
		||||
  TERMKEY_SYM_DOWN,
 | 
			
		||||
  TERMKEY_SYM_LEFT,
 | 
			
		||||
  TERMKEY_SYM_RIGHT,
 | 
			
		||||
  TERMKEY_SYM_BEGIN,
 | 
			
		||||
  TERMKEY_SYM_FIND,
 | 
			
		||||
  TERMKEY_SYM_INSERT,
 | 
			
		||||
  TERMKEY_SYM_DELETE,
 | 
			
		||||
  TERMKEY_SYM_SELECT,
 | 
			
		||||
  TERMKEY_SYM_PAGEUP,
 | 
			
		||||
  TERMKEY_SYM_PAGEDOWN,
 | 
			
		||||
  TERMKEY_SYM_HOME,
 | 
			
		||||
  TERMKEY_SYM_END,
 | 
			
		||||
 | 
			
		||||
  /* Special keys from terminfo */
 | 
			
		||||
  TERMKEY_SYM_CANCEL,
 | 
			
		||||
  TERMKEY_SYM_CLEAR,
 | 
			
		||||
  TERMKEY_SYM_CLOSE,
 | 
			
		||||
  TERMKEY_SYM_COMMAND,
 | 
			
		||||
  TERMKEY_SYM_COPY,
 | 
			
		||||
  TERMKEY_SYM_EXIT,
 | 
			
		||||
  TERMKEY_SYM_HELP,
 | 
			
		||||
  TERMKEY_SYM_MARK,
 | 
			
		||||
  TERMKEY_SYM_MESSAGE,
 | 
			
		||||
  TERMKEY_SYM_MOVE,
 | 
			
		||||
  TERMKEY_SYM_OPEN,
 | 
			
		||||
  TERMKEY_SYM_OPTIONS,
 | 
			
		||||
  TERMKEY_SYM_PRINT,
 | 
			
		||||
  TERMKEY_SYM_REDO,
 | 
			
		||||
  TERMKEY_SYM_REFERENCE,
 | 
			
		||||
  TERMKEY_SYM_REFRESH,
 | 
			
		||||
  TERMKEY_SYM_REPLACE,
 | 
			
		||||
  TERMKEY_SYM_RESTART,
 | 
			
		||||
  TERMKEY_SYM_RESUME,
 | 
			
		||||
  TERMKEY_SYM_SAVE,
 | 
			
		||||
  TERMKEY_SYM_SUSPEND,
 | 
			
		||||
  TERMKEY_SYM_UNDO,
 | 
			
		||||
 | 
			
		||||
  /* Numeric keypad special keys */
 | 
			
		||||
  TERMKEY_SYM_KP0,
 | 
			
		||||
  TERMKEY_SYM_KP1,
 | 
			
		||||
  TERMKEY_SYM_KP2,
 | 
			
		||||
  TERMKEY_SYM_KP3,
 | 
			
		||||
  TERMKEY_SYM_KP4,
 | 
			
		||||
  TERMKEY_SYM_KP5,
 | 
			
		||||
  TERMKEY_SYM_KP6,
 | 
			
		||||
  TERMKEY_SYM_KP7,
 | 
			
		||||
  TERMKEY_SYM_KP8,
 | 
			
		||||
  TERMKEY_SYM_KP9,
 | 
			
		||||
  TERMKEY_SYM_KPENTER,
 | 
			
		||||
  TERMKEY_SYM_KPPLUS,
 | 
			
		||||
  TERMKEY_SYM_KPMINUS,
 | 
			
		||||
  TERMKEY_SYM_KPMULT,
 | 
			
		||||
  TERMKEY_SYM_KPDIV,
 | 
			
		||||
  TERMKEY_SYM_KPCOMMA,
 | 
			
		||||
  TERMKEY_SYM_KPPERIOD,
 | 
			
		||||
  TERMKEY_SYM_KPEQUALS,
 | 
			
		||||
 | 
			
		||||
  /* et cetera ad nauseum */
 | 
			
		||||
  TERMKEY_N_SYMS
 | 
			
		||||
} TermKeySym;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  TERMKEY_TYPE_UNICODE,
 | 
			
		||||
  TERMKEY_TYPE_FUNCTION,
 | 
			
		||||
  TERMKEY_TYPE_KEYSYM,
 | 
			
		||||
  TERMKEY_TYPE_MOUSE,
 | 
			
		||||
  TERMKEY_TYPE_POSITION,
 | 
			
		||||
  TERMKEY_TYPE_MODEREPORT,
 | 
			
		||||
  TERMKEY_TYPE_DCS,
 | 
			
		||||
  TERMKEY_TYPE_OSC,
 | 
			
		||||
  /* add other recognised types here */
 | 
			
		||||
 | 
			
		||||
  TERMKEY_TYPE_UNKNOWN_CSI = -1
 | 
			
		||||
} TermKeyType;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  TERMKEY_RES_NONE,
 | 
			
		||||
  TERMKEY_RES_KEY,
 | 
			
		||||
  TERMKEY_RES_EOF,
 | 
			
		||||
  TERMKEY_RES_AGAIN,
 | 
			
		||||
  TERMKEY_RES_ERROR
 | 
			
		||||
} TermKeyResult;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  TERMKEY_MOUSE_UNKNOWN,
 | 
			
		||||
  TERMKEY_MOUSE_PRESS,
 | 
			
		||||
  TERMKEY_MOUSE_DRAG,
 | 
			
		||||
  TERMKEY_MOUSE_RELEASE
 | 
			
		||||
} TermKeyMouseEvent;
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
  TERMKEY_KEYMOD_SHIFT = 1 << 0,
 | 
			
		||||
  TERMKEY_KEYMOD_ALT   = 1 << 1,
 | 
			
		||||
  TERMKEY_KEYMOD_CTRL  = 1 << 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  TermKeyType type;
 | 
			
		||||
  union {
 | 
			
		||||
    long       codepoint; /* TERMKEY_TYPE_UNICODE */
 | 
			
		||||
    int        number;    /* TERMKEY_TYPE_FUNCTION */
 | 
			
		||||
    TermKeySym sym;       /* TERMKEY_TYPE_KEYSYM */
 | 
			
		||||
    char       mouse[4];  /* TERMKEY_TYPE_MOUSE */
 | 
			
		||||
                          /* opaque. see termkey_interpret_mouse */
 | 
			
		||||
  } code;
 | 
			
		||||
 | 
			
		||||
  int modifiers;
 | 
			
		||||
 | 
			
		||||
  /* Any Unicode character can be UTF-8 encoded in no more than 6 bytes, plus
 | 
			
		||||
   * terminating NUL */
 | 
			
		||||
  char utf8[7];
 | 
			
		||||
} TermKeyKey;
 | 
			
		||||
 | 
			
		||||
typedef struct TermKey TermKey;
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
  TERMKEY_FLAG_NOINTERPRET = 1 << 0, /* Do not interpret C0//DEL codes if possible */
 | 
			
		||||
  TERMKEY_FLAG_CONVERTKP   = 1 << 1, /* Convert KP codes to regular keypresses */
 | 
			
		||||
  TERMKEY_FLAG_RAW         = 1 << 2, /* Input is raw bytes, not UTF-8 */
 | 
			
		||||
  TERMKEY_FLAG_UTF8        = 1 << 3, /* Input is definitely UTF-8 */
 | 
			
		||||
  TERMKEY_FLAG_NOTERMIOS   = 1 << 4, /* Do not make initial termios calls on construction */
 | 
			
		||||
  TERMKEY_FLAG_SPACESYMBOL = 1 << 5, /* Sets TERMKEY_CANON_SPACESYMBOL */
 | 
			
		||||
  TERMKEY_FLAG_CTRLC       = 1 << 6, /* Allow Ctrl-C to be read as normal, disabling SIGINT */
 | 
			
		||||
  TERMKEY_FLAG_EINTR       = 1 << 7, /* Return ERROR on signal (EINTR) rather than retry */
 | 
			
		||||
  TERMKEY_FLAG_NOSTART     = 1 << 8  /* Do not call termkey_start() in constructor */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
  TERMKEY_CANON_SPACESYMBOL = 1 << 0, /* Space is symbolic rather than Unicode */
 | 
			
		||||
  TERMKEY_CANON_DELBS       = 1 << 1  /* Del is converted to Backspace */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void termkey_check_version(int major, int minor);
 | 
			
		||||
 | 
			
		||||
TermKey *termkey_new(int fd, int flags);
 | 
			
		||||
TermKey *termkey_new_abstract(const char *term, int flags);
 | 
			
		||||
void     termkey_free(TermKey *tk);
 | 
			
		||||
void     termkey_destroy(TermKey *tk);
 | 
			
		||||
 | 
			
		||||
/* Mostly-undocumented hooks for doing evil evil things */
 | 
			
		||||
typedef const char *TermKey_Terminfo_Getstr_Hook(const char *name, const char *value, void *data);
 | 
			
		||||
void termkey_hook_terminfo_getstr(TermKey *tk, TermKey_Terminfo_Getstr_Hook *hookfn, void *data);
 | 
			
		||||
 | 
			
		||||
int termkey_start(TermKey *tk);
 | 
			
		||||
int termkey_stop(TermKey *tk);
 | 
			
		||||
int termkey_is_started(TermKey *tk);
 | 
			
		||||
 | 
			
		||||
int termkey_get_fd(TermKey *tk);
 | 
			
		||||
 | 
			
		||||
int  termkey_get_flags(TermKey *tk);
 | 
			
		||||
void termkey_set_flags(TermKey *tk, int newflags);
 | 
			
		||||
 | 
			
		||||
int  termkey_get_waittime(TermKey *tk);
 | 
			
		||||
void termkey_set_waittime(TermKey *tk, int msec);
 | 
			
		||||
 | 
			
		||||
int  termkey_get_canonflags(TermKey *tk);
 | 
			
		||||
void termkey_set_canonflags(TermKey *tk, int);
 | 
			
		||||
 | 
			
		||||
size_t termkey_get_buffer_size(TermKey *tk);
 | 
			
		||||
int    termkey_set_buffer_size(TermKey *tk, size_t size);
 | 
			
		||||
 | 
			
		||||
size_t termkey_get_buffer_remaining(TermKey *tk);
 | 
			
		||||
 | 
			
		||||
void termkey_canonicalise(TermKey *tk, TermKeyKey *key);
 | 
			
		||||
 | 
			
		||||
TermKeyResult termkey_getkey(TermKey *tk, TermKeyKey *key);
 | 
			
		||||
TermKeyResult termkey_getkey_force(TermKey *tk, TermKeyKey *key);
 | 
			
		||||
TermKeyResult termkey_waitkey(TermKey *tk, TermKeyKey *key);
 | 
			
		||||
 | 
			
		||||
TermKeyResult termkey_advisereadable(TermKey *tk);
 | 
			
		||||
 | 
			
		||||
size_t termkey_push_bytes(TermKey *tk, const char *bytes, size_t len);
 | 
			
		||||
 | 
			
		||||
TermKeySym termkey_register_keyname(TermKey *tk, TermKeySym sym, const char *name);
 | 
			
		||||
const char *termkey_get_keyname(TermKey *tk, TermKeySym sym);
 | 
			
		||||
const char *termkey_lookup_keyname(TermKey *tk, const char *str, TermKeySym *sym);
 | 
			
		||||
 | 
			
		||||
TermKeySym termkey_keyname2sym(TermKey *tk, const char *keyname);
 | 
			
		||||
 | 
			
		||||
TermKeyResult termkey_interpret_mouse(TermKey *tk, const TermKeyKey *key, TermKeyMouseEvent *event, int *button, int *line, int *col);
 | 
			
		||||
 | 
			
		||||
TermKeyResult termkey_interpret_position(TermKey *tk, const TermKeyKey *key, int *line, int *col);
 | 
			
		||||
 | 
			
		||||
TermKeyResult termkey_interpret_modereport(TermKey *tk, const TermKeyKey *key, int *initial, int *mode, int *value);
 | 
			
		||||
 | 
			
		||||
TermKeyResult termkey_interpret_csi(TermKey *tk, const TermKeyKey *key, long args[], size_t *nargs, unsigned long *cmd);
 | 
			
		||||
 | 
			
		||||
TermKeyResult termkey_interpret_string(TermKey *tk, const TermKeyKey *key, const char **strp);
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  TERMKEY_FORMAT_LONGMOD     = 1 << 0, /* Shift-... instead of S-... */
 | 
			
		||||
  TERMKEY_FORMAT_CARETCTRL   = 1 << 1, /* ^X instead of C-X */
 | 
			
		||||
  TERMKEY_FORMAT_ALTISMETA   = 1 << 2, /* Meta- or M- instead of Alt- or A- */
 | 
			
		||||
  TERMKEY_FORMAT_WRAPBRACKET = 1 << 3, /* Wrap special keys in brackets like <Escape> */
 | 
			
		||||
  TERMKEY_FORMAT_SPACEMOD    = 1 << 4, /* M Foo instead of M-Foo */
 | 
			
		||||
  TERMKEY_FORMAT_LOWERMOD    = 1 << 5, /* meta or m instead of Meta or M */
 | 
			
		||||
  TERMKEY_FORMAT_LOWERSPACE  = 1 << 6, /* page down instead of PageDown */
 | 
			
		||||
 | 
			
		||||
  TERMKEY_FORMAT_MOUSE_POS   = 1 << 8  /* Include mouse position if relevant; @ col,line */
 | 
			
		||||
} TermKeyFormat;
 | 
			
		||||
 | 
			
		||||
/* Some useful combinations */
 | 
			
		||||
 | 
			
		||||
#define TERMKEY_FORMAT_VIM (TermKeyFormat)(TERMKEY_FORMAT_ALTISMETA|TERMKEY_FORMAT_WRAPBRACKET)
 | 
			
		||||
#define TERMKEY_FORMAT_URWID (TermKeyFormat)(TERMKEY_FORMAT_LONGMOD|TERMKEY_FORMAT_ALTISMETA| \
 | 
			
		||||
          TERMKEY_FORMAT_LOWERMOD|TERMKEY_FORMAT_SPACEMOD|TERMKEY_FORMAT_LOWERSPACE)
 | 
			
		||||
 | 
			
		||||
size_t      termkey_strfkey(TermKey *tk, char *buffer, size_t len, TermKeyKey *key, TermKeyFormat format);
 | 
			
		||||
const char *termkey_strpkey(TermKey *tk, const char *str, TermKeyKey *key, TermKeyFormat format);
 | 
			
		||||
 | 
			
		||||
int termkey_keycmp(TermKey *tk, const TermKeyKey *key1, const TermKeyKey *key2);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
		Reference in New Issue
	
	Block a user