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:
parent
1670a7514a
commit
64f1c31401
|
@ -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` <group> argument is now a regex and removes all
|
||||
|
|
|
@ -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 <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_version* `int`::
|
||||
_default_ 0 +
|
||||
|
|
|
@ -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)); }
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<Key> 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<Key> NCursesUI::get_next_key()
|
|||
}
|
||||
|
||||
auto parse_key = [this](int c) -> Optional<Key> {
|
||||
if (c == ERR)
|
||||
return {};
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case KEY_BACKSPACE: case 127: return {Key::Backspace};
|
||||
|
@ -656,65 +655,58 @@ Optional<Key> 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<Key> {
|
||||
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<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;
|
||||
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<Key> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user