From 64f1c314016ef3d4652ed9c1615ae3bac7bd07b1 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Sun, 9 Dec 2018 21:04:22 +1100 Subject: [PATCH] Refactor parsing of keys and introduce a builtin key parser mode By setting the ncurses_builtin_key_parser ui_option to true, we can disable ncurses parsing of key strokes to get less portable parsing but support for more complex modifiers. --- doc/pages/changelog.asciidoc | 4 ++ doc/pages/options.asciidoc | 3 + src/keys.hh | 4 -- src/main.cc | 3 +- src/ncurses_ui.cc | 108 +++++++++++++++++------------------ src/ncurses_ui.hh | 1 + 6 files changed, 63 insertions(+), 60 deletions(-) diff --git a/doc/pages/changelog.asciidoc b/doc/pages/changelog.asciidoc index 58e90822..200b47e6 100644 --- a/doc/pages/changelog.asciidoc +++ b/doc/pages/changelog.asciidoc @@ -8,6 +8,10 @@ released versions. * `auto_complete` has been renamed to `autocomplete` for more consistency. +* Start of a builtin key parser in the ncurses ui bypassing + the ncurses one. Can be favored by setting the ui option + `ncurses_builtin_key_parser` to `true`. + == Kakoune 2018.10.27 * `remove-hooks` argument is now a regex and removes all diff --git a/doc/pages/options.asciidoc b/doc/pages/options.asciidoc index 043b7ba1..ea3df506 100644 --- a/doc/pages/options.asciidoc +++ b/doc/pages/options.asciidoc @@ -301,6 +301,9 @@ are exclusively available to built-in options. Function key from which shifted function key start, if the terminal sends F13 for , this should be set to 12. + *ncurses_builtin_key_parser*::: + Bypass ncurses key parser and use an internal one. + [[startup-info]] *startup_info_version* `int`:: _default_ 0 + diff --git a/src/keys.hh b/src/keys.hh index 57b4fffb..a7684f22 100644 --- a/src/keys.hh +++ b/src/keys.hh @@ -109,10 +109,6 @@ constexpr Key ctrl(Key key) { return { key.modifiers | Key::Modifiers::Control, key.key }; } -constexpr Key shift_alt(Key k) { return shift(alt(k)); } -constexpr Key shift_ctrl(Key k) { return shift(ctrl(k)); } -constexpr Key alt_ctrl(Key k) { return alt(ctrl(k)); } -constexpr Key shift_alt_ctrl(Key k) { return shift(alt(ctrl(k))); } constexpr Codepoint encode_coord(DisplayCoord coord) { return (Codepoint)(((int)coord.line << 16) | ((int)coord.column & 0x0000FFFF)); } diff --git a/src/main.cc b/src/main.cc index 43d97f52..35fc86e4 100644 --- a/src/main.cc +++ b/src/main.cc @@ -412,7 +412,8 @@ void register_options() " ncurses_change_colors bool\n" " ncurses_wheel_up_button int\n" " ncurses_wheel_down_button int\n" - " ncurses_shift_function_key int\n", + " ncurses_shift_function_key int\n" + " ncurses_builtin_key_parser bool\n", UserInterface::Options{}); reg.declare_option("modelinefmt", "format string used to generate the modeline", "%val{bufname} %val{cursor_line}:%val{cursor_char_column} {{context_info}} {{mode_info}} - %val{client}@[%val{session}]"_str); diff --git a/src/ncurses_ui.cc b/src/ncurses_ui.cc index 04d04851..1ef6bfd4 100644 --- a/src/ncurses_ui.cc +++ b/src/ncurses_ui.cc @@ -26,7 +26,6 @@ namespace Kakoune using std::min; using std::max; -using std::function; struct NCursesWin : WINDOW {}; @@ -505,7 +504,7 @@ void NCursesUI::check_resize(bool force) m_window = (NCursesWin*)newpad(ws.ws_row, ws.ws_col); kak_assert(m_window); intrflush(m_window, false); - keypad(m_window, true); + keypad(m_window, not m_builtin_key_parser); meta(m_window, true); m_dimensions = DisplayCoord{ws.ws_row-1, ws.ws_col}; @@ -549,6 +548,9 @@ Optional NCursesUI::get_next_key() const int c = wgetch(m_window); wtimeout(m_window, -1); + if (c == ERR) + return {}; + if (c == KEY_MOUSE) { MEVENT ev; @@ -579,9 +581,6 @@ Optional NCursesUI::get_next_key() } auto parse_key = [this](int c) -> Optional { - if (c == ERR) - return {}; - switch (c) { case KEY_BACKSPACE: case 127: return {Key::Backspace}; @@ -656,65 +655,58 @@ Optional NCursesUI::get_next_key() return {}; }; + constexpr auto direction = make_array({Key::Up, Key::Down, Key::Right, Key::Left, Key::Home, Key::End}); auto parse_csi = [this]() -> Optional { const Codepoint c1 = wgetch(m_window); - switch (c1) + if (c1 >= 'A' and c1 <= 'F') + return Key{direction[c1 - 'A']}; + if (c1 == '1') { - case 'I': return {Key::FocusIn}; - case 'O': return {Key::FocusOut}; - case '1': + const Codepoint c2 = wgetch(m_window); + if (c2 >= 'A' and c2 <= 'F') + return Key{direction[c2 - 'A']}; + if (c2 != ';') { - const Codepoint c2 = wgetch(m_window); - if (c2 != ';') - { - ungetch(c2); ungetch(c1); - break; - } - - const Codepoint c3 = wgetch(m_window); - function f; - switch (c3) - { - case '2': f = shift; break; - case '3': f = alt; break; - case '4': f = shift_alt; break; - case '5': f = ctrl; break; - case '6': f = shift_ctrl; break; - case '7': f = alt_ctrl; break; - case '8': f = shift_alt_ctrl; break; - } - if (!f) - { - ungetch(c3); ungetch(c2); ungetch(c1); - break; - } - - const Codepoint c4 = wgetch(m_window); - switch (c4) - { - case 'A': return f(Key::Up); - case 'B': return f(Key::Down); - case 'C': return f(Key::Right); - case 'D': return f(Key::Left); - case 'H': return f(Key::Home); - case 'F': return f(Key::End); - } - - ungetch(c4); ungetch(c3); ungetch(c2); ungetch(c1); - break; + ungetch(c2); ungetch(c1); + return {}; } - default: - ungetch(c1); - break; + const Codepoint c3 = wgetch(m_window); + if (c3 < '2' or c3 > '8') + { + ungetch(c3); ungetch(c2); ungetch(c1); + return {}; + } + const Codepoint c4 = wgetch(m_window); + if (c4 < 'A' or c4 > 'F') + { + ungetch(c4); ungetch(c3); ungetch(c2); ungetch(c1); + return {}; + } + + Key::Modifiers modifiers = Key::Modifiers::None; + const auto mask = c3 - '1'; + if (mask & 1) + modifiers |= Key::Modifiers::Shift; + if (mask & 2) + modifiers |= Key::Modifiers::Alt; + if (mask & 4) + modifiers |= Key::Modifiers::Control; + return Key{modifiers, direction[c4 - 'A']}; } + if (c1 == 'I') + return {Key::FocusIn}; + if (c1 == 'O') + return {Key::FocusOut}; + + ungetch(c1); return {}; }; - if (c == 27) + if (c < 256 and (c == 27 or (c & 0x80))) { wtimeout(m_window, 0); - const int new_c = wgetch(m_window); - if (new_c == '[') // potential CSI + const int new_c = (c & 0x80) ? (c & ~0x80) : wgetch(m_window); + if (new_c == '[' or c == 0x9b) // potential CSI { if (auto key = parse_csi()) return key; @@ -726,8 +718,6 @@ Optional NCursesUI::get_next_key() else return {Key::Escape}; } - else if (c == 0x9b) - return parse_csi(); return parse_key(c); } @@ -1283,6 +1273,14 @@ void NCursesUI::set_ui_options(const Options& options) m_wheel_down_button = wheel_down_it != options.end() ? str_to_int_ifp(wheel_down_it->value).value_or(5) : 5; } + + { + auto builtin_key_parser_it = options.find("ncurses_builtin_key_parser"_sv); + m_builtin_key_parser = builtin_key_parser_it != options.end() and + (builtin_key_parser_it->value == "yes" or + builtin_key_parser_it->value == "true"); + keypad(m_window, not m_builtin_key_parser); + } } } diff --git a/src/ncurses_ui.hh b/src/ncurses_ui.hh index 69eee61c..c16b6b99 100644 --- a/src/ncurses_ui.hh +++ b/src/ncurses_ui.hh @@ -146,6 +146,7 @@ private: bool m_set_title = true; bool m_change_colors = true; + bool m_builtin_key_parser = false; bool m_dirty = false;