From f2f99580f848949b35311362faf116b1ec0c90a7 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Sun, 8 Sep 2019 17:19:41 +1000 Subject: [PATCH] Get rid of ncurses based input parsing in favor of custom code --- src/main.cc | 3 +- src/ncurses_ui.cc | 306 ++++++++++++++++------------------------------ src/ncurses_ui.hh | 3 - 3 files changed, 108 insertions(+), 204 deletions(-) diff --git a/src/main.cc b/src/main.cc index bdaa4729..a9abba6f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -457,8 +457,7 @@ void register_options() " ncurses_wheel_up_button int\n" " ncurses_wheel_down_button int\n" " ncurses_wheel_scroll_amount int\n" - " ncurses_shift_function_key int\n" - " ncurses_builtin_key_parser bool\n", + " ncurses_shift_function_key int\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 16db4a6a..9cae305c 100644 --- a/src/ncurses_ui.cc +++ b/src/ncurses_ui.cc @@ -144,16 +144,6 @@ void NCursesUI::Window::set_background_color(Palette& palette, Face face) wbkgdset(win, COLOR_PAIR(palette.get_color_pair(face))); } -int NCursesUI::Window::get_char() -{ - return wgetch(win); -} - -void NCursesUI::Window::set_blocking(bool blocking) -{ - wtimeout(win, blocking ? -1 : 0); -} - constexpr int NCursesUI::default_shift_function_key; static constexpr StringView assistant_cat[] = @@ -385,6 +375,8 @@ NCursesUI::NCursesUI() enable_mouse(true); + fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) | O_NONBLOCK); + set_signal_handler(SIGWINCH, &signal_handler<&resize_pending>); set_signal_handler(SIGHUP, &signal_handler<&sighup_raised>); @@ -552,7 +544,6 @@ void NCursesUI::check_resize(bool force) m_window.create({0, 0}, {ws.ws_row, ws.ws_col}); kak_assert(m_window); - keypad(m_window.win, not m_builtin_key_parser); m_dimensions = DisplayCoord{ws.ws_row-1, ws.ws_col}; @@ -591,133 +582,61 @@ Optional NCursesUI::get_next_key() return resize(dimensions()); } - m_window.set_blocking(false); - const int c = m_window.get_char(); - m_window.set_blocking(true); - - if (c == ERR) - return {}; - - if (c == KEY_MOUSE) - { - MEVENT ev; - if (getmouse(&ev) == OK) - { - const auto mask = ev.bstate; - auto coord = encode_coord({ ev.y - content_line_offset(), ev.x }); - - Key::Modifiers mod{}; - if (mask & BUTTON_CTRL) - mod |= Key::Modifiers::Control; - if (mask & BUTTON_ALT) - mod |= Key::Modifiers::Alt; - - if (BUTTON_PRESS(mask, 1)) - return Key{mod | Key::Modifiers::MousePressLeft, coord}; - if (BUTTON_PRESS(mask, 3)) - return Key{mod | Key::Modifiers::MousePressRight, coord}; - if (BUTTON_RELEASE(mask, 1)) - return Key{mod | Key::Modifiers::MouseReleaseLeft, coord}; - if (BUTTON_RELEASE(mask, 3)) - return Key{mod | Key::Modifiers::MouseReleaseRight, coord}; - if (BUTTON_PRESS(mask, m_wheel_down_button)) - return Key{mod | Key::Modifiers::Scroll, static_cast(m_wheel_scroll_amount)}; - if (BUTTON_PRESS(mask, m_wheel_up_button)) - return Key{mod | Key::Modifiers::Scroll, static_cast(-m_wheel_scroll_amount)}; - return Key{mod | Key::Modifiers::MousePos, coord}; - } - } - - auto parse_key = [this](int c) -> Optional { - switch (c) - { - case KEY_BACKSPACE: case 127: return {Key::Backspace}; - case KEY_DC: return {Key::Delete}; - case KEY_SDC: return shift(Key::Delete); - case KEY_UP: return {Key::Up}; - case KEY_SR: return shift(Key::Up); - case KEY_DOWN: return {Key::Down}; - case KEY_SF: return shift(Key::Down); - case KEY_LEFT: return {Key::Left}; - case KEY_SLEFT: return shift(Key::Left); - case KEY_RIGHT: return {Key::Right}; - case KEY_SRIGHT: return shift(Key::Right); - case KEY_PPAGE: return {Key::PageUp}; - case KEY_SPREVIOUS: return shift(Key::PageUp); - case KEY_NPAGE: return {Key::PageDown}; - case KEY_SNEXT: return shift(Key::PageDown); - case KEY_HOME: return {Key::Home}; - case KEY_SHOME: return shift(Key::Home); - case KEY_END: return {Key::End}; - case KEY_SEND: return shift(Key::End); - case KEY_IC: return {Key::Insert}; - case KEY_SIC: return shift(Key::Insert); - case KEY_BTAB: return shift(Key::Tab); - case KEY_RESIZE: return resize(dimensions()); - } - - if (c > 0 and c < 27) - { - if (c == control('m') or c == control('j')) - return {Key::Return}; - if (c == control('i')) - return {Key::Tab}; - if (c == control('h')) - return {Key::Backspace}; - if (c == control('z')) - { - bool mouse_enabled = m_mouse_enabled; - enable_mouse(false); - - kill(0, SIGTSTP); // We suspend at this line - - check_resize(true); - enable_mouse(mouse_enabled); - return {}; - } - return ctrl(Codepoint(c) - 1 + 'a'); - } - - for (int i = 0; i < 12; ++i) - { - if (c == KEY_F(i + 1)) - return {Key::F1 + i}; - if (c == KEY_F(m_shift_function_key + i + 1)) - return shift(Key::F1 + i); - } - - if (c >= 0 and c < 256) - { - ungetch(c); - struct getch_iterator - { - getch_iterator(Window& win) : window(win) {} - int operator*() { return window.get_char(); } - getch_iterator& operator++() { return *this; } - getch_iterator& operator++(int) { return *this; } - bool operator== (const getch_iterator&) const { return false; } - - Window& window; - }; - return Key{utf8::codepoint(getch_iterator{m_window}, - getch_iterator{m_window})}; - } + static auto get_char = []() -> Optional { + unsigned char c = 0; + if (read(0, &c, 1) == 1) + return c; return {}; }; - constexpr auto direction = make_array({Key::Up, Key::Down, Key::Right, Key::Left, Key::Home, Key::End}); - constexpr auto special = make_array({Key::Insert, Key::Delete, Key::NamedKey{}, Key::PageUp, Key::PageDown, Key::Home, Key::End, Key::NamedKey{}, Key::NamedKey{}, - Key::F1, Key::F2, Key::F3, Key::F4, Key::NamedKey{}, Key::F5, Key::F6, Key::F7, Key::F8, Key::F9, Key::F10, Key::NamedKey{}, Key::F11, Key::F12}); - auto parse_csi = [this, &direction, &special]() -> Optional { + const auto c = get_char(); + if (not c) + return {}; + + auto parse_key = [this](unsigned char c) -> Key { + if (c == control('m') or c == control('j')) + return {Key::Return}; + if (c == control('i')) + return {Key::Tab}; + if (c == control('h')) + return {Key::Backspace}; + if (c == control('z')) + { + bool mouse_enabled = m_mouse_enabled; + enable_mouse(false); + + kill(0, SIGTSTP); // We suspend at this line + + check_resize(true); + enable_mouse(mouse_enabled); + return {}; + } + if (c < 27) + return ctrl(Codepoint(c) - 1 + 'a'); + if (c == 127) + return {Key::Backspace}; + + struct getch_iterator + { + unsigned char operator*() { return *c; } + getch_iterator& operator++() { c = get_char(); return *this; } + bool operator==(const getch_iterator&) const { return not c; } + Optional c; + }; + return Key{utf8::codepoint(getch_iterator{c}, getch_iterator{})}; + }; + + auto parse_csi = [this]() -> Optional { + auto next_char = [] { return get_char().value_or((unsigned char)0xff); }; int params[16] = {}; - int c = m_window.get_char(); + auto c = next_char(); char private_mode = 0; if (c == '?' or c == '<' or c == '=' or c == '>') { private_mode = c; - c = m_window.get_char(); + c = next_char(); } - for (int count = 0; count < 16 and c >= 0x30 && c <= 0x3f; c = m_window.get_char()) + for (int count = 0; count < 16 and c >= 0x30 && c <= 0x3f; c = next_char()) { if ('0' <= 'c' and c <= '9') params[count] = params[count] * 10 + c - '0'; @@ -730,7 +649,6 @@ Optional NCursesUI::get_next_key() return {}; auto parse_mask = [](int mask) { - mask = std::max(0, mask - 1); Key::Modifiers mod = Key::Modifiers::None; if (mask & 1) mod |= Key::Modifiers::Shift; @@ -756,83 +674,88 @@ Optional NCursesUI::get_next_key() return Key{mod, coord}; }; - if (c >= 'A' and c <= 'F') - return Key{parse_mask(params[1]), direction[c - 'A']}; - if (c == '~' and 2 <= params[0] and params[0] <= 24) - return Key{parse_mask(params[1]), special[params[0] - 2]}; - if (c == 'Z') - return shift(Key::Tab); - if (c == 'I') - return {Key::FocusIn}; - if (c == 'O') - return {Key::FocusOut}; - if ((c == 'M' or c == 'm') and private_mode == '<') + auto mouse_scroll = [this](Key::Modifiers mod, bool down) -> Key { + return {mod | Key::Modifiers::Scroll, + (Codepoint)((down ? 1 : -1) * m_wheel_scroll_amount)}; + }; + + auto masked_key = [&](Codepoint key) { return Key{parse_mask(std::max(params[1] - 1, 0)), key}; }; + + switch (c) { - auto coord = encode_coord({params[2] - content_line_offset() - 1, params[1] - 1}); - Key::Modifiers mod = parse_mask(1 + ((params[0] >> 2) & 0x7)); - switch (params[0] & 0x43) + case 'A': return masked_key(Key::Up); + case 'B': return masked_key(Key::Down); + case 'C': return masked_key(Key::Right); + case 'D': return masked_key(Key::Left); + case 'F': return masked_key(Key::End); + case 'H': return masked_key(Key::Home); + case 'P': return masked_key(Key::F1); + case 'Q': return masked_key(Key::F2); + case 'R': return masked_key(Key::F3); + case 'S': return masked_key(Key::F4); + case '~': + switch (params[0]) { - case 0: - return mouse_button(mod, coord, true, c == 'm'); - case 2: - return mouse_button(mod, coord, false, c == 'm'); - case 64: - return Key{mod | Key::Modifiers::Scroll, - static_cast(-m_wheel_scroll_amount)}; - case 65: - return Key{mod | Key::Modifiers::Scroll, - static_cast(m_wheel_scroll_amount)}; + case 2: return masked_key(Key::Insert); + case 3: return masked_key(Key::Delete); + case 5: return masked_key(Key::PageUp); + case 6: return masked_key(Key::PageDown); + case 7: return masked_key(Key::Home); + case 8: return masked_key(Key::End); + case 11: case 12: case 13: case 14: case 15: + return masked_key(Key::F1 + params[0] - 11); + case 17: case 18: case 19: case 20: case 21: + return masked_key(Key::F6 + params[0] - 17); + case 23: case 24: + return masked_key(Key::F11 + params[0] - 23); } - } - if (c == 'M') - { - const Codepoint b = m_window.get_char() - 32; - const int x = m_window.get_char() - 32 - 1; - const int y = m_window.get_char() - 32 - 1; + return {}; + case 'Z': return shift(Key::Tab); + case 'I': return {Key::FocusIn}; + case 'O': return {Key::FocusOut}; + case 'M': case 'm': + const bool sgr = private_mode == '<'; + if (not sgr and c != 'M') + return {}; + + const Codepoint b = sgr ? params[0] : next_char() - 32; + const int x = (sgr ? params[1] : next_char() - 32) - 1; + const int y = (sgr ? params[2] : next_char() - 32) - 1; auto coord = encode_coord({y - content_line_offset(), x}); - Key::Modifiers mod = parse_mask(1 + ((b >> 2) & 0x7)); + Key::Modifiers mod = parse_mask((b >> 2) & 0x7); switch (b & 0x43) { - case 0: - return mouse_button(mod, coord, true, false); - case 2: - return mouse_button(mod, coord, false, false); + case 0: return mouse_button(mod, coord, true, c == 'm'); + case 2: return mouse_button(mod, coord, false, c == 'm'); case 3: + if (sgr) + return {}; if (m_mouse_state & 0x1) return mouse_button(mod, coord, true, true); else if (m_mouse_state & 0x2) return mouse_button(mod, coord, false, true); break; - case 64: - return Key{mod | Key::Modifiers::Scroll, - static_cast(-m_wheel_scroll_amount)}; - case 65: - return Key{mod | Key::Modifiers::Scroll, - static_cast(m_wheel_scroll_amount)}; + case 64: return mouse_scroll(mod, false); + case 65: return mouse_scroll(mod, true); } return Key{Key::Modifiers::MousePos, coord}; } return {}; }; - if (c == 27) + if (*c != 27) + return parse_key(*c); + + if (auto next = get_char()) { - m_window.set_blocking(false); - const int new_c = m_window.get_char(); - if (new_c == '[') // potential CSI + if (*next == '[') // potential CSI { if (auto key = parse_csi()) return key; } - m_window.set_blocking(true); - - if (auto key = parse_key(new_c)) - return alt(*key); - else - return {Key::Escape}; + return alt(parse_key(*next)); } - - return parse_key(c); + return Key{Key::Escape}; } template @@ -1300,11 +1223,8 @@ void NCursesUI::enable_mouse(bool enabled) m_mouse_enabled = enabled; if (enabled) { - mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, nullptr); - mouseinterval(0); // force SGR mode - if (m_builtin_key_parser) - fputs("\033[?1006h", stdout); + fputs("\033[?1006h", stdout); // force enable report focus events fputs("\033[?1004h", stdout); // enable mouse @@ -1314,7 +1234,6 @@ void NCursesUI::enable_mouse(bool enabled) } else { - mousemask(0, nullptr); fputs("\033[?1002l", stdout); fputs("\033[?1000l", stdout); fputs("\033[?1004l", stdout); @@ -1385,17 +1304,6 @@ void NCursesUI::set_ui_options(const Options& options) m_wheel_scroll_amount = wheel_scroll_amount_it != options.end() ? str_to_int_ifp(wheel_scroll_amount_it->value).value_or(3) : 3; } - - { - auto builtin_key_parser_it = options.find("ncurses_builtin_key_parser"_sv); - bool mouse_enabled = m_mouse_enabled; - enable_mouse(false); - 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.win, not m_builtin_key_parser); - enable_mouse(mouse_enabled); - } } } diff --git a/src/ncurses_ui.hh b/src/ncurses_ui.hh index 35a98aa8..ae25361c 100644 --- a/src/ncurses_ui.hh +++ b/src/ncurses_ui.hh @@ -105,8 +105,6 @@ private: void clear_to_end_of_line(); void set_face(Palette& palette, Face face, const Face& default_face); void set_background_color(Palette& palette, Face face); - int get_char(); - void set_blocking(bool blocking); explicit operator bool() const { return win; } @@ -169,7 +167,6 @@ private: int m_shift_function_key = default_shift_function_key; bool m_set_title = true; - bool m_builtin_key_parser = false; bool m_dirty = false;