From d3374e7e5f7af9f4fdf33200f91847b3cddf9e67 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Sun, 28 Jun 2020 19:48:55 +1000 Subject: [PATCH] Refactor mouse press/release handling to support 3 buttons Change button to be an additional parameter instead of having separate events for left/right buttons. Fixes #3471 --- doc/json_ui.asciidoc | 11 ++++----- src/input_handler.cc | 53 ++++++++++++++++++++++++-------------------- src/json_ui.cc | 41 +++++++++++++++++----------------- src/keys.cc | 33 +++++++++++++++++++-------- src/keys.hh | 20 ++++++++++++----- src/ncurses_ui.cc | 23 +++++++++---------- 6 files changed, 103 insertions(+), 78 deletions(-) diff --git a/doc/json_ui.asciidoc b/doc/json_ui.asciidoc index e8dcad09..0fc0378b 100644 --- a/doc/json_ui.asciidoc +++ b/doc/json_ui.asciidoc @@ -59,11 +59,8 @@ The requests that the json ui can interpret on stdin are: * keys(String key1, String key2...): keystrokes * resize(int rows, int columns): notify ui resize * scroll(int amount): scroll by given line amount -* mouse(String type, int line, int column): mouse event. line and column relate to - the cursor position. type can be: - - 'move' - - 'press_left' - - 'press_right' - - 'release_left' - - 'release_right' +* mouse_move(int line, int column): line and column relate to the cursor position. +* mouse_press(String button, int line, int column): line and column relate to + cursor position, button can be 'left', 'middle' or 'right' +* mouse_release(String button, int line, int column): same. * menu_select(int index): explicit select of given menu entry diff --git a/src/input_handler.cc b/src/input_handler.cc index 2773c482..f13c513e 100644 --- a/src/input_handler.cc +++ b/src/input_handler.cc @@ -92,35 +92,40 @@ struct MouseHandler Buffer& buffer = context.buffer(); BufferCoord cursor; auto& selections = context.selections(); - constexpr auto modifiers = Key::Modifiers::Control | Key::Modifiers::Alt | Key::Modifiers::Shift; + constexpr auto modifiers = Key::Modifiers::Control | Key::Modifiers::Alt | Key::Modifiers::Shift | Key::Modifiers::MouseButtonMask; switch ((key.modifiers & ~modifiers).value) { - case Key::Modifiers::MousePressRight: - m_dragging = false; - cursor = context.window().buffer_coord(key.coord()); - if (key.modifiers & Key::Modifiers::Control) - selections = {{selections.begin()->anchor(), cursor}}; - else - selections.main() = {selections.main().anchor(), cursor}; - selections.sort_and_merge_overlapping(); - return true; - - case Key::Modifiers::MousePressLeft: - m_dragging = true; - m_anchor = context.window().buffer_coord(key.coord()); - if (not (key.modifiers & Key::Modifiers::Control)) - context.selections_write_only() = { buffer, m_anchor}; - else + case Key::Modifiers::MousePress: + switch (key.mouse_button()) { - size_t main = selections.size(); - selections.push_back({m_anchor}); - selections.set_main_index(main); + case Key::MouseButton::Right: + m_dragging = false; + cursor = context.window().buffer_coord(key.coord()); + if (key.modifiers & Key::Modifiers::Control) + selections = {{selections.begin()->anchor(), cursor}}; + else + selections.main() = {selections.main().anchor(), cursor}; selections.sort_and_merge_overlapping(); - } - return true; + return true; - case Key::Modifiers::MouseReleaseLeft: - case Key::Modifiers::MouseReleaseRight: + case Key::MouseButton::Left: + m_dragging = true; + m_anchor = context.window().buffer_coord(key.coord()); + if (not (key.modifiers & Key::Modifiers::Control)) + context.selections_write_only() = { buffer, m_anchor}; + else + { + size_t main = selections.size(); + selections.push_back({m_anchor}); + selections.set_main_index(main); + selections.sort_and_merge_overlapping(); + } + return true; + + default: return true; + } + + case Key::Modifiers::MouseRelease: if (not m_dragging) return true; m_dragging = false; diff --git a/src/json_ui.cc b/src/json_ui.cc index b4c6528c..007c8ab0 100644 --- a/src/json_ui.cc +++ b/src/json_ui.cc @@ -248,31 +248,30 @@ void JsonUI::eval_json(const Value& json) m_on_key(key); } } - else if (method == "mouse") + else if (method == "mouse_move") { - if (params.size() != 3) - throw invalid_rpc_request("mouse type/coordinates not specified"); + if (params.size() != 2) + throw invalid_rpc_request("mouse coordinates not specified"); - if (not params[0].is_a()) - throw invalid_rpc_request("mouse type is not a string"); - else if (not params[1].is_a() or - not params[2].is_a()) + if (not params[0].is_a() or not params[1].is_a()) throw invalid_rpc_request("mouse coordinates are not integers"); - const StringView type = params[0].as(); - const Codepoint coord = encode_coord({params[1].as(), params[2].as()}); - if (type == "move") - m_on_key({Key::Modifiers::MousePos, coord}); - else if (type == "press_left") - m_on_key({Key::Modifiers::MousePressLeft, coord}); - else if (type == "press_right") - m_on_key({Key::Modifiers::MousePressRight, coord}); - else if (type == "release_left") - m_on_key({Key::Modifiers::MouseReleaseLeft, coord}); - else if (type == "release_right") - m_on_key({Key::Modifiers::MouseReleaseRight, coord}); - else - throw invalid_rpc_request(format("invalid mouse event type: {}", type)); + m_on_key({Key::Modifiers::MousePos, encode_coord({params[0].as(), params[1].as()})}); + } + else if (method == "mouse_press" or method == "mouse_release") + { + if (params.size() != 3) + throw invalid_rpc_request("mouse button/coordinates not specified"); + + if (not params[0].is_a()) + throw invalid_rpc_request("mouse button is not a string"); + if (not params[1].is_a() or not params[2].is_a()) + throw invalid_rpc_request("mouse coordinates are not integers"); + + auto event = method == "mouse_press" ? Key::Modifiers::MousePress : Key::Modifiers::MouseRelease; + auto button = str_to_button(params[0].as()); + + m_on_key({event | Key::to_modifier(button), encode_coord({params[1].as(), params[2].as()})}); } else if (method == "scroll") { diff --git a/src/keys.cc b/src/keys.cc index 90cb0310..29fa9ce1 100644 --- a/src/keys.cc +++ b/src/keys.cc @@ -157,21 +157,36 @@ KeyList parse_keys(StringView str) return result; } +StringView button_to_str(Key::MouseButton button) +{ + switch (button) + { + case Key::MouseButton::Left: return "left"; + case Key::MouseButton::Middle: return "middle"; + case Key::MouseButton::Right: return "right"; + default: kak_assert(false); throw logic_error{}; + } +} + +Key::MouseButton str_to_button(StringView str) +{ + if (str == "left") return Key::MouseButton::Left; + if (str == "middle") return Key::MouseButton::Middle; + if (str == "right") return Key::MouseButton::Right; + throw runtime_error(format("invalid mouse button name {}", str)); +} + String key_to_str(Key key) { const auto coord = key.coord() + DisplayCoord{1,1}; - switch (key.modifiers) + switch (Key::Modifiers(key.modifiers & ~Key::Modifiers::MouseButtonMask)) { case Key::Modifiers::MousePos: return format("", coord.line, coord.column); - case Key::Modifiers::MousePressLeft: - return format("", coord.line, coord.column); - case Key::Modifiers::MousePressRight: - return format("", coord.line, coord.column); - case Key::Modifiers::MouseReleaseLeft: - return format("", coord.line, coord.column); - case Key::Modifiers::MouseReleaseRight: - return format("", coord.line, coord.column); + case Key::Modifiers::MousePress: + return format("", button_to_str(key.mouse_button()), coord.line, coord.column); + case Key::Modifiers::MouseRelease: + return format("", button_to_str(key.mouse_button()), coord.line, coord.column); case Key::Modifiers::Scroll: return format("", static_cast(key.key)); case Key::Modifiers::Resize: diff --git a/src/keys.hh b/src/keys.hh index 84707faf..0f5fc0e7 100644 --- a/src/keys.hh +++ b/src/keys.hh @@ -14,6 +14,12 @@ namespace Kakoune struct Key { + enum class MouseButton + { + Left, + Middle, + Right + }; enum class Modifiers : int { None = 0, @@ -21,11 +27,11 @@ struct Key Alt = 1 << 1, Shift = 1 << 2, - MousePressLeft = 1 << 3, - MousePressRight = 1 << 4, - MouseReleaseLeft = 1 << 5, - MouseReleaseRight = 1 << 6, - MousePos = 1 << 7, + MousePress = 1 << 3, + MouseRelease = 1 << 4, + MousePos = 1 << 5, + MouseButtonMask= 0b11 << 6, + Scroll = 1 << 8, Resize = 1 << 9, MenuSelect = 1 << 10, @@ -82,6 +88,8 @@ struct Key constexpr bool operator<(Key other) const { return val() < other.val(); } constexpr DisplayCoord coord() const { return {(int)((key & 0xFFFF0000) >> 16), (int)(key & 0x0000FFFF)}; } + constexpr MouseButton mouse_button() { return MouseButton{((int)modifiers & (int)Modifiers::MouseButtonMask) >> 6}; } + static Modifiers to_modifier(MouseButton button) { return Key::Modifiers{((int)button << 6) & (int)Modifiers::MouseButtonMask}; } Optional codepoint() const; }; @@ -95,6 +103,8 @@ class StringView; KeyList parse_keys(StringView str); String key_to_str(Key key); +StringView button_to_str(Key::MouseButton button); +Key::MouseButton str_to_button(StringView str); constexpr Key shift(Key key) { diff --git a/src/ncurses_ui.cc b/src/ncurses_ui.cc index 0f37abfd..f54a244a 100644 --- a/src/ncurses_ui.cc +++ b/src/ncurses_ui.cc @@ -19,6 +19,7 @@ #include #include #include +#include constexpr char control(char c) { return c & 037; } @@ -669,19 +670,19 @@ Optional NCursesUI::get_next_key() return mod; }; - auto mouse_button = [this](Key::Modifiers mod, Codepoint coord, bool left, bool release) { - auto mask = left ? 0x1 : 0x2; + auto mouse_button = [this](Key::Modifiers mod, Key::MouseButton button, Codepoint coord, bool release) { + auto mask = 1 << (int)button; if (not release) { - mod |= (m_mouse_state & mask) ? Key::Modifiers::MousePos : (left ? Key::Modifiers::MousePressLeft : Key::Modifiers::MousePressRight); + mod |= (m_mouse_state & mask) ? Key::Modifiers::MousePos : Key::Modifiers::MousePress; m_mouse_state |= mask; } else { - mod |= left ? Key::Modifiers::MouseReleaseLeft : Key::Modifiers::MouseReleaseRight; + mod |= Key::Modifiers::MouseRelease; m_mouse_state &= ~mask; } - return Key{mod, coord}; + return Key{mod | Key::to_modifier(button), coord}; }; auto mouse_scroll = [this](Key::Modifiers mod, bool down) -> Key { @@ -752,17 +753,15 @@ Optional NCursesUI::get_next_key() const int y = (sgr ? params[2] : next_char() - 32) - 1; auto coord = encode_coord({y - content_line_offset(), x}); Key::Modifiers mod = parse_mask((b >> 2) & 0x7); - switch (b & 0x43) + switch (auto code = b & 0x43; code) { - case 0: return mouse_button(mod, coord, true, c == 'm'); - case 2: return mouse_button(mod, coord, false, c == 'm'); + case 0: case 1: case 2: + return mouse_button(mod, Key::MouseButton{code}, coord, 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); + else if (int guess = ffs(m_mouse_state) - 1; 0 <= guess and guess < 3) + return mouse_button(mod, Key::MouseButton{guess}, coord, true); break; case 64: return mouse_scroll(mod, false); case 65: return mouse_scroll(mod, true);