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
|
* `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
|
||||||
|
|
|
@ -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 +
|
||||||
|
|
|
@ -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)); }
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user