commit ed7de4967af46afb3c3ef8802a3d4686d267d415
parent 3635053539feb212e20ddd7a4b9e17fd26ff3114
Author: Alexandre BIQUE <bique.alexandre@gmail.com>
Date: Wed, 14 Jul 2021 10:19:50 +0200
More work on the remote GUI
Diffstat:
6 files changed, 188 insertions(+), 19 deletions(-)
diff --git a/examples/plugins/CMakeLists.txt b/examples/plugins/CMakeLists.txt
@@ -15,6 +15,7 @@ add_library(
remote-gui.cc
remote-channel.hh
remote-channel.cc
+ buffer.hh
dc-offset/dc-offset.hh
dc-offset/dc-offset.cc
diff --git a/examples/plugins/buffer.hh b/examples/plugins/buffer.hh
@@ -1,34 +1,68 @@
#pragma once
+#include <algorithm>
#include <array>
+#include <cassert>
#include <cstddef>
-#include <cstdint>
-#include <cstring>
namespace clap {
- template <size_t CAPACITY>
+ template <typename T, size_t CAPACITY>
class Buffer {
+ public:
+ Buffer() { assert(checkInvariants()); }
- const uint8_t *readData() const noexcept { return &data_[roff_]; }
+ Buffer(const Buffer<T, CAPACITY> &) = delete;
+ Buffer(Buffer<T, CAPACITY> &&) = delete;
+ Buffer<T, CAPACITY> &operator=(const Buffer<T, CAPACITY> &) = delete;
+ Buffer<T, CAPACITY> &operator=(Buffer<T, CAPACITY> &&) = delete;
+
+ const T *readData() const noexcept { return &data_[roff_]; }
size_t readAvail() const noexcept { return woff_ - roff_; }
- void read(size_t bytes) noexcept { roff_ += bytes; }
+ void read(size_t bytes) noexcept {
+ roff_ += bytes;
+ assert(checkInvariants());
+ }
- uint8_t *writeData() const noexcept { return &data_[woff_]; }
+ void write(const T *&data, size_t &size) {
+ auto avail = std::min(size, writeAvail());
+ auto end = data + avail;
+ std::copy(data, data + avail, writeData());
+ data = end;
+ size -= avail;
+ }
+ T *writeData() noexcept { return &data_[woff_]; }
size_t writeAvail() const noexcept { return CAPACITY - woff_; }
- void wrote(size_t bytes) noexcept { woff_ += bytes; }
+ void wrote(size_t bytes) noexcept {
+ woff_ += bytes;
+ assert(checkInvariants());
+ }
- void rewind() {
+ void rewind() noexcept {
if (woff_ == 0)
return;
// this is inefficient but simple
// TODO: use scatter/gather IO
- std::memmove(&data_[0], &data_[roff_], woff_ - roff_);
+ auto rptr = readData();
+ auto avail = readAvail();
+ std::copy(rptr, rptr + avail, &data_[0]);
+
woff_ -= roff_;
roff_ = 0;
+
+ assert(checkInvariants());
+ }
+
+ private:
+#ifndef NDEBUG
+ bool checkInvariants() const noexcept {
+ assert(woff_ <= data_.size());
+ assert(roff_ <= woff_);
+ return true;
}
+#endif
- std::array<uint8_t, CAPACITY> data_;
+ std::array<T, CAPACITY> data_;
size_t roff_ = 0;
size_t woff_ = 0;
};
diff --git a/examples/plugins/remote-channel.cc b/examples/plugins/remote-channel.cc
@@ -1,4 +1,5 @@
#ifdef __unix__
+# include <errno.h>
# include <unistd.h>
#endif
@@ -6,10 +7,77 @@
namespace clap {
- RemoteChannel::RemoteChannel(int socket) : socket_(socket) {}
+ RemoteChannel::RemoteChannel(Handler &handler, EventControl &evControl, int socket)
+ : handler_(handler), evControl_(evControl), socket_(socket) {}
RemoteChannel::~RemoteChannel() { close(); }
+ void RemoteChannel::onRead() {
+ ssize_t nbytes = ::read(socket_, inputBuffer_.writeData(), inputBuffer_.writeAvail());
+ if (nbytes < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
+ return;
+
+ close();
+ return;
+ }
+
+ inputBuffer_.wrote(nbytes);
+ parseInput();
+ inputBuffer_.rewind();
+ }
+
+ void RemoteChannel::write(const uint8_t *data, size_t size) {
+ while (size > 0) {
+ auto &buffer = nextWriteBuffer();
+ buffer.write(data, size);
+ }
+
+ assert(size == 0);
+ }
+
+ RemoteChannel::WriteBuffer &RemoteChannel::nextWriteBuffer() {
+ if (outputBuffers_.empty()) {
+ outputBuffers_.emplace();
+ return outputBuffers_.back();
+ }
+
+ auto &buffer = outputBuffers_.back();
+ if (buffer.writeAvail() > 0)
+ return buffer;
+
+ outputBuffers_.emplace();
+ return outputBuffers_.back();
+ }
+
+ void RemoteChannel::onWrite() {
+ while (!outputBuffers_.empty()) {
+ auto &buffer = outputBuffers_.front();
+
+ auto avail = buffer.readAvail();
+ while (avail > 0) {
+ auto nbytes = ::write(socket_, buffer.readData(), avail);
+ if (nbytes == -1) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR) {
+ evControl_.modifyFd(CLAP_FD_READ | CLAP_FD_WRITE);
+ return;
+ }
+
+ close();
+ return;
+ }
+
+ buffer.wrote(nbytes);
+ avail -= nbytes;
+ assert(avail == buffer.readAvail());
+ }
+
+ outputBuffers_.pop();
+ }
+
+ evControl_.modifyFd(CLAP_FD_READ);
+ }
+
void RemoteChannel::close() {
if (socket_ == -1)
return;
diff --git a/examples/plugins/remote-channel.hh b/examples/plugins/remote-channel.hh
@@ -1,19 +1,80 @@
#pragma once
+#include <queue>
+#include <memory>
+
+#include <clap/all.h>
+
+#include "buffer.hh"
+
namespace clap {
class RemoteChannel final {
public:
- RemoteChannel(int socket);
+ class EventControl {
+ public:
+ virtual void modifyFd(clap_fd_flags flags);
+ };
+
+ class Handler {
+ public:
+ virtual ~Handler() = default;
+
+ // GUI callbacks
+ virtual void defineParameter(const clap_param_info &info) {}
+ virtual bool attachCocoa(void *nsView) { return false; }
+ virtual bool attachWin32(clap_hwnd window) { return false; }
+ virtual bool attachX11(const char *display_name, unsigned long window) { return false; }
+
+ virtual void size(int32_t *width, int32_t *height) {}
+ virtual void setScale(double scale) {}
+
+ virtual bool show() { return false; }
+ virtual bool hide() { return false; }
+
+ virtual void close() {}
+
+ // Plugin callbacks
+ virtual void beginAdjust(clap_id paramId) {}
+ virtual void adjust(clap_id paramId, double value) {}
+ virtual void endAdjust(clap_id paramId) {}
+ };
+
+ RemoteChannel(Handler &handler, EventControl &evControl, clap_fd socket);
~RemoteChannel();
- RemoteChannel(const RemoteChannel&) = delete;
- RemoteChannel(RemoteChannel&&) = delete;
- RemoteChannel& operator=(const RemoteChannel&) = delete;
- RemoteChannel& operator=(RemoteChannel&&) = delete;
+ RemoteChannel(const RemoteChannel &) = delete;
+ RemoteChannel(RemoteChannel &&) = delete;
+ RemoteChannel &operator=(const RemoteChannel &) = delete;
+ RemoteChannel &operator=(RemoteChannel &&) = delete;
+
+ void defineParameter(const clap_param_info &info);
+ void setParameterValue(clap_id paramId, double value);
void close();
+ // Called when there is data to be read, non-blocking
+ void onRead();
+
+ // Called when data can be written, non-blocking
+ void onWrite();
+
+
+
+
private:
- int socket_;
+ using ReadBuffer = Buffer<uint8_t, 128 * 1024>;
+ using WriteBuffer = Buffer<uint8_t, 32 * 1024>;
+
+ void write(const uint8_t *data, size_t size);
+ WriteBuffer& nextWriteBuffer();
+
+ void parseInput();
+
+ Handler &handler_;
+ EventControl &evControl_;
+ clap_fd socket_;
+
+ ReadBuffer inputBuffer_;
+ std::queue<WriteBuffer> outputBuffers_;
};
} // namespace clap
\ No newline at end of file
diff --git a/examples/plugins/remote-gui.cc b/examples/plugins/remote-gui.cc
@@ -40,7 +40,7 @@ namespace clap {
::close(sockets[1]);
}
- channel_.reset(new RemoteChannel(sockets[0]));
+ channel_.reset(new RemoteChannel(*this, *this, sockets[0]));
return true;
#else
diff --git a/examples/plugins/remote-gui.hh b/examples/plugins/remote-gui.hh
@@ -10,7 +10,7 @@
#include "remote-channel.hh"
namespace clap {
- class RemoteGui : public AbstractGui {
+ class RemoteGui : public AbstractGui, public RemoteChannel::Handler, public RemoteChannel::EventControl {
RemoteGui(PluginHelper &plugin) : AbstractGui(plugin) {}
bool spawn();
@@ -27,6 +27,11 @@ namespace clap {
void close() noexcept override;
+ // RemoteChannel::Handler
+ void beginAdjust(clap_id paramId) override;
+ void adjust(clap_id paramId, double value) override;
+ void endAdjust(clap_id paramId) override;
+
private:
std::unique_ptr<RemoteChannel> channel_;