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.
This commit is contained in:
Maxime Coste 2018-12-09 21:04:22 +11:00
parent 1670a7514a
commit 64f1c31401
6 changed files with 63 additions and 60 deletions

View File

@ -8,6 +8,10 @@ released versions.
* `auto_complete` has been renamed to `autocomplete` for more * `auto_complete` has been renamed to `autocomplete` for more
consistency. 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 == Kakoune 2018.10.27
* `remove-hooks` <group> argument is now a regex and removes all * `remove-hooks` <group> argument is now a regex and removes all

View File

@ -301,6 +301,9 @@ are exclusively available to built-in options.
Function key from which shifted function key start, if the Function key from which shifted function key start, if the
terminal sends F13 for <s-F1>, this should be set to 12. terminal sends F13 for <s-F1>, this should be set to 12.
*ncurses_builtin_key_parser*:::
Bypass ncurses key parser and use an internal one.
[[startup-info]] [[startup-info]]
*startup_info_version* `int`:: *startup_info_version* `int`::
_default_ 0 + _default_ 0 +

View File

@ -109,10 +109,6 @@ constexpr Key ctrl(Key key)
{ {
return { key.modifiers | Key::Modifiers::Control, 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)); } constexpr Codepoint encode_coord(DisplayCoord coord) { return (Codepoint)(((int)coord.line << 16) | ((int)coord.column & 0x0000FFFF)); }

View File

@ -412,7 +412,8 @@ void register_options()
" ncurses_change_colors bool\n" " ncurses_change_colors bool\n"
" ncurses_wheel_up_button int\n" " ncurses_wheel_up_button int\n"
" ncurses_wheel_down_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{}); UserInterface::Options{});
reg.declare_option("modelinefmt", "format string used to generate the modeline", 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); "%val{bufname} %val{cursor_line}:%val{cursor_char_column} {{context_info}} {{mode_info}} - %val{client}@[%val{session}]"_str);

View File

@ -26,7 +26,6 @@ namespace Kakoune
using std::min; using std::min;
using std::max; using std::max;
using std::function;
struct NCursesWin : WINDOW {}; struct NCursesWin : WINDOW {};
@ -505,7 +504,7 @@ void NCursesUI::check_resize(bool force)
m_window = (NCursesWin*)newpad(ws.ws_row, ws.ws_col); m_window = (NCursesWin*)newpad(ws.ws_row, ws.ws_col);
kak_assert(m_window); kak_assert(m_window);
intrflush(m_window, false); intrflush(m_window, false);
keypad(m_window, true); keypad(m_window, not m_builtin_key_parser);
meta(m_window, true); meta(m_window, true);
m_dimensions = DisplayCoord{ws.ws_row-1, ws.ws_col}; m_dimensions = DisplayCoord{ws.ws_row-1, ws.ws_col};
@ -549,6 +548,9 @@ Optional<Key> NCursesUI::get_next_key()
const int c = wgetch(m_window); const int c = wgetch(m_window);
wtimeout(m_window, -1); wtimeout(m_window, -1);
if (c == ERR)
return {};
if (c == KEY_MOUSE) if (c == KEY_MOUSE)
{ {
MEVENT ev; MEVENT ev;
@ -579,9 +581,6 @@ Optional<Key> NCursesUI::get_next_key()
} }
auto parse_key = [this](int c) -> Optional<Key> { auto parse_key = [this](int c) -> Optional<Key> {
if (c == ERR)
return {};
switch (c) switch (c)
{ {
case KEY_BACKSPACE: case 127: return {Key::Backspace}; case KEY_BACKSPACE: case 127: return {Key::Backspace};
@ -656,65 +655,58 @@ Optional<Key> NCursesUI::get_next_key()
return {}; return {};
}; };
constexpr auto direction = make_array({Key::Up, Key::Down, Key::Right, Key::Left, Key::Home, Key::End});
auto parse_csi = [this]() -> Optional<Key> { auto parse_csi = [this]() -> Optional<Key> {
const Codepoint c1 = wgetch(m_window); 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}; const Codepoint c2 = wgetch(m_window);
case 'O': return {Key::FocusOut}; if (c2 >= 'A' and c2 <= 'F')
case '1': return Key{direction[c2 - 'A']};
if (c2 != ';')
{ {
const Codepoint c2 = wgetch(m_window); ungetch(c2); ungetch(c1);
if (c2 != ';') return {};
{
ungetch(c2); ungetch(c1);
break;
}
const Codepoint c3 = wgetch(m_window);
function<Key(Key)> 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;
} }
default: const Codepoint c3 = wgetch(m_window);
ungetch(c1); if (c3 < '2' or c3 > '8')
break; {
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 {}; return {};
}; };
if (c == 27) if (c < 256 and (c == 27 or (c & 0x80)))
{ {
wtimeout(m_window, 0); wtimeout(m_window, 0);
const int new_c = wgetch(m_window); const int new_c = (c & 0x80) ? (c & ~0x80) : wgetch(m_window);
if (new_c == '[') // potential CSI if (new_c == '[' or c == 0x9b) // potential CSI
{ {
if (auto key = parse_csi()) if (auto key = parse_csi())
return key; return key;
@ -726,8 +718,6 @@ Optional<Key> NCursesUI::get_next_key()
else else
return {Key::Escape}; return {Key::Escape};
} }
else if (c == 0x9b)
return parse_csi();
return parse_key(c); 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() ? m_wheel_down_button = wheel_down_it != options.end() ?
str_to_int_ifp(wheel_down_it->value).value_or(5) : 5; 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);
}
} }
} }

View File

@ -146,6 +146,7 @@ private:
bool m_set_title = true; bool m_set_title = true;
bool m_change_colors = true; bool m_change_colors = true;
bool m_builtin_key_parser = false;
bool m_dirty = false; bool m_dirty = false;