From a5a9c0a3566c45ad405e4eea6047cc8cd738d2ce Mon Sep 17 00:00:00 2001 From: Alexandre Bique Date: Mon, 16 Sep 2024 16:53:53 +0200 Subject: [PATCH] Work on undo example and remove the remote gui --- CMakePresets.json | 33 --- README.md | 53 +--- plugins/CMakeLists.txt | 16 +- plugins/core-plugin.cc | 19 +- plugins/core-plugin.hh | 2 + plugins/gui/CMakeLists.txt | 62 ++--- plugins/gui/abstract-gui-listener.hh | 3 + plugins/gui/abstract-gui.hh | 5 + plugins/gui/gui.cc | 18 +- plugins/gui/gui.hh | 7 + plugins/gui/remote-gui-factory-proxy.cc | 350 ------------------------ plugins/gui/remote-gui-factory-proxy.hh | 71 ----- plugins/gui/remote-gui-factory.cc | 321 ---------------------- plugins/gui/remote-gui-factory.hh | 60 ---- plugins/gui/remote-gui-listener.cc | 44 --- plugins/gui/remote-gui-listener.hh | 32 --- plugins/gui/remote-gui-proxy.cc | 302 -------------------- plugins/gui/remote-gui-proxy.hh | 65 ----- plugins/gui/threaded-gui-proxy.cc | 24 +- plugins/gui/threaded-gui-proxy.hh | 5 + plugins/gui/undo-proxy.cc | 45 +++ plugins/gui/undo-proxy.hh | 51 ++++ plugins/io/messages.hh | 10 + 23 files changed, 212 insertions(+), 1386 deletions(-) delete mode 100644 plugins/gui/remote-gui-factory-proxy.cc delete mode 100644 plugins/gui/remote-gui-factory-proxy.hh delete mode 100644 plugins/gui/remote-gui-factory.cc delete mode 100644 plugins/gui/remote-gui-factory.hh delete mode 100644 plugins/gui/remote-gui-listener.cc delete mode 100644 plugins/gui/remote-gui-listener.hh delete mode 100644 plugins/gui/remote-gui-proxy.cc delete mode 100644 plugins/gui/remote-gui-proxy.hh create mode 100644 plugins/gui/undo-proxy.cc create mode 100644 plugins/gui/undo-proxy.hh diff --git a/CMakePresets.json b/CMakePresets.json index 51e5be0..dcff45f 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -45,10 +45,6 @@ "type": "FILEPATH", "value": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake" }, - "CLAP_PLUGINS_REMOTE_GUI": { - "type": "BOOL", - "value": false - }, "CLAP_PLUGINS_EMBED_QML": { "type": "BOOL", "value": true @@ -63,24 +59,6 @@ "vcpkg" ] }, - { - "name": "ninja-system", - "description": "Ninja + System Libraries", - "binaryDir": "${sourceDir}/builds/${presetName}", - "inherits": [ - "base" - ], - "cacheVariables": { - "CLAP_PLUGINS_REMOTE_GUI": { - "type": "BOOL", - "value": true - }, - "CLAP_PLUGINS_EMBED_QML": { - "type": "BOOL", - "value": false - } - } - }, { "name": "ninja-headless", "description": "Ninja + System Libraries + Headless", @@ -121,12 +99,6 @@ "configurePreset": "ninja-vcpkg", "inherits": "base" }, - { - "name": "ninja-system", - "inherits": "base", - "description": "Build using Ninja and system libraries", - "configurePreset": "ninja-system" - }, { "name": "ninja-headless", "inherits": "base", @@ -152,11 +124,6 @@ "description": "Test using Ninja and VCPKG", "configurePreset": "ninja-vcpkg" }, - { - "name": "ninja-system", - "description": "Test using Ninja and system libraries", - "configurePreset": "ninja-system" - }, { "name": "ninja-headless", "description": "Test using Ninja and system libraries (headless)", diff --git a/README.md b/README.md index 54ecefe..20b1791 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,10 @@ - [Notes on GUI, static build vs dynamic build and symbols](#notes-on-gui-static-build-vs-dynamic-build-and-symbols) - [Building on various platforms](#building-on-various-platforms) - [Headless](#headless) - - [macOS, dynamic build with brew](#macos-dynamic-build-with-brew) - [macOS with vcpkg](#macos-with-vcpkg) - [Windows](#windows) - [Enable long path support](#enable-long-path-support) - [Build](#build) - - [Linux, using system libraries (dynamic)](#linux-using-system-libraries-dynamic) - [Linux, using vcpkg (static)](#linux-using-vcpkg-static) # Example Clap Plugins @@ -33,33 +31,14 @@ Objective-C classes but with the same, they will clash which will result in unde Qt uses a few Objective-C classes on macOS. So it is crucial to use `QT_NAMESPACE`. -We have two different strategies to work with that. -1. **local**: statically link every thing -2. **remote**: start the gui in a child process - -**1.** has the advantage of being simple to deploy. -**2.** is more complex due to its inter-process nature. It has a few advantages: -- if the GUI crash, the audio engine does not -- the GUI can use any libraries, won't be subject to symbol or library clash etc... - We abstracted the relation between the plugin and the GUI: [`AbstractGui`](plugins/gui/abstract-gui.hh) and [`AbstractGuiListener`](plugins/gui/abstract-gui-listener.hh) -which lets us transparently insert proxies to support the **remote** model. +which lets us transparently insert proxies. The GUI itself work with proxy objects to the parameters, transport info, ... They are then bound into QML objects. See [`Knob.qml`](plugins/gui/qml/clap/Knob.qml) and [`parameter-proxy.hh`](plugins/gui/parameter-proxy.hh). -We offer two options: -- static build, cmake preset: `ninja-vcpkg` or `vs-vcpkg` on Windows. -- dynamic builg, cmake preset: `ninja-system` - -Static builds are convenient for deployment as they are self containded. They use the **local** gui model. - -Dynamic builds will get your started quickly if your system provides Qt6, -and you have an host that do not expose the Qt symbols. -Static builds will require more time and space. - # Building on various platforms ## Headless @@ -73,21 +52,6 @@ cmake --preset ninja-headless cmake --build --preset ninja-headless ``` -## macOS, dynamic build with brew - -```shell -# Install dependencies -brew install qt6 boost ninja cmake - -# Checkout the code -git clone --recurse-submodules https://github.com/free-audio/clap-plugins -cd clap-plugins - -# Build -cmake --preset ninja-system -cmake --build --preset ninja-system -``` - ## macOS with vcpkg ```shell @@ -126,24 +90,15 @@ cd c-p scripts/build-gui.sh ``` -## Linux, using system libraries (dynamic) +## Linux, using vcpkg (static) ```bash # on unbuntu, adapt to your distribution and package manager -sudo apt install qt6-declarative-dev git ninja-build cmake +sudo apt install git ninja-build cmake # on archlinux, adapt to your distribution and package manager -sudo pacman -S qt boost git ninja cmake +sudo pacman -S git ninja cmake -git clone --recurse-submodules https://github.com/free-audio/clap-plugins -cd clap-plugins -cmake --preset ninja-system -cmake --build --preset ninja-system -``` - -## Linux, using vcpkg (static) - -```bash git clone --recurse-submodules https://github.com/free-audio/clap-plugins cd clap-plugins scripts/build-gui.sh diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index e1a2dce..83a0a8e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,4 +1,3 @@ -set(CLAP_PLUGINS_REMOTE_GUI TRUE CACHE BOOL "Should the GUI be ran from another process?") set(CLAP_PLUGINS_EMBED_QML TRUE CACHE BOOL "Embed QML resources into the plugin") set(CLAP_PLUGINS_HEADLESS FALSE CACHE BOOL "Compile the plugins without a GUI") @@ -99,21 +98,10 @@ set_target_properties(clap-plugins-core PROPERTIES CXX_STANDARD 20) set_target_properties(clap-plugins-core PROPERTIES POSITION_INDEPENDENT_CODE TRUE) if(NOT CLAP_PLUGINS_HEADLESS) - if(CLAP_PLUGINS_REMOTE_GUI) - target_compile_definitions(clap-plugins-core PUBLIC CLAP_REMOTE_GUI) - target_link_libraries(clap-plugins-core PUBLIC clap-plugin-remote-gui) - add_dependencies(clap-plugins-core clap-gui) - else() - target_compile_definitions(clap-plugins-core PUBLIC CLAP_LOCAL_GUI) - target_link_libraries(clap-plugins-core PUBLIC clap-plugin-local-gui) - endif() - - target_link_libraries(clap-plugins-core PUBLIC clap-io) + target_compile_definitions(clap-plugins-core PUBLIC CLAP_LOCAL_GUI) + target_link_libraries(clap-plugins-core PUBLIC clap-plugin-local-gui clap-io clap-plugin-gui-common) endif() -if (NOT CLAP_PLUGINS_HEADLESS) - target_link_libraries(clap-plugins-core PUBLIC clap-plugin-gui-common) -endif() target_link_libraries(clap-plugins-core PUBLIC clap-helpers) add_library(clap-plugins MODULE clap-entry.cc) diff --git a/plugins/core-plugin.cc b/plugins/core-plugin.cc index 8c60602..1cfc60c 100644 --- a/plugins/core-plugin.cc +++ b/plugins/core-plugin.cc @@ -122,7 +122,7 @@ namespace clap { try { ClapOStream os(stream); yas::binary_oarchive ar(os); - ar &_parameters; + ar & _parameters; } catch (...) { return false; } @@ -141,7 +141,7 @@ namespace clap { ClapIStream is(stream); #endif yas::binary_iarchive ar(is); - ar &_parameters; + ar & _parameters; } catch (...) { return false; } @@ -341,6 +341,21 @@ namespace clap { void CorePlugin::onGuiWindowClosed(bool wasDestroyed) { runOnMainThread([this, wasDestroyed] { _host.guiClosed(wasDestroyed); }); } + + void CorePlugin::onGuiUndo() { + runOnMainThread([this] { + if (_host.canUseUndo()) + _host.undoUndo(); + }); + } + + void CorePlugin::onGuiRedo() { + runOnMainThread([this] { + if (_host.canUseUndo()) + _host.undoRedo(); + }); + } + #endif // CLAP_PLUGINS_HEADLESS //------------------// diff --git a/plugins/core-plugin.hh b/plugins/core-plugin.hh index cba3d5f..92c577d 100644 --- a/plugins/core-plugin.hh +++ b/plugins/core-plugin.hh @@ -175,6 +175,8 @@ namespace clap { void onGuiParamEndAdjust(clap_id paramId) override; void onGuiSetTransportIsSubscribed(bool isSubscribed) override; void onGuiWindowClosed(bool wasDestroyed) override; + void onGuiUndo() override; + void onGuiRedo() override; #endif //------------------------// diff --git a/plugins/gui/CMakeLists.txt b/plugins/gui/CMakeLists.txt index c30963e..0810e65 100644 --- a/plugins/gui/CMakeLists.txt +++ b/plugins/gui/CMakeLists.txt @@ -24,7 +24,9 @@ add_library( track-info-proxy.hh track-info-proxy.cc transport-proxy.hh - transport-proxy.cc) + transport-proxy.cc + undo-proxy.hh + undo-proxy.cc) set_property(TARGET clap-plugin-gui PROPERTY CXX_STANDARD 20) target_link_libraries(clap-plugin-gui PUBLIC clap-io clap-plugin-gui-common) target_link_libraries(clap-plugin-gui PUBLIC Qt6::Quick) @@ -36,43 +38,21 @@ if(CLAP_PLUGINS_EMBED_QML) target_link_libraries(clap-plugin-gui PUBLIC clap-qml-skins clap-qml-lib clap-qml-libplugin) endif() -if(CLAP_PLUGINS_REMOTE_GUI) - # Code for having the GUI in a child process - add_library( - clap-plugin-remote-gui - remote-gui-factory-proxy.hh remote-gui-factory-proxy.cc remote-gui-proxy.hh - remote-gui-proxy.cc) - set_property(TARGET clap-plugin-remote-gui PROPERTY CXX_STANDARD 20) - set_property(TARGET clap-plugin-remote-gui PROPERTY POSITION_INDEPENDENT_CODE true) - target_link_libraries(clap-plugin-remote-gui PUBLIC clap-plugin-gui-common clap-io) - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - target_link_libraries(clap-plugin-remote-gui PUBLIC pthread) - endif() - - add_executable( - clap-gui gui-main.cc remote-gui-factory.hh remote-gui-factory.cc - remote-gui-listener.hh remote-gui-listener.cc) - target_link_libraries(clap-gui PUBLIC clap-plugin-gui) - set_target_properties(clap-gui PROPERTIES CXX_STANDARD 20) - - install(TARGETS clap-gui DESTINATION "bin") -else() - # Code for having the GUI with the plugin - add_library( - clap-plugin-local-gui - local-gui-factory.hh - local-gui-factory.cc - threaded-gui-factory.hh - threaded-gui-factory.cc - threaded-gui-proxy.hh - threaded-gui-proxy.cc - timer.hh - timer.cc - cf-timer.hh - cf-timer.cc - win32-timer.hh - win32-timer.cc) - set_property(TARGET clap-plugin-local-gui PROPERTY CXX_STANDARD 20) - set_property(TARGET clap-plugin-local-gui PROPERTY POSITION_INDEPENDENT_CODE true) - target_link_libraries(clap-plugin-local-gui PUBLIC clap-plugin-gui) -endif() +# Code for having the GUI with the plugin +add_library( + clap-plugin-local-gui + local-gui-factory.hh + local-gui-factory.cc + threaded-gui-factory.hh + threaded-gui-factory.cc + threaded-gui-proxy.hh + threaded-gui-proxy.cc + timer.hh + timer.cc + cf-timer.hh + cf-timer.cc + win32-timer.hh + win32-timer.cc) +set_property(TARGET clap-plugin-local-gui PROPERTY CXX_STANDARD 20) +set_property(TARGET clap-plugin-local-gui PROPERTY POSITION_INDEPENDENT_CODE true) +target_link_libraries(clap-plugin-local-gui PUBLIC clap-plugin-gui) diff --git a/plugins/gui/abstract-gui-listener.hh b/plugins/gui/abstract-gui-listener.hh index 85fb472..261fb07 100644 --- a/plugins/gui/abstract-gui-listener.hh +++ b/plugins/gui/abstract-gui-listener.hh @@ -21,5 +21,8 @@ namespace clap { virtual void onGuiSetTransportIsSubscribed(bool isSubscribed) = 0; virtual void onGuiWindowClosed(bool wasDestroyed) = 0; + + virtual void onGuiUndo() = 0; + virtual void onGuiRedo() = 0; }; } // namespace clap \ No newline at end of file diff --git a/plugins/gui/abstract-gui.hh b/plugins/gui/abstract-gui.hh index c646d38..c7cc3a2 100644 --- a/plugins/gui/abstract-gui.hh +++ b/plugins/gui/abstract-gui.hh @@ -47,6 +47,11 @@ namespace clap { virtual void destroy() = 0; + virtual void setCanUndo(bool can_undo) = 0; + virtual void setCanRedo(bool can_redo) = 0; + virtual void setUndoName(std::string name) = 0; + virtual void setRedoName(std::string name) = 0; + AbstractGuiListener& listener() const { return _listener; } protected: diff --git a/plugins/gui/gui.cc b/plugins/gui/gui.cc index 8e70626..5dae3c3 100644 --- a/plugins/gui/gui.cc +++ b/plugins/gui/gui.cc @@ -25,6 +25,7 @@ namespace clap { _pluginProxy = std::make_unique(*this); _transportProxy = std::make_unique(*this); _trackInfoProxy = std::make_unique(*this); + _undoProxy = std::make_unique(*this); //////////////////////// // QML initialization // @@ -35,6 +36,7 @@ namespace clap { qmlContext->setContextProperty("plugin", _pluginProxy.get()); qmlContext->setContextProperty("transport", _transportProxy.get()); qmlContext->setContextProperty("trackInfo", _trackInfoProxy.get()); + qmlContext->setContextProperty("undo", _undoProxy.get()); setRootScale(1); connect( @@ -83,7 +85,9 @@ namespace clap { p->setMappingIndication(color, label, description); } - void Gui::setParameterAutomationIndication(clap_id paramId, uint32_t automationState, clap_color color) { + void Gui::setParameterAutomationIndication(clap_id paramId, + uint32_t automationState, + clap_color color) { qDebug() << "clap-gui: setParameterAutomationIndication(" << paramId << ")"; auto p = _pluginProxy->param(paramId); assert(p); @@ -308,4 +312,16 @@ namespace clap { _pluginProxy.reset(); _isFloating = false; } + + void Gui::setCanUndo(bool can_undo) { _undoProxy->setCanUndo(can_undo); } + + void Gui::setCanRedo(bool can_redo) { _undoProxy->setCanRedo(can_redo); } + + void Gui::setUndoName(std::string name) { + _undoProxy->setUndoName(QString::fromStdString(name)); + } + + void Gui::setRedoName(std::string name) { + _undoProxy->setRedoName(QString::fromStdString(name)); + } } // namespace clap \ No newline at end of file diff --git a/plugins/gui/gui.hh b/plugins/gui/gui.hh index a64f370..5e0d164 100644 --- a/plugins/gui/gui.hh +++ b/plugins/gui/gui.hh @@ -7,6 +7,7 @@ #include "plugin-proxy.hh" #include "transport-proxy.hh" #include "track-info-proxy.hh" +#include "undo-proxy.hh" QT_BEGIN_NAMESPACE class QQuickView; @@ -71,6 +72,11 @@ namespace clap { void destroy() override; + void setCanUndo(bool can_undo) override; + void setCanRedo(bool can_redo) override; + void setUndoName(std::string name) override; + void setRedoName(std::string name) override; + auto &guiListener() const { return _listener; } private: @@ -89,6 +95,7 @@ namespace clap { std::unique_ptr _pluginProxy; std::unique_ptr _transportProxy; std::unique_ptr _trackInfoProxy; + std::unique_ptr _undoProxy; double _rootScale = 1.; }; diff --git a/plugins/gui/remote-gui-factory-proxy.cc b/plugins/gui/remote-gui-factory-proxy.cc deleted file mode 100644 index 526e0c0..0000000 --- a/plugins/gui/remote-gui-factory-proxy.cc +++ /dev/null @@ -1,350 +0,0 @@ -#if (defined(__unix__) || defined(__APPLE__)) -# include -# include -# include -# include -# include -#elif defined(_WIN32) -# include -#endif - -#include -#include -#include -#include -#include - -#include "../io/messages.hh" -#include "../io/remote-channel.hh" - -#include "abstract-gui-listener.hh" -#include "remote-gui-factory-proxy.hh" -#include "remote-gui-proxy.hh" - -namespace clap { -#ifdef _WIN32 - struct RemoteGuiWin32Data final { - STARTUPINFO _si; - PROCESS_INFORMATION _childInfo; - }; - - std::string escapeArg(const std::string &s) { - return "\"" + std::regex_replace(s, std::regex("\""), "\\\"") + "\""; - } -#endif - - std::weak_ptr RemoteGuiFactoryProxy::_instance; - - RemoteGuiFactoryProxy::RemoteGuiFactoryProxy(const std::string &guiPath) : _guiPath(guiPath) { - - if (!spawnChild()) { - _quit = true; - return; - } - - assert(!_thread); - _thread = std::make_unique([this] { run(); }); - } - - RemoteGuiFactoryProxy::~RemoteGuiFactoryProxy() { - if (_thread && _thread->joinable()) { - exec([this] { - messages::DestroyRequest rq; - _channel->sendRequestAsync(0, rq); - }); - _quit = true; - _thread->join(); - _thread.reset(); - } - } - - std::shared_ptr - RemoteGuiFactoryProxy::getInstance(const std::string &guiPath) { - auto ptr = _instance.lock(); - if (ptr) - return ptr; - ptr.reset(new RemoteGuiFactoryProxy(guiPath)); - _instance = ptr; - return ptr; - } - - std::unique_ptr RemoteGuiFactoryProxy::createGui(AbstractGuiListener &listener) { - messages::CreateClientRequest rq; - messages::CreateClientResponse rp; - - if (!_channel->sendRequestSync(0, rq, rp)) - return nullptr; - - auto ptr = std::make_shared(listener, *this, rp.clientId); - - _clientIdMap.emplace(rp.clientId, ptr); - _clients.emplace(&listener, ptr); - - return std::make_unique(_instance.lock(), ptr); - } - - void RemoteGuiFactoryProxy::releaseGui(GuiHandle &handle) { - auto g = dynamic_cast(&handle.gui()); - auto l = &g->listener(); - - _clients.erase(l); - _clientIdMap.erase(g->_clientId); - } - - bool RemoteGuiFactoryProxy::spawnChild() { -#if (defined(__unix__) || defined(__APPLE__)) - assert(_child == -1); -#elif defined(_WIN32) - assert(!_data); -#endif - assert(!_channel); - - static const constexpr size_t KPIPE_BUFSZ = 128 * 1024; - -#if (defined(__unix__) || defined(__APPLE__)) - /* create a socket pair */ - int sockets[2]; - if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sockets)) { - return false; - } - - _child = ::fork(); - if (_child == -1) { - ::close(sockets[0]); - ::close(sockets[1]); - return false; - } - - if (_child == 0) { - // Child - printf("About to start GUI: %s --socket %d\n", _guiPath.c_str(), sockets[1]); - - ::close(sockets[0]); - char socketStr[16]; - ::snprintf(socketStr, sizeof(socketStr), "%d", sockets[1]); - ::execl(_guiPath.c_str(), _guiPath.c_str(), "--socket", socketStr, nullptr); - printf("Failed to start child process: %m\n"); - std::terminate(); - } else { - // Parent - ::close(sockets[1]); - } - - _channel.reset(new RemoteChannel( - [this](const RemoteChannel::Message &msg) { onMessage(msg); }, true, *this, sockets[0])); - return true; -#else - std::ostringstream cmdline; - HANDLE pluginToGuiPipe; - HANDLE guiToPluginPipe; - SECURITY_ATTRIBUTES secAttrs; - char buffer[32 * 1024]; - char pipeInPath[256]; - char pipeOutPath[256]; - static int counter{0}; - - snprintf(pipeInPath, - sizeof(pipeInPath), - "\\\\.\\pipe\\clap-plugtogui.%08x.%08x", - GetCurrentProcessId(), - ++counter); - - snprintf(pipeOutPath, - sizeof(pipeOutPath), - "\\\\.\\pipe\\clap-guitoplug.%08x.%08x", - GetCurrentProcessId(), - counter); - - secAttrs.nLength = sizeof(secAttrs); - secAttrs.lpSecurityDescriptor = nullptr; - secAttrs.bInheritHandle = true; - - _data = std::make_unique(); - memset(&_data->_si, 0, sizeof(_data->_si)); - memset(&_data->_childInfo, 0, sizeof(_data->_childInfo)); - - pluginToGuiPipe = CreateNamedPipe(pipeInPath, - PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE | PIPE_WAIT, - 1, - KPIPE_BUFSZ, - KPIPE_BUFSZ, - 0, - nullptr); - if (!pluginToGuiPipe) - goto fail0; - - guiToPluginPipe = CreateNamedPipe(pipeOutPath, - PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE | PIPE_WAIT, - 1, - KPIPE_BUFSZ, - KPIPE_BUFSZ, - 0, - nullptr); - if (!guiToPluginPipe) - goto fail1; - - cmdline << escapeArg(path) << " --pipe-in " << pipeInPath << " --pipe-out " << pipeOutPath; - - snprintf(buffer, sizeof(buffer), "%s", cmdline.str().c_str()); - - if (!CreateProcess(nullptr, - buffer, - nullptr, - nullptr, - true, - NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | STARTF_USESTDHANDLES, - nullptr, - nullptr, - &_data->_si, - &_data->_childInfo)) - goto fail2; - - ConnectNamedPipe(guiToPluginPipe, nullptr); - ConnectNamedPipe(pluginToGuiPipe, nullptr); - - _channel = std::make_unique( - [this](const RemoteChannel::Message &msg) { onMessage(msg); }, - true, - guiToPluginPipe, - pluginToGuiPipe); - - return true; - - fail2: - CloseHandle(guiToPluginPipe); - fail1: - CloseHandle(pluginToGuiPipe); - fail0: - _data.reset(); - return false; -#endif - } - - void RemoteGuiFactoryProxy::waitChild() { -#ifdef __unix__ - if (_child == -1) - return; - int stat = 0; - int ret; - - do { - ret = ::waitpid(_child, &stat, 0); - } while (ret == -1 && errno == EINTR); - - _child = -1; - -#elif defined(_WIN32) - - if (!_data) - return; - - WaitForSingleObject(_data->_childInfo.hProcess, INFINITE); - _data.reset(); -#endif - } - - void RemoteGuiFactoryProxy::onMessage(const RemoteChannel::Message &msg) { - auto it = _clientIdMap.find(msg.header.clientId); - if (it == _clientIdMap.end()) - return; - - auto proxy = it->second.lock(); - if (!proxy) - return; - - proxy->onMessage(msg); - } - - void RemoteGuiFactoryProxy::execAsync(std::function cb) { - if (_thread->get_id() == std::this_thread::get_id()) - { - cb(); - return; - } - - std::lock_guard guard(_callbacksLock); - _callbacks.push(std::move(cb)); - } - - void RemoteGuiFactoryProxy::exec(const std::function &cb) { - if (_thread->get_id() == std::this_thread::get_id()) - { - cb(); - return; - } - - std::promise promise; - execAsync([&cb, &promise] { - cb(); - promise.set_value(); - }); - promise.get_future().wait(); - } - - void RemoteGuiFactoryProxy::run() { -#if (defined(__unix__) || defined(__APPLE__)) - posixLoop(); -#elif defined(_WIN32) - windowsLoop(); -#else -# error "unsupported target" -#endif - - _channel.reset(); - waitChild(); - } - - void RemoteGuiFactoryProxy::runCallbacks() { - std::lock_guard guard(_callbacksLock); - while (!_callbacks.empty()) { - _callbacks.front()(); - _callbacks.pop(); - } - } - - void RemoteGuiFactoryProxy::runGuiPoll() { - for (auto &client : _clients) - client.first->onGuiPoll(); - } - -#if (defined(__unix__) || defined(__APPLE__)) - void RemoteGuiFactoryProxy::posixLoop() { - while (!_quit && _channel->isOpen()) { - runCallbacks(); - runGuiPoll(); - - pollfd pfd; - pfd.fd = _channel->fd(); - pfd.events = POLLIN; - pfd.revents = 0; - - if (_pollFlags & CLAP_POSIX_FD_WRITE) - pfd.events |= POLLOUT; - if (_pollFlags & CLAP_POSIX_FD_ERROR) - pfd.events |= POLLERR; - - auto ret = poll(&pfd, 1, 10); - if (ret < 0) { - if (errno == EAGAIN || errno == EINTR || errno == ETIMEDOUT) - continue; - auto errStr = strerror(errno); - std::cerr << "[clap-plugins] poll(): " << errStr << std::endl; - return; - } - - if (ret == 0) - // hmmm why? - continue; - - if (pfd.revents & POLLIN) - _channel->tryReceive(); - if (pfd.revents & POLLOUT && _channel) - _channel->trySend(); - if (pfd.revents & POLLERR && _channel) - _channel->onError(); - } - } -#endif -} // namespace clap diff --git a/plugins/gui/remote-gui-factory-proxy.hh b/plugins/gui/remote-gui-factory-proxy.hh deleted file mode 100644 index a106fdc..0000000 --- a/plugins/gui/remote-gui-factory-proxy.hh +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "../io/remote-channel.hh" - -#include "abstract-gui-factory.hh" - -namespace clap { - - class RemoteGuiProxy; - - class RemoteGuiFactoryProxy : public AbstractGuiFactory, public RemoteChannel::EventControl { - public: - RemoteGuiFactoryProxy(const std::string &guiPath); - ~RemoteGuiFactoryProxy() override; - - static std::shared_ptr getInstance(const std::string &guiPath); - - std::unique_ptr createGui(AbstractGuiListener &listener) override; - - void releaseGui(GuiHandle &handle) override; - - void exec(const std::function& cb); - void execAsync(std::function cb); - - private: - friend class RemoteGuiProxy; - - static std::weak_ptr _instance; - - void run(); - void runCallbacks(); - void runGuiPoll(); - - bool spawnChild(); - void waitChild(); - - void onMessage(const RemoteChannel::Message &msg); - - // RemoteChannel::EventControl - void modifyFd(int flags) override { _pollFlags = flags; } - void removeFd() override {} - - const std::string _guiPath; - std::unique_ptr _thread; - std::unique_ptr _channel; - - bool _quit = false; - - std::unordered_map> _clients; - std::unordered_map> _clientIdMap; - - std::recursive_mutex _callbacksLock; - std::queue> _callbacks; - -#if (defined(__unix__) || defined(__APPLE__)) - void posixLoop(); - - pid_t _child = -1; - int _pollFlags = 0; - -#elif defined(_WIN32) - void windowsLoop(); - - std::unique_ptr _data; -#endif - }; -} // namespace clap \ No newline at end of file diff --git a/plugins/gui/remote-gui-factory.cc b/plugins/gui/remote-gui-factory.cc deleted file mode 100644 index c9622b1..0000000 --- a/plugins/gui/remote-gui-factory.cc +++ /dev/null @@ -1,321 +0,0 @@ -#include -#include -#include - -#include "remote-gui-factory.hh" -#include "remote-gui-listener.hh" - -namespace clap { - - RemoteGuiFactory::RemoteGuiFactory(int socket) { - //////////////////////// - // I/O initialization // - //////////////////////// - -#if defined(Q_OS_UNIX) - _channel.reset(new clap::RemoteChannel( - [this](const clap::RemoteChannel::Message &msg) { onMessage(msg); }, - false, - *this, - socket)); - - _socketReadNotifier.reset(new QSocketNotifier(socket, QSocketNotifier::Read, this)); - connect(_socketReadNotifier.get(), - &QSocketNotifier::activated, - [this](QSocketDescriptor socket, QSocketNotifier::Type type) { - _channel->tryReceive(); - if (!_channel->isOpen()) - QCoreApplication::quit(); - }); - - _socketWriteNotifier.reset(new QSocketNotifier(socket, QSocketNotifier::Write, this)); - connect(_socketWriteNotifier.get(), - &QSocketNotifier::activated, - [this](QSocketDescriptor socket, QSocketNotifier::Type type) { - _channel->trySend(); - if (!_channel->isOpen()) { - QCoreApplication::quit(); - } - }); - - _socketReadNotifier->setEnabled(true); - _socketWriteNotifier->setEnabled(false); -#elif defined(Q_OS_WINDOWS) - _remoteChannel.reset(new clap::RemoteChannel( - [this](const clap::RemoteChannel::Message &msg) { onMessage(msg); }, - false, - pipeInHandle, - pipeOutHandle, - [] { QCoreApplication::quit(); })); -#endif - } - - void RemoteGuiFactory::modifyFd(int flags) { - _socketReadNotifier->setEnabled(flags & CLAP_POSIX_FD_READ); - _socketWriteNotifier->setEnabled(flags & CLAP_POSIX_FD_WRITE); - } - - void RemoteGuiFactory::removeFd() { - _socketReadNotifier.reset(); - _socketWriteNotifier.reset(); - QCoreApplication::quit(); - } - - uint32_t RemoteGuiFactory::createClient() { - const uint32_t clientId = ++_nextClientId; - auto context = std::make_unique(clientId); - context->listenner = std::make_unique(*this, clientId); - context->client = std::make_unique(*context->listenner); - _guiClients.emplace(clientId, std::move(context)); - return clientId; - } - - void RemoteGuiFactory::destroyClient(uint32_t clientId) { - _guiClients.erase(clientId); - } - - Gui *RemoteGuiFactory::getClient(uint32_t clientId) const { - auto it = _guiClients.find(clientId); - if (it != _guiClients.end()) - return it->second->client.get(); - return nullptr; - } - - void RemoteGuiFactory::onMessage(const RemoteChannel::Message &msg) { - auto c = getClient(msg.header.clientId); - - switch (msg.header.type) { - case messages::kCreateClientRequest: { - assert(!c); - messages::CreateClientRequest rq; - messages::CreateClientResponse rp; - msg.get(rq); - - auto clientId = createClient(); - rp.clientId = clientId; - - _channel->sendResponseAsync(msg, rp); - break; - } - - case messages::kDestroyClientRequest: { - if (!c) - return; - - messages::DestroyClientRequest rq; - messages::DestroyClientResponse rp; - msg.get(rq); - - destroyClient(msg.header.clientId); - - _channel->sendResponseAsync(msg, rp); - break; - } - - case messages::kDestroyRequest: - assert(!c); - messages::DestroyResponse rp; - _channel->sendResponseAsync(msg, rp); - qApp->quit(); - break; - - case messages::kUpdateTransportRequest: { - if (!c) - return; - - messages::UpdateTransportRequest rq; - msg.get(rq); - c->updateTransport(rq.transport); - break; - } - - case messages::kUpdateTrackInfoRequest: { - if (!c) - return; - - messages::UpdateTrackInfoRequest rq; - msg.get(rq); - c->updateTrackInfo(rq.hasTrackInfo, rq.trackInfo); - break; - } - - case messages::kAddImportPathRequest: { - if (!c) - return; - - messages::AddImportPathRequest rq; - msg.get(rq); - c->addImportPath(rq.importPath); - break; - } - - case messages::kSetSkinRequest: { - if (!c) - return; - - messages::SetSkinRequest rq; - msg.get(rq); - c->setSkin(rq.skinUrl); - break; - } - - case messages::kDefineParameterRequest: { - if (!c) - return; - - messages::DefineParameterRequest rq; - msg.get(rq); - c->defineParameter(rq.info); - break; - } - - case messages::kSetParameterMappingIndicationRequest: { - if (!c) - return; - - messages::SetParameterMappingIndicationRequest rq; - msg.get(rq); - c->setParameterMappingIndication(rq.paramId, rq.hasIndication, rq.color, rq.label, rq.description); - break; - } - - case messages::kSetParameterAutomationIndicationRequest: { - if (!c) - return; - - messages::SetParameterAutomationIndicationRequest rq; - msg.get(rq); - c->setParameterAutomationIndication(rq.paramId, rq.automationState, rq.color); - break; - } - - case messages::kParameterValueRequest: { - if (!c) - return; - - messages::ParameterValueRequest rq; - msg.get(rq); - c->updateParameter(rq.paramId, rq.value, rq.modulation); - break; - } - - case messages::kCanResizeRequest: { - messages::CanResizeRequest rq; - messages::CanResizeResponse rp{false}; - msg.get(rq); - if (c) - rp.succeed = c->canResize(); - _channel->sendResponseAsync(msg, rp); - break; - } - - case messages::kGetSizeRequest: { - messages::GetSizeRequest rq; - messages::GetSizeResponse rp{0, 0, false}; - msg.get(rq); - if (c) - rp.succeed = c->getSize(&rp.width, &rp.height); - _channel->sendResponseAsync(msg, rp); - break; - } - - case messages::kSetSizeRequest: { - messages::SetSizeRequest rq; - messages::SetSizeResponse rp{false}; - msg.get(rq); - if (c) - rp.succeed = c->setSize(rq.width, rq.height); - _channel->sendResponseAsync(msg, rp); - break; - } - - case messages::kRoundSizeRequest: { - messages::RoundSizeRequest rq; - messages::RoundSizeResponse rp{0, 0, false}; - - if (c) { - msg.get(rq); - rp.width = rq.width; - rp.height = rq.height; - rp.succeed = c->roundSize(&rp.width, &rp.height); - } - - _channel->sendResponseAsync(msg, rp); - break; - } - - case messages::kSetScaleRequest: { - messages::SetScaleRequest rq; - messages::SetScaleResponse rp{false}; - msg.get(rq); - if (c) - rp.succeed = c->setScale(rq.scale); - _channel->sendResponseAsync(msg, rp); - break; - } - - case messages::kOpenWindowRequest: { - messages::OpenWindowRequest rq; - messages::AttachResponse rp{false}; - msg.get(rq); - if (c) - rp.succeed = c->openWindow(); - _channel->sendResponseAsync(msg, rp); - break; - } - - case messages::kAttachX11Request: { - messages::AttachX11Request rq; - messages::AttachResponse rp{false}; - msg.get(rq); - if (c) - rp.succeed = c->attachX11(rq.window); - _channel->sendResponseAsync(msg, rp); - break; - } - - case messages::kAttachWin32Request: { - messages::AttachWin32Request rq; - messages::AttachResponse rp{false}; - msg.get(rq); - if (c) - rp.succeed = c->attachWin32(rq.hwnd); - _channel->sendResponseAsync(msg, rp); - break; - } - - case messages::kAttachCocoaRequest: { - messages::AttachCocoaRequest rq; - messages::AttachResponse rp{false}; - msg.get(rq); - if (c) - rp.succeed = c->attachCocoa(rq.nsView); - _channel->sendResponseAsync(msg, rp); - break; - } - - case messages::kShowRequest: { - messages::ShowRequest rq; - messages::ShowResponse rp; - msg.get(rq); - if (c) - rp.succeed = c->show(); - _channel->sendResponseAsync(msg, rp); - break; - } - - case messages::kHideRequest: { - messages::HideRequest rq; - messages::HideResponse rp; - msg.get(rq); - if (c) - rp.succeed = c->hide(); - _channel->sendResponseAsync(msg, rp); - break; - } - } - } - - RemoteGuiFactory::ClientContext::ClientContext(uint32_t cId) : clientId(cId) {} - RemoteGuiFactory::ClientContext::~ClientContext() = default; -} // namespace clap \ No newline at end of file diff --git a/plugins/gui/remote-gui-factory.hh b/plugins/gui/remote-gui-factory.hh deleted file mode 100644 index 0c85ae2..0000000 --- a/plugins/gui/remote-gui-factory.hh +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include -#include - -#include - -#include "../io/messages.hh" -#include "../io/remote-channel.hh" - -#include "gui.hh" - -QT_BEGIN_NAMESPACE -class QSocketNotifier; -QT_END_NAMESPACE - -namespace clap { - class RemoteGuiListener; - class RemoteGuiFactory : public QObject, public BasicRemoteChannel::EventControl { - public: - RemoteGuiFactory(int socket); - - RemoteGuiFactory(void *pipeIn, void *pipeOut); - - private: - uint32_t createClient(); - void destroyClient(uint32_t clientId); - - Gui *getClient(uint32_t clientId) const; - - template - Gui *getClient(const T &msg) const { - return getClient(msg.clientId); - } - - void onMessage(const clap::RemoteChannel::Message &msg); - - void modifyFd(int flags) override; - void removeFd() override; - - friend class RemoteGuiListener; - - struct ClientContext { - ClientContext(uint32_t cId); - ~ClientContext(); - - const uint32_t clientId = 0; - std::unique_ptr listenner; - std::unique_ptr client; - }; - - uint32_t _nextClientId = 0; - std::unordered_map> _guiClients; - - std::unique_ptr _channel; - std::unique_ptr _socketReadNotifier; - std::unique_ptr _socketWriteNotifier; - }; - -} // namespace clap \ No newline at end of file diff --git a/plugins/gui/remote-gui-listener.cc b/plugins/gui/remote-gui-listener.cc deleted file mode 100644 index 4d68238..0000000 --- a/plugins/gui/remote-gui-listener.cc +++ /dev/null @@ -1,44 +0,0 @@ -#include "../io/remote-channel.hh" - -#include "remote-gui-factory.hh" -#include "remote-gui-listener.hh" - -namespace clap { - - RemoteGuiListener::RemoteGuiListener(RemoteGuiFactory &clientFactory, uint32_t clientId) - : _clientFactory(clientFactory), _clientId(clientId) {} - - RemoteGuiListener::~RemoteGuiListener() = default; - - void RemoteGuiListener::onGuiPoll() {} - - void RemoteGuiListener::onGuiParamBeginAdjust(clap_id paramId) { - messages::ParamBeginAdjustRequest rq; - rq.paramId = paramId; - _clientFactory._channel->sendRequestAsync(_clientId, rq); - } - - void RemoteGuiListener::onGuiParamEndAdjust(clap_id paramId) { - messages::ParamEndAdjustRequest rq; - rq.paramId = paramId; - _clientFactory._channel->sendRequestAsync(_clientId, rq); - } - - void RemoteGuiListener::onGuiParamAdjust(clap_id paramId, double value) { - messages::ParamAdjustRequest rq; - rq.paramId = paramId; - rq.value = value; - _clientFactory._channel->sendRequestAsync(_clientId, rq); - } - - void RemoteGuiListener::onGuiSetTransportIsSubscribed(bool isSubscribed) { - messages::SubscribeToTransportRequest rq; - rq.isSubscribed = isSubscribed; - _clientFactory._channel->sendRequestAsync(_clientId, rq); - } - - void RemoteGuiListener::onGuiWindowClosed(bool wasDestroyed) { - messages::WindowClosedNotification notification{wasDestroyed}; - _clientFactory._channel->sendRequestAsync(_clientId, notification); - } -} // namespace clap \ No newline at end of file diff --git a/plugins/gui/remote-gui-listener.hh b/plugins/gui/remote-gui-listener.hh deleted file mode 100644 index 0c45d05..0000000 --- a/plugins/gui/remote-gui-listener.hh +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include - -#include "../io/remote-channel.hh" - -#include "abstract-gui-listener.hh" - -namespace clap { - class RemoteGuiFactory; - class RemoteGuiListener : public AbstractGuiListener { - public: - RemoteGuiListener(RemoteGuiFactory& clientFactory, uint32_t clientId); - ~RemoteGuiListener() override; - - void onGuiPoll() override; - - void onGuiParamBeginAdjust(clap_id paramId) override; - void onGuiParamAdjust(clap_id paramId, double value) override; - void onGuiParamEndAdjust(clap_id paramId) override; - void onGuiSetTransportIsSubscribed(bool isSubscribed) override; - - void onGuiWindowClosed(bool wasDestroyed) override; - - protected: - friend class RemoteGuiFactory; - - RemoteGuiFactory& _clientFactory; - const uint32_t _clientId; - }; - -} // namespace clap \ No newline at end of file diff --git a/plugins/gui/remote-gui-proxy.cc b/plugins/gui/remote-gui-proxy.cc deleted file mode 100644 index 1daca61..0000000 --- a/plugins/gui/remote-gui-proxy.cc +++ /dev/null @@ -1,302 +0,0 @@ -#include "../io/messages.hh" -#include "../io/remote-channel.hh" - -#include "abstract-gui-listener.hh" -#include "remote-gui-factory-proxy.hh" -#include "remote-gui-proxy.hh" - -namespace clap { - RemoteGuiProxy::RemoteGuiProxy(AbstractGuiListener &listener, - RemoteGuiFactoryProxy &factory, - uint32_t clientId) - : super(listener), _clientFactory(factory), _clientId(clientId) {} - - void RemoteGuiProxy::addImportPath(const std::string &importPath) { - messages::AddImportPathRequest rq; - snprintf(rq.importPath, sizeof(rq.importPath), "%s", importPath.c_str()); - - _clientFactory.exec([&] { _clientFactory._channel->sendRequestAsync(_clientId, rq); }); - } - - void RemoteGuiProxy::setSkin(const std::string &skinUrl) { - messages::SetSkinRequest rq; - snprintf(rq.skinUrl, sizeof(rq.skinUrl), "%s", skinUrl.c_str()); - - _clientFactory.exec([&] { _clientFactory._channel->sendRequestAsync(_clientId, rq); }); - } - - void RemoteGuiProxy::defineParameter(const clap_param_info &info) { - messages::DefineParameterRequest rq{info}; - _clientFactory.exec([&] { _clientFactory._channel->sendRequestAsync(_clientId, rq); }); - } - - void RemoteGuiProxy::updateParameter(clap_id paramId, double value, double modAmount) { - messages::ParameterValueRequest rq{paramId, value, modAmount}; - _clientFactory.exec([&] { _clientFactory._channel->sendRequestAsync(_clientId, rq); }); - } - - void RemoteGuiProxy::setParameterMappingIndication(clap_id paramId, - bool hasIndication, - clap_color color, - const char *label, - const char *description) { - messages::SetParameterMappingIndicationRequest rq{paramId, hasIndication, color}; - snprintf(rq.label, sizeof(rq.label), "%s", label); - snprintf(rq.description, sizeof(rq.description), "%s", description); - _clientFactory.exec([&] { _clientFactory._channel->sendRequestAsync(_clientId, rq); }); - } - - void RemoteGuiProxy::setParameterAutomationIndication(clap_id paramId, - uint32_t automationState, - clap_color color) { - messages::SetParameterAutomationIndicationRequest rq{paramId, automationState, color}; - _clientFactory.exec([&] { _clientFactory._channel->sendRequestAsync(_clientId, rq); }); - } - - bool RemoteGuiProxy::canResize() { - bool sent = false; - messages::CanResizeRequest request; - messages::CanResizeResponse response; - - _clientFactory.exec( - [&] { sent = _clientFactory._channel->sendRequestSync(_clientId, request, response); }); - - if (!sent) - return false; - - return response.succeed; - } - - bool RemoteGuiProxy::getSize(uint32_t *width, uint32_t *height) { - bool sent = false; - messages::GetSizeRequest request; - messages::GetSizeResponse response; - - _clientFactory.exec( - [&] { sent = _clientFactory._channel->sendRequestSync(_clientId, request, response); }); - - if (!sent) - return false; - - if (!response.succeed) - return false; - - *width = response.width; - *height = response.height; - return true; - } - - bool RemoteGuiProxy::setSize(uint32_t width, uint32_t height) { - bool sent = false; - messages::SetSizeRequest request{width, height}; - messages::SetSizeResponse response; - - _clientFactory.exec( - [&] { sent = _clientFactory._channel->sendRequestSync(_clientId, request, response); }); - - if (!sent) - return false; - - return response.succeed; - } - - bool RemoteGuiProxy::roundSize(uint32_t *width, uint32_t *height) { - bool sent = false; - messages::RoundSizeRequest request; - messages::RoundSizeResponse response; - - request.width = *width; - request.height = *height; - - _clientFactory.exec( - [&] { sent = _clientFactory._channel->sendRequestSync(_clientId, request, response); }); - - if (!sent) - return false; - - if (!response.succeed) - return false; - - *width = response.width; - *height = response.height; - return true; - } - - bool RemoteGuiProxy::setScale(double scale) { - bool sent = false; - messages::SetScaleRequest request{scale}; - messages::SetScaleResponse response; - - _clientFactory.exec( - [&] { sent = _clientFactory._channel->sendRequestSync(_clientId, request, response); }); - - if (!sent) - return false; - - return response.succeed; - } - - bool RemoteGuiProxy::show() { - bool sent = false; - messages::ShowRequest request; - - _clientFactory.exec( - [&] { sent = _clientFactory._channel->sendRequestAsync(_clientId, request); }); - - return sent; - } - - bool RemoteGuiProxy::hide() { - bool sent = false; - messages::HideRequest request; - - _clientFactory.exec( - [&] { sent = _clientFactory._channel->sendRequestAsync(_clientId, request); }); - - return sent; - } - - void RemoteGuiProxy::destroy() { - if (!_clientFactory._channel) - return; - - _clientFactory.exec([&] { - messages::DestroyClientRequest request; - _clientFactory._channel->sendRequestAsync(_clientId, request); - }); - } - - bool RemoteGuiProxy::openWindow() { - bool sent = false; - messages::OpenWindowRequest request; - messages::AttachResponse response; - - _clientFactory.exec( - [&] { sent = _clientFactory._channel->sendRequestSync(_clientId, request, response); }); - - return sent; - } - - bool RemoteGuiProxy::attachCocoa(clap_nsview nsView) { - bool sent = false; - messages::AttachCocoaRequest request{nsView}; - messages::AttachResponse response; - - _clientFactory.exec( - [&] { sent = _clientFactory._channel->sendRequestSync(_clientId, request, response); }); - - return sent; - } - - bool RemoteGuiProxy::attachWin32(clap_hwnd window) { - bool sent = false; - messages::AttachWin32Request request{window}; - messages::AttachResponse response; - - _clientFactory.exec( - [&] { sent = _clientFactory._channel->sendRequestSync(_clientId, request, response); }); - - return sent; - } - - bool RemoteGuiProxy::attachX11(clap_xwnd window) { - bool sent = false; - messages::AttachX11Request request{window}; - messages::AttachResponse response; - - _clientFactory.exec( - [&] { sent = _clientFactory._channel->sendRequestSync(_clientId, request, response); }); - - return sent; - } - - bool RemoteGuiProxy::setTransientCocoa(clap_nsview nsView) { - bool sent = false; - messages::SetTransientCocoaRequest request{nsView}; - messages::SetTransientResponse response; - - _clientFactory.exec( - [&] { sent = _clientFactory._channel->sendRequestSync(_clientId, request, response); }); - - return sent; - } - - bool RemoteGuiProxy::setTransientWin32(clap_hwnd window) { - bool sent = false; - messages::SetTransientWin32Request request{window}; - messages::SetTransientResponse response; - - _clientFactory.exec( - [&] { sent = _clientFactory._channel->sendRequestSync(_clientId, request, response); }); - - return sent; - } - - bool RemoteGuiProxy::setTransientX11(clap_xwnd window) { - bool sent = false; - messages::SetTransientX11Request request{window}; - messages::SetTransientResponse response; - - _clientFactory.exec( - [&] { sent = _clientFactory._channel->sendRequestSync(_clientId, request, response); }); - - return sent; - } - - void RemoteGuiProxy::clearTransport() { - messages::UpdateTransportRequest rq{false}; - - _clientFactory.exec([&] { _clientFactory._channel->sendRequestAsync(_clientId, rq); }); - } - - void RemoteGuiProxy::updateTransport(const clap_event_transport &transport) { - messages::UpdateTransportRequest rq{true, transport}; - _clientFactory.exec([&] { _clientFactory._channel->sendRequestAsync(_clientId, rq); }); - } - - void RemoteGuiProxy::updateTrackInfo(bool hasTrackInfo, const clap_track_info &info) { - messages::UpdateTrackInfoRequest rq{hasTrackInfo, info}; - _clientFactory.exec([&] { _clientFactory._channel->sendRequestAsync(_clientId, rq); }); - } - - void RemoteGuiProxy::onMessage(const RemoteChannel::Message &msg) { - assert(msg.header.clientId == _clientId); - - switch (msg.header.type) { - case messages::kParamBeginAdjustRequest: { - messages::ParamBeginAdjustRequest rq; - msg.get(rq); - _listener.onGuiParamBeginAdjust(rq.paramId); - break; - } - - case messages::kParamEndAdjustRequest: { - messages::ParamEndAdjustRequest rq; - msg.get(rq); - _listener.onGuiParamEndAdjust(rq.paramId); - break; - } - - case messages::kParamAdjustRequest: { - messages::ParamAdjustRequest rq; - msg.get(rq); - _listener.onGuiParamAdjust(rq.paramId, rq.value); - break; - } - - case messages::kSubscribeToTransportRequest: { - messages::SubscribeToTransportRequest rq; - msg.get(rq); - _listener.onGuiSetTransportIsSubscribed(rq.isSubscribed); - break; - } - - case messages::kWindowClosedNotification: { - messages::WindowClosedNotification notification; - msg.get(notification); - _listener.onGuiWindowClosed(notification.wasDestroyed); - break; - } - } - } -} // namespace clap diff --git a/plugins/gui/remote-gui-proxy.hh b/plugins/gui/remote-gui-proxy.hh deleted file mode 100644 index aa72e0f..0000000 --- a/plugins/gui/remote-gui-proxy.hh +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include "../io/remote-channel.hh" - -#include "abstract-gui.hh" - -namespace clap { - class RemoteGuiFactoryProxy; - class RemoteGuiProxy : public AbstractGui { - using super = AbstractGui; - - public: - RemoteGuiProxy(AbstractGuiListener &listener, - RemoteGuiFactoryProxy &factory, - uint32_t clientId); - - void addImportPath(const std::string &importPath) override; - void setSkin(const std::string &skinPath) override; - - void defineParameter(const clap_param_info ¶mInfo) override; - void updateParameter(clap_id paramId, double value, double modAmount) override; - void setParameterMappingIndication(clap_id paramId, - bool hasIndication, - clap_color color, - const char *label, - const char *description) override; - void setParameterAutomationIndication(clap_id paramId, - uint32_t automationState, - clap_color color) override; - - void clearTransport() override; - void updateTransport(const clap_event_transport &transport) override; - - void updateTrackInfo(bool hasTrackInfo, const clap_track_info &info) override; - - bool openWindow() override; - - bool attachCocoa(clap_nsview nsView) override; - bool attachWin32(clap_hwnd window) override; - bool attachX11(clap_xwnd window) override; - - bool setTransientX11(clap_xwnd window) override; - bool setTransientWin32(clap_hwnd window) override; - bool setTransientCocoa(clap_nsview nsView) override; - - bool canResize() override; - bool getSize(uint32_t *width, uint32_t *height) override; - bool setSize(uint32_t width, uint32_t height) override; - bool roundSize(uint32_t *width, uint32_t *height) override; - bool setScale(double scale) override; - - bool show() override; - bool hide() override; - - void destroy() override; - - private: - friend class RemoteGuiFactoryProxy; - - void onMessage(const RemoteChannel::Message &msg); - - RemoteGuiFactoryProxy &_clientFactory; - const uint32_t _clientId; - }; -} // namespace clap \ No newline at end of file diff --git a/plugins/gui/threaded-gui-proxy.cc b/plugins/gui/threaded-gui-proxy.cc index e522b83..5cba4d0 100644 --- a/plugins/gui/threaded-gui-proxy.cc +++ b/plugins/gui/threaded-gui-proxy.cc @@ -70,7 +70,9 @@ namespace clap { void ThreadedGuiProxy::updateTrackInfo(bool hasTrackInfo, const clap_track_info &info) { QMetaObject::invokeMethod( - _gui.get(), [=, this] { _gui->updateTrackInfo(hasTrackInfo, info); }, Qt::QueuedConnection); + _gui.get(), + [=, this] { _gui->updateTrackInfo(hasTrackInfo, info); }, + Qt::QueuedConnection); } bool ThreadedGuiProxy::openWindow() { @@ -199,4 +201,24 @@ namespace clap { QMetaObject::invokeMethod( _gui.get(), [=, this] { _gui->destroy(); }, Qt::BlockingQueuedConnection); } + + void ThreadedGuiProxy::setCanUndo(bool can_undo) { + QMetaObject::invokeMethod( + _gui.get(), [=, this] { _gui->setCanUndo(can_undo); }, Qt::BlockingQueuedConnection); + } + + void ThreadedGuiProxy::setCanRedo(bool can_redo) { + QMetaObject::invokeMethod( + _gui.get(), [=, this] { _gui->setCanRedo(can_redo); }, Qt::BlockingQueuedConnection); + } + + void ThreadedGuiProxy::setUndoName(std::string name) { + QMetaObject::invokeMethod( + _gui.get(), [=, this] { _gui->setUndoName(name); }, Qt::BlockingQueuedConnection); + } + + void ThreadedGuiProxy::setRedoName(std::string name) { + QMetaObject::invokeMethod( + _gui.get(), [=, this] { _gui->setRedoName(name); }, Qt::BlockingQueuedConnection); + } } // namespace clap diff --git a/plugins/gui/threaded-gui-proxy.hh b/plugins/gui/threaded-gui-proxy.hh index 19cd090..45a3699 100644 --- a/plugins/gui/threaded-gui-proxy.hh +++ b/plugins/gui/threaded-gui-proxy.hh @@ -53,6 +53,11 @@ namespace clap { void destroy() override; + void setCanUndo(bool can_undo) override; + void setCanRedo(bool can_redo) override; + void setUndoName(std::string name) override; + void setRedoName(std::string name) override; + auto gui() { return _gui; } private: diff --git a/plugins/gui/undo-proxy.cc b/plugins/gui/undo-proxy.cc new file mode 100644 index 0000000..1d8acb4 --- /dev/null +++ b/plugins/gui/undo-proxy.cc @@ -0,0 +1,45 @@ +#include "undo-proxy.hh" +#include "abstract-gui-listener.hh" +#include "gui.hh" + +namespace clap { + UndoProxy::UndoProxy(Gui &client) : _client(client) {} + + void UndoProxy::setCanUndo(bool can) { + if (_canUndo == can) + return; + _canUndo = can; + canUndoChanged(); + } + + void UndoProxy::setCanRedo(bool can) { + if (_canRedo == can) + return; + _canRedo = can; + canRedoChanged(); + } + + void UndoProxy::setUndoName(QString name) { + if (_undoName == name) + return; + _undoName = std::move(name); + undoNameChanged(); + } + + void UndoProxy::setRedoName(QString name) { + if (_redoName == name) + return; + _redoName = std::move(name); + redoNameChanged(); + } + + void UndoProxy::undo() { + if (_canUndo) + _client.guiListener().onGuiUndo(); + } + + void UndoProxy::redo() { + if (_canRedo) + _client.guiListener().onGuiRedo(); + } +} // namespace clap \ No newline at end of file diff --git a/plugins/gui/undo-proxy.hh b/plugins/gui/undo-proxy.hh new file mode 100644 index 0000000..829afed --- /dev/null +++ b/plugins/gui/undo-proxy.hh @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +namespace clap { + + class Gui; + class UndoProxy : public QObject { + using super = QObject; + + Q_OBJECT + + Q_PROPERTY(bool hasUndo READ hasUndo NOTIFY canUndoChanged) + Q_PROPERTY(bool hasRedo READ hasRedo NOTIFY canRedoChanged) + Q_PROPERTY(QString undoName READ undoName NOTIFY undoNameChanged) + Q_PROPERTY(QString redoName READ redoName NOTIFY redoNameChanged) + + public: + explicit UndoProxy(Gui &client); + + void setCanUndo(bool can); + void setCanRedo(bool can); + + void setUndoName(QString name); + void setRedoName(QString name); + + [[nodiscard]] const bool hasUndo() const noexcept { return _canUndo; } + [[nodiscard]] const bool hasRedo() const noexcept { return _canRedo; } + + [[nodiscard]] const QString &undoName() const noexcept { return _undoName; } + [[nodiscard]] const QString &redoName() const noexcept { return _redoName; } + + Q_INVOKABLE void undo(); + Q_INVOKABLE void redo(); + + signals: + void canUndoChanged(); + void canRedoChanged(); + void undoNameChanged(); + void redoNameChanged(); + + private: + Gui &_client; + QString _undoName; + QString _redoName; + bool _canUndo{false}; + bool _canRedo{false}; + }; + +} // namespace clap \ No newline at end of file diff --git a/plugins/io/messages.hh b/plugins/io/messages.hh index bfe4028..fc6e1c2 100644 --- a/plugins/io/messages.hh +++ b/plugins/io/messages.hh @@ -59,6 +59,8 @@ namespace clap::messages { kResizeRequest, kResizeResponse, kWindowClosedNotification, + kUndoRequest, + kRedoRequest, }; struct CreateClientRequest final { @@ -291,4 +293,12 @@ namespace clap::messages { static const constexpr Type type = kWindowClosedNotification; bool wasDestroyed; }; + + struct UndoRequest final { + static const constexpr Type type = kUndoRequest; + }; + + struct RedoRequest final { + static const constexpr Type type = kRedoRequest; + }; } // namespace clap::messages \ No newline at end of file