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