diff --git a/src/client.cc b/src/client.cc index 6400df48..55fba617 100644 --- a/src/client.cc +++ b/src/client.cc @@ -37,7 +37,12 @@ Client::Client(std::unique_ptr&& ui, m_window->options().register_watcher(*this); m_ui->set_ui_options(m_window->options()["ui_options"].get()); - m_ui->set_input_callback([this](EventMode mode) { handle_available_input(mode); }); + m_ui->set_on_key([this](Key key) { + if (key == ctrl('c')) + killpg(getpgrp(), SIGINT); + else + m_pending_keys.push_back(key); + }); m_window->hooks().run_hook("WinDisplay", m_window->buffer().name(), context()); @@ -50,54 +55,31 @@ Client::~Client() m_window->set_client(nullptr); } -Optional Client::get_next_key(EventMode mode) +void Client::process_pending_inputs() { - if (not m_pending_keys.empty()) - { - Key key = m_pending_keys.front(); - m_pending_keys.erase(m_pending_keys.begin()); - return key; - } - if (mode != EventMode::Pending and m_ui->is_key_available()) - return m_ui->get_key(); - return {}; -} - -void Client::handle_available_input(EventMode mode) -{ - if (mode == EventMode::Urgent) - { - Key key = m_ui->get_key(); - if (key == ctrl('c')) - killpg(getpgrp(), SIGINT); - else - m_pending_keys.push_back(key); - return; - } - try { const bool debug_keys = (bool)(context().options()["debug"].get() & DebugFlags::Keys); - while (Optional key = get_next_key(mode)) + // steal keys as we might receive new keys while handling them. + Vector keys = std::move(m_pending_keys); + for (auto& key : keys) { if (debug_keys) write_to_debug_buffer(format("Client '{}' got key '{}'", - context().name(), key_to_str(*key))); + context().name(), key_to_str(key))); - if (*key == ctrl('c')) - killpg(getpgrp(), SIGINT); - else if (*key == Key::FocusIn) + if (key == Key::FocusIn) context().hooks().run_hook("FocusIn", context().name(), context()); - else if (*key == Key::FocusOut) + else if (key == Key::FocusOut) context().hooks().run_hook("FocusOut", context().name(), context()); - else if (key->modifiers == Key::Modifiers::Resize) + else if (key.modifiers == Key::Modifiers::Resize) { m_window->set_dimensions(m_ui->dimensions()); force_redraw(); } else - m_input_handler.handle_key(*key); + m_input_handler.handle_key(key); } } catch (Kakoune::runtime_error& error) diff --git a/src/client.hh b/src/client.hh index d2cf0c3d..2ee7a867 100644 --- a/src/client.hh +++ b/src/client.hh @@ -34,8 +34,7 @@ public: Client(Client&&) = delete; - // handle all the keys currently available in the user interface - void handle_available_input(EventMode mode); + void process_pending_inputs(); void menu_show(Vector choices, BufferCoord anchor, MenuStyle style); void menu_select(int selected); diff --git a/src/client_manager.cc b/src/client_manager.cc index 72a3eb28..a77d7d13 100644 --- a/src/client_manager.cc +++ b/src/client_manager.cc @@ -61,10 +61,10 @@ Client* ClientManager::create_client(std::unique_ptr&& ui, return contains(m_clients, client) ? client : nullptr; } -void ClientManager::handle_pending_inputs() const +void ClientManager::process_pending_inputs() const { for (auto& client : m_clients) - client->handle_available_input(EventMode::Pending); + client->process_pending_inputs(); } void ClientManager::remove_client(Client& client, bool graceful) diff --git a/src/client_manager.hh b/src/client_manager.hh index cb05d83e..de99fceb 100644 --- a/src/client_manager.hh +++ b/src/client_manager.hh @@ -34,7 +34,7 @@ public: void add_free_window(std::unique_ptr&& window, SelectionList selections); void redraw_clients() const; - void handle_pending_inputs() const; + void process_pending_inputs() const; Client* get_client_ifp(StringView name); Client& get_client(StringView name); diff --git a/src/event_manager.hh b/src/event_manager.hh index 49edb1af..5121b324 100644 --- a/src/event_manager.hh +++ b/src/event_manager.hh @@ -18,7 +18,6 @@ enum class EventMode { Normal, Urgent, - Pending }; class FDWatcher diff --git a/src/json_ui.cc b/src/json_ui.cc index f24b78c5..0a82e07e 100644 --- a/src/json_ui.cc +++ b/src/json_ui.cc @@ -184,18 +184,6 @@ void JsonUI::draw_status(const DisplayLine& status_line, rpc_call("draw_status", status_line, mode_line, default_face); } -bool JsonUI::is_key_available() -{ - return not m_pending_keys.empty(); -} - -Key JsonUI::get_key() -{ - kak_assert(not m_pending_keys.empty()); - Key key = m_pending_keys.front(); - m_pending_keys.erase(m_pending_keys.begin()); - return key; -} void JsonUI::menu_show(ConstArrayView items, DisplayCoord anchor, Face fg, Face bg, @@ -231,11 +219,6 @@ void JsonUI::refresh(bool force) rpc_call("refresh", force); } -void JsonUI::set_input_callback(InputCallback callback) -{ - m_input_callback = std::move(callback); -} - void JsonUI::set_ui_options(const Options& options) { // rpc_call("set_ui_options", options); @@ -246,6 +229,11 @@ DisplayCoord JsonUI::dimensions() return m_dimensions; } +void JsonUI::set_on_key(OnKeyCallback callback) +{ + m_on_key = std::move(callback); +} + using JsonArray = Vector; using JsonObject = IdMap; @@ -384,7 +372,7 @@ void JsonUI::eval_json(const Value& json) for (auto& key_val : params) { for (auto& key : parse_keys(key_val.as())) - m_pending_keys.push_back(key); + m_on_key(key); } } else if (method == "resize") @@ -394,7 +382,7 @@ void JsonUI::eval_json(const Value& json) DisplayCoord dim{params[0].as(), params[1].as()}; m_dimensions = dim; - m_pending_keys.push_back(resize(dim)); + m_on_key(resize(dim)); } else throw runtime_error("unknown method"); @@ -413,6 +401,9 @@ void JsonUI::parse_requests(EventMode mode) m_requests += StringView{buf, buf + size}; } + if (not m_on_key) + return; + while (not m_requests.empty()) { const char* pos = nullptr; @@ -435,9 +426,6 @@ void JsonUI::parse_requests(EventMode mode) m_requests = String{pos, m_requests.end()}; } - - while (m_input_callback and not m_pending_keys.empty()) - m_input_callback(mode); } UnitTest test_json_parser{[]() diff --git a/src/json_ui.hh b/src/json_ui.hh index 026481dc..6ced3ec4 100644 --- a/src/json_ui.hh +++ b/src/json_ui.hh @@ -26,9 +26,6 @@ public: const DisplayLine& mode_line, const Face& default_face) override; - bool is_key_available() override; - Key get_key() override; - void menu_show(ConstArrayView items, DisplayCoord anchor, Face fg, Face bg, MenuStyle style) override; @@ -42,18 +39,16 @@ public: void refresh(bool force) override; - void set_input_callback(InputCallback callback) override; - - void set_ui_options(const Options& options) override; - DisplayCoord dimensions() override; + void set_on_key(OnKeyCallback callback) override; + void set_ui_options(const Options& options) override; private: void parse_requests(EventMode mode); void eval_json(const Value& value); - InputCallback m_input_callback; FDWatcher m_stdin_watcher; + OnKeyCallback m_on_key; Vector m_pending_keys; DisplayCoord m_dimensions; String m_requests; diff --git a/src/main.cc b/src/main.cc index 5dc28d5c..3385d423 100644 --- a/src/main.cc +++ b/src/main.cc @@ -332,10 +332,8 @@ std::unique_ptr make_ui(UIType ui_type) void draw(const DisplayBuffer&, const Face&, const Face&) override {} void draw_status(const DisplayLine&, const DisplayLine&, const Face&) override {} DisplayCoord dimensions() override { return {24,80}; } - bool is_key_available() override { return false; } - Key get_key() override { return Key::Invalid; } void refresh(bool) override {} - void set_input_callback(InputCallback) override {} + void set_on_key(OnKeyCallback callback) override {} void set_ui_options(const Options&) override {} }; @@ -587,7 +585,7 @@ int run_server(StringView session, StringView init_command, { client_manager.redraw_clients(); event_manager.handle_next_events(EventMode::Normal); - client_manager.handle_pending_inputs(); + client_manager.process_pending_inputs(); client_manager.clear_client_trash(); client_manager.clear_window_trash(); buffer_manager.clear_buffer_trash(); diff --git a/src/ncurses_ui.cc b/src/ncurses_ui.cc index 6653a718..89aefcc0 100644 --- a/src/ncurses_ui.cc +++ b/src/ncurses_ui.cc @@ -220,8 +220,11 @@ void on_term_resize(int) NCursesUI::NCursesUI() : m_stdin_watcher{0, [this](FDWatcher&, EventMode mode) { - if (m_input_callback) - m_input_callback(mode); + if (not m_on_key) + return; + + while (auto key = get_next_key()) + m_on_key(*key); }}, m_assistant(assistant_clippy), m_colors{ @@ -485,26 +488,19 @@ void NCursesUI::on_sighup() m_window = nullptr; } -bool NCursesUI::is_key_available() +Optional NCursesUI::get_next_key() { if (not m_window) - return false; + return {}; check_resize(); wtimeout(m_window, 0); const int c = wgetch(m_window); - if (c != ERR) - ungetch(c); wtimeout(m_window, -1); - return c != ERR; -} -Key NCursesUI::get_key() -{ - check_resize(); - - const int c = wgetch(m_window); + if (c == ERR) + return {}; if (c == KEY_MOUSE) { @@ -530,21 +526,21 @@ Key NCursesUI::get_key() return res | Key::Modifiers::MousePos; }; - return { get_modifiers(ev.bstate), - encode_coord({ ev.y - (m_status_on_top ? 1 : 0), ev.x }) }; + return Key{ get_modifiers(ev.bstate), + encode_coord({ ev.y - (m_status_on_top ? 1 : 0), ev.x }) }; } } if (c > 0 and c < 27) { if (c == control('m') or c == control('j')) - return Key::Return; + return {Key::Return}; if (c == control('i')) - return Key::Tab; + return {Key::Tab}; if (c == control('z')) { raise(SIGTSTP); - return Key::Invalid; + return {}; } return ctrl(Codepoint(c) - 1 + 'a'); } @@ -557,8 +553,8 @@ Key NCursesUI::get_key() const Codepoint csi_val = wgetch(m_window); switch (csi_val) { - case 'I': return Key::FocusIn; - case 'O': return Key::FocusOut; + case 'I': return {Key::FocusIn}; + case 'O': return {Key::FocusOut}; default: break; // nothing } } @@ -570,28 +566,28 @@ Key NCursesUI::get_key() return alt(new_c); } else - return Key::Escape; + return {Key::Escape}; } else switch (c) { - case KEY_BACKSPACE: case 127: return Key::Backspace; - case KEY_DC: return Key::Delete; - case KEY_UP: return Key::Up; - case KEY_DOWN: return Key::Down; - case KEY_LEFT: return Key::Left; - case KEY_RIGHT: return Key::Right; - case KEY_PPAGE: return Key::PageUp; - case KEY_NPAGE: return Key::PageDown; - case KEY_HOME: return Key::Home; - case KEY_END: return Key::End; - case KEY_BTAB: return Key::BackTab; + case KEY_BACKSPACE: case 127: return {Key::Backspace}; + case KEY_DC: return {Key::Delete}; + case KEY_UP: return {Key::Up}; + case KEY_DOWN: return {Key::Down}; + case KEY_LEFT: return {Key::Left}; + case KEY_RIGHT: return {Key::Right}; + case KEY_PPAGE: return {Key::PageUp}; + case KEY_NPAGE: return {Key::PageDown}; + case KEY_HOME: return {Key::Home}; + case KEY_END: return {Key::End}; + case KEY_BTAB: return {Key::BackTab}; case KEY_RESIZE: return resize(m_dimensions); } for (int i = 0; i < 12; ++i) { if (c == KEY_F(i+1)) - return Key::F1 + i; + return {Key::F1 + i}; } if (c >= 0 and c < 256) @@ -607,9 +603,10 @@ Key NCursesUI::get_key() WINDOW* window; }; - return utf8::codepoint(getch_iterator{m_window}, getch_iterator{m_window}); + return Key{utf8::codepoint(getch_iterator{m_window}, + getch_iterator{m_window})}; } - return Key::Invalid; + return {}; } template @@ -954,16 +951,16 @@ void NCursesUI::mark_dirty(const Window& win) wredrawln(m_window, (int)win.pos.line, (int)win.size.line); } +void NCursesUI::set_on_key(OnKeyCallback callback) +{ + m_on_key = std::move(callback); +} + DisplayCoord NCursesUI::dimensions() { return m_dimensions; } -void NCursesUI::set_input_callback(InputCallback callback) -{ - m_input_callback = std::move(callback); -} - void NCursesUI::abort() { endwin(); diff --git a/src/ncurses_ui.hh b/src/ncurses_ui.hh index 6efbbcea..c4501c91 100644 --- a/src/ncurses_ui.hh +++ b/src/ncurses_ui.hh @@ -30,9 +30,6 @@ public: const DisplayLine& mode_line, const Face& default_face) override; - bool is_key_available() override; - Key get_key() override; - void menu_show(ConstArrayView items, DisplayCoord anchor, Face fg, Face bg, MenuStyle style) override; @@ -46,11 +43,10 @@ public: void refresh(bool force) override; - void set_input_callback(InputCallback callback) override; - - void set_ui_options(const Options& options) override; DisplayCoord dimensions() override; + void set_on_key(OnKeyCallback callback) override; + void set_ui_options(const Options& options) override; static void abort(); @@ -74,6 +70,8 @@ private: ColumnCount col_index, ColumnCount max_column, const Face& default_face); + Optional get_next_key(); + NCursesWin* m_window = nullptr; DisplayCoord m_dimensions; @@ -119,8 +117,8 @@ private: InfoStyle style; } m_info; - FDWatcher m_stdin_watcher; - InputCallback m_input_callback; + FDWatcher m_stdin_watcher; + OnKeyCallback m_on_key; bool m_status_on_top = false; ConstArrayView m_assistant; diff --git a/src/remote.cc b/src/remote.cc index b86ddc6a..682971ef 100644 --- a/src/remote.cc +++ b/src/remote.cc @@ -324,11 +324,10 @@ public: void refresh(bool force) override; - bool is_key_available() override; - Key get_key() override; - DisplayCoord dimensions() override; + DisplayCoord dimensions() override { return m_dimensions; } - void set_input_callback(InputCallback callback) override; + void set_on_key(OnKeyCallback callback) override + { m_on_key = std::move(callback); } void set_ui_options(const Options& options) override; @@ -336,46 +335,45 @@ public: Client* client() const { return m_client.get(); } private: - FDWatcher m_socket_watcher; - MsgReader m_reader; - DisplayCoord m_dimensions; - InputCallback m_input_callback; + FDWatcher m_socket_watcher; + MsgReader m_reader; + DisplayCoord m_dimensions; + OnKeyCallback m_on_key; SafePtr m_client; - Optional m_pending_key; }; RemoteUI::RemoteUI(int socket, DisplayCoord dimensions) : m_socket_watcher(socket, [this](FDWatcher& watcher, EventMode mode) { const int sock = watcher.fd(); - bool disconnect = false; try { - while (fd_readable(sock) and not m_reader.ready()) + while (fd_readable(sock)) + { m_reader.read_available(sock); - if (m_reader.ready() and not m_pending_key) - { - if (m_reader.type() == MessageType::Key) + if (not m_reader.ready()) + continue; + + if (m_reader.type() != MessageType::Key) { - m_pending_key = m_reader.read(); - m_reader.reset(); + ClientManager::instance().remove_client(*m_client, false); + return; } - else - disconnect = true; + + auto key = m_reader.read(); + m_reader.reset(); + if (key.modifiers == Key::Modifiers::Resize) + m_dimensions = key.coord(); + m_on_key(key); } } catch (const remote_error& err) { write_to_debug_buffer(format("Error while reading remote message: {}", err.what())); - disconnect = true; - } - - if (disconnect) ClientManager::instance().remove_client(*m_client, false); - else if (m_pending_key and m_input_callback) - m_input_callback(mode); + } }), m_dimensions(dimensions) { @@ -460,31 +458,6 @@ void RemoteUI::set_ui_options(const Options& options) msg.write(options); } -bool RemoteUI::is_key_available() -{ - return (bool)m_pending_key; -} - -Key RemoteUI::get_key() -{ - kak_assert(m_pending_key); - auto key = *m_pending_key; - m_pending_key.reset(); - if (key.modifiers == Key::Modifiers::Resize) - m_dimensions = key.coord(); - return key; -} - -DisplayCoord RemoteUI::dimensions() -{ - return m_dimensions; -} - -void RemoteUI::set_input_callback(InputCallback callback) -{ - m_input_callback = std::move(callback); -} - static sockaddr_un session_addr(StringView session) { sockaddr_un addr; @@ -527,7 +500,10 @@ RemoteClient::RemoteClient(StringView session, std::unique_ptr&& msg.write(env_vars); } - m_ui->set_input_callback([this](EventMode){ send_available_keys(); }); + m_ui->set_on_key([this](Key key){ + MsgWriter msg(m_socket_watcher->fd(), MessageType::Key); + msg.write(key); + }); MsgReader reader; m_socket_watcher.reset(new FDWatcher{sock, [this, reader](FDWatcher& watcher, EventMode) mutable { @@ -598,15 +574,6 @@ RemoteClient::RemoteClient(StringView session, std::unique_ptr&& }}); } -void RemoteClient::send_available_keys() -{ - while (m_ui->is_key_available()) - { - MsgWriter msg(m_socket_watcher->fd(), MessageType::Key); - msg.write(m_ui->get_key()); - } -} - void send_command(StringView session, StringView command) { int sock = connect_to(session); diff --git a/src/remote.hh b/src/remote.hh index 7f237258..38340d48 100644 --- a/src/remote.hh +++ b/src/remote.hh @@ -19,6 +19,7 @@ struct remote_error : runtime_error class FDWatcher; class UserInterface; +struct Key; // A remote client handle communication between a client running on the server // and a user interface running on the local process. @@ -29,8 +30,6 @@ public: const EnvVarMap& env_vars, StringView init_command); private: - void send_available_keys(); - std::unique_ptr m_ui; std::unique_ptr m_socket_watcher; }; diff --git a/src/user_interface.hh b/src/user_interface.hh index 51f75bbd..38abaefd 100644 --- a/src/user_interface.hh +++ b/src/user_interface.hh @@ -33,7 +33,7 @@ enum class InfoStyle enum class EventMode; -using InputCallback = std::function; +using OnKeyCallback = std::function; class UserInterface { @@ -60,12 +60,10 @@ public: const Face& default_face) = 0; virtual DisplayCoord dimensions() = 0; - virtual bool is_key_available() = 0; - virtual Key get_key() = 0; virtual void refresh(bool force) = 0; - virtual void set_input_callback(InputCallback callback) = 0; + virtual void set_on_key(OnKeyCallback callback) = 0; using Options = IdMap; virtual void set_ui_options(const Options& options) = 0;