diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt index a16000aba8..47683b198c 100644 --- a/config/CMakeLists.txt +++ b/config/CMakeLists.txt @@ -26,6 +26,8 @@ if(NOT HAVE_SYS_WAIT_H AND UNIX) message(SEND_ERROR "header sys/wait.h is required for Unix") endif() check_include_files(sys/utsname.h HAVE_SYS_UTSNAME_H) +check_include_files(termio.h HAVE_TERMIO_H) +check_include_files(termios.h HAVE_TERMIOS_H) check_include_files(utime.h HAVE_UTIME_H) check_include_files(sys/uio.h HAVE_SYS_UIO_H) diff --git a/config/config.h.in b/config/config.h.in index 4c35b3b1cb..e70c30e17c 100644 --- a/config/config.h.in +++ b/config/config.h.in @@ -43,6 +43,8 @@ #cmakedefine HAVE_STRNCASECMP #cmakedefine HAVE_SYS_UTSNAME_H #cmakedefine HAVE_SYS_WAIT_H +#cmakedefine HAVE_TERMIO_H +#cmakedefine HAVE_TERMIOS_H #cmakedefine HAVE_UTIME #cmakedefine HAVE_UTIME_H #cmakedefine HAVE_UTIMES diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 1307ab5e5a..b60a7eed36 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -39,8 +39,6 @@ static int events_enabled = 0; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/input.c.generated.h" #endif -// Helper function used to push bytes from the 'event' key sequence partially -// between calls to os_inchar when maxlen < 3 void input_init(void) { @@ -389,6 +387,8 @@ static void process_interrupts(void) } } +// Helper function used to push bytes from the 'event' key sequence partially +// between calls to os_inchar when maxlen < 3 static int push_event_key(uint8_t *buf, int maxlen) { static const uint8_t key[3] = { K_SPECIAL, KS_EXTRA, KE_EVENT }; diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 70d87a7ab2..8e5adb14f9 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -32,7 +32,14 @@ void term_input_init(TermInput *input, Loop *loop) term = ""; // termkey_new_abstract assumes non-null (#2745) } +#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18 + input->tk = termkey_new_abstract(term, + TERMKEY_FLAG_UTF8 | TERMKEY_FLAG_NOSTART); + termkey_hook_terminfo_getstr(input->tk, input->tk_ti_hook_fn, NULL); + termkey_start(input->tk); +#else input->tk = termkey_new_abstract(term, TERMKEY_FLAG_UTF8); +#endif int curflags = termkey_get_canonflags(input->tk); termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS); diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h index d7ee2b9e52..69a4fae50d 100644 --- a/src/nvim/tui/input.h +++ b/src/nvim/tui/input.h @@ -12,6 +12,7 @@ typedef struct term_input { bool paste_enabled; bool waiting; TermKey *tk; + TermKey_Terminfo_Getstr_Hook *tk_ti_hook_fn; ///< libtermkey terminfo hook TimeWatcher timer_handle; Loop *loop; Stream read_stream; diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index bceb4ca4ff..22af61994c 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -7,9 +7,13 @@ #include #include +#if defined(HAVE_TERMIOS_H) +# include +#endif #include "nvim/lib/kvec.h" +#include "nvim/ascii.h" #include "nvim/vim.h" #include "nvim/log.h" #include "nvim/ui.h" @@ -195,6 +199,9 @@ static void tui_terminal_start(UI *ui) terminfo_start(ui); update_size(ui); signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH); + + data->input.tk_ti_hook_fn = tui_tk_ti_getstr; + term_input_init(&data->input, data->loop); term_input_start(&data->input); } @@ -227,8 +234,6 @@ static void tui_main(UIBridgeData *bridge, UI *ui) signal_watcher_init(data->loop, &data->winch_handle, ui); signal_watcher_init(data->loop, &data->cont_handle, data); signal_watcher_start(&data->cont_handle, sigcont_cb, SIGCONT); - // initialize input reading structures - term_input_init(&data->input, &tui_loop); tui_terminal_start(ui); data->stop = false; // allow the main thread to continue, we are ready to start handling UI @@ -957,3 +962,50 @@ static void flush_buf(UI *ui, bool toggle_cursor) unibi_out(ui, unibi_cursor_invisible); } } + +/// Try to get "kbs" code from stty because "the terminfo kbs entry is extremely +/// unreliable." (Vim, Bash, and tmux also do this.) +/// +/// @see tmux/tty-keys.c fe4e9470bb504357d073320f5d305b22663ee3fd +/// @see https://bugzilla.redhat.com/show_bug.cgi?id=142659 +static const char *tui_get_stty_erase(void) +{ + static char stty_erase[2] = { 0 }; +#if defined(ECHOE) && defined(ICANON) \ + && (defined(HAVE_TERMIO_H) || defined(HAVE_TERMIOS_H)) + struct termios t; + if (tcgetattr(input_global_fd(), &t) != -1) { + stty_erase[0] = (char)t.c_cc[VERASE]; + stty_erase[1] = '\0'; + ILOG("stty/termios:erase=%s", stty_erase); + } +#endif + return stty_erase; +} + +/// libtermkey hook to override terminfo entries. +/// @see TermInput.tk_ti_hook_fn +static const char *tui_tk_ti_getstr(const char *name, const char *value, + void *data) +{ + static const char *stty_erase = NULL; + if (stty_erase == NULL) { + stty_erase = tui_get_stty_erase(); + } + + if (strcmp(name, "key_backspace") == 0) { + ILOG("libtermkey:kbs=%s", value); + if (stty_erase != NULL && stty_erase[0] != 0) { + return stty_erase; + } + } else if (strcmp(name, "key_dc") == 0) { + ILOG("libtermkey:kdch1=%s", value); + // Vim: "If and are now the same, redefine ." + if (stty_erase != NULL && strcmp(stty_erase, value) == 0) { + return stty_erase[0] == DEL ? (char *)CTRL_H_STR : (char *)DEL_STR; + } + } + + return value; +} + diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index f746b88276..f7e2a9510b 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -105,8 +105,8 @@ set(LUAROCKS_SHA256 cae709111c5701235770047dfd7169f66b82ae1c7b9b79207f9df0afb722 set(UNIBILIUM_URL https://github.com/mauke/unibilium/archive/v1.2.0.tar.gz) set(UNIBILIUM_SHA256 623af1099515e673abfd3cae5f2fa808a09ca55dda1c65a7b5c9424eb304ead8) -set(LIBTERMKEY_URL http://www.leonerd.org.uk/code/libtermkey/libtermkey-0.18.tar.gz) -set(LIBTERMKEY_SHA256 239746de41c845af52bb3c14055558f743292dd6c24ac26c2d6567a5a6093926) +set(LIBTERMKEY_URL http://www.leonerd.org.uk/code/libtermkey/libtermkey-0.19.tar.gz) +set(LIBTERMKEY_SHA256 c505aa4cb48c8fa59c526265576b97a19e6ebe7b7da20f4ecaae898b727b48b7) set(LIBVTERM_URL https://github.com/neovim/libvterm/archive/a9c7c6fd20fa35e0ad3e0e98901ca12dfca9c25c.tar.gz) set(LIBVTERM_SHA256 1a4272be91d9614dc183a503786df83b6584e4afaab7feaaa5409f841afbd796)