aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/test.yml1
-rw-r--r--BUILD.md8
-rw-r--r--MAINTAIN.md4
-rw-r--r--cmake.deps/CMakeLists.txt5
-rw-r--r--cmake.deps/CMakePresets.json1
-rw-r--r--cmake.deps/cmake/BuildLibvterm.cmake8
-rw-r--r--cmake.deps/cmake/LibvtermCMakeLists.txt28
-rw-r--r--cmake.deps/deps.txt3
-rw-r--r--cmake/FindLibvterm.cmake31
-rw-r--r--contrib/local.mk.example1
-rw-r--r--runtime/doc/dev_vimpatch.txt1
-rwxr-xr-xsrc/clint.py1
-rw-r--r--src/nvim/CMakeLists.txt6
-rw-r--r--src/nvim/os/win_defs.h3
-rw-r--r--src/nvim/terminal.c4
-rw-r--r--src/vterm/LICENSE23
-rw-r--r--src/vterm/encoding.c230
-rw-r--r--src/vterm/encoding/DECdrawing.inc36
-rw-r--r--src/vterm/encoding/uk.inc6
-rw-r--r--src/vterm/fullwidth.inc111
-rw-r--r--src/vterm/keyboard.c226
-rw-r--r--src/vterm/mouse.c99
-rw-r--r--src/vterm/parser.c402
-rw-r--r--src/vterm/pen.c608
-rw-r--r--src/vterm/rect.h56
-rw-r--r--src/vterm/screen.c1191
-rw-r--r--src/vterm/state.c2347
-rw-r--r--src/vterm/unicode.c313
-rw-r--r--src/vterm/utf8.h39
-rw-r--r--src/vterm/vterm.c430
-rw-r--r--src/vterm/vterm.h637
-rw-r--r--src/vterm/vterm_internal.h297
-rw-r--r--src/vterm/vterm_keycodes.h61
33 files changed, 7126 insertions, 91 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 55319bb012..ef921cc3a4 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -248,7 +248,6 @@ jobs:
luajit \
lua-luv-dev
# libtree-sitter-dev \
- # libvterm-dev
# Remove comments from packages once we start using these external
# dependencies.
diff --git a/BUILD.md b/BUILD.md
index 895aec5fd1..710aa226d3 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -240,7 +240,7 @@ cmake --build build
### How to build without "bundled" dependencies
1. Manually install the dependencies:
- - libuv libluv libutf8proc libvterm luajit lua-lpeg tree-sitter tree-sitter-c tree-sitter-lua tree-sitter-markdown tree-sitter-query tree-sitter-vim tree-sitter-vimdoc unibilium
+ - libuv libluv libutf8proc luajit lua-lpeg tree-sitter tree-sitter-c tree-sitter-lua tree-sitter-markdown tree-sitter-query tree-sitter-vim tree-sitter-vimdoc unibilium
2. Run CMake:
```sh
cmake -B build -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo
@@ -248,7 +248,7 @@ cmake --build build
```
If all the dependencies are not available in the package, you can use only some of the bundled dependencies as follows (example of using `ninja`):
```sh
- cmake -S cmake.deps -B .deps -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -DUSE_BUNDLED=OFF -DUSE_BUNDLED_LIBVTERM=ON -DUSE_BUNDLED_TS=ON
+ cmake -S cmake.deps -B .deps -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -DUSE_BUNDLED=OFF -DUSE_BUNDLED_TS=ON
cmake --build .deps
cmake -B build -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build
@@ -261,7 +261,7 @@ cmake --build build
```sh
sudo apt install luajit libluajit-5.1-dev lua-lpeg libunibilium-dev
-cmake -S cmake.deps -B .deps -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -DUSE_BUNDLED=OFF -DUSE_BUNDLED_LIBUV=ON -DUSE_BUNDLED_LUV=ON -DUSE_BUNDLED_LIBVTERM=ON -DUSE_BUNDLED_TS=ON -DUSE_BUNDLED_UTF8PROC=ON
+cmake -S cmake.deps -B .deps -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -DUSE_BUNDLED=OFF -DUSE_BUNDLED_LIBUV=ON -DUSE_BUNDLED_LUV=ON -DUSE_BUNDLED_TS=ON -DUSE_BUNDLED_UTF8PROC=ON
cmake --build .deps
cmake -B build -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build
@@ -275,7 +275,7 @@ cmake --build build
```
- Example of using a package with some dependencies:
```
- make BUNDLED_CMAKE_FLAG="-DUSE_BUNDLED=OFF -DUSE_BUNDLED_LUV=ON -DUSE_BUNDLED_TS=ON -DUSE_BUNDLED_LIBVTERM=ON -DUSE_BUNDLED_LIBUV=ON"
+ make BUNDLED_CMAKE_FLAG="-DUSE_BUNDLED=OFF -DUSE_BUNDLED_LUV=ON -DUSE_BUNDLED_TS=ON -DUSE_BUNDLED_LIBUV=ON"
```
## Build prerequisites
diff --git a/MAINTAIN.md b/MAINTAIN.md
index f7f0c5769c..442fd996a6 100644
--- a/MAINTAIN.md
+++ b/MAINTAIN.md
@@ -129,8 +129,6 @@ Some can be auto-bumped by `scripts/bump_deps.lua`.
* [gettext](https://ftp.gnu.org/pub/gnu/gettext/)
* [libiconv](https://ftp.gnu.org/pub/gnu/libiconv)
* [libuv](https://github.com/libuv/libuv)
-* [libvterm](https://www.leonerd.org.uk/code/libvterm/)
- * Downloading from the original source is unreliable, so we use our [mirror](https://github.com/neovim/libvterm) instead.
* [lua-compat](https://github.com/keplerproject/lua-compat-5.3)
* [tree-sitter](https://github.com/tree-sitter/tree-sitter)
* [unibilium](https://github.com/neovim/unibilium)
@@ -146,6 +144,8 @@ These dependencies are "vendored" (inlined), we must update the sources manually
* `src/xdiff/`: [xdiff](https://github.com/git/git/tree/master/xdiff)
* `src/cjson/`: [lua-cjson](https://github.com/openresty/lua-cjson)
* `src/klib/`: [Klib](https://github.com/attractivechaos/klib)
+* `src/vterm/`: [libvterm](https://www.leonerd.org.uk/code/libvterm/),
+ [mirror](https://github.com/neovim/libvterm)
* `runtime/lua/vim/inspect.lua`: [inspect.lua](https://github.com/kikito/inspect.lua)
* `src/nvim/tui/terminfo_defs.h`: terminfo definitions
* Run `scripts/update_terminfo.sh` to update these definitions.
diff --git a/cmake.deps/CMakeLists.txt b/cmake.deps/CMakeLists.txt
index 05db090765..43f50b4485 100644
--- a/cmake.deps/CMakeLists.txt
+++ b/cmake.deps/CMakeLists.txt
@@ -27,7 +27,6 @@ set(DEPS_IGNORE_SHA FALSE)
option(USE_BUNDLED "Use bundled dependencies." ON)
option(USE_BUNDLED_LIBUV "Use the bundled libuv." ${USE_BUNDLED})
-option(USE_BUNDLED_LIBVTERM "Use the bundled libvterm." ${USE_BUNDLED})
option(USE_BUNDLED_LPEG "Use the bundled lpeg." ${USE_BUNDLED})
# PUC Lua is only used for tests, unless explicitly requested.
option(USE_BUNDLED_LUA "Use the bundled version of lua." OFF)
@@ -96,10 +95,6 @@ if(USE_BUNDLED_UNIBILIUM)
include(BuildUnibilium)
endif()
-if(USE_BUNDLED_LIBVTERM)
- include(BuildLibvterm)
-endif()
-
if(USE_BUNDLED_LIBUV)
include(BuildLibuv)
endif()
diff --git a/cmake.deps/CMakePresets.json b/cmake.deps/CMakePresets.json
index 7d9f473581..043124c29c 100644
--- a/cmake.deps/CMakePresets.json
+++ b/cmake.deps/CMakePresets.json
@@ -16,7 +16,6 @@
"description": "Build neovim with external deps on ubuntu",
"cacheVariables": {
"USE_BUNDLED":"OFF",
- "USE_BUNDLED_LIBVTERM":"ON",
"USE_BUNDLED_TS":"ON",
"USE_BUNDLED_UTF8PROC":"ON"
},
diff --git a/cmake.deps/cmake/BuildLibvterm.cmake b/cmake.deps/cmake/BuildLibvterm.cmake
deleted file mode 100644
index 3415d8debe..0000000000
--- a/cmake.deps/cmake/BuildLibvterm.cmake
+++ /dev/null
@@ -1,8 +0,0 @@
-get_externalproject_options(libvterm ${DEPS_IGNORE_SHA})
-ExternalProject_Add(libvterm
- DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libvterm
- PATCH_COMMAND ${CMAKE_COMMAND} -E copy
- ${CMAKE_CURRENT_SOURCE_DIR}/cmake/LibvtermCMakeLists.txt
- ${DEPS_BUILD_DIR}/src/libvterm/CMakeLists.txt
- CMAKE_ARGS ${DEPS_CMAKE_ARGS}
- ${EXTERNALPROJECT_OPTIONS})
diff --git a/cmake.deps/cmake/LibvtermCMakeLists.txt b/cmake.deps/cmake/LibvtermCMakeLists.txt
deleted file mode 100644
index c197523786..0000000000
--- a/cmake.deps/cmake/LibvtermCMakeLists.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-cmake_minimum_required(VERSION 3.13)
-project(libvterm C)
-
-add_compile_options(-w)
-
-include(GNUInstallDirs)
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR})
-
-file(GLOB VTERM_SOURCES ${CMAKE_SOURCE_DIR}/src/*.c)
-add_library(vterm ${VTERM_SOURCES})
-install(TARGETS vterm ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
-
-install(FILES include/vterm.h include/vterm_keycodes.h
- DESTINATION include)
-
-if(NOT WIN32)
- file(GLOB BIN_SOURCES ${CMAKE_SOURCE_DIR}/bin/*.c)
- foreach(EXE_C ${BIN_SOURCES})
- get_filename_component(target_name ${EXE_C} NAME_WE)
- add_executable(${target_name} ${EXE_C})
- target_link_libraries(${target_name} vterm)
- install(TARGETS ${target_name} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
- endforeach()
-endif()
-
-# vim: set ft=cmake:
diff --git a/cmake.deps/deps.txt b/cmake.deps/deps.txt
index 63ec369f76..03e10f2026 100644
--- a/cmake.deps/deps.txt
+++ b/cmake.deps/deps.txt
@@ -10,9 +10,6 @@ LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333
UNIBILIUM_URL https://github.com/neovim/unibilium/archive/d72c3598e7ac5d1ebf86ee268b8b4ed95c0fa628.tar.gz
UNIBILIUM_SHA256 9c4747c862ab5e3076dcf8fa8f0ea7a6b50f20ec5905618b9536655596797487
-LIBVTERM_URL https://github.com/neovim/libvterm/archive/0a15c6e983b0db7ef8276e0792414a805d01bdaf.tar.gz
-LIBVTERM_SHA256 c4683e7a2d71c04781fd0ab7719a94202800e97a9e091514c16983bb732b0fa7
-
LUV_URL https://github.com/luvit/luv/releases/download/1.48.0-2/luv-1.48.0-2.tar.gz
LUV_SHA256 2c3a1ddfebb4f6550293a40ee789f7122e97647eede51511f57203de48c03b7a
diff --git a/cmake/FindLibvterm.cmake b/cmake/FindLibvterm.cmake
deleted file mode 100644
index 68c2646d47..0000000000
--- a/cmake/FindLibvterm.cmake
+++ /dev/null
@@ -1,31 +0,0 @@
-find_path2(LIBVTERM_INCLUDE_DIR vterm.h)
-find_library2(LIBVTERM_LIBRARY vterm)
-
-if(LIBVTERM_INCLUDE_DIR AND EXISTS "${LIBVTERM_INCLUDE_DIR}/vterm.h")
- file(STRINGS ${LIBVTERM_INCLUDE_DIR}/vterm.h VTERM_VERSION_MAJOR REGEX "#define VTERM_VERSION_MAJOR")
- string(REGEX MATCH "[0-9]+" VTERM_VERSION_MAJOR ${VTERM_VERSION_MAJOR})
-
- file(STRINGS ${LIBVTERM_INCLUDE_DIR}/vterm.h VTERM_VERSION_MINOR REGEX "#define VTERM_VERSION_MINOR")
- string(REGEX MATCH "[0-9]+" VTERM_VERSION_MINOR ${VTERM_VERSION_MINOR})
-
- file(STRINGS ${LIBVTERM_INCLUDE_DIR}/vterm.h VTERM_VERSION_PATCH REGEX "#define VTERM_VERSION_PATCH")
-
- # The following is needed to give a coherent error for versions 0.3.2 and
- # smaller.
- if(VTERM_VERSION_PATCH)
- string(REGEX MATCH "[0-9]+" VTERM_VERSION_PATCH ${VTERM_VERSION_PATCH})
- string(PREPEND VTERM_VERSION_PATCH ".")
- endif()
-
- set(VTERM_VERSION ${VTERM_VERSION_MAJOR}.${VTERM_VERSION_MINOR}${VTERM_VERSION_PATCH})
-endif()
-
-find_package_handle_standard_args(Libvterm
- REQUIRED_VARS LIBVTERM_INCLUDE_DIR LIBVTERM_LIBRARY
- VERSION_VAR VTERM_VERSION)
-
-add_library(libvterm INTERFACE)
-target_include_directories(libvterm SYSTEM BEFORE INTERFACE ${LIBVTERM_INCLUDE_DIR})
-target_link_libraries(libvterm INTERFACE ${LIBVTERM_LIBRARY})
-
-mark_as_advanced(LIBVTERM_INCLUDE_DIR LIBVTERM_LIBRARY)
diff --git a/contrib/local.mk.example b/contrib/local.mk.example
index 61fc0b95b8..bdb889dfcd 100644
--- a/contrib/local.mk.example
+++ b/contrib/local.mk.example
@@ -45,7 +45,6 @@
# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_GETTEXT=OFF
# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_LIBICONV=OFF
# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_LIBUV=OFF
-# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_LIBVTERM=OFF
# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_LUAJIT=OFF
# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_LUV=OFF
# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_TS=OFF
diff --git a/runtime/doc/dev_vimpatch.txt b/runtime/doc/dev_vimpatch.txt
index 9a2380b162..5119613b55 100644
--- a/runtime/doc/dev_vimpatch.txt
+++ b/runtime/doc/dev_vimpatch.txt
@@ -139,7 +139,6 @@ TYPES OF "NOT APPLICABLE" VIM PATCHES ~
- NA files: `src/gui_*`, `src/gvim_*`, `src/GvimExt/*`, `src/testdir/test_gui*`
- `balloon` changes: Nvim does not support balloon feature
- NA files: `src/beval_*`, `src/testdir/test_balloon_*`
-- libvterm changes: Nvim does not vendor libvterm in `src/`.
- Screendump tests from `test_popupwin.vim`, `test_popupwin_textprop.vim`:
https://github.com/neovim/neovim/pull/12741#issuecomment-704677141
- json changes: incompatible API https://github.com/neovim/neovim/pull/4131
diff --git a/src/clint.py b/src/clint.py
index 67185a7340..cc144543a0 100755
--- a/src/clint.py
+++ b/src/clint.py
@@ -880,6 +880,7 @@ def CheckIncludes(filename, lines, error):
"mpack/object.h",
"nvim/func_attr.h",
"termkey/termkey.h",
+ "vterm/vterm.h",
]
for i in check_includes_ignore:
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index d153c9e3ef..87c9506709 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -30,7 +30,6 @@ target_link_libraries(main_lib INTERFACE ${LUV_LIBRARY})
find_package(Iconv REQUIRED)
find_package(Libuv 1.28.0 REQUIRED)
-find_package(Libvterm 0.3.3 REQUIRED)
find_package(Lpeg REQUIRED)
find_package(Treesitter 0.22.6 REQUIRED)
find_package(Unibilium 2.0 REQUIRED)
@@ -38,7 +37,6 @@ find_package(UTF8proc REQUIRED)
target_link_libraries(main_lib INTERFACE
iconv
- libvterm
lpeg
treesitter
unibilium
@@ -366,8 +364,8 @@ file(MAKE_DIRECTORY ${TOUCHES_DIR} ${GENERATED_DIR} ${GENERATED_INCLUDES_DIR})
file(GLOB NVIM_SOURCES CONFIGURE_DEPENDS *.c)
file(GLOB NVIM_HEADERS CONFIGURE_DEPENDS *.h)
-file(GLOB EXTERNAL_SOURCES CONFIGURE_DEPENDS ../xdiff/*.c ../mpack/*.c ../cjson/*.c ../klib/*.c ../termkey/*.c)
-file(GLOB EXTERNAL_HEADERS CONFIGURE_DEPENDS ../xdiff/*.h ../mpack/*.h ../cjson/*.h ../klib/*.h ../termkey/*.h)
+file(GLOB EXTERNAL_SOURCES CONFIGURE_DEPENDS ../xdiff/*.c ../mpack/*.c ../cjson/*.c ../klib/*.c ../termkey/*.c ../vterm/*.c)
+file(GLOB EXTERNAL_HEADERS CONFIGURE_DEPENDS ../xdiff/*.h ../mpack/*.h ../cjson/*.h ../klib/*.h ../termkey/*.h ../vterm/*.h)
file(GLOB NLUA0_SOURCES CONFIGURE_DEPENDS ../mpack/*.c)
diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h
index 852059f78b..024719806c 100644
--- a/src/nvim/os/win_defs.h
+++ b/src/nvim/os/win_defs.h
@@ -16,6 +16,9 @@
#include <sys/stat.h>
#include <windows.h>
+// vterm.h defines an `unsigned int small` in a struct, triggering error C2632
+#undef small
+
// Windows does not have S_IFLNK but libuv defines it
// and sets the flag for us when calling uv_fs_stat.
#include <uv.h>
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 507a9e3a2a..abc9b3534b 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -40,8 +40,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <vterm.h>
-#include <vterm_keycodes.h>
#include "klib/kvec.h"
#include "nvim/api/private/helpers.h"
@@ -94,6 +92,8 @@
#include "nvim/ui.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
+#include "vterm/vterm.h"
+#include "vterm/vterm_keycodes.h"
typedef struct {
VimState state;
diff --git a/src/vterm/LICENSE b/src/vterm/LICENSE
new file mode 100644
index 0000000000..0d051634b2
--- /dev/null
+++ b/src/vterm/LICENSE
@@ -0,0 +1,23 @@
+
+
+The MIT License
+
+Copyright (c) 2008 Paul Evans <leonerd@leonerd.org.uk>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/src/vterm/encoding.c b/src/vterm/encoding.c
new file mode 100644
index 0000000000..434ac3f99b
--- /dev/null
+++ b/src/vterm/encoding.c
@@ -0,0 +1,230 @@
+#include "vterm_internal.h"
+
+#define UNICODE_INVALID 0xFFFD
+
+#if defined(DEBUG) && DEBUG > 1
+# define DEBUG_PRINT_UTF8
+#endif
+
+struct UTF8DecoderData {
+ // number of bytes remaining in this codepoint
+ int bytes_remaining;
+
+ // number of bytes total in this codepoint once it's finished
+ // (for detecting overlongs)
+ int bytes_total;
+
+ int this_cp;
+};
+
+static void init_utf8(VTermEncoding *enc, void *data_)
+{
+ struct UTF8DecoderData *data = data_;
+
+ data->bytes_remaining = 0;
+ data->bytes_total = 0;
+}
+
+static void decode_utf8(VTermEncoding *enc, void *data_,
+ uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t bytelen)
+{
+ struct UTF8DecoderData *data = data_;
+
+#ifdef DEBUG_PRINT_UTF8
+ printf("BEGIN UTF-8\n");
+#endif
+
+ for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
+ unsigned char c = bytes[*pos];
+
+#ifdef DEBUG_PRINT_UTF8
+ printf(" pos=%zd c=%02x rem=%d\n", *pos, c, data->bytes_remaining);
+#endif
+
+ if(c < 0x20) // C0
+ return;
+
+ else if(c >= 0x20 && c < 0x7f) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ cp[(*cpi)++] = c;
+#ifdef DEBUG_PRINT_UTF8
+ printf(" UTF-8 char: U+%04x\n", c);
+#endif
+ data->bytes_remaining = 0;
+ }
+
+ else if(c == 0x7f) // DEL
+ return;
+
+ else if(c >= 0x80 && c < 0xc0) {
+ if(!data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ continue;
+ }
+
+ data->this_cp <<= 6;
+ data->this_cp |= c & 0x3f;
+ data->bytes_remaining--;
+
+ if(!data->bytes_remaining) {
+#ifdef DEBUG_PRINT_UTF8
+ printf(" UTF-8 raw char U+%04x bytelen=%d ", data->this_cp, data->bytes_total);
+#endif
+ // Check for overlong sequences
+ switch(data->bytes_total) {
+ case 2:
+ if(data->this_cp < 0x0080) data->this_cp = UNICODE_INVALID;
+ break;
+ case 3:
+ if(data->this_cp < 0x0800) data->this_cp = UNICODE_INVALID;
+ break;
+ case 4:
+ if(data->this_cp < 0x10000) data->this_cp = UNICODE_INVALID;
+ break;
+ case 5:
+ if(data->this_cp < 0x200000) data->this_cp = UNICODE_INVALID;
+ break;
+ case 6:
+ if(data->this_cp < 0x4000000) data->this_cp = UNICODE_INVALID;
+ break;
+ }
+ // Now look for plain invalid ones
+ if((data->this_cp >= 0xD800 && data->this_cp <= 0xDFFF) ||
+ data->this_cp == 0xFFFE ||
+ data->this_cp == 0xFFFF)
+ data->this_cp = UNICODE_INVALID;
+#ifdef DEBUG_PRINT_UTF8
+ printf(" char: U+%04x\n", data->this_cp);
+#endif
+ cp[(*cpi)++] = data->this_cp;
+ }
+ }
+
+ else if(c >= 0xc0 && c < 0xe0) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ data->this_cp = c & 0x1f;
+ data->bytes_total = 2;
+ data->bytes_remaining = 1;
+ }
+
+ else if(c >= 0xe0 && c < 0xf0) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ data->this_cp = c & 0x0f;
+ data->bytes_total = 3;
+ data->bytes_remaining = 2;
+ }
+
+ else if(c >= 0xf0 && c < 0xf8) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ data->this_cp = c & 0x07;
+ data->bytes_total = 4;
+ data->bytes_remaining = 3;
+ }
+
+ else if(c >= 0xf8 && c < 0xfc) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ data->this_cp = c & 0x03;
+ data->bytes_total = 5;
+ data->bytes_remaining = 4;
+ }
+
+ else if(c >= 0xfc && c < 0xfe) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ data->this_cp = c & 0x01;
+ data->bytes_total = 6;
+ data->bytes_remaining = 5;
+ }
+
+ else {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+ }
+}
+
+static VTermEncoding encoding_utf8 = {
+ .init = &init_utf8,
+ .decode = &decode_utf8,
+};
+
+static void decode_usascii(VTermEncoding *enc, void *data,
+ uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t bytelen)
+{
+ int is_gr = bytes[*pos] & 0x80;
+
+ for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
+ unsigned char c = bytes[*pos] ^ is_gr;
+
+ if(c < 0x20 || c == 0x7f || c >= 0x80)
+ return;
+
+ cp[(*cpi)++] = c;
+ }
+}
+
+static VTermEncoding encoding_usascii = {
+ .decode = &decode_usascii,
+};
+
+struct StaticTableEncoding {
+ const VTermEncoding enc;
+ const uint32_t chars[128];
+};
+
+static void decode_table(VTermEncoding *enc, void *data,
+ uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t bytelen)
+{
+ struct StaticTableEncoding *table = (struct StaticTableEncoding *)enc;
+ int is_gr = bytes[*pos] & 0x80;
+
+ for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
+ unsigned char c = bytes[*pos] ^ is_gr;
+
+ if(c < 0x20 || c == 0x7f || c >= 0x80)
+ return;
+
+ if(table->chars[c])
+ cp[(*cpi)++] = table->chars[c];
+ else
+ cp[(*cpi)++] = c;
+ }
+}
+
+#include "encoding/DECdrawing.inc"
+#include "encoding/uk.inc"
+
+static struct {
+ VTermEncodingType type;
+ char designation;
+ VTermEncoding *enc;
+}
+encodings[] = {
+ { ENC_UTF8, 'u', &encoding_utf8 },
+ { ENC_SINGLE_94, '0', (VTermEncoding*)&encoding_DECdrawing },
+ { ENC_SINGLE_94, 'A', (VTermEncoding*)&encoding_uk },
+ { ENC_SINGLE_94, 'B', &encoding_usascii },
+ { 0 },
+};
+
+/* This ought to be INTERNAL but isn't because it's used by unit testing */
+VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation)
+{
+ for(int i = 0; encodings[i].designation; i++)
+ if(encodings[i].type == type && encodings[i].designation == designation)
+ return encodings[i].enc;
+ return NULL;
+}
diff --git a/src/vterm/encoding/DECdrawing.inc b/src/vterm/encoding/DECdrawing.inc
new file mode 100644
index 0000000000..627397bcc2
--- /dev/null
+++ b/src/vterm/encoding/DECdrawing.inc
@@ -0,0 +1,36 @@
+static const struct StaticTableEncoding encoding_DECdrawing = {
+ { .decode = &decode_table },
+ {
+ [0x60] = 0x25C6, // BLACK DIAMOND
+ [0x61] = 0x2592, // MEDIUM SHADE (checkerboard)
+ [0x62] = 0x2409, // SYMBOL FOR HORIZONTAL TAB
+ [0x63] = 0x240C, // SYMBOL FOR FORM FEED
+ [0x64] = 0x240D, // SYMBOL FOR CARRIAGE RETURN
+ [0x65] = 0x240A, // SYMBOL FOR LINE FEED
+ [0x66] = 0x00B0, // DEGREE SIGN
+ [0x67] = 0x00B1, // PLUS-MINUS SIGN (plus or minus)
+ [0x68] = 0x2424, // SYMBOL FOR NEW LINE
+ [0x69] = 0x240B, // SYMBOL FOR VERTICAL TAB
+ [0x6a] = 0x2518, // BOX DRAWINGS LIGHT UP AND LEFT (bottom-right corner)
+ [0x6b] = 0x2510, // BOX DRAWINGS LIGHT DOWN AND LEFT (top-right corner)
+ [0x6c] = 0x250C, // BOX DRAWINGS LIGHT DOWN AND RIGHT (top-left corner)
+ [0x6d] = 0x2514, // BOX DRAWINGS LIGHT UP AND RIGHT (bottom-left corner)
+ [0x6e] = 0x253C, // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL (crossing lines)
+ [0x6f] = 0x23BA, // HORIZONTAL SCAN LINE-1
+ [0x70] = 0x23BB, // HORIZONTAL SCAN LINE-3
+ [0x71] = 0x2500, // BOX DRAWINGS LIGHT HORIZONTAL
+ [0x72] = 0x23BC, // HORIZONTAL SCAN LINE-7
+ [0x73] = 0x23BD, // HORIZONTAL SCAN LINE-9
+ [0x74] = 0x251C, // BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+ [0x75] = 0x2524, // BOX DRAWINGS LIGHT VERTICAL AND LEFT
+ [0x76] = 0x2534, // BOX DRAWINGS LIGHT UP AND HORIZONTAL
+ [0x77] = 0x252C, // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+ [0x78] = 0x2502, // BOX DRAWINGS LIGHT VERTICAL
+ [0x79] = 0x2A7D, // LESS-THAN OR SLANTED EQUAL-TO
+ [0x7a] = 0x2A7E, // GREATER-THAN OR SLANTED EQUAL-TO
+ [0x7b] = 0x03C0, // GREEK SMALL LETTER PI
+ [0x7c] = 0x2260, // NOT EQUAL TO
+ [0x7d] = 0x00A3, // POUND SIGN
+ [0x7e] = 0x00B7, // MIDDLE DOT
+ }
+};
diff --git a/src/vterm/encoding/uk.inc b/src/vterm/encoding/uk.inc
new file mode 100644
index 0000000000..5c7700226b
--- /dev/null
+++ b/src/vterm/encoding/uk.inc
@@ -0,0 +1,6 @@
+static const struct StaticTableEncoding encoding_uk = {
+ { .decode = &decode_table },
+ {
+ [0x23] = 0x00a3, // £
+ }
+};
diff --git a/src/vterm/fullwidth.inc b/src/vterm/fullwidth.inc
new file mode 100644
index 0000000000..a703529a76
--- /dev/null
+++ b/src/vterm/fullwidth.inc
@@ -0,0 +1,111 @@
+ { 0x1100, 0x115f },
+ { 0x231a, 0x231b },
+ { 0x2329, 0x232a },
+ { 0x23e9, 0x23ec },
+ { 0x23f0, 0x23f0 },
+ { 0x23f3, 0x23f3 },
+ { 0x25fd, 0x25fe },
+ { 0x2614, 0x2615 },
+ { 0x2648, 0x2653 },
+ { 0x267f, 0x267f },
+ { 0x2693, 0x2693 },
+ { 0x26a1, 0x26a1 },
+ { 0x26aa, 0x26ab },
+ { 0x26bd, 0x26be },
+ { 0x26c4, 0x26c5 },
+ { 0x26ce, 0x26ce },
+ { 0x26d4, 0x26d4 },
+ { 0x26ea, 0x26ea },
+ { 0x26f2, 0x26f3 },
+ { 0x26f5, 0x26f5 },
+ { 0x26fa, 0x26fa },
+ { 0x26fd, 0x26fd },
+ { 0x2705, 0x2705 },
+ { 0x270a, 0x270b },
+ { 0x2728, 0x2728 },
+ { 0x274c, 0x274c },
+ { 0x274e, 0x274e },
+ { 0x2753, 0x2755 },
+ { 0x2757, 0x2757 },
+ { 0x2795, 0x2797 },
+ { 0x27b0, 0x27b0 },
+ { 0x27bf, 0x27bf },
+ { 0x2b1b, 0x2b1c },
+ { 0x2b50, 0x2b50 },
+ { 0x2b55, 0x2b55 },
+ { 0x2e80, 0x2e99 },
+ { 0x2e9b, 0x2ef3 },
+ { 0x2f00, 0x2fd5 },
+ { 0x2ff0, 0x2ffb },
+ { 0x3000, 0x303e },
+ { 0x3041, 0x3096 },
+ { 0x3099, 0x30ff },
+ { 0x3105, 0x312f },
+ { 0x3131, 0x318e },
+ { 0x3190, 0x31ba },
+ { 0x31c0, 0x31e3 },
+ { 0x31f0, 0x321e },
+ { 0x3220, 0x3247 },
+ { 0x3250, 0x4dbf },
+ { 0x4e00, 0xa48c },
+ { 0xa490, 0xa4c6 },
+ { 0xa960, 0xa97c },
+ { 0xac00, 0xd7a3 },
+ { 0xf900, 0xfaff },
+ { 0xfe10, 0xfe19 },
+ { 0xfe30, 0xfe52 },
+ { 0xfe54, 0xfe66 },
+ { 0xfe68, 0xfe6b },
+ { 0xff01, 0xff60 },
+ { 0xffe0, 0xffe6 },
+ { 0x16fe0, 0x16fe3 },
+ { 0x17000, 0x187f7 },
+ { 0x18800, 0x18af2 },
+ { 0x1b000, 0x1b11e },
+ { 0x1b150, 0x1b152 },
+ { 0x1b164, 0x1b167 },
+ { 0x1b170, 0x1b2fb },
+ { 0x1f004, 0x1f004 },
+ { 0x1f0cf, 0x1f0cf },
+ { 0x1f18e, 0x1f18e },
+ { 0x1f191, 0x1f19a },
+ { 0x1f200, 0x1f202 },
+ { 0x1f210, 0x1f23b },
+ { 0x1f240, 0x1f248 },
+ { 0x1f250, 0x1f251 },
+ { 0x1f260, 0x1f265 },
+ { 0x1f300, 0x1f320 },
+ { 0x1f32d, 0x1f335 },
+ { 0x1f337, 0x1f37c },
+ { 0x1f37e, 0x1f393 },
+ { 0x1f3a0, 0x1f3ca },
+ { 0x1f3cf, 0x1f3d3 },
+ { 0x1f3e0, 0x1f3f0 },
+ { 0x1f3f4, 0x1f3f4 },
+ { 0x1f3f8, 0x1f43e },
+ { 0x1f440, 0x1f440 },
+ { 0x1f442, 0x1f4fc },
+ { 0x1f4ff, 0x1f53d },
+ { 0x1f54b, 0x1f54e },
+ { 0x1f550, 0x1f567 },
+ { 0x1f57a, 0x1f57a },
+ { 0x1f595, 0x1f596 },
+ { 0x1f5a4, 0x1f5a4 },
+ { 0x1f5fb, 0x1f64f },
+ { 0x1f680, 0x1f6c5 },
+ { 0x1f6cc, 0x1f6cc },
+ { 0x1f6d0, 0x1f6d2 },
+ { 0x1f6d5, 0x1f6d5 },
+ { 0x1f6eb, 0x1f6ec },
+ { 0x1f6f4, 0x1f6fa },
+ { 0x1f7e0, 0x1f7eb },
+ { 0x1f90d, 0x1f971 },
+ { 0x1f973, 0x1f976 },
+ { 0x1f97a, 0x1f9a2 },
+ { 0x1f9a5, 0x1f9aa },
+ { 0x1f9ae, 0x1f9ca },
+ { 0x1f9cd, 0x1f9ff },
+ { 0x1fa70, 0x1fa73 },
+ { 0x1fa78, 0x1fa7a },
+ { 0x1fa80, 0x1fa82 },
+ { 0x1fa90, 0x1fa95 },
diff --git a/src/vterm/keyboard.c b/src/vterm/keyboard.c
new file mode 100644
index 0000000000..d31c8be12d
--- /dev/null
+++ b/src/vterm/keyboard.c
@@ -0,0 +1,226 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+
+#include "utf8.h"
+
+void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod)
+{
+ /* The shift modifier is never important for Unicode characters
+ * apart from Space
+ */
+ if(c != ' ')
+ mod &= ~VTERM_MOD_SHIFT;
+
+ if(mod == 0) {
+ // Normal text - ignore just shift
+ char str[6];
+ int seqlen = fill_utf8(c, str);
+ vterm_push_output_bytes(vt, str, seqlen);
+ return;
+ }
+
+ int needs_CSIu;
+ switch(c) {
+ /* Special Ctrl- letters that can't be represented elsewise */
+ case 'i': case 'j': case 'm': case '[':
+ needs_CSIu = 1;
+ break;
+ /* Ctrl-\ ] ^ _ don't need CSUu */
+ case '\\': case ']': case '^': case '_':
+ needs_CSIu = 0;
+ break;
+ /* Shift-space needs CSIu */
+ case ' ':
+ needs_CSIu = !!(mod & VTERM_MOD_SHIFT);
+ break;
+ /* All other characters needs CSIu except for letters a-z */
+ default:
+ needs_CSIu = (c < 'a' || c > 'z');
+ }
+
+ /* ALT we can just prefix with ESC; anything else requires CSI u */
+ if(needs_CSIu && (mod & ~VTE