summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMaxime Coste <frrrwww@gmail.com>2012-10-23 22:55:44 +0200
committerMaxime Coste <frrrwww@gmail.com>2012-10-23 22:55:44 +0200
commitb9eb939e05f8b63354f54d1986b74c2bb8dde75e (patch)
tree50a5fc1216aa42e673e13857d88672f44e4606aa /src
parentb1637119637e2dc3e10635a08ac06e256018ea78 (diff)
Add some remote client support protocol code
Diffstat (limited to 'src')
-rw-r--r--src/display_buffer.hh4
-rw-r--r--src/remote.cc230
-rw-r--r--src/remote.hh47
3 files changed, 281 insertions, 0 deletions
diff --git a/src/display_buffer.hh b/src/display_buffer.hh
index 869ef7a6..5a5f1558 100644
--- a/src/display_buffer.hh
+++ b/src/display_buffer.hh
@@ -129,6 +129,8 @@ public:
using const_iterator = AtomList::const_iterator;
explicit DisplayLine(LineCount buffer_line) : m_buffer_line(buffer_line) {}
+ DisplayLine(LineCount buffer_line, AtomList atoms)
+ : m_buffer_line(buffer_line), m_atoms(std::move(atoms)) {}
LineCount buffer_line() const { return m_buffer_line; }
@@ -138,6 +140,8 @@ public:
const_iterator begin() const { return m_atoms.begin(); }
const_iterator end() const { return m_atoms.end(); }
+ const AtomList& atoms() const { return m_atoms; }
+
// Split atom pointed by it at pos, returns an iterator to the first atom
iterator split(iterator it, BufferIterator pos);
diff --git a/src/remote.cc b/src/remote.cc
new file mode 100644
index 00000000..4dadeeba
--- /dev/null
+++ b/src/remote.cc
@@ -0,0 +1,230 @@
+#include "remote.hh"
+
+#include "display_buffer.hh"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+namespace Kakoune
+{
+
+enum class RemoteUIMsg
+{
+ PrintStatus,
+ MenuShow,
+ MenuSelect,
+ MenuHide,
+ Draw
+};
+
+class Message
+{
+public:
+ Message(int sock) : m_socket(sock) {}
+ ~Message() { ::write(m_socket, m_stream.data(), m_stream.size()); }
+
+ void write(const char* val, size_t size)
+ {
+ m_stream.insert(m_stream.end(), val, val + size);
+ }
+
+private:
+ std::vector<char> m_stream;
+ int m_socket;
+};
+
+template<typename T>
+void write(Message& msg, const T& val)
+{
+ msg.write((const char*)&val, sizeof(val));
+};
+
+void write(Message& msg, const String& str)
+{
+ write(msg, str.length());
+ msg.write(str.c_str(), (int)str.length());
+};
+
+template<typename T>
+void write(Message& msg, const memoryview<T>& view)
+{
+ write<uint32_t>(msg, view.size());
+ for (auto& val : view)
+ write(msg, val);
+};
+
+template<typename T>
+void write(Message& msg, const std::vector<T>& vec)
+{
+ write(msg, memoryview<T>(vec));
+}
+
+void write(Message& msg, const DisplayAtom& atom)
+{
+ write(msg, atom.fg_color);
+ write(msg, atom.bg_color);
+ write(msg, atom.attribute);
+ write(msg, atom.content.content());
+}
+
+void write(Message& msg, const DisplayLine& line)
+{
+ write(msg, line.atoms());
+}
+
+void write(Message& msg, const DisplayBuffer& display_buffer)
+{
+ write(msg, display_buffer.lines());
+}
+
+template<typename T>
+T read(int socket)
+{
+ char value[sizeof(T)];
+ auto res = ::read(socket, value, sizeof(T));
+ assert(res == sizeof(T));
+ return *(T*)(value);
+};
+
+template<>
+String read<String>(int socket)
+{
+ ByteCount length = read<ByteCount>(socket);
+ char buffer[2048];
+ assert(length < 2048);
+ auto res = ::read(socket, buffer, (int)length);
+ assert(res == (int)length);
+ return String(buffer, buffer+(int)length);
+};
+
+template<typename T>
+std::vector<T> read_vector(int socket)
+{
+ uint32_t size = read<uint32_t>(socket);
+ std::vector<T> res;
+ res.reserve(size);
+ while (size--)
+ res.push_back(read<T>(socket));
+ return res;
+};
+
+template<>
+DisplayAtom read<DisplayAtom>(int socket)
+{
+ Color fg_color = read<Color>(socket);
+ Color bg_color = read<Color>(socket);
+ Attribute attribute = read<Attribute>(socket);
+ DisplayAtom atom(AtomContent(read<String>(socket)));
+ atom.fg_color = fg_color;
+ atom.bg_color = bg_color;
+ atom.attribute = attribute;
+ return atom;
+}
+template<>
+DisplayLine read<DisplayLine>(int socket)
+{
+ return DisplayLine(0, read_vector<DisplayAtom>(socket));
+}
+
+template<>
+DisplayBuffer read<DisplayBuffer>(int socket)
+{
+ DisplayBuffer db;
+ db.lines() = read_vector<DisplayLine>(socket);
+ return db;
+}
+
+void RemoteUI::print_status(const String& status, CharCount cursor_pos)
+{
+ Message msg(m_socket);
+ write(msg, RemoteUIMsg::PrintStatus);
+ write(msg, status);
+ write(msg, cursor_pos);
+}
+
+void RemoteUI::menu_show(const memoryview<String>& choices,
+ const DisplayCoord& anchor, MenuStyle style)
+{
+ Message msg(m_socket);
+ write(msg, RemoteUIMsg::MenuShow);
+ write(msg, choices);
+ write(msg, anchor);
+ write(msg, style);
+}
+
+void RemoteUI::menu_select(int selected)
+{
+ Message msg(m_socket);
+ write(msg, RemoteUIMsg::MenuSelect);
+ write(msg, selected);
+}
+
+void RemoteUI::menu_hide()
+{
+ Message msg(m_socket);
+ write(msg, RemoteUIMsg::MenuHide);
+}
+
+void RemoteUI::draw(const DisplayBuffer& display_buffer,
+ const String& status_line)
+{
+ Message msg(m_socket);
+ write(msg, RemoteUIMsg::Draw);
+ write(msg, display_buffer);
+ write(msg, status_line);
+}
+
+Key RemoteUI::get_key()
+{
+ Key key = read<Key>(m_socket);
+ return key;
+}
+
+DisplayCoord RemoteUI::dimensions()
+{
+ return m_dimensions;
+}
+
+void RemoteClient::process_next_message()
+{
+ RemoteUIMsg msg = read<RemoteUIMsg>(m_socket);
+ switch (msg)
+ {
+ case RemoteUIMsg::PrintStatus:
+ {
+ auto status = read<String>(m_socket);
+ auto cursor_pos = read<CharCount>(m_socket);
+ m_ui->print_status(status, cursor_pos);
+ break;
+ }
+ case RemoteUIMsg::MenuShow:
+ {
+ auto choices = read_vector<String>(m_socket);
+ auto anchor = read<DisplayCoord>(m_socket);
+ auto style = read<MenuStyle>(m_socket);
+ m_ui->menu_show(choices, anchor, style);
+ break;
+ }
+ case RemoteUIMsg::MenuSelect:
+ m_ui->menu_select(read<int>(m_socket));
+ break;
+ case RemoteUIMsg::MenuHide:
+ m_ui->menu_hide();
+ break;
+ case RemoteUIMsg::Draw:
+ {
+ DisplayBuffer display_buffer = read<DisplayBuffer>(m_socket);
+ String status_line = read<String>(m_socket);
+ m_ui->draw(display_buffer, status_line);
+ break;
+ }
+ }
+}
+
+void RemoteClient::write_next_key()
+{
+ Message msg(m_socket);
+ write(msg, m_ui->get_key());
+}
+
+}
diff --git a/src/remote.hh b/src/remote.hh
new file mode 100644
index 00000000..0717e7ed
--- /dev/null
+++ b/src/remote.hh
@@ -0,0 +1,47 @@
+#ifndef remote_hh_INCLUDED
+#define remote_hh_INCLUDED
+
+#include "user_interface.hh"
+#include "display_buffer.hh"
+
+namespace Kakoune
+{
+
+class RemoteUI : public UserInterface
+{
+public:
+ RemoteUI(int socket) : m_socket(socket) {}
+
+ void print_status(const String& status, CharCount cursor_pos) override;
+ void menu_show(const memoryview<String>& choices,
+ const DisplayCoord& anchor, MenuStyle style) override;
+ void menu_select(int selected) override;
+ void menu_hide() override;
+ void draw(const DisplayBuffer& display_buffer,
+ const String& status_line) override;
+ Key get_key() override;
+ DisplayCoord dimensions() override;
+ void set_dimensions(const DisplayCoord dim) { m_dimensions = dim; }
+
+private:
+ int m_socket;
+ DisplayCoord m_dimensions;
+};
+
+class RemoteClient
+{
+public:
+ RemoteClient(int socket, UserInterface* ui) : m_ui(ui), m_socket(socket) {}
+
+ void process_next_message();
+ void write_next_key();
+
+private:
+ int m_socket;
+ std::unique_ptr<UserInterface> m_ui;
+};
+
+}
+
+#endif // remote_hh_INCLUDED
+