Separate events between normal and urgent ones

Run urgent ones while executing %sh blocks.

Fixes #236
This commit is contained in:
Maxime Coste 2014-11-25 01:00:18 +00:00
parent 0272da65c0
commit 49931fbf05
13 changed files with 140 additions and 76 deletions

View File

@ -105,7 +105,10 @@ Buffer* create_fifo_buffer(String name, int fd, bool scroll)
ValueId fifo_watcher_id = s_fifo_watcher_id;
std::unique_ptr<FDWatcher, decltype(watcher_deleter)> watcher(
new FDWatcher(fd, [buffer, scroll, fifo_watcher_id](FDWatcher& watcher) {
new FDWatcher(fd, [buffer, scroll, fifo_watcher_id](FDWatcher& watcher, EventMode mode) {
if (mode != EventMode::Normal)
return;
constexpr size_t buffer_size = 2048;
// if we read data slower than it arrives in the fifo, limiting the
// iteration number allows us to go back go back to the event loop and

View File

@ -7,8 +7,12 @@
#include "file.hh"
#include "remote.hh"
#include "client_manager.hh"
#include "event_manager.hh"
#include "window.hh"
#include <signal.h>
#include <unistd.h>
namespace Kakoune
{
@ -34,8 +38,19 @@ Client::~Client()
m_window->options().unregister_watcher(*this);
}
void Client::handle_available_input()
void Client::handle_available_input(EventMode mode)
{
if (mode == EventMode::Normal)
{
try
{
for (auto& key : m_pending_keys)
{
m_input_handler.handle_key(key);
m_input_handler.clear_mode_trash();
}
m_pending_keys.clear();
while (m_ui->is_key_available())
{
m_input_handler.handle_key(m_ui->get_key());
@ -43,6 +58,25 @@ void Client::handle_available_input()
}
context().window().forget_timestamp();
}
catch (Kakoune::runtime_error& error)
{
context().print_status({ error.what(), get_face("Error") });
context().hooks().run_hook("RuntimeError", error.what(), context());
}
catch (Kakoune::client_removed&)
{
ClientManager::instance().remove_client(*this);
}
}
else
{
Key key = m_ui->get_key();
if (key == ctrl('c'))
killpg(getpgrp(), SIGINT);
else
m_pending_keys.push_back(key);
}
}
void Client::print_status(DisplayLine status_line)
{

View File

@ -14,6 +14,9 @@ namespace Kakoune
class UserInterface;
class Window;
class String;
struct Key;
enum class EventMode;
class Client : public SafeCountable, public OptionManagerWatcher
{
@ -28,7 +31,7 @@ public:
Client(Client&&) = delete;
// handle all the keys currently available in the user interface
void handle_available_input();
void handle_available_input(EventMode mode);
void print_status(DisplayLine status_line);
@ -64,6 +67,8 @@ private:
DisplayLine m_status_line;
DisplayLine m_pending_status_line;
DisplayLine m_mode_line;
std::vector<Key> m_pending_keys;
};
}

View File

@ -50,26 +50,19 @@ Client* ClientManager::create_client(std::unique_ptr<UserInterface>&& ui,
return nullptr;
}
client->ui().set_input_callback([client, this]() {
try
{
client->handle_available_input();
}
catch (Kakoune::runtime_error& error)
{
client->context().print_status({ error.what(), get_face("Error") });
client->context().hooks().run_hook("RuntimeError", error.what(),
client->context());
}
catch (Kakoune::client_removed&)
{
ClientManager::instance().remove_client(*client);
}
client->ui().set_input_callback([client](EventMode mode) {
client->handle_available_input(mode);
});
return client;
}
void ClientManager::handle_available_inputs() const
{
for (auto& client : m_clients)
client->handle_available_input(EventMode::Normal);
}
void ClientManager::remove_client(Client& client)
{
for (auto it = m_clients.begin(); it != m_clients.end(); ++it)

View File

@ -35,6 +35,7 @@ public:
void redraw_clients() const;
void clear_mode_trashes() const;
void handle_available_inputs() const;
Client* get_client_ifp(StringView name);
Client& get_client(StringView name);

View File

@ -16,8 +16,13 @@ FDWatcher::~FDWatcher()
EventManager::instance().m_fd_watchers.erase(this);
}
Timer::Timer(TimePoint date, Callback callback)
: m_date{date}, m_callback{std::move(callback)}
void FDWatcher::run(EventMode mode)
{
m_callback(*this, mode);
}
Timer::Timer(TimePoint date, Callback callback, EventMode mode)
: m_date{date}, m_callback{std::move(callback)}, m_mode(mode)
{
if (EventManager::has_instance())
EventManager::instance().m_timers.insert(this);
@ -29,11 +34,16 @@ Timer::~Timer()
EventManager::instance().m_timers.erase(this);
}
void Timer::run()
void Timer::run(EventMode mode)
{
if (mode & m_mode)
{
m_date = TimePoint::max();
m_callback(*this);
}
else // try again a little later
m_date = Clock::now() + std::chrono::milliseconds{10};
}
EventManager::EventManager()
{
@ -46,7 +56,7 @@ EventManager::~EventManager()
kak_assert(m_timers.empty());
}
void EventManager::handle_next_events()
void EventManager::handle_next_events(EventMode mode)
{
std::vector<pollfd> events;
events.reserve(m_fd_watchers.size());
@ -76,7 +86,7 @@ void EventManager::handle_next_events()
auto it = find_if(m_fd_watchers,
[fd](FDWatcher* w) { return w->fd() == fd; });
if (it != m_fd_watchers.end())
(*it)->run();
(*it)->run(mode);
}
}
@ -84,7 +94,7 @@ void EventManager::handle_next_events()
for (auto& timer : m_timers)
{
if (timer->next_date() <= now)
timer->run();
timer->run(mode);
}
}

View File

@ -2,6 +2,7 @@
#define event_manager_hh_INCLUDED
#include "utils.hh"
#include "flags.hh"
#include <chrono>
#include <unordered_set>
@ -9,15 +10,23 @@
namespace Kakoune
{
enum class EventMode
{
Normal = 1 << 0,
Urgent = 1 << 1
};
template<> struct WithBitOps<EventMode> : std::true_type {};
class FDWatcher
{
public:
using Callback = std::function<void (FDWatcher& watcher)>;
using Callback = std::function<void (FDWatcher& watcher, EventMode mode)>;
FDWatcher(int fd, Callback callback);
~FDWatcher();
int fd() const { return m_fd; }
void run() { m_callback(*this); }
void run(EventMode mode);
private:
FDWatcher(const FDWatcher&) = delete;
@ -33,15 +42,17 @@ class Timer
public:
using Callback = std::function<void (Timer& timer)>;
Timer(TimePoint date, Callback callback);
Timer(TimePoint date, Callback callback,
EventMode mode = EventMode::Normal);
~Timer();
TimePoint next_date() const { return m_date; }
void set_next_date(TimePoint date) { m_date = date; }
void run();
void run(EventMode mode);
private:
TimePoint m_date;
EventMode m_mode;
Callback m_callback;
};
@ -56,7 +67,7 @@ public:
EventManager();
~EventManager();
void handle_next_events();
void handle_next_events(EventMode mode);
// force the watchers associated with fd to be executed
// on next handle_next_events call.

View File

@ -305,7 +305,7 @@ int run_client(StringView session, StringView init_command)
RemoteClient client{session, make_unique<NCursesUI>(),
get_env_vars(), init_command};
while (true)
event_manager.handle_next_events();
event_manager.handle_next_events(EventMode::Normal);
}
catch (peer_disconnected&)
{
@ -407,7 +407,8 @@ int run_server(StringView session, StringView init_command,
while (not terminate and (not client_manager.empty() or daemon))
{
event_manager.handle_next_events();
event_manager.handle_next_events(EventMode::Normal);
client_manager.handle_available_inputs();
client_manager.clear_mode_trashes();
buffer_manager.clear_buffer_trash();
client_manager.redraw_clients();

View File

@ -219,8 +219,10 @@ void on_sigint(int)
}
NCursesUI::NCursesUI()
: m_stdin_watcher{0, [this](FDWatcher&){ if (m_input_callback)
m_input_callback(); }}
: m_stdin_watcher{0, [this](FDWatcher&, EventMode mode) {
if (m_input_callback)
m_input_callback(mode);
}}
{
initscr();
raw();

View File

@ -18,9 +18,6 @@
#include "user_interface.hh"
#include "window.hh"
#include <signal.h>
#include <unistd.h>
namespace Kakoune
{
@ -1400,8 +1397,6 @@ KeyMap keymap =
{ Key::PageUp, { "scroll one page up", scroll<Key::PageUp> } },
{ Key::PageDown, { "scroll one page down", scroll<Key::PageDown> } },
{ ctrl('c'), { "interupt", [](Context&, int) { killpg(getpgrp(), SIGINT); } } },
};
}

View File

@ -282,9 +282,9 @@ private:
RemoteUI::RemoteUI(int socket)
: m_socket_watcher(socket, [this](FDWatcher&) {
: m_socket_watcher(socket, [this](FDWatcher&, EventMode mode) {
if (m_input_callback)
m_input_callback();
m_input_callback(mode);
})
{
write_debug("remote client connected: " +
@ -453,9 +453,9 @@ RemoteClient::RemoteClient(StringView session, std::unique_ptr<UserInterface>&&
msg.write(key);
}
m_ui->set_input_callback([this]{ write_next_key(); });
m_ui->set_input_callback([this](EventMode){ write_next_key(); });
m_socket_watcher.reset(new FDWatcher{sock, [this](FDWatcher&){ process_available_messages(); }});
m_socket_watcher.reset(new FDWatcher{sock, [this](FDWatcher&, EventMode){ process_available_messages(); }});
}
void RemoteClient::process_available_messages()
@ -560,7 +560,10 @@ class Server::Accepter
public:
Accepter(int socket)
: m_socket_watcher(socket,
[this](FDWatcher&) { handle_available_input(); })
[this](FDWatcher&, EventMode mode) {
if (mode == EventMode::Normal)
handle_available_input();
})
{}
private:
@ -626,7 +629,7 @@ Server::Server(String session_name)
if (listen(listen_sock, 4) == -1)
throw runtime_error("unable to listen on socket "_str + addr.sun_path);
auto accepter = [this](FDWatcher& watcher) {
auto accepter = [this](FDWatcher& watcher, EventMode mode) {
sockaddr_un client_addr;
socklen_t client_addr_len = sizeof(sockaddr_un);
int sock = accept(watcher.fd(), (sockaddr*) &client_addr,

View File

@ -2,6 +2,7 @@
#include "context.hh"
#include "debug.hh"
#include "event_manager.hh"
#include "file.hh"
#include <cstring>
@ -59,25 +60,34 @@ String ShellManager::pipe(StringView input,
write(write_pipe[1], input.data(), (int)input.length());
close(write_pipe[1]);
String error;
{
auto pipe_reader = [](String& output, bool& closed) {
return [&output, &closed](FDWatcher& watcher, EventMode) {
if (closed)
return;
const int fd = watcher.fd();
char buffer[1024];
while (size_t size = read(read_pipe[0], buffer, 1024))
size_t size = read(fd, buffer, 1024);
if (size <= 0)
{
if (size == -1)
break;
close(fd);
closed = true;
}
output += String(buffer, buffer+size);
}
close(read_pipe[0]);
};
};
String errorout;
while (size_t size = read(error_pipe[0], buffer, 1024))
{
if (size == -1)
break;
errorout += String(buffer, buffer+size);
bool stdout_closed = false, stderr_closed = false;
FDWatcher stdout_watcher{read_pipe[0], pipe_reader(output, stdout_closed)};
FDWatcher stderr_watcher{error_pipe[0], pipe_reader(error, stderr_closed)};
while (not stdout_closed or not stderr_closed)
EventManager::instance().handle_next_events(EventMode::Urgent);
}
close(error_pipe[0]);
if (not errorout.empty())
write_debug("shell stderr: <<<\n" + errorout + ">>>");
if (not error.empty())
write_debug("shell stderr: <<<\n" + error + ">>>");
waitpid(pid, exit_status, 0);
if (exit_status)
@ -105,13 +115,7 @@ String ShellManager::pipe(StringView input,
{
auto& match = *it;
StringView name;
if (match[1].matched)
name = StringView(match[1].first, match[1].second);
else if (match[2].matched)
name = StringView(match[2].first, match[2].second);
else
kak_assert(false);
StringView name = StringView(match[1].first, match[1].second);
kak_assert(name.length() > 0);
auto local_var = env_vars.find(name);

View File

@ -32,7 +32,9 @@ enum class InfoStyle
MenuDoc
};
using InputCallback = std::function<void()>;
enum class EventMode;
using InputCallback = std::function<void(EventMode mode)>;
class UserInterface : public SafeCountable
{