diff --git a/src/client_manager.cc b/src/client_manager.cc new file mode 100644 index 00000000..6f30277d --- /dev/null +++ b/src/client_manager.cc @@ -0,0 +1,24 @@ +#include "client_manager.hh" + +namespace Kakoune +{ + +void ClientManager::add_client(Client&& client) +{ + m_clients.emplace_back(std::move(client)); +} + +void ClientManager::remove_client_by_context(Context& context) +{ + for (auto it = m_clients.begin(); it != m_clients.end(); ++it) + { + if (it->context.get() == &context) + { + m_clients.erase(it); + return; + } + } + assert(false); +} + +} diff --git a/src/client_manager.hh b/src/client_manager.hh new file mode 100644 index 00000000..a9dac1a0 --- /dev/null +++ b/src/client_manager.hh @@ -0,0 +1,42 @@ +#ifndef client_manager_hh_INCLUDED +#define client_manager_hh_INCLUDED + +#include "context.hh" +#include "input_handler.hh" + +namespace Kakoune +{ + +struct Client +{ + std::unique_ptr ui; + std::unique_ptr input_handler; + std::unique_ptr context; + + Client(UserInterface* ui, Window& window) + : ui(ui), + input_handler(new InputHandler{}), + context(new Context(*input_handler, window, *ui)) {} + + Client(Client&&) = default; + Client& operator=(Client&&) = default; +}; + +struct client_removed{}; + +class ClientManager : public Singleton +{ +public: + void add_client(Client&& client); + void remove_client_by_context(Context& context); + + bool empty() const { return m_clients.empty(); } + size_t count() const { return m_clients.size(); } +private: + std::vector m_clients; +}; + +} + +#endif // client_manager_hh_INCLUDED + diff --git a/src/commands.cc b/src/commands.cc index 392fa500..f6b3f767 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -16,6 +16,7 @@ #include "shell_manager.hh" #include "event_manager.hh" #include "color_registry.hh" +#include "client_manager.hh" #include #include @@ -334,7 +335,7 @@ void quit(const CommandParameters& params, Context& context) if (params.size() != 0) throw wrong_argument_count(); - if (not force) + if (not force and ClientManager::instance().count() == 1) { std::vector names; for (auto& buffer : BufferManager::instance()) @@ -355,7 +356,8 @@ void quit(const CommandParameters& params, Context& context) throw runtime_error(message); } } - quit_requested = true; + ClientManager::instance().remove_client_by_context(context); + throw client_removed{}; } template diff --git a/src/main.cc b/src/main.cc index b20a917c..eb2ceedb 100644 --- a/src/main.cc +++ b/src/main.cc @@ -21,6 +21,7 @@ #include "file.hh" #include "color_registry.hh" #include "remote.hh" +#include "client_manager.hh" #if defined(__APPLE__) #include @@ -38,8 +39,6 @@ using namespace std::placeholders; namespace Kakoune { -bool quit_requested = false; - template void do_insert(Context& context) { @@ -494,21 +493,7 @@ void register_registers() } } -struct Client -{ - std::unique_ptr ui; - std::unique_ptr input_handler; - std::unique_ptr context; - - Client(UserInterface* ui, Window& window) - : ui(ui), - input_handler(new InputHandler{}), - context(new Context(*input_handler, window, *ui)) {} - - Client() {} -}; - -Client create_local_client(const String& file) +void create_local_client(const String& file) { Buffer* buffer = nullptr; UserInterface* ui = new NCursesUI{}; @@ -537,14 +522,16 @@ Client create_local_client(const String& file) { ui->print_status(error.description(), -1); } + catch (Kakoune::client_removed&) + { + EventManager::instance().unwatch(0); + } }); context->draw_ifn(); - return client; + ClientManager::instance().add_client(std::move(client)); } -std::vector clients; - struct Server { Server() @@ -583,8 +570,13 @@ struct Server { ui->print_status(error.description(), -1); } + catch (Kakoune::client_removed&) + { + EventManager::instance().unwatch(sock); + close(sock); + } }); - clients.push_back(std::move(client)); + ClientManager::instance().add_client(std::move(client)); }; EventManager::instance().watch(m_listen_sock, accepter); } @@ -647,9 +639,16 @@ int main(int argc, char* argv[]) if (argc == 3 and String("-c") == argv[1]) { - std::unique_ptr client(connect_to(argv[2])); - while(not quit_requested) - event_manager.handle_next_events(); + try + { + std::unique_ptr client(connect_to(argv[2])); + while (true) + event_manager.handle_next_events(); + } + catch (peer_disconnected&) + { + puts("disconnected"); + } return 0; } @@ -662,6 +661,7 @@ int main(int argc, char* argv[]) HighlighterRegistry highlighter_registry; FilterRegistry filter_registry; ColorRegistry color_registry; + ClientManager client_manager; run_unit_tests(); @@ -677,7 +677,6 @@ int main(int argc, char* argv[]) Server server; - Client local_client; try { Context initialisation_context; @@ -688,9 +687,10 @@ int main(int argc, char* argv[]) { write_debug("error while parsing kakrc: " + error.description()); } - local_client = create_local_client(argc > 1 ? argv[1] : ""); - while(not quit_requested) + create_local_client(argc > 1 ? argv[1] : ""); + + while (not client_manager.empty()) event_manager.handle_next_events(); } catch (Kakoune::exception& error) diff --git a/src/remote.cc b/src/remote.cc index 7a715274..b3a0b6fd 100644 --- a/src/remote.cc +++ b/src/remote.cc @@ -108,6 +108,8 @@ template<> String read(int socket) { ByteCount length = read(socket); + if (length == 0) + return String{}; char buffer[2048]; assert(length < 2048); read(socket, buffer, (int)length); diff --git a/src/remote.hh b/src/remote.hh index aa14eeb3..976a5bf8 100644 --- a/src/remote.hh +++ b/src/remote.hh @@ -7,10 +7,7 @@ namespace Kakoune { -struct peer_disconnected : public runtime_error -{ - peer_disconnected() : runtime_error("peer disconnected") {} -}; +struct peer_disconnected {}; class RemoteUI : public UserInterface {