From 5692f32bcfa049fc2b5555b5a883045b217349b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Thu, 9 Oct 2014 23:47:24 +0200 Subject: CMake-ify, rename to termkey2 for the time being --- .gitignore | 2 + CMakeLists.txt | 151 +++++ Makefile | 178 ------ README | 60 ++ config-version.cmake.in | 10 + config.cmake.in | 8 + demo-async.c | 2 +- demo-glib.c | 2 +- demo.c | 2 +- driver-csi.c | 4 +- driver-ti.c | 6 +- t/01base.c | 29 - t/02getkey.c | 92 --- t/03utf8.c | 188 ------ t/04flags.c | 40 -- t/05read.c | 85 --- t/06buffer.c | 38 -- t/10keyname.c | 40 -- t/11strfkey.c | 151 ----- t/12strpkey.c | 157 ----- t/13cmpkey.c | 72 --- t/20canon.c | 74 --- t/30mouse.c | 174 ------ t/31position.c | 38 -- t/32modereport.c | 45 -- t/39csi.c | 52 -- t/taplib.c | 79 --- t/taplib.h | 7 - termkey-internal.h | 115 ---- termkey.c | 1556 ----------------------------------------------- termkey.h.in | 272 --------- termkey.pc.in | 8 - termkey2-config.h.in | 8 + termkey2-internal.h | 115 ++++ termkey2.c | 1556 +++++++++++++++++++++++++++++++++++++++++++++++ termkey2.h | 271 +++++++++ tests/01base.c | 29 + tests/02getkey.c | 92 +++ tests/03utf8.c | 188 ++++++ tests/04flags.c | 40 ++ tests/05read.c | 85 +++ tests/06buffer.c | 38 ++ tests/10keyname.c | 40 ++ tests/11strfkey.c | 151 +++++ tests/12strpkey.c | 157 +++++ tests/13cmpkey.c | 72 +++ tests/20canon.c | 74 +++ tests/30mouse.c | 174 ++++++ tests/31position.c | 38 ++ tests/32modereport.c | 45 ++ tests/39csi.c | 52 ++ tests/taplib.c | 79 +++ tests/taplib.h | 7 + 53 files changed, 3550 insertions(+), 3498 deletions(-) create mode 100644 .gitignore create mode 100644 CMakeLists.txt delete mode 100644 Makefile create mode 100644 README create mode 100644 config-version.cmake.in create mode 100644 config.cmake.in delete mode 100644 t/01base.c delete mode 100644 t/02getkey.c delete mode 100644 t/03utf8.c delete mode 100644 t/04flags.c delete mode 100644 t/05read.c delete mode 100644 t/06buffer.c delete mode 100644 t/10keyname.c delete mode 100644 t/11strfkey.c delete mode 100644 t/12strpkey.c delete mode 100644 t/13cmpkey.c delete mode 100644 t/20canon.c delete mode 100644 t/30mouse.c delete mode 100644 t/31position.c delete mode 100644 t/32modereport.c delete mode 100644 t/39csi.c delete mode 100644 t/taplib.c delete mode 100644 t/taplib.h delete mode 100644 termkey-internal.h delete mode 100644 termkey.c delete mode 100644 termkey.h.in delete mode 100644 termkey.pc.in create mode 100644 termkey2-config.h.in create mode 100644 termkey2-internal.h create mode 100644 termkey2.c create mode 100644 termkey2.h create mode 100644 tests/01base.c create mode 100644 tests/02getkey.c create mode 100644 tests/03utf8.c create mode 100644 tests/04flags.c create mode 100644 tests/05read.c create mode 100644 tests/06buffer.c create mode 100644 tests/10keyname.c create mode 100644 tests/11strfkey.c create mode 100644 tests/12strpkey.c create mode 100644 tests/13cmpkey.c create mode 100644 tests/20canon.c create mode 100644 tests/30mouse.c create mode 100644 tests/31position.c create mode 100644 tests/32modereport.c create mode 100644 tests/39csi.c create mode 100644 tests/taplib.c create mode 100644 tests/taplib.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e5148b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Qt Creator +/CMakeLists.txt.user* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..81dbb2c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,151 @@ +project (termkey2 C) +cmake_minimum_required (VERSION 2.8.5) + +if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC) + set (CMAKE_C_FLAGS "-std=c99") + set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -Wextra") +endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC) + +# Version +set (project_VERSION_MAJOR 0) +set (project_VERSION_MINOR 1) +set (project_VERSION_PATCH 0) + +set (project_VERSION ${project_VERSION_MAJOR}) +set (project_VERSION ${project_VERSION}.${project_VERSION_MINOR}) +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") + +# Dependecies +find_package (Curses) +find_package (PkgConfig REQUIRED) +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) +include_directories (${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR}) + +# Project source files +set (lib_sources + termkey2.c + driver-csi.c + driver-ti.c) +set (lib_headers + termkey2.h + termkey2-internal.h + ${PROJECT_BINARY_DIR}/termkey2-config.h) + +# Project libraries +if (unibilium_FOUND) + include_directories (${unibilium_INCLUDE_DIRS}) + set (lib_libraries ${unibilium_LIBRARIES}) + add_definitions (-DHAVE_UNIBILIUM) +elseif (CURSES_FOUND) + include_directories (${CURSES_INCLUDE_DIR}) + set (lib_libraries ${CURSES_LIBRARY}) +else (CURSES_FOUND) + message (SEND_ERROR "Unibilium not found, Curses not 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 + 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 + 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}) + +add_executable (demo EXCLUDE_FROM_ALL demo.c) +target_link_libraries (demo termkey2-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}) + list (APPEND demos demo-glib) +endif (glib_FOUND) + +add_custom_target (demos DEPENDS ${demos}) + +# The files to be installed +include (GNUInstallDirs) +install (TARGETS termkey2 termkey2-static DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR}) +install (FILES termkey2.h ${PROJECT_BINARY_DIR}/termkey2-config.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${project_INCLUDE_NAME}) + +# Configuration for other CMake projects +configure_file (config.cmake.in + ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake @ONLY) +configure_file (config-version.cmake.in + ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake @ONLY) + +install (FILES ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake + ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}-${project_VERSION}) + +# Do some unit tests +option (BUILD_TESTING "Build tests" OFF) +# TODO: glob, port the tests to CTest +set (project_tests) + +if (BUILD_TESTING) + enable_testing () + set (test_common_sources t/taplib.c t/taplib.h) + + foreach (name ${project_tests}) + add_executable (test-${name} t/${name}.c ${test_common_sources}) + target_link_libraries (test-${name} ${lib_libraries}) + add_test (test-${name} test-${name}) + endforeach (name) +endif (BUILD_TESTING) + +# pkg-config +file (WRITE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.pc" + "Name: ${PROJECT_NAME}\n" + "Description: Terminal key input library\n" + "Version: ${project_VERSION}\n" + "Libs: -L${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} -l${project_LIB_NAME}\n" + "Libs.private: ${lib_libraries}\n" + "Cflags: -I${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/${project_INCLUDE_NAME}\n") +install (FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + +# CPack +set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Terminal key input library") +set (CPACK_PACKAGE_VENDOR "Premysl Janouch") +set (CPACK_PACKAGE_CONTACT "Přemysl Janouch ") +set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") +set (CPACK_PACKAGE_VERSION_MAJOR ${project_VERSION_MAJOR}) +set (CPACK_PACKAGE_VERSION_MINOR ${project_VERSION_MINOR}) +set (CPACK_PACKAGE_VERSION_PATCH ${project_VERSION_PATCH}) +set (CPACK_GENERATOR "TGZ;ZIP") +set (CPACK_PACKAGE_FILE_NAME + "${CMAKE_PROJECT_NAME}-${project_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}") +set (CPACK_PACKAGE_INSTALL_DIRECTORY "${CMAKE_PROJECT_NAME}-${project_VERSION}") +set (CPACK_SOURCE_GENERATOR "TGZ;ZIP") +set (CPACK_SOURCE_IGNORE_FILES "/\\\\.git;/build;/CMakeLists.txt.user") +set (CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${project_VERSION}") + +include (CPack) diff --git a/Makefile b/Makefile deleted file mode 100644 index b96a1d6..0000000 --- a/Makefile +++ /dev/null @@ -1,178 +0,0 @@ -ifeq ($(shell uname),Darwin) - LIBTOOL ?= glibtool -else - LIBTOOL ?= libtool -endif - -ifneq ($(VERBOSE),1) - LIBTOOL +=--quiet -endif - -CFLAGS +=-Wall -Wextra -std=c99 - -ifeq ($(DEBUG),1) - CFLAGS +=-ggdb -DDEBUG -endif - -ifeq ($(PROFILE),1) - CFLAGS +=-pg - LDFLAGS+=-pg -endif - -ifeq ($(shell pkg-config --atleast-version=0.1.0 unibilium && echo 1),1) - CFLAGS +=$(shell pkg-config --cflags unibilium) -DHAVE_UNIBILIUM - LDFLAGS+=$(shell pkg-config --libs unibilium) -else ifeq ($(shell pkg-config tinfo && echo 1),1) - CFLAGS +=$(shell pkg-config --cflags tinfo) - LDFLAGS+=$(shell pkg-config --libs tinfo) -else ifeq ($(shell pkg-config ncursesw && echo 1),1) - CFLAGS +=$(shell pkg-config --cflags ncursesw) - LDFLAGS+=$(shell pkg-config --libs ncursesw) -else - LDFLAGS+=-lncurses -endif - -OBJECTS=termkey.lo driver-csi.lo driver-ti.lo -LIBRARY=libtermkey.la - -DEMOS=demo demo-async - -ifeq ($(shell pkg-config glib-2.0 && echo 1),1) - DEMOS+=demo-glib -endif - -DEMO_OBJECTS=$(DEMOS:=.lo) - -TESTSOURCES=$(wildcard t/[0-9]*.c) -TESTFILES=$(TESTSOURCES:.c=.t) - -VERSION_MAJOR=0 -VERSION_MINOR=17 - -VERSION_CURRENT=12 -VERSION_REVISION=0 -VERSION_AGE=11 - -PREFIX=/usr/local -LIBDIR=$(PREFIX)/lib -INCDIR=$(PREFIX)/include -MANDIR=$(PREFIX)/share/man -MAN3DIR=$(MANDIR)/man3 -MAN7DIR=$(MANDIR)/man7 - -all: $(LIBRARY) $(DEMOS) - -%.lo: %.c termkey.h termkey-internal.h - $(LIBTOOL) --mode=compile --tag=CC $(CC) $(CFLAGS) -o $@ -c $< - -$(LIBRARY): $(OBJECTS) - $(LIBTOOL) --mode=link --tag=CC $(CC) -rpath $(LIBDIR) -version-info $(VERSION_CURRENT):$(VERSION_REVISION):$(VERSION_AGE) $(LDFLAGS) -o $@ $^ - -demo: $(LIBRARY) demo.lo - $(LIBTOOL) --mode=link --tag=CC $(CC) -o $@ $^ - -demo-async: $(LIBRARY) demo-async.lo - $(LIBTOOL) --mode=link --tag=CC $(CC) -o $@ $^ - -demo-glib.lo: demo-glib.c termkey.h - $(LIBTOOL) --mode=compile --tag=CC $(CC) -o $@ -c $< $(shell pkg-config glib-2.0 --cflags) - -demo-glib: $(LIBRARY) demo-glib.lo - $(LIBTOOL) --mode=link --tag=CC $(CC) -o $@ $^ $(shell pkg-config glib-2.0 --libs) - -t/%.t: t/%.c $(LIBRARY) t/taplib.lo - $(LIBTOOL) --mode=link --tag=CC $(CC) -o $@ $^ - -t/taplib.lo: t/taplib.c - $(LIBTOOL) --mode=compile --tag=CC $(CC) $(CFLAGS) -o $@ -c $^ - -.PHONY: test -test: $(TESTFILES) - prove -e "" - -.PHONY: clean-test -clean-test: - $(LIBTOOL) --mode=clean rm -f $(TESTFILES) t/taplib.lo - -.PHONY: clean -clean: clean-test - $(LIBTOOL) --mode=clean rm -f $(OBJECTS) $(DEMO_OBJECTS) - $(LIBTOOL) --mode=clean rm -f $(LIBRARY) - $(LIBTOOL) --mode=clean rm -rf $(DEMOS) - -.PHONY: install -install: install-inc install-lib install-man - $(LIBTOOL) --mode=finish $(DESTDIR)$(LIBDIR) - -install-inc: termkey.h - install -d $(DESTDIR)$(INCDIR) - install -m644 termkey.h $(DESTDIR)$(INCDIR) - install -d $(DESTDIR)$(LIBDIR)/pkgconfig - sed "s,@LIBDIR@,$(LIBDIR),;s,@INCDIR@,$(INCDIR)," $(DESTDIR)$(LIBDIR)/pkgconfig/termkey.pc - -install-lib: $(LIBRARY) - install -d $(DESTDIR)$(LIBDIR) - $(LIBTOOL) --mode=install install libtermkey.la $(DESTDIR)$(LIBDIR)/libtermkey.la - -install-man: - install -d $(DESTDIR)$(MAN3DIR) - install -d $(DESTDIR)$(MAN7DIR) - for F in man/*.3; do \ - gzip <$$F >$(DESTDIR)$(MAN3DIR)/$${F#man/}.gz; \ - done - for F in man/*.7; do \ - gzip <$$F >$(DESTDIR)$(MAN7DIR)/$${F#man/}.gz; \ - done - while read FROM EQ TO; do \ - echo ln -sf $$TO.gz $(DESTDIR)$(MAN3DIR)/$$FROM.gz; \ - done < man/also - -# DIST CUT - -MANSOURCE=$(wildcard man/*.3.sh) -BUILTMAN=$(MANSOURCE:.3.sh=.3) - -VERSION=$(VERSION_MAJOR).$(VERSION_MINOR) - -all: doc - -doc: $(BUILTMAN) - -%.3: %.3.sh - sh $< >$@ - -clean: clean-built - -clean-built: - rm -f $(BUILTMAN) termkey.h - -termkey.h: termkey.h.in Makefile - rm -f $@ - sed -e 's/@@VERSION_MAJOR@@/$(VERSION_MAJOR)/g' \ - -e 's/@@VERSION_MINOR@@/$(VERSION_MINOR)/g' \ - $< >$@ - chmod a-w $@ - -DISTDIR=libtermkey-$(VERSION) - -distdir: all - mkdir __distdir - cp *.c *.h LICENSE __distdir - mkdir __distdir/t - cp t/*.c t/*.h __distdir/t - mkdir __distdir/man - cp man/*.[37] man/also __distdir/man - sed "s,@VERSION@,$(VERSION)," __distdir/termkey.pc.in - sed "/^# DIST CUT/Q" __distdir/Makefile - mv __distdir $(DISTDIR) - -TARBALL=$(DISTDIR).tar.gz - -dist: distdir - tar -czf $(TARBALL) $(DISTDIR) - rm -rf $(DISTDIR) - -HTMLDIR=html - -htmldocs: $(BUILTMAN) - perl $(HOME)/src/perl/Parse-Man/examples/man-to-html.pl -O $(HTMLDIR) --file-extension tmpl --link-extension html --template home_lou.tt2 --also man/also man/*.3 man/*.7 --index index.tmpl diff --git a/README b/README new file mode 100644 index 0000000..3a1c2ee --- /dev/null +++ b/README @@ -0,0 +1,60 @@ +termkey2 +======== + +`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 +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 +doesn't aspire to also replace the output part of ncurses, but is rather +complementary to it. In the end it makes use of its terminfo library. + +The API isn't stable yet. Tell me what needs to be done so I can fix it first. + +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 + $ mkdir build + $ cd build + $ cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local + +To install the library, you can do either the usual: + $ make install + +Or you can try telling CMake to make a package for you. For Debian it is: + $ cpack -G DEB + # dpkg -i termkey2-*.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: + + $ make demos + +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, +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 + +Another change worth mentioning is the usage of CMake instead of the problematic +libtool-based Makefile. Now you can include this project in your other CMake- +-based projects and simply import the target. No package maintainer action is +needed for you to enjoy the benefits of proper terminal input. + +The rest is just me going silly over formatting and various unimportant stuff. +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 +, with additional changes made by Přemysl Janouch +. + +You may use the software under the terms of the MIT license, the text of which +is included within the package, see the file LICENSE. diff --git a/config-version.cmake.in b/config-version.cmake.in new file mode 100644 index 0000000..dbd7077 --- /dev/null +++ b/config-version.cmake.in @@ -0,0 +1,10 @@ +set (PACKAGE_VERSION "@project_VERSION@") + +if ("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") + set (PACKAGE_VERSION_COMPATIBLE FALSE) +else ("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") + set (PACKAGE_VERSION_COMPATIBLE TRUE) + if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") + set (PACKAGE_VERSION_EXACT TRUE) + endif ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") +endif ("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") diff --git a/config.cmake.in b/config.cmake.in new file mode 100644 index 0000000..9a19a6f --- /dev/null +++ b/config.cmake.in @@ -0,0 +1,8 @@ +# - Config file for @project_CMAKE_NAME@ +# It defines the following variables: +# @project_CMAKE_NAME@_INCLUDE_DIRS +# @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) + diff --git a/demo-async.c b/demo-async.c index 6efcf28..88d68a5 100644 --- a/demo-async.c +++ b/demo-async.c @@ -6,7 +6,7 @@ #include #include -#include "termkey.h" +#include "termkey2.h" static void on_key (termkey_t *tk, termkey_key_t *key) diff --git a/demo-glib.c b/demo-glib.c index 0162042..dfe5cd4 100644 --- a/demo-glib.c +++ b/demo-glib.c @@ -3,7 +3,7 @@ #include #include -#include "termkey.h" +#include "termkey2.h" static termkey_t *tk; static int timeout_id; diff --git a/demo.c b/demo.c index 1f89ea0..5bb9fc2 100644 --- a/demo.c +++ b/demo.c @@ -6,7 +6,7 @@ #include #include -#include "termkey.h" +#include "termkey2.h" int main(int argc, char *argv[]) diff --git a/driver-csi.c b/driver-csi.c index 4e1f275..23f38e7 100644 --- a/driver-csi.c +++ b/driver-csi.c @@ -1,5 +1,5 @@ -#include "termkey.h" -#include "termkey-internal.h" +#include "termkey2.h" +#include "termkey2-internal.h" #include #include diff --git a/driver-ti.c b/driver-ti.c index ed0e9df..93fda30 100644 --- a/driver-ti.c +++ b/driver-ti.c @@ -1,8 +1,8 @@ // we want strdup() #define _XOPEN_SOURCE 600 -#include "termkey.h" -#include "termkey-internal.h" +#include "termkey2.h" +#include "termkey2-internal.h" #ifdef HAVE_UNIBILIUM # include @@ -240,7 +240,7 @@ load_terminfo (termkey_ti_t *ti, const char *term) if (node && !insert_seq (ti, value, node)) { - free(node); + free (node); return 0; } } diff --git a/t/01base.c b/t/01base.c deleted file mode 100644 index 39e0e39..0000000 --- a/t/01base.c +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include "../termkey.h" -#include "taplib.h" - -int -main (int argc, char *argv[]) -{ - termkey_t *tk; - - plan_tests (6); - - tk = termkey_new_abstract ("vt100", NULL, 0); - ok (!!tk, "termkey_new_abstract"); - is_int (termkey_get_buffer_size (tk), 256, "termkey_get_buffer_size"); - ok (termkey_is_started (tk), "termkey_is_started true after construction"); - - termkey_stop (tk); - ok (!termkey_is_started (tk), - "termkey_is_started false after termkey_stop()"); - - termkey_start (tk); - ok (termkey_is_started (tk), - "termkey_is_started true after termkey_start()"); - - termkey_destroy (tk); - - ok (1, "termkey_free"); - return exit_status (); -} diff --git a/t/02getkey.c b/t/02getkey.c deleted file mode 100644 index 8cba991..0000000 --- a/t/02getkey.c +++ /dev/null @@ -1,92 +0,0 @@ -#include "../termkey.h" -#include "taplib.h" - -int -main (int argc, char *argv[]) -{ - termkey_t *tk; - termkey_key_t key; - - plan_tests (31); - - tk = termkey_new_abstract ("vt100", NULL, 0); - - is_int (termkey_get_buffer_remaining (tk), - 256, "buffer free initially 256"); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_NONE, - "getkey yields RES_NONE when empty"); - - is_int (termkey_push_bytes (tk, "h", 1), 1, "push_bytes returns 1"); - - is_int (termkey_get_buffer_remaining (tk), 255, - "buffer free 255 after push_bytes"); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY after h"); - - is_int (key.type, TERMKEY_TYPE_KEY, "key.type after h"); - is_int (key.code.codepoint, 'h', "key.code.codepoint after h"); - is_int (key.modifiers, 0, "key.modifiers after h"); - is_str (key.multibyte, "h", "key.multibyte after h"); - - is_int (termkey_get_buffer_remaining (tk), 256, - "buffer free 256 after getkey"); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_NONE, - "getkey yields RES_NONE a second time"); - - termkey_push_bytes (tk, "\x01", 1); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY after C-a"); - - is_int (key.type, TERMKEY_TYPE_KEY, "key.type after C-a"); - is_int (key.code.codepoint, 'a', "key.code.codepoint after C-a"); - is_int (key.modifiers, TERMKEY_KEYMOD_CTRL, "key.modifiers after C-a"); - - termkey_push_bytes (tk, "\033OA", 3); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY after Up"); - - is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type after Up"); - is_int (key.code.sym, TERMKEY_SYM_UP, "key.code.sym after Up"); - is_int (key.modifiers, 0, "key.modifiers after Up"); - - is_int (termkey_push_bytes (tk, "\033O", 2), 2, "push_bytes returns 2"); - - is_int (termkey_get_buffer_remaining (tk), 254, - "buffer free 254 after partial write"); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, - "getkey yields RES_AGAIN after partial write"); - - termkey_push_bytes (tk, "C", 1); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY after Right completion"); - - is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type after Right"); - is_int (key.code.sym, TERMKEY_SYM_RIGHT, "key.code.sym after Right"); - is_int (key.modifiers, 0, "key.modifiers after Right"); - - is_int (termkey_get_buffer_remaining (tk), 256, - "buffer free 256 after completion"); - - termkey_push_bytes (tk, "\033[27;5u", 7); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY after Ctrl-Escape"); - - is_int (key.type, TERMKEY_TYPE_KEYSYM, - "key.type after Ctrl-Escape"); - is_int (key.code.sym, TERMKEY_SYM_ESCAPE, - "key.code.sym after Ctrl-Escape"); - is_int (key.modifiers, TERMKEY_KEYMOD_CTRL, - "key.modifiers after Ctrl-Escape"); - - termkey_destroy (tk); - - return exit_status (); -} diff --git a/t/03utf8.c b/t/03utf8.c deleted file mode 100644 index 9b06e2e..0000000 --- a/t/03utf8.c +++ /dev/null @@ -1,188 +0,0 @@ -#include "../termkey.h" -#include "taplib.h" - -int -main (int argc, char *argv[]) -{ - termkey_t *tk; - termkey_key_t key; - - plan_tests (33 /* 57 */); - - tk = termkey_new_abstract ("vt100", "UTF-8", 0); - - termkey_push_bytes (tk, "a", 1); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY low ASCII"); - is_int (key.type, TERMKEY_TYPE_KEY, "key.type low ASCII"); - is_int (key.code.codepoint, 'a', "key.code.codepoint low ASCII"); - - /* 2-byte UTF-8 range is U+0080 to U+07FF (0xDF 0xBF) */ - /* However, we'd best avoid the C1 range, so we'll start at U+00A0 (0xC2 0xA0) */ - - termkey_push_bytes (tk, "\xC2\xA0", 2); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 2 low"); - is_int (key.type, TERMKEY_TYPE_KEY, "key.type UTF-8 2 low"); - is_int (key.code.codepoint, 0x00A0, "key.code.codepoint UTF-8 2 low"); - - termkey_push_bytes (tk, "\xDF\xBF", 2); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 2 high"); - is_int (key.type, TERMKEY_TYPE_KEY, "key.type UTF-8 2 high"); - is_int (key.code.codepoint, 0x07FF, "key.code.codepoint UTF-8 2 high"); - - /* 3-byte UTF-8 range is U+0800 (0xE0 0xA0 0x80) to U+FFFD (0xEF 0xBF 0xBD) */ - - termkey_push_bytes (tk, "\xE0\xA0\x80", 3); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 3 low"); - is_int (key.type, TERMKEY_TYPE_KEY, "key.type UTF-8 3 low"); - is_int (key.code.codepoint, 0x0800, "key.code.codepoint UTF-8 3 low"); - - termkey_push_bytes (tk, "\xEF\xBF\xBD", 3); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 3 high"); - is_int (key.type, TERMKEY_TYPE_KEY, "key.type UTF-8 3 high"); - is_int (key.code.codepoint, 0xFFFD, "key.code.codepoint UTF-8 3 high"); - - /* 4-byte UTF-8 range is U+10000 (0xF0 0x90 0x80 0x80) to U+10FFFF (0xF4 0x8F 0xBF 0xBF) */ - - termkey_push_bytes (tk, "\xF0\x90\x80\x80", 4); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 4 low"); - is_int (key.type, TERMKEY_TYPE_KEY, "key.type UTF-8 4 low"); - is_int (key.code.codepoint, 0x10000, "key.code.codepoint UTF-8 4 low"); - - termkey_push_bytes (tk, "\xF4\x8F\xBF\xBF", 4); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 4 high"); - is_int (key.type, TERMKEY_TYPE_KEY, "key.type UTF-8 4 high"); - is_int (key.code.codepoint, 0x10FFFF, "key.code.codepoint UTF-8 4 high"); - -#if 0 - /* XXX: With the move to iconv, this has changed significantly. */ - - /* Invalid continuations */ - - termkey_push_bytes (tk, "\xC2!", 2); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 2 invalid cont"); - is_int (key.code.codepoint, 0xFFFD, - "key.code.codepoint UTF-8 2 invalid cont"); - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 2 invalid after"); - is_int (key.code.codepoint, '!', - "key.code.codepoint UTF-8 2 invalid after"); - - termkey_push_bytes (tk, "\xE0!", 2); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 3 invalid cont"); - is_int (key.code.codepoint, 0xFFFD, - "key.code.codepoint UTF-8 3 invalid cont"); - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 3 invalid after"); - is_int (key.code.codepoint, '!', - "key.code.codepoint UTF-8 3 invalid after"); - - termkey_push_bytes (tk, "\xE0\xA0!", 3); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 3 invalid cont 2"); - is_int (key.code.codepoint, 0xFFFD, - "key.code.codepoint UTF-8 3 invalid cont 2"); - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 3 invalid after"); - is_int (key.code.codepoint, '!', - "key.code.codepoint UTF-8 3 invalid after"); - - termkey_push_bytes (tk, "\xF0!", 2); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 4 invalid cont"); - is_int (key.code.codepoint, 0xFFFD, - "key.code.codepoint UTF-8 4 invalid cont"); - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 4 invalid after"); - is_int (key.code.codepoint, '!', - "key.code.codepoint UTF-8 4 invalid after"); - - termkey_push_bytes (tk, "\xF0\x90!", 3); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 4 invalid cont 2"); - is_int (key.code.codepoint, 0xFFFD, - "key.code.codepoint UTF-8 4 invalid cont 2"); - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 4 invalid after"); - is_int (key.code.codepoint, '!', - "key.code.codepoint UTF-8 4 invalid after"); - - termkey_push_bytes (tk, "\xF0\x90\x80!", 4); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 4 invalid cont 3"); - is_int (key.code.codepoint, 0xFFFD, - "key.code.codepoint UTF-8 4 invalid cont 3"); - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 4 invalid after"); - is_int (key.code.codepoint, '!', - "key.code.codepoint UTF-8 4 invalid after"); -#endif - - /* Partials */ - - termkey_push_bytes (tk, "\xC2", 1); - is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, - "getkey yields RES_AGAIN UTF-8 2 partial"); - - termkey_push_bytes (tk, "\xA0", 1); - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 2 partial"); - is_int (key.code.codepoint, 0x00A0, - "key.code.codepoint UTF-8 2 partial"); - - termkey_push_bytes (tk, "\xE0", 1); - is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, - "getkey yields RES_AGAIN UTF-8 3 partial"); - - termkey_push_bytes (tk, "\xA0", 1); - is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, - "getkey yields RES_AGAIN UTF-8 3 partial"); - - termkey_push_bytes (tk, "\x80", 1); - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 3 partial"); - is_int (key.code.codepoint, 0x0800, - "key.code.codepoint UTF-8 3 partial"); - - termkey_push_bytes (tk, "\xF0", 1); - is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, - "getkey yields RES_AGAIN UTF-8 4 partial"); - - termkey_push_bytes (tk, "\x90", 1); - is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, - "getkey yields RES_AGAIN UTF-8 4 partial"); - - termkey_push_bytes (tk, "\x80", 1); - is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, - "getkey yields RES_AGAIN UTF-8 4 partial"); - - termkey_push_bytes (tk, "\x80", 1); - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY UTF-8 4 partial"); - is_int (key.code.codepoint, 0x10000, - "key.code.codepoint UTF-8 4 partial"); - - termkey_destroy (tk); - return exit_status (); -} diff --git a/t/04flags.c b/t/04flags.c deleted file mode 100644 index eb6e20b..0000000 --- a/t/04flags.c +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include "../termkey.h" -#include "taplib.h" - -int -main (int argc, char *argv[]) -{ - termkey_t *tk; - termkey_key_t key; - - plan_tests (8); - - tk = termkey_new_abstract ("vt100", NULL, 0); - - termkey_push_bytes (tk, " ", 1); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY after space"); - - is_int (key.type, TERMKEY_TYPE_KEY, "key.type after space"); - is_int (key.code.codepoint, ' ', "key.code.codepoint after space"); - is_int (key.modifiers, 0, "key.modifiers after space"); - - termkey_set_flags (tk, TERMKEY_FLAG_SPACESYMBOL); - - termkey_push_bytes (tk, " ", 1); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY after space"); - - is_int (key.type, TERMKEY_TYPE_KEYSYM, - "key.type after space with FLAG_SPACESYMBOL"); - is_int (key.code.sym, TERMKEY_SYM_SPACE, - "key.code.sym after space with FLAG_SPACESYMBOL"); - is_int (key.modifiers, 0, - "key.modifiers after space with FLAG_SPACESYMBOL"); - - termkey_destroy (tk); - return exit_status (); -} diff --git a/t/05read.c b/t/05read.c deleted file mode 100644 index 2a5530a..0000000 --- a/t/05read.c +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include "../termkey.h" -#include "taplib.h" - -int -main (int argc, char *argv[]) -{ - int fd[2]; - termkey_t *tk; - termkey_key_t key; - - plan_tests (21); - - /* We'll need a real filehandle we can write/read. - * pipe () can make us one */ - pipe (fd); - - /* Sanitise this just in case */ - putenv ("TERM=vt100"); - - tk = termkey_new (fd[0], NULL, TERMKEY_FLAG_NOTERMIOS); - - is_int (termkey_get_buffer_remaining (tk), 256, - "buffer free initially 256"); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_NONE, - "getkey yields RES_NONE when empty"); - - write (fd[1], "h", 1); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_NONE, - "getkey yields RES_NONE before advisereadable"); - - is_int (termkey_advisereadable (tk), TERMKEY_RES_AGAIN, - "advisereadable yields RES_AGAIN after h"); - - is_int (termkey_get_buffer_remaining (tk), 255, - "buffer free 255 after advisereadable"); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY after h"); - - is_int (key.type, TERMKEY_TYPE_KEY, "key.type after h"); - is_int (key.code.codepoint, 'h', "key.code.codepoint after h"); - is_int (key.modifiers, 0, "key.modifiers after h"); - is_str (key.multibyte, "h", "key.multibyte after h"); - - is_int (termkey_get_buffer_remaining (tk), 256, - "buffer free 256 after getkey"); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_NONE, - "getkey yields RES_NONE a second time"); - - write (fd[1], "\033O", 2); - termkey_advisereadable (tk); - - is_int (termkey_get_buffer_remaining (tk), 254, - "buffer free 254 after partial write"); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, - "getkey yields RES_AGAIN after partial write"); - - write (fd[1], "C", 1); - termkey_advisereadable (tk); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY after Right completion"); - - is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type after Right"); - is_int (key.code.sym, TERMKEY_SYM_RIGHT, "key.code.sym after Right"); - is_int (key.modifiers, 0, "key.modifiers after Right"); - - is_int (termkey_get_buffer_remaining (tk), 256, - "buffer free 256 after completion"); - - termkey_stop (tk); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_ERROR, - "getkey yields RES_ERROR after termkey_stop ()"); - is_int (errno, EINVAL, "getkey error is EINVAL"); - - termkey_destroy (tk); - return exit_status (); -} diff --git a/t/06buffer.c b/t/06buffer.c deleted file mode 100644 index af415b6..0000000 --- a/t/06buffer.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include "../termkey.h" -#include "taplib.h" - -int main (int argc, char *argv[]) -{ - termkey_t *tk; - termkey_key_t key; - - plan_tests (9); - - tk = termkey_new_abstract ("vt100", NULL, 0); - - is_int (termkey_get_buffer_remaining (tk), 256, - "buffer free initially 256"); - is_int (termkey_get_buffer_size (tk), 256, - "buffer size initially 256"); - - is_int (termkey_push_bytes (tk, "h", 1), 1, "push_bytes returns 1"); - - is_int (termkey_get_buffer_remaining (tk), 255, - "buffer free 255 after push_bytes"); - is_int (termkey_get_buffer_size (tk), 256, - "buffer size 256 after push_bytes"); - - ok (!!termkey_set_buffer_size (tk, 512), "buffer set size OK"); - - is_int (termkey_get_buffer_remaining (tk), 511, - "buffer free 511 after push_bytes"); - is_int (termkey_get_buffer_size (tk), 512, - "buffer size 512 after push_bytes"); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "buffered key still useable after resize"); - - termkey_destroy (tk); - return exit_status (); -} diff --git a/t/10keyname.c b/t/10keyname.c deleted file mode 100644 index 20ac000..0000000 --- a/t/10keyname.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "../termkey.h" -#include "taplib.h" - -int -main (int argc, char *argv[]) -{ - termkey_t *tk; - termkey_sym_t sym; - const char *end; - - plan_tests (10); - - tk = termkey_new_abstract ("vt100", NULL, 0); - - sym = termkey_keyname2sym (tk, "Space"); - is_int (sym, TERMKEY_SYM_SPACE, "keyname2sym Space"); - - sym = termkey_keyname2sym (tk, "SomeUnknownKey"); - is_int (sym, TERMKEY_SYM_UNKNOWN, "keyname2sym SomeUnknownKey"); - - end = termkey_lookup_keyname (tk, "Up", &sym); - ok (!!end, "termkey_get_keyname Up returns non-NULL"); - is_str (end, "", "termkey_get_keyname Up return points at endofstring"); - is_int (sym, TERMKEY_SYM_UP, "termkey_get_keyname Up yields Up symbol"); - - end = termkey_lookup_keyname (tk, "DownMore", &sym); - ok (!!end, "termkey_get_keyname DownMore returns non-NULL"); - is_str (end, "More", "termkey_get_keyname DownMore return points at More"); - is_int (sym, TERMKEY_SYM_DOWN, - "termkey_get_keyname DownMore yields Down symbol"); - - end = termkey_lookup_keyname (tk, "SomeUnknownKey", &sym); - ok (!end, "termkey_get_keyname SomeUnknownKey returns NULL"); - - is_str (termkey_get_keyname (tk, TERMKEY_SYM_SPACE), "Space", - "get_keyname SPACE"); - - termkey_destroy (tk); - return exit_status (); -} diff --git a/t/11strfkey.c b/t/11strfkey.c deleted file mode 100644 index 4c77bdf..0000000 --- a/t/11strfkey.c +++ /dev/null @@ -1,151 +0,0 @@ -#include "../termkey.h" -#include "taplib.h" - -int -main (int argc, char *argv[]) -{ - termkey_t *tk; - termkey_key_t key; - char buffer[16]; - size_t len; - - plan_tests (44); - - tk = termkey_new_abstract ("vt100", NULL, 0); - - key.type = TERMKEY_TYPE_KEY; - key.code.codepoint = 'A'; - key.modifiers = 0; - key.multibyte[0] = 0; - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); - is_int (len, 1, "length for unicode/A/0"); - is_str (buffer, "A", "buffer for unicode/A/0"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, - TERMKEY_FORMAT_WRAPBRACKET); - is_int (len, 1, "length for unicode/A/0 wrapbracket"); - is_str (buffer, "A", "buffer for unicode/A/0 wrapbracket"); - - key.type = TERMKEY_TYPE_KEY; - key.code.codepoint = 'b'; - key.modifiers = TERMKEY_KEYMOD_CTRL; - key.multibyte[0] = 0; - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); - is_int (len, 3, "length for unicode/b/CTRL"); - is_str (buffer, "C-b", "buffer for unicode/b/CTRL"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, - TERMKEY_FORMAT_LONGMOD); - is_int (len, 6, "length for unicode/b/CTRL longmod"); - is_str (buffer, "Ctrl-b", "buffer for unicode/b/CTRL longmod"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, - TERMKEY_FORMAT_LONGMOD | TERMKEY_FORMAT_SPACEMOD); - is_int (len, 6, "length for unicode/b/CTRL longmod|spacemod"); - is_str (buffer, "Ctrl b", "buffer for unicode/b/CTRL longmod|spacemod"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, - TERMKEY_FORMAT_LONGMOD | TERMKEY_FORMAT_LOWERMOD); - is_int (len, 6, "length for unicode/b/CTRL longmod|lowermod"); - is_str (buffer, "ctrl-b", "buffer for unicode/b/CTRL longmod|lowermod"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, - TERMKEY_FORMAT_LONGMOD | TERMKEY_FORMAT_SPACEMOD - | TERMKEY_FORMAT_LOWERMOD); - is_int (len, 6, "length for unicode/b/CTRL longmod|spacemod|lowermode"); - is_str (buffer, "ctrl b", - "buffer for unicode/b/CTRL longmod|spacemod|lowermode"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, - TERMKEY_FORMAT_CARETCTRL); - is_int (len, 2, "length for unicode/b/CTRL caretctrl"); - is_str (buffer, "^B", "buffer for unicode/b/CTRL caretctrl"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, - TERMKEY_FORMAT_WRAPBRACKET); - is_int (len, 5, "length for unicode/b/CTRL wrapbracket"); - is_str (buffer, "", "buffer for unicode/b/CTRL wrapbracket"); - - key.type = TERMKEY_TYPE_KEY; - key.code.codepoint = 'c'; - key.modifiers = TERMKEY_KEYMOD_ALT; - key.multibyte[0] = 0; - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); - is_int (len, 3, "length for unicode/c/ALT"); - is_str (buffer, "A-c", "buffer for unicode/c/ALT"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, - TERMKEY_FORMAT_LONGMOD); - is_int (len, 5, "length for unicode/c/ALT longmod"); - is_str (buffer, "Alt-c", "buffer for unicode/c/ALT longmod"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, - TERMKEY_FORMAT_ALTISMETA); - is_int (len, 3, "length for unicode/c/ALT altismeta"); - is_str (buffer, "M-c", "buffer for unicode/c/ALT altismeta"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, - TERMKEY_FORMAT_LONGMOD|TERMKEY_FORMAT_ALTISMETA); - is_int (len, 6, "length for unicode/c/ALT longmod|altismeta"); - is_str (buffer, "Meta-c", "buffer for unicode/c/ALT longmod|altismeta"); - - key.type = TERMKEY_TYPE_KEYSYM; - key.code.sym = TERMKEY_SYM_UP; - key.modifiers = 0; - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); - is_int (len, 2, "length for sym/Up/0"); - is_str (buffer, "Up", "buffer for sym/Up/0"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, - TERMKEY_FORMAT_WRAPBRACKET); - is_int (len, 4, "length for sym/Up/0 wrapbracket"); - is_str (buffer, "", "buffer for sym/Up/0 wrapbracket"); - - key.type = TERMKEY_TYPE_KEYSYM; - key.code.sym = TERMKEY_SYM_PAGEUP; - key.modifiers = 0; - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); - is_int (len, 6, "length for sym/PageUp/0"); - is_str (buffer, "PageUp", "buffer for sym/PageUp/0"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, - TERMKEY_FORMAT_LOWERSPACE); - is_int (len, 7, "length for sym/PageUp/0 lowerspace"); - is_str (buffer, "page up", "buffer for sym/PageUp/0 lowerspace"); - - /* If size of buffer is too small, - * strfkey should return something consistent */ - len = termkey_strfkey (tk, buffer, 4, &key, 0); - is_int (len, 6, "length for sym/PageUp/0"); - is_str (buffer, "Pag", "buffer of len 4 for sym/PageUp/0"); - - len = termkey_strfkey (tk, buffer, 4, &key, TERMKEY_FORMAT_LOWERSPACE); - is_int (len, 7, "length for sym/PageUp/0 lowerspace"); - is_str (buffer, "pag", "buffer of len 4 for sym/PageUp/0 lowerspace"); - - key.type = TERMKEY_TYPE_FUNCTION; - key.code.number = 5; - key.modifiers = 0; - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); - is_int (len, 2, "length for func/5/0"); - is_str (buffer, "F5", "buffer for func/5/0"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, - TERMKEY_FORMAT_WRAPBRACKET); - is_int (len, 4, "length for func/5/0 wrapbracket"); - is_str (buffer, "", "buffer for func/5/0 wrapbracket"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, - TERMKEY_FORMAT_LOWERSPACE); - is_int (len, 2, "length for func/5/0 lowerspace"); - is_str (buffer, "f5", "buffer for func/5/0 lowerspace"); - - termkey_destroy (tk); - return exit_status (); -} diff --git a/t/12strpkey.c b/t/12strpkey.c deleted file mode 100644 index f67b67a..0000000 --- a/t/12strpkey.c +++ /dev/null @@ -1,157 +0,0 @@ -#include "../termkey.h" -#include "taplib.h" - -int -main (int argc, char *argv[]) -{ - termkey_t *tk; - termkey_key_t key; - const char *endp; - -#define CLEAR_KEY do { key.type = -1; key.code.codepoint = -1; \ - key.modifiers = -1; key.multibyte[0] = 0; } while (0) - - plan_tests (62); - - tk = termkey_new_abstract ("vt100", NULL, 0); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "A", &key, 0); - is_int (key.type, TERMKEY_TYPE_KEY, "key.type for unicode/A/0"); - is_int (key.code.codepoint, 'A', "key.code.codepoint for unicode/A/0"); - is_int (key.modifiers, 0, "key.modifiers for unicode/A/0"); - is_str (key.multibyte, "A", "key.multibyte for unicode/A/0"); - is_str (endp, "", "consumed entire input for unicode/A/0"); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "A and more", &key, 0); - is_int (key.type, TERMKEY_TYPE_KEY, - "key.type for unicode/A/0 trailing"); - is_int (key.code.codepoint, 'A', - "key.code.codepoint for unicode/A/0 trailing"); - is_int (key.modifiers, 0, "key.modifiers for unicode/A/0 trailing"); - is_str (key.multibyte, "A", "key.multibyte for unicode/A/0 trailing"); - is_str (endp, " and more", - "points at string tail for unicode/A/0 trailing"); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "C-b", &key, 0); - is_int (key.type, TERMKEY_TYPE_KEY, "key.type for unicode/b/CTRL"); - is_int (key.code.codepoint, 'b', "key.code.codepoint for unicode/b/CTRL"); - is_int (key.modifiers, TERMKEY_KEYMOD_CTRL, - "key.modifiers for unicode/b/CTRL"); - is_str (key.multibyte, "b", "key.multibyte for unicode/b/CTRL"); - is_str (endp, "", "consumed entire input for unicode/b/CTRL"); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "Ctrl-b", &key, TERMKEY_FORMAT_LONGMOD); - is_int (key.type, TERMKEY_TYPE_KEY, - "key.type for unicode/b/CTRL longmod"); - is_int (key.code.codepoint, 'b', - "key.code.codepoint for unicode/b/CTRL longmod"); - is_int (key.modifiers, TERMKEY_KEYMOD_CTRL, - "key.modifiers for unicode/b/CTRL longmod"); - is_str (key.multibyte, "b", "key.multibyte for unicode/b/CTRL longmod"); - is_str (endp, "", "consumed entire input for unicode/b/CTRL longmod"); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "^B", &key, TERMKEY_FORMAT_CARETCTRL); - is_int (key.type, TERMKEY_TYPE_KEY, - "key.type for unicode/b/CTRL caretctrl"); - is_int (key.code.codepoint, 'b', - "key.code.codepoint for unicode/b/CTRL caretctrl"); - is_int (key.modifiers, TERMKEY_KEYMOD_CTRL, - "key.modifiers for unicode/b/CTRL caretctrl"); - is_str (key.multibyte, "b", "key.multibyte for unicode/b/CTRL caretctrl"); - is_str (endp, "", "consumed entire input for unicode/b/CTRL caretctrl"); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "A-c", &key, 0); - is_int (key.type, TERMKEY_TYPE_KEY, "key.type for unicode/c/ALT"); - is_int (key.code.codepoint, 'c', "key.code.codepoint for unicode/c/ALT"); - is_int (key.modifiers, TERMKEY_KEYMOD_ALT, - "key.modifiers for unicode/c/ALT"); - is_str (key.multibyte, "c", "key.multibyte for unicode/c/ALT"); - is_str (endp, "", "consumed entire input for unicode/c/ALT"); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "Alt-c", &key, TERMKEY_FORMAT_LONGMOD); - is_int (key.type, TERMKEY_TYPE_KEY, - "key.type for unicode/c/ALT longmod"); - is_int (key.code.codepoint, 'c', - "key.code.codepoint for unicode/c/ALT longmod"); - is_int (key.modifiers, TERMKEY_KEYMOD_ALT, - "key.modifiers for unicode/c/ALT longmod"); - is_str (key.multibyte, "c", "key.multibyte for unicode/c/ALT longmod"); - is_str (endp, "", "consumed entire input for unicode/c/ALT longmod"); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "M-c", &key, TERMKEY_FORMAT_ALTISMETA); - is_int (key.type, TERMKEY_TYPE_KEY, - "key.type for unicode/c/ALT altismeta"); - is_int (key.code.codepoint, 'c', - "key.code.codepoint for unicode/c/ALT altismeta"); - is_int (key.modifiers, TERMKEY_KEYMOD_ALT, - "key.modifiers for unicode/c/ALT altismeta"); - is_str (key.multibyte, "c", "key.multibyte for unicode/c/ALT altismeta"); - is_str (endp, "", "consumed entire input for unicode/c/ALT altismeta"); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "Meta-c", &key, - TERMKEY_FORMAT_ALTISMETA | TERMKEY_FORMAT_LONGMOD); - is_int (key.type, TERMKEY_TYPE_KEY, - "key.type for unicode/c/ALT altismeta+longmod"); - is_int (key.code.codepoint, 'c', - "key.code.codepoint for unicode/c/ALT altismeta+longmod"); - is_int (key.modifiers, TERMKEY_KEYMOD_ALT, - "key.modifiers for unicode/c/ALT altismeta+longmod"); - is_str (key.multibyte, "c", "key.multibyte for unicode/c/ALT altismeta+longmod"); - is_str (endp, "", - "consumed entire input for unicode/c/ALT altismeta+longmod"); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "meta c", &key, - TERMKEY_FORMAT_ALTISMETA | TERMKEY_FORMAT_LONGMOD - | TERMKEY_FORMAT_SPACEMOD | TERMKEY_FORMAT_LOWERMOD); - is_int (key.type, TERMKEY_TYPE_KEY, - "key.type for unicode/c/ALT altismeta+long/space+lowermod"); - is_int (key.code.codepoint, 'c', - "key.code.codepoint for unicode/c/ALT altismeta+long/space+lowermod"); - is_int (key.modifiers, TERMKEY_KEYMOD_ALT, - "key.modifiers for unicode/c/ALT altismeta+long/space+lowermod"); - is_str (key.multibyte, "c", - "key.multibyte for unicode/c/ALT altismeta+long/space_lowermod"); - is_str (endp, "", - "consumed entire input for unicode/c/ALT altismeta+long/space+lowermod"); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "ctrl alt page up", &key, - TERMKEY_FORMAT_LONGMOD | TERMKEY_FORMAT_SPACEMOD - | TERMKEY_FORMAT_LOWERMOD | TERMKEY_FORMAT_LOWERSPACE); - is_int (key.type, TERMKEY_TYPE_KEYSYM, - "key.type for sym/PageUp/CTRL+ALT long/space/lowermod+lowerspace"); - is_int (key.code.sym, TERMKEY_SYM_PAGEUP, - "key.code.codepoint for sym/PageUp/CTRL+ALT long/space/lowermod+lowerspace"); - is_int (key.modifiers, TERMKEY_KEYMOD_ALT | TERMKEY_KEYMOD_CTRL, - "key.modifiers for sym/PageUp/CTRL+ALT long/space/lowermod+lowerspace"); - is_str (endp, "", - "consumed entire input for sym/PageUp/CTRL+ALT" - " long/space/lowermod+lowerspace"); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "Up", &key, 0); - is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type for sym/Up/0"); - is_int (key.code.sym, TERMKEY_SYM_UP, "key.code.codepoint for sym/Up/0"); - is_int (key.modifiers, 0, "key.modifiers for sym/Up/0"); - is_str (endp, "", "consumed entire input for sym/Up/0"); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "F5", &key, 0); - is_int (key.type, TERMKEY_TYPE_FUNCTION, "key.type for func/5/0"); - is_int (key.code.number, 5, "key.code.number for func/5/0"); - is_int (key.modifiers, 0, "key.modifiers for func/5/0"); - is_str (endp, "", "consumed entire input for func/5/0"); - - termkey_destroy (tk); - return exit_status (); -} diff --git a/t/13cmpkey.c b/t/13cmpkey.c deleted file mode 100644 index 1e12b5c..0000000 --- a/t/13cmpkey.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "../termkey.h" -#include "taplib.h" - -int -main (int argc, char *argv[]) -{ - termkey_t *tk; - termkey_key_t key1, key2; - - plan_tests (12); - - tk = termkey_new_abstract ("vt100", NULL, 0); - - key1.type = TERMKEY_TYPE_KEY; - key1.code.codepoint = 'A'; - key1.modifiers = 0; - - is_int (termkey_keycmp (tk, &key1, &key1), 0, "cmpkey same structure"); - - key2.type = TERMKEY_TYPE_KEY; - key2.code.codepoint = 'A'; - key2.modifiers = 0; - - is_int (termkey_keycmp (tk, &key1, &key2), 0, "cmpkey identical structure"); - - key2.modifiers = TERMKEY_KEYMOD_CTRL; - - ok (termkey_keycmp (tk, &key1, &key2) < 0, - "cmpkey orders CTRL after nomod"); - ok (termkey_keycmp (tk, &key2, &key1) > 0, - "cmpkey orders nomod before CTRL"); - - key2.code.codepoint = 'B'; - key2.modifiers = 0; - - ok (termkey_keycmp (tk, &key1, &key2) < 0, "cmpkey orders 'B' after 'A'"); - ok (termkey_keycmp (tk, &key2, &key1) > 0, "cmpkey orders 'A' before 'B'"); - - key1.modifiers = TERMKEY_KEYMOD_CTRL; - - ok (termkey_keycmp (tk, &key1, &key2) < 0, - "cmpkey orders nomod 'B' after CTRL 'A'"); - ok (termkey_keycmp (tk, &key2, &key1) > 0, - "cmpkey orders CTRL 'A' before nomod 'B'"); - - key2.type = TERMKEY_TYPE_KEYSYM; - key2.code.sym = TERMKEY_SYM_UP; - - ok (termkey_keycmp (tk, &key1, &key2) < 0, - "cmpkey orders KEYSYM after KEY"); - ok (termkey_keycmp (tk, &key2, &key1) > 0, - "cmpkey orders KEY before KEYSYM"); - - key1.type = TERMKEY_TYPE_KEYSYM; - key1.code.sym = TERMKEY_SYM_SPACE; - key1.modifiers = 0; - key2.type = TERMKEY_TYPE_KEY; - key2.code.codepoint = ' '; - key2.modifiers = 0; - - is_int (termkey_keycmp (tk, &key1, &key2), 0, - "cmpkey considers KEYSYM/SPACE and KEY/SP identical"); - - termkey_set_canonflags (tk, - termkey_get_canonflags (tk) | TERMKEY_CANON_SPACESYMBOL); - is_int (termkey_keycmp (tk, &key1, &key2), 0, - "cmpkey considers KEYSYM/SPACE and KEY/SP" - " identical under SPACESYMBOL"); - - termkey_destroy (tk); - return exit_status (); -} diff --git a/t/20canon.c b/t/20canon.c deleted file mode 100644 index a60c2ef..0000000 --- a/t/20canon.c +++ /dev/null @@ -1,74 +0,0 @@ -#include "../termkey.h" -#include "taplib.h" - -int -main (int argc, char *argv[]) -{ - termkey_t *tk; - termkey_key_t key; - const char *endp; - -#define CLEAR_KEY do { key.type = -1; key.code.codepoint = -1; \ - key.modifiers = -1; key.multibyte[0] = 0; } while (0) - - plan_tests (26); - - tk = termkey_new_abstract ("vt100", NULL, 0); - - CLEAR_KEY; - endp = termkey_strpkey (tk, " ", &key, 0); - is_int (key.type, TERMKEY_TYPE_KEY, "key.type for SP/unicode"); - is_int (key.code.codepoint, ' ', "key.code.codepoint for SP/unicode"); - is_int (key.modifiers, 0, "key.modifiers for SP/unicode"); - is_str (key.multibyte, " ", "key.multibyte for SP/unicode"); - is_str (endp, "", "consumed entire input for SP/unicode"); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "Space", &key, 0); - is_int (key.type, TERMKEY_TYPE_KEY, "key.type for Space/unicode"); - is_int (key.code.codepoint, ' ', "key.code.codepoint for Space/unicode"); - is_int (key.modifiers, 0, "key.modifiers for Space/unicode"); - is_str (key.multibyte, " ", "key.multibyte for Space/unicode"); - is_str (endp, "", "consumed entire input for Space/unicode"); - - termkey_set_canonflags (tk, - termkey_get_canonflags (tk) | TERMKEY_CANON_SPACESYMBOL); - - CLEAR_KEY; - endp = termkey_strpkey (tk, " ", &key, 0); - is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type for SP/symbol"); - is_int (key.code.sym, TERMKEY_SYM_SPACE, - "key.code.codepoint for SP/symbol"); - is_int (key.modifiers, 0, "key.modifiers for SP/symbol"); - is_str (endp, "", "consumed entire input for SP/symbol"); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "Space", &key, 0); - is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type for Space/symbol"); - is_int (key.code.sym, TERMKEY_SYM_SPACE, - "key.code.codepoint for Space/symbol"); - is_int (key.modifiers, 0, "key.modifiers for Space/symbol"); - is_str (endp, "", "consumed entire input for Space/symbol"); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "DEL", &key, 0); - is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type for Del/unconverted"); - is_int (key.code.sym, TERMKEY_SYM_DEL, - "key.code.codepoint for Del/unconverted"); - is_int (key.modifiers, 0, "key.modifiers for Del/unconverted"); - is_str (endp, "", "consumed entire input for Del/unconverted"); - - termkey_set_canonflags (tk, - termkey_get_canonflags (tk) | TERMKEY_CANON_DELBS); - - CLEAR_KEY; - endp = termkey_strpkey (tk, "DEL", &key, 0); - is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type for Del/as-backspace"); - is_int (key.code.sym, TERMKEY_SYM_BACKSPACE, - "key.code.codepoint for Del/as-backspace"); - is_int (key.modifiers, 0, "key.modifiers for Del/as-backspace"); - is_str (endp, "", "consumed entire input for Del/as-backspace"); - - termkey_destroy (tk); - return exit_status (); -} diff --git a/t/30mouse.c b/t/30mouse.c deleted file mode 100644 index e4f4b27..0000000 --- a/t/30mouse.c +++ /dev/null @@ -1,174 +0,0 @@ -#include "../termkey.h" -#include "taplib.h" - -int -main (int argc, char *argv[]) -{ - termkey_t *tk; - termkey_key_t key; - termkey_mouse_event_t ev; - int button, line, col; - char buffer[32]; - size_t len; - - plan_tests (60); - - tk = termkey_new_abstract ("vt100", NULL, 0); - - termkey_push_bytes (tk, "\e[M !!", 6); - - key.type = -1; - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY for mouse press"); - - is_int (key.type, TERMKEY_TYPE_MOUSE, "key.type for mouse press"); - - ev = -1; button = -1; line = -1; col = -1; - is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), - TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); - - is_int (ev, TERMKEY_MOUSE_PRESS, "mouse event for press"); - is_int (button, 1, "mouse button for press"); - is_int (line, 1, "mouse line for press"); - is_int (col, 1, "mouse column for press"); - is_int (key.modifiers, 0, "modifiers for press"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); - is_int (len, 13, "string length for press"); - is_str (buffer, "MousePress(1)", "string buffer for press"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, - &key, TERMKEY_FORMAT_MOUSE_POS); - is_int (len, 21, "string length for press"); - is_str (buffer, "MousePress(1) @ (1,1)", "string buffer for press"); - - termkey_push_bytes (tk, "\e[M@\"!", 6); - - key.type = -1; - ev = -1; button = -1; line = -1; col = -1; - termkey_getkey (tk, &key); - is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), - TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); - - is_int (ev, TERMKEY_MOUSE_DRAG, "mouse event for drag"); - is_int (button, 1, "mouse button for drag"); - is_int (line, 1, "mouse line for drag"); - is_int (col, 2, "mouse column for drag"); - is_int (key.modifiers, 0, "modifiers for press"); - - termkey_push_bytes (tk, "\e[M##!", 6); - - key.type = -1; - ev = -1; button = -1; line = -1; col = -1; - termkey_getkey (tk, &key); - is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), - TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); - - is_int (ev, TERMKEY_MOUSE_RELEASE, "mouse event for release"); - is_int (line, 1, "mouse line for release"); - is_int (col, 3, "mouse column for release"); - is_int (key.modifiers, 0, "modifiers for press"); - - termkey_push_bytes (tk, "\e[M0++", 6); - - key.type = -1; - ev = -1; button = -1; line = -1; col = -1; - termkey_getkey (tk, &key); - is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), - TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); - - is_int (ev, TERMKEY_MOUSE_PRESS, "mouse event for Ctrl-press"); - is_int (button, 1, "mouse button for Ctrl-press"); - is_int (line, 11, "mouse line for Ctrl-press"); - is_int (col, 11, "mouse column for Ctrl-press"); - is_int (key.modifiers, TERMKEY_KEYMOD_CTRL, "modifiers for Ctrl-press"); - - len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); - is_int (len, 15, "string length for Ctrl-press"); - is_str (buffer, "C-MousePress(1)", "string buffer for Ctrl-press"); - - // rxvt protocol - termkey_push_bytes (tk, "\e[0;20;20M", 10); - - key.type = -1; - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY for mouse press rxvt protocol"); - - is_int (key.type, TERMKEY_TYPE_MOUSE, - "key.type for mouse press rxvt protocol"); - - is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), - TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); - - is_int (ev, TERMKEY_MOUSE_PRESS, "mouse event for press rxvt protocol"); - is_int (button, 1, "mouse button for press rxvt protocol"); - is_int (line, 20, "mouse line for press rxvt protocol"); - is_int (col, 20, "mouse column for press rxvt protocol"); - is_int (key.modifiers, 0, "modifiers for press rxvt protocol"); - - termkey_push_bytes (tk, "\e[3;20;20M", 10); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY for mouse release rxvt protocol"); - - is_int (key.type, TERMKEY_TYPE_MOUSE, - "key.type for mouse release rxvt protocol"); - - ev = -1; button = -1; line = -1; col = -1; - is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), - TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); - - is_int (ev, TERMKEY_MOUSE_RELEASE, "mouse event for release rxvt protocol"); - is_int (line, 20, "mouse line for release rxvt protocol"); - is_int (col, 20, "mouse column for release rxvt protocol"); - is_int (key.modifiers, 0, "modifiers for release rxvt protocol"); - - // SGR protocol - termkey_push_bytes (tk, "\e[<0;30;30M", 11); - - key.type = -1; - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY for mouse press SGR encoding"); - - is_int (key.type, TERMKEY_TYPE_MOUSE, - "key.type for mouse press SGR encoding"); - - ev = -1; button = -1; line = -1; col = -1; - is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), - TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); - - is_int (ev, TERMKEY_MOUSE_PRESS, "mouse event for press SGR"); - is_int (button, 1, "mouse button for press SGR"); - is_int (line, 30, "mouse line for press SGR"); - is_int (col, 30, "mouse column for press SGR"); - is_int (key.modifiers, 0, "modifiers for press SGR"); - - termkey_push_bytes (tk, "\e[<0;30;30m", 11); - - key.type = -1; - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY for mouse release SGR encoding"); - - is_int (key.type, TERMKEY_TYPE_MOUSE, - "key.type for mouse release SGR encoding"); - - ev = -1; button = -1; line = -1; col = -1; - is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), - TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); - - is_int (ev, TERMKEY_MOUSE_RELEASE, "mouse event for release SGR"); - - termkey_push_bytes (tk, "\e[<0;500;300M", 13); - - key.type = -1; - ev = -1; button = -1; line = -1; col = -1; - termkey_getkey (tk, &key); - termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col); - - is_int (line, 300, "mouse line for press SGR wide"); - is_int (col, 500, "mouse column for press SGR wide"); - - termkey_destroy (tk); - - return exit_status (); -} diff --git a/t/31position.c b/t/31position.c deleted file mode 100644 index a62be7a..0000000 --- a/t/31position.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "../termkey.h" -#include "taplib.h" - -int main (int argc, char *argv[]) -{ - termkey_t *tk; - termkey_key_t key; - int line, col; - - plan_tests (8); - - tk = termkey_new_abstract ("vt100", NULL, 0); - - termkey_push_bytes (tk, "\e[?15;7R", 8); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY for position report"); - - is_int (key.type, TERMKEY_TYPE_POSITION, "key.type for position report"); - - is_int (termkey_interpret_position (tk, &key, &line, &col), TERMKEY_RES_KEY, - "interpret_position yields RES_KEY"); - - is_int (line, 15, "line for position report"); - is_int (col, 7, "column for position report"); - - /* A plain CSI R is likely to be though. This is tricky :/ */ - termkey_push_bytes (tk, "\e[R", 3); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY for "); - - is_int (key.type, TERMKEY_TYPE_FUNCTION, "key.type for "); - is_int (key.code.number, 3, "key.code.number for "); - - termkey_destroy (tk); - return exit_status (); -} diff --git a/t/32modereport.c b/t/32modereport.c deleted file mode 100644 index 5d79725..0000000 --- a/t/32modereport.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "../termkey.h" -#include "taplib.h" - -int -main (int argc, char *argv[]) -{ - termkey_t *tk; - termkey_key_t key; - int initial, mode, value; - - plan_tests (12); - - tk = termkey_new_abstract ("vt100", NULL, 0); - - termkey_push_bytes (tk, "\e[?1;2$y", 8); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY for mode report"); - - is_int (key.type, TERMKEY_TYPE_MODEREPORT, "key.type for mode report"); - - is_int (termkey_interpret_modereport (tk, &key, &initial, &mode, &value), - TERMKEY_RES_KEY, "interpret_modereoprt yields RES_KEY"); - - is_int (initial, '?', "initial indicator from mode report"); - is_int (mode, 1, "mode number from mode report"); - is_int (value, 2, "mode value from mode report"); - - termkey_push_bytes (tk, "\e[4;1$y", 7); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY for mode report"); - - is_int (key.type, TERMKEY_TYPE_MODEREPORT, "key.type for mode report"); - - is_int (termkey_interpret_modereport (tk, &key, &initial, &mode, &value), - TERMKEY_RES_KEY, "interpret_modereoprt yields RES_KEY"); - - is_int (initial, 0, "initial indicator from mode report"); - is_int (mode, 4, "mode number from mode report"); - is_int (value, 1, "mode value from mode report"); - - termkey_destroy (tk); - return exit_status (); -} diff --git a/t/39csi.c b/t/39csi.c deleted file mode 100644 index 32a15b0..0000000 --- a/t/39csi.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "../termkey.h" -#include "taplib.h" - -int -main (int argc, char *argv[]) -{ - termkey_t *tk; - termkey_key_t key; - long args[16]; - size_t nargs = 16; - unsigned long command; - - plan_tests (15); - - tk = termkey_new_abstract ("vt100", NULL, 0); - - termkey_push_bytes (tk, "\e[5;25v", 7); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY for CSI v"); - - is_int (key.type, TERMKEY_TYPE_UNKNOWN_CSI, "key.type for unknown CSI"); - - is_int (termkey_interpret_csi (tk, &key, args, &nargs, &command), - TERMKEY_RES_KEY, "interpret_csi yields RES_KEY"); - - is_int (nargs, 2, "nargs for unknown CSI"); - is_int (args[0], 5, "args[0] for unknown CSI"); - is_int (args[1], 25, "args[1] for unknown CSI"); - is_int (command, 'v', "command for unknown CSI"); - - termkey_push_bytes (tk, "\e[?w", 4); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY for CSI ? w"); - is_int (key.type, TERMKEY_TYPE_UNKNOWN_CSI, "key.type for unknown CSI"); - is_int (termkey_interpret_csi (tk, &key, args, &nargs, &command), - TERMKEY_RES_KEY, "interpret_csi yields RES_KEY"); - is_int (command, ('?' << 8) | 'w', "command for unknown CSI"); - - termkey_push_bytes (tk, "\e[?$x", 5); - - is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, - "getkey yields RES_KEY for CSI ? $x"); - is_int (key.type, TERMKEY_TYPE_UNKNOWN_CSI, "key.type for unknown CSI"); - is_int (termkey_interpret_csi (tk, &key, args, &nargs, &command), - TERMKEY_RES_KEY, "interpret_csi yields RES_KEY"); - is_int (command, ('$' << 16) | ('?' << 8) | 'x', "command for unknown CSI"); - - termkey_destroy (tk); - return exit_status (); -} diff --git a/t/taplib.c b/t/taplib.c deleted file mode 100644 index bfb70f8..0000000 --- a/t/taplib.c +++ /dev/null @@ -1,79 +0,0 @@ -#include "taplib.h" - -#include -#include -#include - -static int g_nexttest = 1; -static int g_exit_status = 0; - -void -plan_tests (int n) -{ - printf ("1..%d\n", n); -} - -void -pass (char *name) -{ - printf ("ok %d - %s\n", g_nexttest++, name); -} - -void -fail (char *name) -{ - printf ("not ok %d - %s\n", g_nexttest++, name); - g_exit_status = 1; -} - -void -ok (int cmp, char *name) -{ - if (cmp) - pass (name); - else - fail (name); -} - -void -diag (char *fmt, ...) -{ - va_list args; - va_start (args, fmt); - - fprintf (stderr, "# "); - vfprintf (stderr, fmt, args); - fprintf (stderr, "\n"); - - va_end (args); -} - -void -is_int (int got, int expect, char *name) -{ - if (got == expect) - ok (1, name); - else - { - ok (0, name); - diag ("got %d expected %d", got, expect); - } -} - -void -is_str (const char *got, const char *expect, char *name) -{ - if (strcmp (got, expect) == 0) - ok (1, name); - else - { - ok (0, name); - diag ("got '%s' expected '%s'", got, expect); - } -} - -int -exit_status(void) -{ - return g_exit_status; -} diff --git a/t/taplib.h b/t/taplib.h deleted file mode 100644 index 9045c32..0000000 --- a/t/taplib.h +++ /dev/null @@ -1,7 +0,0 @@ -void plan_tests (int n); -void ok (int cmp, char *name); -void pass (char *name); -void fail (char *name); -void is_int (int got, int expect, char *name); -void is_str (const char *got, const char *expect, char *name); -int exit_status (void); diff --git a/termkey-internal.h b/termkey-internal.h deleted file mode 100644 index 7282454..0000000 --- a/termkey-internal.h +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef TERMKEY_INTERNAL_H -#define TERMKEY_INTERNAL_H - -#include "termkey.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 = (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 (termkey_key_t *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 termkey_driver_t termkey_driver_csi; -extern termkey_driver_t termkey_driver_ti; - -#endif // ! TERMKEY_INTERNAL_H - diff --git a/termkey.c b/termkey.c deleted file mode 100644 index ad80657..0000000 --- a/termkey.c +++ /dev/null @@ -1,1556 +0,0 @@ -#include "termkey.h" -#include "termkey-internal.h" - -#include -#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 lowecase instead - key->code.codepoint = codepoint + 0x60; - else - key->code.codepoint = codepoint + 0x40; - key->modifiers = TERMKEY_KEYMOD_CTRL; - } - else - key->type = TERMKEY_TYPE_KEYSYM; - } - else if (codepoint == 0x7f && !(tk->flags & TERMKEY_FLAG_NOINTERPRET)) - { - // ASCII DEL - key->type = TERMKEY_TYPE_KEYSYM; - key->code.sym = TERMKEY_SYM_DEL; - key->modifiers = 0; - } - else - { - key->type = TERMKEY_TYPE_KEY; - key->code.codepoint = codepoint; - key->modifiers = 0; - } - - termkey_canonicalise (tk, key); - - if (key->type == TERMKEY_TYPE_KEY) - fill_multibyte (tk, key); -} - -void -termkey_canonicalise (termkey_t *tk, termkey_key_t *key) -{ - int flags = tk->canonflags; - - if (flags & TERMKEY_CANON_SPACESYMBOL) - { - if (key->type == TERMKEY_TYPE_KEY && key->code.codepoint == 0x20) - { - key->type = TERMKEY_TYPE_KEYSYM; - key->code.sym = TERMKEY_SYM_SPACE; - } - } - else - { - if (key->type == TERMKEY_TYPE_KEYSYM - && key->code.sym == TERMKEY_SYM_SPACE) - { - key->type = TERMKEY_TYPE_KEY; - key->code.codepoint = 0x20; - fill_multibyte (tk, key); - } - } - - if (flags & TERMKEY_CANON_DELBS) - if (key->type == TERMKEY_TYPE_KEYSYM - && key->code.sym == TERMKEY_SYM_DEL) - key->code.sym = TERMKEY_SYM_BACKSPACE; -} - -static termkey_result_t -peekkey (termkey_t *tk, termkey_key_t *key, int force, size_t *nbytep) -{ - int again = 0; - - if (!tk->is_started) - { - errno = EINVAL; - return TERMKEY_RES_ERROR; - } - -#ifdef DEBUG - fprintf (stderr, "getkey(force=%d): buffer ", force); - print_buffer (tk); - fprintf (stderr, "\n"); -#endif - - if (tk->hightide) - { - tk->buffstart += tk->hightide; - tk->buffcount -= tk->hightide; - tk->hightide = 0; - } - - termkey_result_t ret; - termkey_driver_node_t *p; - for (p = tk->drivers; p; p = p->next) - { - ret = (p->driver->peekkey) (tk, p->info, key, force, nbytep); - -#ifdef DEBUG - fprintf (stderr, "Driver %s yields %s\n", - p->driver->name, res2str (ret)); -#endif - - switch (ret) - { - case TERMKEY_RES_KEY: - { -#ifdef DEBUG - print_key (tk, key); fprintf (stderr, "\n"); -#endif - // Slide the data down to stop it running away - size_t halfsize = tk->buffsize / 2; - if (tk->buffstart > halfsize) - { - memcpy (tk->buffer, tk->buffer + halfsize, halfsize); - tk->buffstart -= halfsize; - } - - /* fallthrough */ - } - case TERMKEY_RES_EOF: - case TERMKEY_RES_ERROR: - return ret; - - case TERMKEY_RES_AGAIN: - if (!force) - again = 1; - case TERMKEY_RES_NONE: - break; - } - } - - if (again) - return TERMKEY_RES_AGAIN; - - ret = peekkey_simple (tk, key, force, nbytep); - -#ifdef DEBUG - fprintf (stderr, "getkey_simple(force=%d) yields %s\n", - force, res2str (ret)); - if (ret == TERMKEY_RES_KEY) - { - print_key (tk, key); - fprintf (stderr, "\n"); - } -#endif - - return ret; -} - -static termkey_result_t -peekkey_simple (termkey_t *tk, termkey_key_t *key, int force, size_t *nbytep) -{ - if (tk->buffcount == 0) - return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE; - - unsigned char b0 = CHARAT (0); - if (b0 == 0x1b) - { - // Escape-prefixed value? Might therefore be Alt+key - if (tk->buffcount == 1) - { - // This might be an press, or it may want to be part - // of a longer sequence - if (!force) - return TERMKEY_RES_AGAIN; - - (*tk->method.emit_codepoint) (tk, b0, key); - *nbytep = 1; - return TERMKEY_RES_KEY; - } - - // Try another key there - tk->buffstart++; - tk->buffcount--; - - // Run the full driver - termkey_result_t metakey_result = peekkey (tk, key, force, nbytep); - - tk->buffstart--; - tk->buffcount++; - - switch (metakey_result) - { - case TERMKEY_RES_KEY: - key->modifiers |= TERMKEY_KEYMOD_ALT; - (*nbytep)++; - break; - - case TERMKEY_RES_NONE: - case TERMKEY_RES_EOF: - case TERMKEY_RES_AGAIN: - case TERMKEY_RES_ERROR: - break; - } - - return metakey_result; - } - else if (!(tk->flags & TERMKEY_FLAG_RAW)) - { - uint32_t codepoint; - termkey_result_t res = parse_multibyte - (tk, tk->buffer + tk->buffstart, tk->buffcount, &codepoint, nbytep); - - if (res == TERMKEY_RES_AGAIN && force) - { - /* There weren't enough bytes for a complete character but - * caller demands an answer. About the best thing we can do here - * is eat as many bytes as we have, and emit a MULTIBYTE_INVALID. - * If the remaining bytes arrive later, they'll be invalid too. - */ - codepoint = MULTIBYTE_INVALID; - *nbytep = tk->buffcount; - res = TERMKEY_RES_KEY; - } - - key->type = TERMKEY_TYPE_KEY; - key->modifiers = 0; - (*tk->method.emit_codepoint) (tk, codepoint, key); - return res; - } - else - { - // Non multibyte case - just report the raw byte - key->type = TERMKEY_TYPE_KEY; - key->code.codepoint = b0; - key->modifiers = 0; - - key->multibyte[0] = b0; - key->multibyte[1] = 0; - - *nbytep = 1; - return TERMKEY_RES_KEY; - } -} - -static termkey_result_t -peekkey_mouse (termkey_t *tk, termkey_key_t *key, size_t *nbytep) -{ - if (tk->buffcount < 3) - return TERMKEY_RES_AGAIN; - - key->type = TERMKEY_TYPE_MOUSE; - key->code.mouse[0] = CHARAT (0) - 0x20; - key->code.mouse[1] = CHARAT (1) - 0x20; - key->code.mouse[2] = CHARAT (2) - 0x20; - key->code.mouse[3] = 0; - - key->modifiers = (key->code.mouse[0] & 0x1c) >> 2; - key->code.mouse[0] &= ~0x1c; - - *nbytep = 3; - return TERMKEY_RES_KEY; -} - -termkey_result_t -termkey_getkey (termkey_t *tk, termkey_key_t *key) -{ - size_t nbytes = 0; - termkey_result_t ret = peekkey (tk, key, 0, &nbytes); - - if (ret == TERMKEY_RES_KEY) - eat_bytes (tk, nbytes); - - if (ret == TERMKEY_RES_AGAIN) - /* Call peekkey() again in force mode to obtain whatever it can */ - (void) peekkey (tk, key, 1, &nbytes); - /* Don't eat it yet though */ - - return ret; -} - -termkey_result_t -termkey_getkey_force (termkey_t *tk, termkey_key_t *key) -{ - size_t nbytes = 0; - termkey_result_t ret = peekkey (tk, key, 1, &nbytes); - - if (ret == TERMKEY_RES_KEY) - eat_bytes (tk, nbytes); - - return ret; -} - -termkey_result_t -termkey_waitkey (termkey_t *tk, termkey_key_t *key) -{ - if (tk->fd == -1) - { - errno = EBADF; - return TERMKEY_RES_ERROR; - } - - while (1) - { - termkey_result_t ret = termkey_getkey (tk, key); - - switch (ret) - { - case TERMKEY_RES_KEY: - case TERMKEY_RES_EOF: - case TERMKEY_RES_ERROR: - return ret; - - case TERMKEY_RES_NONE: - ret = termkey_advisereadable (tk); - if (ret == TERMKEY_RES_ERROR) - return ret; - break; - - case TERMKEY_RES_AGAIN: - { - if (tk->is_closed) - // We're closed now. Never going to get more bytes - // so just go with what we have - return termkey_getkey_force (tk, key); - - struct pollfd fd; -retry: - fd.fd = tk->fd; - fd.events = POLLIN; - - int pollret = poll (&fd, 1, tk->waittime); - if (pollret == -1) - { - if (errno == EINTR && !(tk->flags & TERMKEY_FLAG_EINTR)) - goto retry; - - return TERMKEY_RES_ERROR; - } - - if (fd.revents & (POLLIN | POLLHUP | POLLERR)) - ret = termkey_advisereadable (tk); - else - ret = TERMKEY_RES_NONE; - - if (ret == TERMKEY_RES_ERROR) - return ret; - if (ret == TERMKEY_RES_NONE) - return termkey_getkey_force (tk, key); - } - } - } - - /* UNREACHABLE */ -} - -termkey_result_t -termkey_advisereadable (termkey_t *tk) -{ - if (tk->fd == -1) - { - errno = EBADF; - return TERMKEY_RES_ERROR; - } - - if (tk->buffstart) - { - memmove (tk->buffer, tk->buffer + tk->buffstart, tk->buffcount); - tk->buffstart = 0; - } - - /* Not expecting it ever to be greater but doesn't hurt to handle that */ - if (tk->buffcount >= tk->buffsize) - { - errno = ENOMEM; - return TERMKEY_RES_ERROR; - } - - ssize_t len; -retry: - len = read (tk->fd, tk->buffer + tk->buffcount, - tk->buffsize - tk->buffcount); - - if (len == -1) - { - if (errno == EAGAIN) - return TERMKEY_RES_NONE; - if (errno == EINTR && !(tk->flags & TERMKEY_FLAG_EINTR)) - goto retry; - return TERMKEY_RES_ERROR; - } - if (len < 1) - { - tk->is_closed = true; - return TERMKEY_RES_NONE; - } - tk->buffcount += len; - return TERMKEY_RES_AGAIN; -} - -size_t -termkey_push_bytes (termkey_t *tk, const char *bytes, size_t len) -{ - if (tk->buffstart) - { - memmove (tk->buffer, tk->buffer + tk->buffstart, tk->buffcount); - tk->buffstart = 0; - } - - /* Not expecting it ever to be greater but doesn't hurt to handle that */ - if (tk->buffcount >= tk->buffsize) - { - errno = ENOMEM; - return (size_t)-1; - } - - if (len > tk->buffsize - tk->buffcount) - len = tk->buffsize - tk->buffcount; - - // memcpy(), not strncpy() in case of null bytes in input - memcpy (tk->buffer + tk->buffcount, bytes, len); - tk->buffcount += len; - - return len; -} - -termkey_sym_t -termkey_register_keyname (termkey_t *tk, termkey_sym_t sym, const char *name) -{ - if (!sym) - sym = tk->nkeynames; - - if (sym >= tk->nkeynames) - { - const char **new_keynames = - realloc (tk->keynames, sizeof new_keynames[0] * (sym + 1)); - if (!new_keynames) - return -1; - - tk->keynames = new_keynames; - - // Fill in the hole - for (int i = tk->nkeynames; i < sym; i++) - tk->keynames[i] = NULL; - - tk->nkeynames = sym + 1; - } - - tk->keynames[sym] = name; - return sym; -} - -const char * -termkey_get_keyname (termkey_t *tk, termkey_sym_t sym) -{ - if (sym == TERMKEY_SYM_UNKNOWN) - return "UNKNOWN"; - if (sym < tk->nkeynames) - return tk->keynames[sym]; - return "UNKNOWN"; -} - -static const char * -termkey_lookup_keyname_format (termkey_t *tk, - const char *str, termkey_sym_t *sym, termkey_format_t format) -{ - /* We store an array, so we can't do better than a linear search. Doesn't - * matter because user won't be calling this too often */ - - for (*sym = 0; *sym < tk->nkeynames; (*sym)++) - { - const char *thiskey = tk->keynames[*sym]; - if (!thiskey) - continue; - size_t len = strlen (thiskey); - if (format & TERMKEY_FORMAT_LOWERSPACE) - { - const char *thisstr = str; - if (strpncmp_camel (&thisstr, &thiskey, len) == 0) - return thisstr; - } - else if (!strncmp (str, thiskey, len)) - return (char *) str + len; - } - return NULL; -} - -const char * -termkey_lookup_keyname (termkey_t *tk, const char *str, termkey_sym_t *sym) -{ - return termkey_lookup_keyname_format (tk, str, sym, 0); -} - -termkey_sym_t -termkey_keyname2sym (termkey_t *tk, const char *keyname) -{ - termkey_sym_t sym; - const char *endp = termkey_lookup_keyname (tk, keyname, &sym); - if (!endp || endp[0]) - return TERMKEY_SYM_UNKNOWN; - return sym; -} - -static termkey_sym_t -register_c0 (termkey_t *tk, - termkey_sym_t sym, unsigned char ctrl, const char *name) -{ - return register_c0_full (tk, sym, 0, 0, ctrl, name); -} - -static termkey_sym_t -register_c0_full (termkey_t *tk, termkey_sym_t sym, - int modifier_set, int modifier_mask, unsigned char ctrl, const char *name) -{ - if (ctrl >= 0x20) - { - errno = EINVAL; - return -1; - } - - if (name) - sym = termkey_register_keyname (tk, sym, name); - - tk->c0[ctrl].sym = sym; - tk->c0[ctrl].modifier_set = modifier_set; - tk->c0[ctrl].modifier_mask = modifier_mask; - return sym; -} - -static struct modnames -{ - const char *shift, *alt, *ctrl; -} -modnames[] = -{ - { "S", "A", "C" }, // 0 - { "Shift", "Alt", "Ctrl" }, // LONGMOD - { "S", "M", "C" }, // ALTISMETA - { "Shift", "Meta", "Ctrl" }, // ALTISMETA+LONGMOD - { "s", "a", "c" }, // LOWERMOD - { "shift", "alt", "ctrl" }, // LOWERMOD+LONGMOD - { "s", "m", "c" }, // LOWERMOD+ALTISMETA - { "shift", "meta", "ctrl" }, // LOWERMOD+ALTISMETA+LONGMOD -}; - -size_t -termkey_strfkey (termkey_t *tk, char *buffer, size_t len, - termkey_key_t *key, termkey_format_t format) -{ - size_t pos = 0; - size_t l = 0; - - struct modnames *mods = &modnames[ - !!(format & TERMKEY_FORMAT_LONGMOD) + - !!(format & TERMKEY_FORMAT_ALTISMETA) * 2 + - !!(format & TERMKEY_FORMAT_LOWERMOD) * 4]; - - int wrapbracket = (format & TERMKEY_FORMAT_WRAPBRACKET) && - (key->type != TERMKEY_TYPE_KEY || key->modifiers != 0); - - char sep = (format & TERMKEY_FORMAT_SPACEMOD) ? ' ' : '-'; - - if (format & TERMKEY_FORMAT_CARETCTRL && - key->type == TERMKEY_TYPE_KEY && - key->modifiers == TERMKEY_KEYMOD_CTRL) - { - uint32_t codepoint = key->code.codepoint; - - // Handle some of the special casesfirst - if (codepoint >= 'a' && codepoint <= 'z') - { - l = snprintf (buffer + pos, len - pos, - wrapbracket ? "<^%c>" : "^%c", (char) codepoint - 0x20); - if (l <= 0) - return pos; - pos += l; - return pos; - } - else if ((codepoint >= '@' && codepoint < 'A') || - (codepoint > 'Z' && codepoint <= '_')) - { - l = snprintf (buffer + pos, len - pos, - wrapbracket ? "<^%c>" : "^%c", (char) codepoint); - if(l <= 0) - return pos; - pos += l; - return pos; - } - } - - if (wrapbracket) - { - l = snprintf (buffer + pos, len - pos, "<"); - if (l <= 0) - return pos; - pos += l; - } - - if (key->modifiers & TERMKEY_KEYMOD_ALT) - { - l = snprintf (buffer + pos, len - pos, "%s%c", mods->alt, sep); - if (l <= 0) - return pos; - pos += l; - } - if (key->modifiers & TERMKEY_KEYMOD_CTRL) - { - l = snprintf (buffer + pos, len - pos, "%s%c", mods->ctrl, sep); - if (l <= 0) - return pos; - pos += l; - } - if (key->modifiers & TERMKEY_KEYMOD_SHIFT) - { - l = snprintf (buffer + pos, len - pos, "%s%c", mods->shift, sep); - if (l <= 0) - return pos; - pos += l; - } - - switch (key->type) - { - case TERMKEY_TYPE_KEY: - if (!key->multibyte[0]) // In case of user-supplied key structures - fill_multibyte (tk, key); - l = snprintf (buffer + pos, len - pos, "%s", key->multibyte); - break; - case TERMKEY_TYPE_KEYSYM: - { - const char *name = termkey_get_keyname (tk, key->code.sym); - if (format & TERMKEY_FORMAT_LOWERSPACE) - l = snprint_cameltospaces (buffer + pos, len - pos, name); - else - l = snprintf (buffer + pos, len - pos, "%s", name); - break; - } - case TERMKEY_TYPE_FUNCTION: - l = snprintf (buffer + pos, len - pos, "%c%d", - (format & TERMKEY_FORMAT_LOWERSPACE ? 'f' : 'F'), key->code.number); - break; - case TERMKEY_TYPE_MOUSE: - { - termkey_mouse_event_t ev; - int button; - int line, col; - termkey_interpret_mouse (tk, key, &ev, &button, &line, &col); - - static const char *evnames[] = - { "Unknown", "Press", "Drag", "Release" }; - l = snprintf (buffer + pos, len - pos, - "Mouse%s(%d)", evnames[ev], button); - if (format & TERMKEY_FORMAT_MOUSE_POS) - { - if (l <= 0) - return pos; - pos += l; - l = snprintf (buffer + pos, len - pos, " @ (%u,%u)", col, line); - } - break; - } - case TERMKEY_TYPE_POSITION: - l = snprintf (buffer + pos, len - pos, "Position"); - break; - case TERMKEY_TYPE_MODEREPORT: - { - int initial, mode, value; - termkey_interpret_modereport (tk, key, &initial, &mode, &value); - if (initial) - l = snprintf (buffer + pos, len - pos, - "Mode(%c%d=%d)", initial, mode, value); - else - l = snprintf (buffer + pos, len - pos, - "Mode(%d=%d)", mode, value); - break; - } - case TERMKEY_TYPE_UNKNOWN_CSI: - l = snprintf (buffer + pos, len - pos, - "CSI %c", key->code.number & 0xff); - break; - } - - if (l <= 0) - return pos; - pos += l; - - if (wrapbracket) - { - l = snprintf (buffer + pos, len - pos, ">"); - if (l <= 0) - return pos; - pos += l; - } - return pos; -} - -const char * -termkey_strpkey (termkey_t *tk, - const char *str, termkey_key_t *key, termkey_format_t format) -{ - struct modnames *mods = &modnames[ - !!(format & TERMKEY_FORMAT_LONGMOD) + - !!(format & TERMKEY_FORMAT_ALTISMETA) * 2 + - !!(format & TERMKEY_FORMAT_LOWERMOD) * 4]; - - key->modifiers = 0; - - if ((format & TERMKEY_FORMAT_CARETCTRL) && str[0] == '^' && str[1]) - { - str = termkey_strpkey (tk, - str + 1, key, format & ~TERMKEY_FORMAT_CARETCTRL); - - if (!str - || key->type != TERMKEY_TYPE_KEY - || key->code.codepoint < '@' - || key->code.codepoint > '_' - || key->modifiers != 0) - return NULL; - - if (key->code.codepoint >= 'A' - && key->code.codepoint <= 'Z') - key->code.codepoint += 0x20; - key->modifiers = TERMKEY_KEYMOD_CTRL; - fill_multibyte (tk, key); - return (char *) str; - } - - const char *sep_at; - while ((sep_at = strchr (str, - (format & TERMKEY_FORMAT_SPACEMOD) ? ' ' : '-'))) - { - size_t n = sep_at - str; - if (n == strlen (mods->alt) && !strncmp (mods->alt, str, n)) - key->modifiers |= TERMKEY_KEYMOD_ALT; - else if (n == strlen (mods->ctrl) && !strncmp (mods->ctrl, str, n)) - key->modifiers |= TERMKEY_KEYMOD_CTRL; - else if (n == strlen (mods->shift) && !strncmp (mods->shift, str, n)) - key->modifiers |= TERMKEY_KEYMOD_SHIFT; - else - break; - - str = sep_at + 1; - } - - size_t nbytes; - ssize_t snbytes; - const char *endstr; - - if ((endstr = termkey_lookup_keyname_format - (tk, str, &key->code.sym, format))) - { - key->type = TERMKEY_TYPE_KEYSYM; - str = endstr; - } - else if (sscanf(str, "F%d%zn", &key->code.number, &snbytes) == 1) - { - key->type = TERMKEY_TYPE_FUNCTION; - str += snbytes; - } - // Multibyte must be last - else if (parse_multibyte (tk, (unsigned const char *) str, strlen (str), - &key->code.codepoint, &nbytes) == TERMKEY_RES_KEY) - { - key->type = TERMKEY_TYPE_KEY; - fill_multibyte (tk, key); - str += nbytes; - } - // TODO: Consider mouse events? - else - return NULL; - - termkey_canonicalise (tk, key); - return (char *) str; -} - -int -termkey_keycmp (termkey_t *tk, - const termkey_key_t *key1p, const termkey_key_t *key2p) -{ - /* Copy the key structs since we'll be modifying them */ - termkey_key_t key1 = *key1p, key2 = *key2p; - - termkey_canonicalise (tk, &key1); - termkey_canonicalise (tk, &key2); - - if (key1.type != key2.type) - return key1.type - key2.type; - - switch (key1.type) - { - case TERMKEY_TYPE_KEY: - if (key1.code.codepoint != key2.code.codepoint) - return key1.code.codepoint - key2.code.codepoint; - break; - case TERMKEY_TYPE_KEYSYM: - if (key1.code.sym != key2.code.sym) - return key1.code.sym - key2.code.sym; - break; - case TERMKEY_TYPE_FUNCTION: - case TERMKEY_TYPE_UNKNOWN_CSI: - if (key1.code.number != key2.code.number) - return key1.code.number - key2.code.number; - break; - case TERMKEY_TYPE_MOUSE: - { - int cmp = strncmp (key1.code.mouse, key2.code.mouse, 4); - if (cmp != 0) - return cmp; - break; - } - case TERMKEY_TYPE_POSITION: - { - int line1, col1, line2, col2; - termkey_interpret_position (tk, &key1, &line1, &col1); - termkey_interpret_position (tk, &key2, &line2, &col2); - if (line1 != line2) - return line1 - line2; - return col1 - col2; - } - case TERMKEY_TYPE_MODEREPORT: - { - int initial1, initial2, mode1, mode2, value1, value2; - termkey_interpret_modereport (tk, &key1, &initial1, &mode1, &value1); - termkey_interpret_modereport (tk, &key2, &initial2, &mode2, &value2); - if (initial1 != initial2) - return initial1 - initial2; - if (mode1 != mode2) - return mode1 - mode2; - return value1 - value2; - } - } - return key1.modifiers - key2.modifiers; -} - diff --git a/termkey.h.in b/termkey.h.in deleted file mode 100644 index 2a4fa8d..0000000 --- a/termkey.h.in +++ /dev/null @@ -1,272 +0,0 @@ -#ifndef TERMKEY_H -#define TERMKEY_H - -#include -#include -#include - -#define TERMKEY_VERSION_MAJOR @@VERSION_MAJOR@@ -#define TERMKEY_VERSION_MINOR @@VERSION_MINOR@@ - -#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 */ - char mouse[4]; /* TERMKEY_TYPE_MOUSE */ - /* opaque, see termkey_interpret_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 // ! TERMKEY_H - diff --git a/termkey.pc.in b/termkey.pc.in deleted file mode 100644 index 31113be..0000000 --- a/termkey.pc.in +++ /dev/null @@ -1,8 +0,0 @@ -libdir=@LIBDIR@ -includedir=@INCDIR@ - -Name: termkey -Description: Abstract terminal key input library -Version: @VERSION@ -Libs: -L${libdir} -ltermkey -Cflags: -I${includedir} diff --git a/termkey2-config.h.in b/termkey2-config.h.in new file mode 100644 index 0000000..676a2c0 --- /dev/null +++ b/termkey2-config.h.in @@ -0,0 +1,8 @@ +#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 new file mode 100644 index 0000000..b76ba79 --- /dev/null +++ b/termkey2-internal.h @@ -0,0 +1,115 @@ +#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 = (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 (termkey_key_t *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 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 new file mode 100644 index 0000000..e9b574c --- /dev/null +++ b/termkey2.c @@ -0,0 +1,1556 @@ +#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; + } +} + +static termkey_result_t +peekkey_mouse (termkey_t *tk, termkey_key_t *key, size_t *nbytep) +{ + if (tk->buffcount < 3) + return TERMKEY_RES_AGAIN; + + key->type = TERMKEY_TYPE_MOUSE; + key->code.mouse[0] = CHARAT (0) - 0x20; + key->code.mouse[1] = CHARAT (1) - 0x20; + key->code.mouse[2] = CHARAT (2) - 0x20; + key->code.mouse[3] = 0; + + key->modifiers = (key->code.mouse[0] & 0x1c) >> 2; + key->code.mouse[0] &= ~0x1c; + + *nbytep = 3; + return TERMKEY_RES_KEY; +} + +termkey_result_t +termkey_getkey (termkey_t *tk, termkey_key_t *key) +{ + size_t nbytes = 0; + termkey_result_t ret = peekkey (tk, key, 0, &nbytes); + + if (ret == TERMKEY_RES_KEY) + eat_bytes (tk, nbytes); + + if (ret == TERMKEY_RES_AGAIN) + /* Call peekkey() again in force mode to obtain whatever it can */ + (void) peekkey (tk, key, 1, &nbytes); + /* Don't eat it yet though */ + + return ret; +} + +termkey_result_t +termkey_getkey_force (termkey_t *tk, termkey_key_t *key) +{ + size_t nbytes = 0; + termkey_result_t ret = peekkey (tk, key, 1, &nbytes); + + if (ret == TERMKEY_RES_KEY) + eat_bytes (tk, nbytes); + + return ret; +} + +termkey_result_t +termkey_waitkey (termkey_t *tk, termkey_key_t *key) +{ + if (tk->fd == -1) + { + errno = EBADF; + return TERMKEY_RES_ERROR; + } + + while (1) + { + termkey_result_t ret = termkey_getkey (tk, key); + + switch (ret) + { + case TERMKEY_RES_KEY: + case TERMKEY_RES_EOF: + case TERMKEY_RES_ERROR: + return ret; + + case TERMKEY_RES_NONE: + ret = termkey_advisereadable (tk); + if (ret == TERMKEY_RES_ERROR) + return ret; + break; + + case TERMKEY_RES_AGAIN: + { + if (tk->is_closed) + // We're closed now. Never going to get more bytes + // so just go with what we have + return termkey_getkey_force (tk, key); + + struct pollfd fd; +retry: + fd.fd = tk->fd; + fd.events = POLLIN; + + int pollret = poll (&fd, 1, tk->waittime); + if (pollret == -1) + { + if (errno == EINTR && !(tk->flags & TERMKEY_FLAG_EINTR)) + goto retry; + + return TERMKEY_RES_ERROR; + } + + if (fd.revents & (POLLIN | POLLHUP | POLLERR)) + ret = termkey_advisereadable (tk); + else + ret = TERMKEY_RES_NONE; + + if (ret == TERMKEY_RES_ERROR) + return ret; + if (ret == TERMKEY_RES_NONE) + return termkey_getkey_force (tk, key); + } + } + } + + /* UNREACHABLE */ +} + +termkey_result_t +termkey_advisereadable (termkey_t *tk) +{ + if (tk->fd == -1) + { + errno = EBADF; + return TERMKEY_RES_ERROR; + } + + if (tk->buffstart) + { + memmove (tk->buffer, tk->buffer + tk->buffstart, tk->buffcount); + tk->buffstart = 0; + } + + /* Not expecting it ever to be greater but doesn't hurt to handle that */ + if (tk->buffcount >= tk->buffsize) + { + errno = ENOMEM; + return TERMKEY_RES_ERROR; + } + + ssize_t len; +retry: + len = read (tk->fd, tk->buffer + tk->buffcount, + tk->buffsize - tk->buffcount); + + if (len == -1) + { + if (errno == EAGAIN) + return TERMKEY_RES_NONE; + if (errno == EINTR && !(tk->flags & TERMKEY_FLAG_EINTR)) + goto retry; + return TERMKEY_RES_ERROR; + } + if (len < 1) + { + tk->is_closed = true; + return TERMKEY_RES_NONE; + } + tk->buffcount += len; + return TERMKEY_RES_AGAIN; +} + +size_t +termkey_push_bytes (termkey_t *tk, const char *bytes, size_t len) +{ + if (tk->buffstart) + { + memmove (tk->buffer, tk->buffer + tk->buffstart, tk->buffcount); + tk->buffstart = 0; + } + + /* Not expecting it ever to be greater but doesn't hurt to handle that */ + if (tk->buffcount >= tk->buffsize) + { + errno = ENOMEM; + return (size_t)-1; + } + + if (len > tk->buffsize - tk->buffcount) + len = tk->buffsize - tk->buffcount; + + // memcpy(), not strncpy() in case of null bytes in input + memcpy (tk->buffer + tk->buffcount, bytes, len); + tk->buffcount += len; + + return len; +} + +termkey_sym_t +termkey_register_keyname (termkey_t *tk, termkey_sym_t sym, const char *name) +{ + if (!sym) + sym = tk->nkeynames; + + if (sym >= tk->nkeynames) + { + const char **new_keynames = + realloc (tk->keynames, sizeof new_keynames[0] * (sym + 1)); + if (!new_keynames) + return -1; + + tk->keynames = new_keynames; + + // Fill in the hole + for (int i = tk->nkeynames; i < sym; i++) + tk->keynames[i] = NULL; + + tk->nkeynames = sym + 1; + } + + tk->keynames[sym] = name; + return sym; +} + +const char * +termkey_get_keyname (termkey_t *tk, termkey_sym_t sym) +{ + if (sym == TERMKEY_SYM_UNKNOWN) + return "UNKNOWN"; + if (sym < tk->nkeynames) + return tk->keynames[sym]; + return "UNKNOWN"; +} + +static const char * +termkey_lookup_keyname_format (termkey_t *tk, + const char *str, termkey_sym_t *sym, termkey_format_t format) +{ + /* We store an array, so we can't do better than a linear search. Doesn't + * matter because user won't be calling this too often */ + + for (*sym = 0; *sym < tk->nkeynames; (*sym)++) + { + const char *thiskey = tk->keynames[*sym]; + if (!thiskey) + continue; + size_t len = strlen (thiskey); + if (format & TERMKEY_FORMAT_LOWERSPACE) + { + const char *thisstr = str; + if (strpncmp_camel (&thisstr, &thiskey, len) == 0) + return thisstr; + } + else if (!strncmp (str, thiskey, len)) + return (char *) str + len; + } + return NULL; +} + +const char * +termkey_lookup_keyname (termkey_t *tk, const char *str, termkey_sym_t *sym) +{ + return termkey_lookup_keyname_format (tk, str, sym, 0); +} + +termkey_sym_t +termkey_keyname2sym (termkey_t *tk, const char *keyname) +{ + termkey_sym_t sym; + const char *endp = termkey_lookup_keyname (tk, keyname, &sym); + if (!endp || endp[0]) + return TERMKEY_SYM_UNKNOWN; + return sym; +} + +static termkey_sym_t +register_c0 (termkey_t *tk, + termkey_sym_t sym, unsigned char ctrl, const char *name) +{ + return register_c0_full (tk, sym, 0, 0, ctrl, name); +} + +static termkey_sym_t +register_c0_full (termkey_t *tk, termkey_sym_t sym, + int modifier_set, int modifier_mask, unsigned char ctrl, const char *name) +{ + if (ctrl >= 0x20) + { + errno = EINVAL; + return -1; + } + + if (name) + sym = termkey_register_keyname (tk, sym, name); + + tk->c0[ctrl].sym = sym; + tk->c0[ctrl].modifier_set = modifier_set; + tk->c0[ctrl].modifier_mask = modifier_mask; + return sym; +} + +static struct modnames +{ + const char *shift, *alt, *ctrl; +} +modnames[] = +{ + { "S", "A", "C" }, // 0 + { "Shift", "Alt", "Ctrl" }, // LONGMOD + { "S", "M", "C" }, // ALTISMETA + { "Shift", "Meta", "Ctrl" }, // ALTISMETA+LONGMOD + { "s", "a", "c" }, // LOWERMOD + { "shift", "alt", "ctrl" }, // LOWERMOD+LONGMOD + { "s", "m", "c" }, // LOWERMOD+ALTISMETA + { "shift", "meta", "ctrl" }, // LOWERMOD+ALTISMETA+LONGMOD +}; + +size_t +termkey_strfkey (termkey_t *tk, char *buffer, size_t len, + termkey_key_t *key, termkey_format_t format) +{ + size_t pos = 0; + size_t l = 0; + + struct modnames *mods = &modnames[ + !!(format & TERMKEY_FORMAT_LONGMOD) + + !!(format & TERMKEY_FORMAT_ALTISMETA) * 2 + + !!(format & TERMKEY_FORMAT_LOWERMOD) * 4]; + + int wrapbracket = (format & TERMKEY_FORMAT_WRAPBRACKET) && + (key->type != TERMKEY_TYPE_KEY || key->modifiers != 0); + + char sep = (format & TERMKEY_FORMAT_SPACEMOD) ? ' ' : '-'; + + if (format & TERMKEY_FORMAT_CARETCTRL && + key->type == TERMKEY_TYPE_KEY && + key->modifiers == TERMKEY_KEYMOD_CTRL) + { + uint32_t codepoint = key->code.codepoint; + + // Handle some of the special casesfirst + if (codepoint >= 'a' && codepoint <= 'z') + { + l = snprintf (buffer + pos, len - pos, + wrapbracket ? "<^%c>" : "^%c", (char) codepoint - 0x20); + if (l <= 0) + return pos; + pos += l; + return pos; + } + else if ((codepoint >= '@' && codepoint < 'A') || + (codepoint > 'Z' && codepoint <= '_')) + { + l = snprintf (buffer + pos, len - pos, + wrapbracket ? "<^%c>" : "^%c", (char) codepoint); + if(l <= 0) + return pos; + pos += l; + return pos; + } + } + + if (wrapbracket) + { + l = snprintf (buffer + pos, len - pos, "<"); + if (l <= 0) + return pos; + pos += l; + } + + if (key->modifiers & TERMKEY_KEYMOD_ALT) + { + l = snprintf (buffer + pos, len - pos, "%s%c", mods->alt, sep); + if (l <= 0) + return pos; + pos += l; + } + if (key->modifiers & TERMKEY_KEYMOD_CTRL) + { + l = snprintf (buffer + pos, len - pos, "%s%c", mods->ctrl, sep); + if (l <= 0) + return pos; + pos += l; + } + if (key->modifiers & TERMKEY_KEYMOD_SHIFT) + { + l = snprintf (buffer + pos, len - pos, "%s%c", mods->shift, sep); + if (l <= 0) + return pos; + pos += l; + } + + switch (key->type) + { + case TERMKEY_TYPE_KEY: + if (!key->multibyte[0]) // In case of user-supplied key structures + fill_multibyte (tk, key); + l = snprintf (buffer + pos, len - pos, "%s", key->multibyte); + break; + case TERMKEY_TYPE_KEYSYM: + { + const char *name = termkey_get_keyname (tk, key->code.sym); + if (format & TERMKEY_FORMAT_LOWERSPACE) + l = snprint_cameltospaces (buffer + pos, len - pos, name); + else + l = snprintf (buffer + pos, len - pos, "%s", name); + break; + } + case TERMKEY_TYPE_FUNCTION: + l = snprintf (buffer + pos, len - pos, "%c%d", + (format & TERMKEY_FORMAT_LOWERSPACE ? 'f' : 'F'), key->code.number); + break; + case TERMKEY_TYPE_MOUSE: + { + termkey_mouse_event_t ev; + int button; + int line, col; + termkey_interpret_mouse (tk, key, &ev, &button, &line, &col); + + static const char *evnames[] = + { "Unknown", "Press", "Drag", "Release" }; + l = snprintf (buffer + pos, len - pos, + "Mouse%s(%d)", evnames[ev], button); + if (format & TERMKEY_FORMAT_MOUSE_POS) + { + if (l <= 0) + return pos; + pos += l; + l = snprintf (buffer + pos, len - pos, " @ (%u,%u)", col, line); + } + break; + } + case TERMKEY_TYPE_POSITION: + l = snprintf (buffer + pos, len - pos, "Position"); + break; + case TERMKEY_TYPE_MODEREPORT: + { + int initial, mode, value; + termkey_interpret_modereport (tk, key, &initial, &mode, &value); + if (initial) + l = snprintf (buffer + pos, len - pos, + "Mode(%c%d=%d)", initial, mode, value); + else + l = snprintf (buffer + pos, len - pos, + "Mode(%d=%d)", mode, value); + break; + } + case TERMKEY_TYPE_UNKNOWN_CSI: + l = snprintf (buffer + pos, len - pos, + "CSI %c", key->code.number & 0xff); + break; + } + + if (l <= 0) + return pos; + pos += l; + + if (wrapbracket) + { + l = snprintf (buffer + pos, len - pos, ">"); + if (l <= 0) + return pos; + pos += l; + } + return pos; +} + +const char * +termkey_strpkey (termkey_t *tk, + const char *str, termkey_key_t *key, termkey_format_t format) +{ + struct modnames *mods = &modnames[ + !!(format & TERMKEY_FORMAT_LONGMOD) + + !!(format & TERMKEY_FORMAT_ALTISMETA) * 2 + + !!(format & TERMKEY_FORMAT_LOWERMOD) * 4]; + + key->modifiers = 0; + + if ((format & TERMKEY_FORMAT_CARETCTRL) && str[0] == '^' && str[1]) + { + str = termkey_strpkey (tk, + str + 1, key, format & ~TERMKEY_FORMAT_CARETCTRL); + + if (!str + || key->type != TERMKEY_TYPE_KEY + || key->code.codepoint < '@' + || key->code.codepoint > '_' + || key->modifiers != 0) + return NULL; + + if (key->code.codepoint >= 'A' + && key->code.codepoint <= 'Z') + key->code.codepoint += 0x20; + key->modifiers = TERMKEY_KEYMOD_CTRL; + fill_multibyte (tk, key); + return (char *) str; + } + + const char *sep_at; + while ((sep_at = strchr (str, + (format & TERMKEY_FORMAT_SPACEMOD) ? ' ' : '-'))) + { + size_t n = sep_at - str; + if (n == strlen (mods->alt) && !strncmp (mods->alt, str, n)) + key->modifiers |= TERMKEY_KEYMOD_ALT; + else if (n == strlen (mods->ctrl) && !strncmp (mods->ctrl, str, n)) + key->modifiers |= TERMKEY_KEYMOD_CTRL; + else if (n == strlen (mods->shift) && !strncmp (mods->shift, str, n)) + key->modifiers |= TERMKEY_KEYMOD_SHIFT; + else + break; + + str = sep_at + 1; + } + + size_t nbytes; + ssize_t snbytes; + const char *endstr; + + if ((endstr = termkey_lookup_keyname_format + (tk, str, &key->code.sym, format))) + { + key->type = TERMKEY_TYPE_KEYSYM; + str = endstr; + } + else if (sscanf(str, "F%d%zn", &key->code.number, &snbytes) == 1) + { + key->type = TERMKEY_TYPE_FUNCTION; + str += snbytes; + } + // Multibyte must be last + else if (parse_multibyte (tk, (unsigned const char *) str, strlen (str), + &key->code.codepoint, &nbytes) == TERMKEY_RES_KEY) + { + key->type = TERMKEY_TYPE_KEY; + fill_multibyte (tk, key); + str += nbytes; + } + // TODO: Consider mouse events? + else + return NULL; + + termkey_canonicalise (tk, key); + return (char *) str; +} + +int +termkey_keycmp (termkey_t *tk, + const termkey_key_t *key1p, const termkey_key_t *key2p) +{ + /* Copy the key structs since we'll be modifying them */ + termkey_key_t key1 = *key1p, key2 = *key2p; + + termkey_canonicalise (tk, &key1); + termkey_canonicalise (tk, &key2); + + if (key1.type != key2.type) + return key1.type - key2.type; + + switch (key1.type) + { + case TERMKEY_TYPE_KEY: + if (key1.code.codepoint != key2.code.codepoint) + return key1.code.codepoint - key2.code.codepoint; + break; + case TERMKEY_TYPE_KEYSYM: + if (key1.code.sym != key2.code.sym) + return key1.code.sym - key2.code.sym; + break; + case TERMKEY_TYPE_FUNCTION: + case TERMKEY_TYPE_UNKNOWN_CSI: + if (key1.code.number != key2.code.number) + return key1.code.number - key2.code.number; + break; + case TERMKEY_TYPE_MOUSE: + { + int cmp = strncmp (key1.code.mouse, key2.code.mouse, 4); + if (cmp != 0) + return cmp; + break; + } + case TERMKEY_TYPE_POSITION: + { + int line1, col1, line2, col2; + termkey_interpret_position (tk, &key1, &line1, &col1); + termkey_interpret_position (tk, &key2, &line2, &col2); + if (line1 != line2) + return line1 - line2; + return col1 - col2; + } + case TERMKEY_TYPE_MODEREPORT: + { + int initial1, initial2, mode1, mode2, value1, value2; + termkey_interpret_modereport (tk, &key1, &initial1, &mode1, &value1); + termkey_interpret_modereport (tk, &key2, &initial2, &mode2, &value2); + if (initial1 != initial2) + return initial1 - initial2; + if (mode1 != mode2) + return mode1 - mode2; + return value1 - value2; + } + } + return key1.modifiers - key2.modifiers; +} + diff --git a/termkey2.h b/termkey2.h new file mode 100644 index 0000000..9362215 --- /dev/null +++ b/termkey2.h @@ -0,0 +1,271 @@ +#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 */ + char mouse[4]; /* TERMKEY_TYPE_MOUSE */ + /* opaque, see termkey_interpret_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/tests/01base.c b/tests/01base.c new file mode 100644 index 0000000..39e0e39 --- /dev/null +++ b/tests/01base.c @@ -0,0 +1,29 @@ +#include +#include "../termkey.h" +#include "taplib.h" + +int +main (int argc, char *argv[]) +{ + termkey_t *tk; + + plan_tests (6); + + tk = termkey_new_abstract ("vt100", NULL, 0); + ok (!!tk, "termkey_new_abstract"); + is_int (termkey_get_buffer_size (tk), 256, "termkey_get_buffer_size"); + ok (termkey_is_started (tk), "termkey_is_started true after construction"); + + termkey_stop (tk); + ok (!termkey_is_started (tk), + "termkey_is_started false after termkey_stop()"); + + termkey_start (tk); + ok (termkey_is_started (tk), + "termkey_is_started true after termkey_start()"); + + termkey_destroy (tk); + + ok (1, "termkey_free"); + return exit_status (); +} diff --git a/tests/02getkey.c b/tests/02getkey.c new file mode 100644 index 0000000..8cba991 --- /dev/null +++ b/tests/02getkey.c @@ -0,0 +1,92 @@ +#include "../termkey.h" +#include "taplib.h" + +int +main (int argc, char *argv[]) +{ + termkey_t *tk; + termkey_key_t key; + + plan_tests (31); + + tk = termkey_new_abstract ("vt100", NULL, 0); + + is_int (termkey_get_buffer_remaining (tk), + 256, "buffer free initially 256"); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_NONE, + "getkey yields RES_NONE when empty"); + + is_int (termkey_push_bytes (tk, "h", 1), 1, "push_bytes returns 1"); + + is_int (termkey_get_buffer_remaining (tk), 255, + "buffer free 255 after push_bytes"); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY after h"); + + is_int (key.type, TERMKEY_TYPE_KEY, "key.type after h"); + is_int (key.code.codepoint, 'h', "key.code.codepoint after h"); + is_int (key.modifiers, 0, "key.modifiers after h"); + is_str (key.multibyte, "h", "key.multibyte after h"); + + is_int (termkey_get_buffer_remaining (tk), 256, + "buffer free 256 after getkey"); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_NONE, + "getkey yields RES_NONE a second time"); + + termkey_push_bytes (tk, "\x01", 1); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY after C-a"); + + is_int (key.type, TERMKEY_TYPE_KEY, "key.type after C-a"); + is_int (key.code.codepoint, 'a', "key.code.codepoint after C-a"); + is_int (key.modifiers, TERMKEY_KEYMOD_CTRL, "key.modifiers after C-a"); + + termkey_push_bytes (tk, "\033OA", 3); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY after Up"); + + is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type after Up"); + is_int (key.code.sym, TERMKEY_SYM_UP, "key.code.sym after Up"); + is_int (key.modifiers, 0, "key.modifiers after Up"); + + is_int (termkey_push_bytes (tk, "\033O", 2), 2, "push_bytes returns 2"); + + is_int (termkey_get_buffer_remaining (tk), 254, + "buffer free 254 after partial write"); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, + "getkey yields RES_AGAIN after partial write"); + + termkey_push_bytes (tk, "C", 1); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY after Right completion"); + + is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type after Right"); + is_int (key.code.sym, TERMKEY_SYM_RIGHT, "key.code.sym after Right"); + is_int (key.modifiers, 0, "key.modifiers after Right"); + + is_int (termkey_get_buffer_remaining (tk), 256, + "buffer free 256 after completion"); + + termkey_push_bytes (tk, "\033[27;5u", 7); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY after Ctrl-Escape"); + + is_int (key.type, TERMKEY_TYPE_KEYSYM, + "key.type after Ctrl-Escape"); + is_int (key.code.sym, TERMKEY_SYM_ESCAPE, + "key.code.sym after Ctrl-Escape"); + is_int (key.modifiers, TERMKEY_KEYMOD_CTRL, + "key.modifiers after Ctrl-Escape"); + + termkey_destroy (tk); + + return exit_status (); +} diff --git a/tests/03utf8.c b/tests/03utf8.c new file mode 100644 index 0000000..9b06e2e --- /dev/null +++ b/tests/03utf8.c @@ -0,0 +1,188 @@ +#include "../termkey.h" +#include "taplib.h" + +int +main (int argc, char *argv[]) +{ + termkey_t *tk; + termkey_key_t key; + + plan_tests (33 /* 57 */); + + tk = termkey_new_abstract ("vt100", "UTF-8", 0); + + termkey_push_bytes (tk, "a", 1); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY low ASCII"); + is_int (key.type, TERMKEY_TYPE_KEY, "key.type low ASCII"); + is_int (key.code.codepoint, 'a', "key.code.codepoint low ASCII"); + + /* 2-byte UTF-8 range is U+0080 to U+07FF (0xDF 0xBF) */ + /* However, we'd best avoid the C1 range, so we'll start at U+00A0 (0xC2 0xA0) */ + + termkey_push_bytes (tk, "\xC2\xA0", 2); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 2 low"); + is_int (key.type, TERMKEY_TYPE_KEY, "key.type UTF-8 2 low"); + is_int (key.code.codepoint, 0x00A0, "key.code.codepoint UTF-8 2 low"); + + termkey_push_bytes (tk, "\xDF\xBF", 2); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 2 high"); + is_int (key.type, TERMKEY_TYPE_KEY, "key.type UTF-8 2 high"); + is_int (key.code.codepoint, 0x07FF, "key.code.codepoint UTF-8 2 high"); + + /* 3-byte UTF-8 range is U+0800 (0xE0 0xA0 0x80) to U+FFFD (0xEF 0xBF 0xBD) */ + + termkey_push_bytes (tk, "\xE0\xA0\x80", 3); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 3 low"); + is_int (key.type, TERMKEY_TYPE_KEY, "key.type UTF-8 3 low"); + is_int (key.code.codepoint, 0x0800, "key.code.codepoint UTF-8 3 low"); + + termkey_push_bytes (tk, "\xEF\xBF\xBD", 3); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 3 high"); + is_int (key.type, TERMKEY_TYPE_KEY, "key.type UTF-8 3 high"); + is_int (key.code.codepoint, 0xFFFD, "key.code.codepoint UTF-8 3 high"); + + /* 4-byte UTF-8 range is U+10000 (0xF0 0x90 0x80 0x80) to U+10FFFF (0xF4 0x8F 0xBF 0xBF) */ + + termkey_push_bytes (tk, "\xF0\x90\x80\x80", 4); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 4 low"); + is_int (key.type, TERMKEY_TYPE_KEY, "key.type UTF-8 4 low"); + is_int (key.code.codepoint, 0x10000, "key.code.codepoint UTF-8 4 low"); + + termkey_push_bytes (tk, "\xF4\x8F\xBF\xBF", 4); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 4 high"); + is_int (key.type, TERMKEY_TYPE_KEY, "key.type UTF-8 4 high"); + is_int (key.code.codepoint, 0x10FFFF, "key.code.codepoint UTF-8 4 high"); + +#if 0 + /* XXX: With the move to iconv, this has changed significantly. */ + + /* Invalid continuations */ + + termkey_push_bytes (tk, "\xC2!", 2); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 2 invalid cont"); + is_int (key.code.codepoint, 0xFFFD, + "key.code.codepoint UTF-8 2 invalid cont"); + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 2 invalid after"); + is_int (key.code.codepoint, '!', + "key.code.codepoint UTF-8 2 invalid after"); + + termkey_push_bytes (tk, "\xE0!", 2); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 3 invalid cont"); + is_int (key.code.codepoint, 0xFFFD, + "key.code.codepoint UTF-8 3 invalid cont"); + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 3 invalid after"); + is_int (key.code.codepoint, '!', + "key.code.codepoint UTF-8 3 invalid after"); + + termkey_push_bytes (tk, "\xE0\xA0!", 3); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 3 invalid cont 2"); + is_int (key.code.codepoint, 0xFFFD, + "key.code.codepoint UTF-8 3 invalid cont 2"); + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 3 invalid after"); + is_int (key.code.codepoint, '!', + "key.code.codepoint UTF-8 3 invalid after"); + + termkey_push_bytes (tk, "\xF0!", 2); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 4 invalid cont"); + is_int (key.code.codepoint, 0xFFFD, + "key.code.codepoint UTF-8 4 invalid cont"); + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 4 invalid after"); + is_int (key.code.codepoint, '!', + "key.code.codepoint UTF-8 4 invalid after"); + + termkey_push_bytes (tk, "\xF0\x90!", 3); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 4 invalid cont 2"); + is_int (key.code.codepoint, 0xFFFD, + "key.code.codepoint UTF-8 4 invalid cont 2"); + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 4 invalid after"); + is_int (key.code.codepoint, '!', + "key.code.codepoint UTF-8 4 invalid after"); + + termkey_push_bytes (tk, "\xF0\x90\x80!", 4); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 4 invalid cont 3"); + is_int (key.code.codepoint, 0xFFFD, + "key.code.codepoint UTF-8 4 invalid cont 3"); + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 4 invalid after"); + is_int (key.code.codepoint, '!', + "key.code.codepoint UTF-8 4 invalid after"); +#endif + + /* Partials */ + + termkey_push_bytes (tk, "\xC2", 1); + is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, + "getkey yields RES_AGAIN UTF-8 2 partial"); + + termkey_push_bytes (tk, "\xA0", 1); + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 2 partial"); + is_int (key.code.codepoint, 0x00A0, + "key.code.codepoint UTF-8 2 partial"); + + termkey_push_bytes (tk, "\xE0", 1); + is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, + "getkey yields RES_AGAIN UTF-8 3 partial"); + + termkey_push_bytes (tk, "\xA0", 1); + is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, + "getkey yields RES_AGAIN UTF-8 3 partial"); + + termkey_push_bytes (tk, "\x80", 1); + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 3 partial"); + is_int (key.code.codepoint, 0x0800, + "key.code.codepoint UTF-8 3 partial"); + + termkey_push_bytes (tk, "\xF0", 1); + is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, + "getkey yields RES_AGAIN UTF-8 4 partial"); + + termkey_push_bytes (tk, "\x90", 1); + is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, + "getkey yields RES_AGAIN UTF-8 4 partial"); + + termkey_push_bytes (tk, "\x80", 1); + is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, + "getkey yields RES_AGAIN UTF-8 4 partial"); + + termkey_push_bytes (tk, "\x80", 1); + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY UTF-8 4 partial"); + is_int (key.code.codepoint, 0x10000, + "key.code.codepoint UTF-8 4 partial"); + + termkey_destroy (tk); + return exit_status (); +} diff --git a/tests/04flags.c b/tests/04flags.c new file mode 100644 index 0000000..eb6e20b --- /dev/null +++ b/tests/04flags.c @@ -0,0 +1,40 @@ +#include +#include "../termkey.h" +#include "taplib.h" + +int +main (int argc, char *argv[]) +{ + termkey_t *tk; + termkey_key_t key; + + plan_tests (8); + + tk = termkey_new_abstract ("vt100", NULL, 0); + + termkey_push_bytes (tk, " ", 1); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY after space"); + + is_int (key.type, TERMKEY_TYPE_KEY, "key.type after space"); + is_int (key.code.codepoint, ' ', "key.code.codepoint after space"); + is_int (key.modifiers, 0, "key.modifiers after space"); + + termkey_set_flags (tk, TERMKEY_FLAG_SPACESYMBOL); + + termkey_push_bytes (tk, " ", 1); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY after space"); + + is_int (key.type, TERMKEY_TYPE_KEYSYM, + "key.type after space with FLAG_SPACESYMBOL"); + is_int (key.code.sym, TERMKEY_SYM_SPACE, + "key.code.sym after space with FLAG_SPACESYMBOL"); + is_int (key.modifiers, 0, + "key.modifiers after space with FLAG_SPACESYMBOL"); + + termkey_destroy (tk); + return exit_status (); +} diff --git a/tests/05read.c b/tests/05read.c new file mode 100644 index 0000000..2a5530a --- /dev/null +++ b/tests/05read.c @@ -0,0 +1,85 @@ +#include +#include +#include "../termkey.h" +#include "taplib.h" + +int +main (int argc, char *argv[]) +{ + int fd[2]; + termkey_t *tk; + termkey_key_t key; + + plan_tests (21); + + /* We'll need a real filehandle we can write/read. + * pipe () can make us one */ + pipe (fd); + + /* Sanitise this just in case */ + putenv ("TERM=vt100"); + + tk = termkey_new (fd[0], NULL, TERMKEY_FLAG_NOTERMIOS); + + is_int (termkey_get_buffer_remaining (tk), 256, + "buffer free initially 256"); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_NONE, + "getkey yields RES_NONE when empty"); + + write (fd[1], "h", 1); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_NONE, + "getkey yields RES_NONE before advisereadable"); + + is_int (termkey_advisereadable (tk), TERMKEY_RES_AGAIN, + "advisereadable yields RES_AGAIN after h"); + + is_int (termkey_get_buffer_remaining (tk), 255, + "buffer free 255 after advisereadable"); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY after h"); + + is_int (key.type, TERMKEY_TYPE_KEY, "key.type after h"); + is_int (key.code.codepoint, 'h', "key.code.codepoint after h"); + is_int (key.modifiers, 0, "key.modifiers after h"); + is_str (key.multibyte, "h", "key.multibyte after h"); + + is_int (termkey_get_buffer_remaining (tk), 256, + "buffer free 256 after getkey"); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_NONE, + "getkey yields RES_NONE a second time"); + + write (fd[1], "\033O", 2); + termkey_advisereadable (tk); + + is_int (termkey_get_buffer_remaining (tk), 254, + "buffer free 254 after partial write"); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_AGAIN, + "getkey yields RES_AGAIN after partial write"); + + write (fd[1], "C", 1); + termkey_advisereadable (tk); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY after Right completion"); + + is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type after Right"); + is_int (key.code.sym, TERMKEY_SYM_RIGHT, "key.code.sym after Right"); + is_int (key.modifiers, 0, "key.modifiers after Right"); + + is_int (termkey_get_buffer_remaining (tk), 256, + "buffer free 256 after completion"); + + termkey_stop (tk); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_ERROR, + "getkey yields RES_ERROR after termkey_stop ()"); + is_int (errno, EINVAL, "getkey error is EINVAL"); + + termkey_destroy (tk); + return exit_status (); +} diff --git a/tests/06buffer.c b/tests/06buffer.c new file mode 100644 index 0000000..af415b6 --- /dev/null +++ b/tests/06buffer.c @@ -0,0 +1,38 @@ +#include +#include "../termkey.h" +#include "taplib.h" + +int main (int argc, char *argv[]) +{ + termkey_t *tk; + termkey_key_t key; + + plan_tests (9); + + tk = termkey_new_abstract ("vt100", NULL, 0); + + is_int (termkey_get_buffer_remaining (tk), 256, + "buffer free initially 256"); + is_int (termkey_get_buffer_size (tk), 256, + "buffer size initially 256"); + + is_int (termkey_push_bytes (tk, "h", 1), 1, "push_bytes returns 1"); + + is_int (termkey_get_buffer_remaining (tk), 255, + "buffer free 255 after push_bytes"); + is_int (termkey_get_buffer_size (tk), 256, + "buffer size 256 after push_bytes"); + + ok (!!termkey_set_buffer_size (tk, 512), "buffer set size OK"); + + is_int (termkey_get_buffer_remaining (tk), 511, + "buffer free 511 after push_bytes"); + is_int (termkey_get_buffer_size (tk), 512, + "buffer size 512 after push_bytes"); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "buffered key still useable after resize"); + + termkey_destroy (tk); + return exit_status (); +} diff --git a/tests/10keyname.c b/tests/10keyname.c new file mode 100644 index 0000000..20ac000 --- /dev/null +++ b/tests/10keyname.c @@ -0,0 +1,40 @@ +#include "../termkey.h" +#include "taplib.h" + +int +main (int argc, char *argv[]) +{ + termkey_t *tk; + termkey_sym_t sym; + const char *end; + + plan_tests (10); + + tk = termkey_new_abstract ("vt100", NULL, 0); + + sym = termkey_keyname2sym (tk, "Space"); + is_int (sym, TERMKEY_SYM_SPACE, "keyname2sym Space"); + + sym = termkey_keyname2sym (tk, "SomeUnknownKey"); + is_int (sym, TERMKEY_SYM_UNKNOWN, "keyname2sym SomeUnknownKey"); + + end = termkey_lookup_keyname (tk, "Up", &sym); + ok (!!end, "termkey_get_keyname Up returns non-NULL"); + is_str (end, "", "termkey_get_keyname Up return points at endofstring"); + is_int (sym, TERMKEY_SYM_UP, "termkey_get_keyname Up yields Up symbol"); + + end = termkey_lookup_keyname (tk, "DownMore", &sym); + ok (!!end, "termkey_get_keyname DownMore returns non-NULL"); + is_str (end, "More", "termkey_get_keyname DownMore return points at More"); + is_int (sym, TERMKEY_SYM_DOWN, + "termkey_get_keyname DownMore yields Down symbol"); + + end = termkey_lookup_keyname (tk, "SomeUnknownKey", &sym); + ok (!end, "termkey_get_keyname SomeUnknownKey returns NULL"); + + is_str (termkey_get_keyname (tk, TERMKEY_SYM_SPACE), "Space", + "get_keyname SPACE"); + + termkey_destroy (tk); + return exit_status (); +} diff --git a/tests/11strfkey.c b/tests/11strfkey.c new file mode 100644 index 0000000..4c77bdf --- /dev/null +++ b/tests/11strfkey.c @@ -0,0 +1,151 @@ +#include "../termkey.h" +#include "taplib.h" + +int +main (int argc, char *argv[]) +{ + termkey_t *tk; + termkey_key_t key; + char buffer[16]; + size_t len; + + plan_tests (44); + + tk = termkey_new_abstract ("vt100", NULL, 0); + + key.type = TERMKEY_TYPE_KEY; + key.code.codepoint = 'A'; + key.modifiers = 0; + key.multibyte[0] = 0; + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); + is_int (len, 1, "length for unicode/A/0"); + is_str (buffer, "A", "buffer for unicode/A/0"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, + TERMKEY_FORMAT_WRAPBRACKET); + is_int (len, 1, "length for unicode/A/0 wrapbracket"); + is_str (buffer, "A", "buffer for unicode/A/0 wrapbracket"); + + key.type = TERMKEY_TYPE_KEY; + key.code.codepoint = 'b'; + key.modifiers = TERMKEY_KEYMOD_CTRL; + key.multibyte[0] = 0; + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); + is_int (len, 3, "length for unicode/b/CTRL"); + is_str (buffer, "C-b", "buffer for unicode/b/CTRL"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, + TERMKEY_FORMAT_LONGMOD); + is_int (len, 6, "length for unicode/b/CTRL longmod"); + is_str (buffer, "Ctrl-b", "buffer for unicode/b/CTRL longmod"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, + TERMKEY_FORMAT_LONGMOD | TERMKEY_FORMAT_SPACEMOD); + is_int (len, 6, "length for unicode/b/CTRL longmod|spacemod"); + is_str (buffer, "Ctrl b", "buffer for unicode/b/CTRL longmod|spacemod"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, + TERMKEY_FORMAT_LONGMOD | TERMKEY_FORMAT_LOWERMOD); + is_int (len, 6, "length for unicode/b/CTRL longmod|lowermod"); + is_str (buffer, "ctrl-b", "buffer for unicode/b/CTRL longmod|lowermod"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, + TERMKEY_FORMAT_LONGMOD | TERMKEY_FORMAT_SPACEMOD + | TERMKEY_FORMAT_LOWERMOD); + is_int (len, 6, "length for unicode/b/CTRL longmod|spacemod|lowermode"); + is_str (buffer, "ctrl b", + "buffer for unicode/b/CTRL longmod|spacemod|lowermode"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, + TERMKEY_FORMAT_CARETCTRL); + is_int (len, 2, "length for unicode/b/CTRL caretctrl"); + is_str (buffer, "^B", "buffer for unicode/b/CTRL caretctrl"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, + TERMKEY_FORMAT_WRAPBRACKET); + is_int (len, 5, "length for unicode/b/CTRL wrapbracket"); + is_str (buffer, "", "buffer for unicode/b/CTRL wrapbracket"); + + key.type = TERMKEY_TYPE_KEY; + key.code.codepoint = 'c'; + key.modifiers = TERMKEY_KEYMOD_ALT; + key.multibyte[0] = 0; + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); + is_int (len, 3, "length for unicode/c/ALT"); + is_str (buffer, "A-c", "buffer for unicode/c/ALT"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, + TERMKEY_FORMAT_LONGMOD); + is_int (len, 5, "length for unicode/c/ALT longmod"); + is_str (buffer, "Alt-c", "buffer for unicode/c/ALT longmod"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, + TERMKEY_FORMAT_ALTISMETA); + is_int (len, 3, "length for unicode/c/ALT altismeta"); + is_str (buffer, "M-c", "buffer for unicode/c/ALT altismeta"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, + TERMKEY_FORMAT_LONGMOD|TERMKEY_FORMAT_ALTISMETA); + is_int (len, 6, "length for unicode/c/ALT longmod|altismeta"); + is_str (buffer, "Meta-c", "buffer for unicode/c/ALT longmod|altismeta"); + + key.type = TERMKEY_TYPE_KEYSYM; + key.code.sym = TERMKEY_SYM_UP; + key.modifiers = 0; + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); + is_int (len, 2, "length for sym/Up/0"); + is_str (buffer, "Up", "buffer for sym/Up/0"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, + TERMKEY_FORMAT_WRAPBRACKET); + is_int (len, 4, "length for sym/Up/0 wrapbracket"); + is_str (buffer, "", "buffer for sym/Up/0 wrapbracket"); + + key.type = TERMKEY_TYPE_KEYSYM; + key.code.sym = TERMKEY_SYM_PAGEUP; + key.modifiers = 0; + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); + is_int (len, 6, "length for sym/PageUp/0"); + is_str (buffer, "PageUp", "buffer for sym/PageUp/0"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, + TERMKEY_FORMAT_LOWERSPACE); + is_int (len, 7, "length for sym/PageUp/0 lowerspace"); + is_str (buffer, "page up", "buffer for sym/PageUp/0 lowerspace"); + + /* If size of buffer is too small, + * strfkey should return something consistent */ + len = termkey_strfkey (tk, buffer, 4, &key, 0); + is_int (len, 6, "length for sym/PageUp/0"); + is_str (buffer, "Pag", "buffer of len 4 for sym/PageUp/0"); + + len = termkey_strfkey (tk, buffer, 4, &key, TERMKEY_FORMAT_LOWERSPACE); + is_int (len, 7, "length for sym/PageUp/0 lowerspace"); + is_str (buffer, "pag", "buffer of len 4 for sym/PageUp/0 lowerspace"); + + key.type = TERMKEY_TYPE_FUNCTION; + key.code.number = 5; + key.modifiers = 0; + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); + is_int (len, 2, "length for func/5/0"); + is_str (buffer, "F5", "buffer for func/5/0"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, + TERMKEY_FORMAT_WRAPBRACKET); + is_int (len, 4, "length for func/5/0 wrapbracket"); + is_str (buffer, "", "buffer for func/5/0 wrapbracket"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, + TERMKEY_FORMAT_LOWERSPACE); + is_int (len, 2, "length for func/5/0 lowerspace"); + is_str (buffer, "f5", "buffer for func/5/0 lowerspace"); + + termkey_destroy (tk); + return exit_status (); +} diff --git a/tests/12strpkey.c b/tests/12strpkey.c new file mode 100644 index 0000000..f67b67a --- /dev/null +++ b/tests/12strpkey.c @@ -0,0 +1,157 @@ +#include "../termkey.h" +#include "taplib.h" + +int +main (int argc, char *argv[]) +{ + termkey_t *tk; + termkey_key_t key; + const char *endp; + +#define CLEAR_KEY do { key.type = -1; key.code.codepoint = -1; \ + key.modifiers = -1; key.multibyte[0] = 0; } while (0) + + plan_tests (62); + + tk = termkey_new_abstract ("vt100", NULL, 0); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "A", &key, 0); + is_int (key.type, TERMKEY_TYPE_KEY, "key.type for unicode/A/0"); + is_int (key.code.codepoint, 'A', "key.code.codepoint for unicode/A/0"); + is_int (key.modifiers, 0, "key.modifiers for unicode/A/0"); + is_str (key.multibyte, "A", "key.multibyte for unicode/A/0"); + is_str (endp, "", "consumed entire input for unicode/A/0"); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "A and more", &key, 0); + is_int (key.type, TERMKEY_TYPE_KEY, + "key.type for unicode/A/0 trailing"); + is_int (key.code.codepoint, 'A', + "key.code.codepoint for unicode/A/0 trailing"); + is_int (key.modifiers, 0, "key.modifiers for unicode/A/0 trailing"); + is_str (key.multibyte, "A", "key.multibyte for unicode/A/0 trailing"); + is_str (endp, " and more", + "points at string tail for unicode/A/0 trailing"); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "C-b", &key, 0); + is_int (key.type, TERMKEY_TYPE_KEY, "key.type for unicode/b/CTRL"); + is_int (key.code.codepoint, 'b', "key.code.codepoint for unicode/b/CTRL"); + is_int (key.modifiers, TERMKEY_KEYMOD_CTRL, + "key.modifiers for unicode/b/CTRL"); + is_str (key.multibyte, "b", "key.multibyte for unicode/b/CTRL"); + is_str (endp, "", "consumed entire input for unicode/b/CTRL"); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "Ctrl-b", &key, TERMKEY_FORMAT_LONGMOD); + is_int (key.type, TERMKEY_TYPE_KEY, + "key.type for unicode/b/CTRL longmod"); + is_int (key.code.codepoint, 'b', + "key.code.codepoint for unicode/b/CTRL longmod"); + is_int (key.modifiers, TERMKEY_KEYMOD_CTRL, + "key.modifiers for unicode/b/CTRL longmod"); + is_str (key.multibyte, "b", "key.multibyte for unicode/b/CTRL longmod"); + is_str (endp, "", "consumed entire input for unicode/b/CTRL longmod"); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "^B", &key, TERMKEY_FORMAT_CARETCTRL); + is_int (key.type, TERMKEY_TYPE_KEY, + "key.type for unicode/b/CTRL caretctrl"); + is_int (key.code.codepoint, 'b', + "key.code.codepoint for unicode/b/CTRL caretctrl"); + is_int (key.modifiers, TERMKEY_KEYMOD_CTRL, + "key.modifiers for unicode/b/CTRL caretctrl"); + is_str (key.multibyte, "b", "key.multibyte for unicode/b/CTRL caretctrl"); + is_str (endp, "", "consumed entire input for unicode/b/CTRL caretctrl"); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "A-c", &key, 0); + is_int (key.type, TERMKEY_TYPE_KEY, "key.type for unicode/c/ALT"); + is_int (key.code.codepoint, 'c', "key.code.codepoint for unicode/c/ALT"); + is_int (key.modifiers, TERMKEY_KEYMOD_ALT, + "key.modifiers for unicode/c/ALT"); + is_str (key.multibyte, "c", "key.multibyte for unicode/c/ALT"); + is_str (endp, "", "consumed entire input for unicode/c/ALT"); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "Alt-c", &key, TERMKEY_FORMAT_LONGMOD); + is_int (key.type, TERMKEY_TYPE_KEY, + "key.type for unicode/c/ALT longmod"); + is_int (key.code.codepoint, 'c', + "key.code.codepoint for unicode/c/ALT longmod"); + is_int (key.modifiers, TERMKEY_KEYMOD_ALT, + "key.modifiers for unicode/c/ALT longmod"); + is_str (key.multibyte, "c", "key.multibyte for unicode/c/ALT longmod"); + is_str (endp, "", "consumed entire input for unicode/c/ALT longmod"); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "M-c", &key, TERMKEY_FORMAT_ALTISMETA); + is_int (key.type, TERMKEY_TYPE_KEY, + "key.type for unicode/c/ALT altismeta"); + is_int (key.code.codepoint, 'c', + "key.code.codepoint for unicode/c/ALT altismeta"); + is_int (key.modifiers, TERMKEY_KEYMOD_ALT, + "key.modifiers for unicode/c/ALT altismeta"); + is_str (key.multibyte, "c", "key.multibyte for unicode/c/ALT altismeta"); + is_str (endp, "", "consumed entire input for unicode/c/ALT altismeta"); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "Meta-c", &key, + TERMKEY_FORMAT_ALTISMETA | TERMKEY_FORMAT_LONGMOD); + is_int (key.type, TERMKEY_TYPE_KEY, + "key.type for unicode/c/ALT altismeta+longmod"); + is_int (key.code.codepoint, 'c', + "key.code.codepoint for unicode/c/ALT altismeta+longmod"); + is_int (key.modifiers, TERMKEY_KEYMOD_ALT, + "key.modifiers for unicode/c/ALT altismeta+longmod"); + is_str (key.multibyte, "c", "key.multibyte for unicode/c/ALT altismeta+longmod"); + is_str (endp, "", + "consumed entire input for unicode/c/ALT altismeta+longmod"); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "meta c", &key, + TERMKEY_FORMAT_ALTISMETA | TERMKEY_FORMAT_LONGMOD + | TERMKEY_FORMAT_SPACEMOD | TERMKEY_FORMAT_LOWERMOD); + is_int (key.type, TERMKEY_TYPE_KEY, + "key.type for unicode/c/ALT altismeta+long/space+lowermod"); + is_int (key.code.codepoint, 'c', + "key.code.codepoint for unicode/c/ALT altismeta+long/space+lowermod"); + is_int (key.modifiers, TERMKEY_KEYMOD_ALT, + "key.modifiers for unicode/c/ALT altismeta+long/space+lowermod"); + is_str (key.multibyte, "c", + "key.multibyte for unicode/c/ALT altismeta+long/space_lowermod"); + is_str (endp, "", + "consumed entire input for unicode/c/ALT altismeta+long/space+lowermod"); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "ctrl alt page up", &key, + TERMKEY_FORMAT_LONGMOD | TERMKEY_FORMAT_SPACEMOD + | TERMKEY_FORMAT_LOWERMOD | TERMKEY_FORMAT_LOWERSPACE); + is_int (key.type, TERMKEY_TYPE_KEYSYM, + "key.type for sym/PageUp/CTRL+ALT long/space/lowermod+lowerspace"); + is_int (key.code.sym, TERMKEY_SYM_PAGEUP, + "key.code.codepoint for sym/PageUp/CTRL+ALT long/space/lowermod+lowerspace"); + is_int (key.modifiers, TERMKEY_KEYMOD_ALT | TERMKEY_KEYMOD_CTRL, + "key.modifiers for sym/PageUp/CTRL+ALT long/space/lowermod+lowerspace"); + is_str (endp, "", + "consumed entire input for sym/PageUp/CTRL+ALT" + " long/space/lowermod+lowerspace"); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "Up", &key, 0); + is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type for sym/Up/0"); + is_int (key.code.sym, TERMKEY_SYM_UP, "key.code.codepoint for sym/Up/0"); + is_int (key.modifiers, 0, "key.modifiers for sym/Up/0"); + is_str (endp, "", "consumed entire input for sym/Up/0"); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "F5", &key, 0); + is_int (key.type, TERMKEY_TYPE_FUNCTION, "key.type for func/5/0"); + is_int (key.code.number, 5, "key.code.number for func/5/0"); + is_int (key.modifiers, 0, "key.modifiers for func/5/0"); + is_str (endp, "", "consumed entire input for func/5/0"); + + termkey_destroy (tk); + return exit_status (); +} diff --git a/tests/13cmpkey.c b/tests/13cmpkey.c new file mode 100644 index 0000000..1e12b5c --- /dev/null +++ b/tests/13cmpkey.c @@ -0,0 +1,72 @@ +#include "../termkey.h" +#include "taplib.h" + +int +main (int argc, char *argv[]) +{ + termkey_t *tk; + termkey_key_t key1, key2; + + plan_tests (12); + + tk = termkey_new_abstract ("vt100", NULL, 0); + + key1.type = TERMKEY_TYPE_KEY; + key1.code.codepoint = 'A'; + key1.modifiers = 0; + + is_int (termkey_keycmp (tk, &key1, &key1), 0, "cmpkey same structure"); + + key2.type = TERMKEY_TYPE_KEY; + key2.code.codepoint = 'A'; + key2.modifiers = 0; + + is_int (termkey_keycmp (tk, &key1, &key2), 0, "cmpkey identical structure"); + + key2.modifiers = TERMKEY_KEYMOD_CTRL; + + ok (termkey_keycmp (tk, &key1, &key2) < 0, + "cmpkey orders CTRL after nomod"); + ok (termkey_keycmp (tk, &key2, &key1) > 0, + "cmpkey orders nomod before CTRL"); + + key2.code.codepoint = 'B'; + key2.modifiers = 0; + + ok (termkey_keycmp (tk, &key1, &key2) < 0, "cmpkey orders 'B' after 'A'"); + ok (termkey_keycmp (tk, &key2, &key1) > 0, "cmpkey orders 'A' before 'B'"); + + key1.modifiers = TERMKEY_KEYMOD_CTRL; + + ok (termkey_keycmp (tk, &key1, &key2) < 0, + "cmpkey orders nomod 'B' after CTRL 'A'"); + ok (termkey_keycmp (tk, &key2, &key1) > 0, + "cmpkey orders CTRL 'A' before nomod 'B'"); + + key2.type = TERMKEY_TYPE_KEYSYM; + key2.code.sym = TERMKEY_SYM_UP; + + ok (termkey_keycmp (tk, &key1, &key2) < 0, + "cmpkey orders KEYSYM after KEY"); + ok (termkey_keycmp (tk, &key2, &key1) > 0, + "cmpkey orders KEY before KEYSYM"); + + key1.type = TERMKEY_TYPE_KEYSYM; + key1.code.sym = TERMKEY_SYM_SPACE; + key1.modifiers = 0; + key2.type = TERMKEY_TYPE_KEY; + key2.code.codepoint = ' '; + key2.modifiers = 0; + + is_int (termkey_keycmp (tk, &key1, &key2), 0, + "cmpkey considers KEYSYM/SPACE and KEY/SP identical"); + + termkey_set_canonflags (tk, + termkey_get_canonflags (tk) | TERMKEY_CANON_SPACESYMBOL); + is_int (termkey_keycmp (tk, &key1, &key2), 0, + "cmpkey considers KEYSYM/SPACE and KEY/SP" + " identical under SPACESYMBOL"); + + termkey_destroy (tk); + return exit_status (); +} diff --git a/tests/20canon.c b/tests/20canon.c new file mode 100644 index 0000000..a60c2ef --- /dev/null +++ b/tests/20canon.c @@ -0,0 +1,74 @@ +#include "../termkey.h" +#include "taplib.h" + +int +main (int argc, char *argv[]) +{ + termkey_t *tk; + termkey_key_t key; + const char *endp; + +#define CLEAR_KEY do { key.type = -1; key.code.codepoint = -1; \ + key.modifiers = -1; key.multibyte[0] = 0; } while (0) + + plan_tests (26); + + tk = termkey_new_abstract ("vt100", NULL, 0); + + CLEAR_KEY; + endp = termkey_strpkey (tk, " ", &key, 0); + is_int (key.type, TERMKEY_TYPE_KEY, "key.type for SP/unicode"); + is_int (key.code.codepoint, ' ', "key.code.codepoint for SP/unicode"); + is_int (key.modifiers, 0, "key.modifiers for SP/unicode"); + is_str (key.multibyte, " ", "key.multibyte for SP/unicode"); + is_str (endp, "", "consumed entire input for SP/unicode"); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "Space", &key, 0); + is_int (key.type, TERMKEY_TYPE_KEY, "key.type for Space/unicode"); + is_int (key.code.codepoint, ' ', "key.code.codepoint for Space/unicode"); + is_int (key.modifiers, 0, "key.modifiers for Space/unicode"); + is_str (key.multibyte, " ", "key.multibyte for Space/unicode"); + is_str (endp, "", "consumed entire input for Space/unicode"); + + termkey_set_canonflags (tk, + termkey_get_canonflags (tk) | TERMKEY_CANON_SPACESYMBOL); + + CLEAR_KEY; + endp = termkey_strpkey (tk, " ", &key, 0); + is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type for SP/symbol"); + is_int (key.code.sym, TERMKEY_SYM_SPACE, + "key.code.codepoint for SP/symbol"); + is_int (key.modifiers, 0, "key.modifiers for SP/symbol"); + is_str (endp, "", "consumed entire input for SP/symbol"); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "Space", &key, 0); + is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type for Space/symbol"); + is_int (key.code.sym, TERMKEY_SYM_SPACE, + "key.code.codepoint for Space/symbol"); + is_int (key.modifiers, 0, "key.modifiers for Space/symbol"); + is_str (endp, "", "consumed entire input for Space/symbol"); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "DEL", &key, 0); + is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type for Del/unconverted"); + is_int (key.code.sym, TERMKEY_SYM_DEL, + "key.code.codepoint for Del/unconverted"); + is_int (key.modifiers, 0, "key.modifiers for Del/unconverted"); + is_str (endp, "", "consumed entire input for Del/unconverted"); + + termkey_set_canonflags (tk, + termkey_get_canonflags (tk) | TERMKEY_CANON_DELBS); + + CLEAR_KEY; + endp = termkey_strpkey (tk, "DEL", &key, 0); + is_int (key.type, TERMKEY_TYPE_KEYSYM, "key.type for Del/as-backspace"); + is_int (key.code.sym, TERMKEY_SYM_BACKSPACE, + "key.code.codepoint for Del/as-backspace"); + is_int (key.modifiers, 0, "key.modifiers for Del/as-backspace"); + is_str (endp, "", "consumed entire input for Del/as-backspace"); + + termkey_destroy (tk); + return exit_status (); +} diff --git a/tests/30mouse.c b/tests/30mouse.c new file mode 100644 index 0000000..e4f4b27 --- /dev/null +++ b/tests/30mouse.c @@ -0,0 +1,174 @@ +#include "../termkey.h" +#include "taplib.h" + +int +main (int argc, char *argv[]) +{ + termkey_t *tk; + termkey_key_t key; + termkey_mouse_event_t ev; + int button, line, col; + char buffer[32]; + size_t len; + + plan_tests (60); + + tk = termkey_new_abstract ("vt100", NULL, 0); + + termkey_push_bytes (tk, "\e[M !!", 6); + + key.type = -1; + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY for mouse press"); + + is_int (key.type, TERMKEY_TYPE_MOUSE, "key.type for mouse press"); + + ev = -1; button = -1; line = -1; col = -1; + is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), + TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); + + is_int (ev, TERMKEY_MOUSE_PRESS, "mouse event for press"); + is_int (button, 1, "mouse button for press"); + is_int (line, 1, "mouse line for press"); + is_int (col, 1, "mouse column for press"); + is_int (key.modifiers, 0, "modifiers for press"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); + is_int (len, 13, "string length for press"); + is_str (buffer, "MousePress(1)", "string buffer for press"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, + &key, TERMKEY_FORMAT_MOUSE_POS); + is_int (len, 21, "string length for press"); + is_str (buffer, "MousePress(1) @ (1,1)", "string buffer for press"); + + termkey_push_bytes (tk, "\e[M@\"!", 6); + + key.type = -1; + ev = -1; button = -1; line = -1; col = -1; + termkey_getkey (tk, &key); + is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), + TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); + + is_int (ev, TERMKEY_MOUSE_DRAG, "mouse event for drag"); + is_int (button, 1, "mouse button for drag"); + is_int (line, 1, "mouse line for drag"); + is_int (col, 2, "mouse column for drag"); + is_int (key.modifiers, 0, "modifiers for press"); + + termkey_push_bytes (tk, "\e[M##!", 6); + + key.type = -1; + ev = -1; button = -1; line = -1; col = -1; + termkey_getkey (tk, &key); + is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), + TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); + + is_int (ev, TERMKEY_MOUSE_RELEASE, "mouse event for release"); + is_int (line, 1, "mouse line for release"); + is_int (col, 3, "mouse column for release"); + is_int (key.modifiers, 0, "modifiers for press"); + + termkey_push_bytes (tk, "\e[M0++", 6); + + key.type = -1; + ev = -1; button = -1; line = -1; col = -1; + termkey_getkey (tk, &key); + is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), + TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); + + is_int (ev, TERMKEY_MOUSE_PRESS, "mouse event for Ctrl-press"); + is_int (button, 1, "mouse button for Ctrl-press"); + is_int (line, 11, "mouse line for Ctrl-press"); + is_int (col, 11, "mouse column for Ctrl-press"); + is_int (key.modifiers, TERMKEY_KEYMOD_CTRL, "modifiers for Ctrl-press"); + + len = termkey_strfkey (tk, buffer, sizeof buffer, &key, 0); + is_int (len, 15, "string length for Ctrl-press"); + is_str (buffer, "C-MousePress(1)", "string buffer for Ctrl-press"); + + // rxvt protocol + termkey_push_bytes (tk, "\e[0;20;20M", 10); + + key.type = -1; + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY for mouse press rxvt protocol"); + + is_int (key.type, TERMKEY_TYPE_MOUSE, + "key.type for mouse press rxvt protocol"); + + is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), + TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); + + is_int (ev, TERMKEY_MOUSE_PRESS, "mouse event for press rxvt protocol"); + is_int (button, 1, "mouse button for press rxvt protocol"); + is_int (line, 20, "mouse line for press rxvt protocol"); + is_int (col, 20, "mouse column for press rxvt protocol"); + is_int (key.modifiers, 0, "modifiers for press rxvt protocol"); + + termkey_push_bytes (tk, "\e[3;20;20M", 10); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY for mouse release rxvt protocol"); + + is_int (key.type, TERMKEY_TYPE_MOUSE, + "key.type for mouse release rxvt protocol"); + + ev = -1; button = -1; line = -1; col = -1; + is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), + TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); + + is_int (ev, TERMKEY_MOUSE_RELEASE, "mouse event for release rxvt protocol"); + is_int (line, 20, "mouse line for release rxvt protocol"); + is_int (col, 20, "mouse column for release rxvt protocol"); + is_int (key.modifiers, 0, "modifiers for release rxvt protocol"); + + // SGR protocol + termkey_push_bytes (tk, "\e[<0;30;30M", 11); + + key.type = -1; + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY for mouse press SGR encoding"); + + is_int (key.type, TERMKEY_TYPE_MOUSE, + "key.type for mouse press SGR encoding"); + + ev = -1; button = -1; line = -1; col = -1; + is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), + TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); + + is_int (ev, TERMKEY_MOUSE_PRESS, "mouse event for press SGR"); + is_int (button, 1, "mouse button for press SGR"); + is_int (line, 30, "mouse line for press SGR"); + is_int (col, 30, "mouse column for press SGR"); + is_int (key.modifiers, 0, "modifiers for press SGR"); + + termkey_push_bytes (tk, "\e[<0;30;30m", 11); + + key.type = -1; + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY for mouse release SGR encoding"); + + is_int (key.type, TERMKEY_TYPE_MOUSE, + "key.type for mouse release SGR encoding"); + + ev = -1; button = -1; line = -1; col = -1; + is_int (termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col), + TERMKEY_RES_KEY, "interpret_mouse yields RES_KEY"); + + is_int (ev, TERMKEY_MOUSE_RELEASE, "mouse event for release SGR"); + + termkey_push_bytes (tk, "\e[<0;500;300M", 13); + + key.type = -1; + ev = -1; button = -1; line = -1; col = -1; + termkey_getkey (tk, &key); + termkey_interpret_mouse (tk, &key, &ev, &button, &line, &col); + + is_int (line, 300, "mouse line for press SGR wide"); + is_int (col, 500, "mouse column for press SGR wide"); + + termkey_destroy (tk); + + return exit_status (); +} diff --git a/tests/31position.c b/tests/31position.c new file mode 100644 index 0000000..a62be7a --- /dev/null +++ b/tests/31position.c @@ -0,0 +1,38 @@ +#include "../termkey.h" +#include "taplib.h" + +int main (int argc, char *argv[]) +{ + termkey_t *tk; + termkey_key_t key; + int line, col; + + plan_tests (8); + + tk = termkey_new_abstract ("vt100", NULL, 0); + + termkey_push_bytes (tk, "\e[?15;7R", 8); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY for position report"); + + is_int (key.type, TERMKEY_TYPE_POSITION, "key.type for position report"); + + is_int (termkey_interpret_position (tk, &key, &line, &col), TERMKEY_RES_KEY, + "interpret_position yields RES_KEY"); + + is_int (line, 15, "line for position report"); + is_int (col, 7, "column for position report"); + + /* A plain CSI R is likely to be though. This is tricky :/ */ + termkey_push_bytes (tk, "\e[R", 3); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY for "); + + is_int (key.type, TERMKEY_TYPE_FUNCTION, "key.type for "); + is_int (key.code.number, 3, "key.code.number for "); + + termkey_destroy (tk); + return exit_status (); +} diff --git a/tests/32modereport.c b/tests/32modereport.c new file mode 100644 index 0000000..5d79725 --- /dev/null +++ b/tests/32modereport.c @@ -0,0 +1,45 @@ +#include "../termkey.h" +#include "taplib.h" + +int +main (int argc, char *argv[]) +{ + termkey_t *tk; + termkey_key_t key; + int initial, mode, value; + + plan_tests (12); + + tk = termkey_new_abstract ("vt100", NULL, 0); + + termkey_push_bytes (tk, "\e[?1;2$y", 8); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY for mode report"); + + is_int (key.type, TERMKEY_TYPE_MODEREPORT, "key.type for mode report"); + + is_int (termkey_interpret_modereport (tk, &key, &initial, &mode, &value), + TERMKEY_RES_KEY, "interpret_modereoprt yields RES_KEY"); + + is_int (initial, '?', "initial indicator from mode report"); + is_int (mode, 1, "mode number from mode report"); + is_int (value, 2, "mode value from mode report"); + + termkey_push_bytes (tk, "\e[4;1$y", 7); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY for mode report"); + + is_int (key.type, TERMKEY_TYPE_MODEREPORT, "key.type for mode report"); + + is_int (termkey_interpret_modereport (tk, &key, &initial, &mode, &value), + TERMKEY_RES_KEY, "interpret_modereoprt yields RES_KEY"); + + is_int (initial, 0, "initial indicator from mode report"); + is_int (mode, 4, "mode number from mode report"); + is_int (value, 1, "mode value from mode report"); + + termkey_destroy (tk); + return exit_status (); +} diff --git a/tests/39csi.c b/tests/39csi.c new file mode 100644 index 0000000..32a15b0 --- /dev/null +++ b/tests/39csi.c @@ -0,0 +1,52 @@ +#include "../termkey.h" +#include "taplib.h" + +int +main (int argc, char *argv[]) +{ + termkey_t *tk; + termkey_key_t key; + long args[16]; + size_t nargs = 16; + unsigned long command; + + plan_tests (15); + + tk = termkey_new_abstract ("vt100", NULL, 0); + + termkey_push_bytes (tk, "\e[5;25v", 7); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY for CSI v"); + + is_int (key.type, TERMKEY_TYPE_UNKNOWN_CSI, "key.type for unknown CSI"); + + is_int (termkey_interpret_csi (tk, &key, args, &nargs, &command), + TERMKEY_RES_KEY, "interpret_csi yields RES_KEY"); + + is_int (nargs, 2, "nargs for unknown CSI"); + is_int (args[0], 5, "args[0] for unknown CSI"); + is_int (args[1], 25, "args[1] for unknown CSI"); + is_int (command, 'v', "command for unknown CSI"); + + termkey_push_bytes (tk, "\e[?w", 4); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY for CSI ? w"); + is_int (key.type, TERMKEY_TYPE_UNKNOWN_CSI, "key.type for unknown CSI"); + is_int (termkey_interpret_csi (tk, &key, args, &nargs, &command), + TERMKEY_RES_KEY, "interpret_csi yields RES_KEY"); + is_int (command, ('?' << 8) | 'w', "command for unknown CSI"); + + termkey_push_bytes (tk, "\e[?$x", 5); + + is_int (termkey_getkey (tk, &key), TERMKEY_RES_KEY, + "getkey yields RES_KEY for CSI ? $x"); + is_int (key.type, TERMKEY_TYPE_UNKNOWN_CSI, "key.type for unknown CSI"); + is_int (termkey_interpret_csi (tk, &key, args, &nargs, &command), + TERMKEY_RES_KEY, "interpret_csi yields RES_KEY"); + is_int (command, ('$' << 16) | ('?' << 8) | 'x', "command for unknown CSI"); + + termkey_destroy (tk); + return exit_status (); +} diff --git a/tests/taplib.c b/tests/taplib.c new file mode 100644 index 0000000..bfb70f8 --- /dev/null +++ b/tests/taplib.c @@ -0,0 +1,79 @@ +#include "taplib.h" + +#include +#include +#include + +static int g_nexttest = 1; +static int g_exit_status = 0; + +void +plan_tests (int n) +{ + printf ("1..%d\n", n); +} + +void +pass (char *name) +{ + printf ("ok %d - %s\n", g_nexttest++, name); +} + +void +fail (char *name) +{ + printf ("not ok %d - %s\n", g_nexttest++, name); + g_exit_status = 1; +} + +void +ok (int cmp, char *name) +{ + if (cmp) + pass (name); + else + fail (name); +} + +void +diag (char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + + fprintf (stderr, "# "); + vfprintf (stderr, fmt, args); + fprintf (stderr, "\n"); + + va_end (args); +} + +void +is_int (int got, int expect, char *name) +{ + if (got == expect) + ok (1, name); + else + { + ok (0, name); + diag ("got %d expected %d", got, expect); + } +} + +void +is_str (const char *got, const char *expect, char *name) +{ + if (strcmp (got, expect) == 0) + ok (1, name); + else + { + ok (0, name); + diag ("got '%s' expected '%s'", got, expect); + } +} + +int +exit_status(void) +{ + return g_exit_status; +} diff --git a/tests/taplib.h b/tests/taplib.h new file mode 100644 index 0000000..9045c32 --- /dev/null +++ b/tests/taplib.h @@ -0,0 +1,7 @@ +void plan_tests (int n); +void ok (int cmp, char *name); +void pass (char *name); +void fail (char *name); +void is_int (int got, int expect, char *name); +void is_str (const char *got, const char *expect, char *name); +int exit_status (void); -- cgit v1.2.3