@soundor/vst3-host 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CMakeLists.txt +194 -0
  2. package/CMakePresets.json +115 -0
  3. package/LICENSE +21 -0
  4. package/NOTICE.md +17 -0
  5. package/README.md +40 -2
  6. package/cmake/Vst3HostDependencies.cmake +179 -0
  7. package/dist/cli.d.mts +1 -0
  8. package/dist/cli.mjs +88 -0
  9. package/dist/cli.mjs.map +1 -0
  10. package/dist/index.d.mts +586 -0
  11. package/dist/index.d.mts.map +1 -0
  12. package/dist/index.mjs +1 -0
  13. package/dist/prepare-ayjTVhfk.mjs +1149 -0
  14. package/dist/prepare-ayjTVhfk.mjs.map +1 -0
  15. package/native/src/Bundle.cpp +253 -0
  16. package/native/src/Bundle.h +32 -0
  17. package/native/src/EditorArea.h +92 -0
  18. package/native/src/EditorAreaMac.mm +52 -0
  19. package/native/src/EditorAreaWindows.cpp +66 -0
  20. package/native/src/EditorAreaX11.cpp +512 -0
  21. package/native/src/EditorWindow.h +72 -0
  22. package/native/src/EditorWindowMac.mm +134 -0
  23. package/native/src/EditorWindowWindows.cpp +148 -0
  24. package/native/src/EventLoop.h +50 -0
  25. package/native/src/EventLoopLinux.cpp +204 -0
  26. package/native/src/EventLoopMac.mm +146 -0
  27. package/native/src/EventLoopWindows.cpp +132 -0
  28. package/native/src/HostContext.cpp +217 -0
  29. package/native/src/HostContext.h +116 -0
  30. package/native/src/HostInterface.cpp +1026 -0
  31. package/native/src/HostInterface.h +138 -0
  32. package/native/src/HostWindow.cpp +901 -0
  33. package/native/src/HostWindow.h +187 -0
  34. package/native/src/Ipc.cpp +158 -0
  35. package/native/src/Ipc.h +91 -0
  36. package/native/src/Json.cpp +426 -0
  37. package/native/src/Json.h +85 -0
  38. package/native/src/LiveAudio.cpp +416 -0
  39. package/native/src/LiveAudio.h +121 -0
  40. package/native/src/Main.cpp +178 -0
  41. package/native/src/Paths.h +20 -0
  42. package/native/src/Plugin.cpp +1865 -0
  43. package/native/src/Plugin.h +321 -0
  44. package/native/src/Session.cpp +656 -0
  45. package/native/src/Session.h +82 -0
  46. package/native/src/Settings.cpp +93 -0
  47. package/native/src/Settings.h +23 -0
  48. package/native/src/Version.h.in +11 -0
  49. package/native/src/X11Library.cpp +127 -0
  50. package/native/src/X11Library.h +83 -0
  51. package/native/tools/Embed.cpp +49 -0
  52. package/package.json +58 -3
  53. package/protocol.json +7 -0
  54. package/targets.json +33 -0
@@ -0,0 +1,146 @@
1
+ #include "EventLoop.h"
2
+
3
+ #import <Cocoa/Cocoa.h>
4
+
5
+ #include <algorithm>
6
+ #include <map>
7
+ #include <memory>
8
+
9
+ namespace soundor::vst3host
10
+ {
11
+ namespace
12
+ {
13
+ // Work is posted to the main dispatch queue, which the main run loop
14
+ // drains in both modes. Headless mode never touches NSApplication.
15
+ class MacEventLoop final : public EventLoop
16
+ {
17
+ public:
18
+ explicit MacEventLoop(bool ui) : ui_(ui)
19
+ {
20
+ if (ui_)
21
+ {
22
+ [NSApplication sharedApplication];
23
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
24
+ [NSApp finishLaunching];
25
+ }
26
+ else
27
+ {
28
+ // A run loop without a source returns at once; this timer
29
+ // keeps CFRunLoopRunInMode waiting for posted work.
30
+ keepAlive_ = CFRunLoopTimerCreate(
31
+ nullptr, CFAbsoluteTimeGetCurrent() + 1e9, 1e9, 0, 0, [](CFRunLoopTimerRef, void*) {}, nullptr);
32
+ CFRunLoopAddTimer(CFRunLoopGetMain(), keepAlive_, kCFRunLoopCommonModes);
33
+ }
34
+ }
35
+
36
+ ~MacEventLoop() override
37
+ {
38
+ for (auto& [id, timer] : timers_)
39
+ {
40
+ CFRunLoopTimerInvalidate(timer->timer);
41
+ CFRelease(timer->timer);
42
+ }
43
+ if (keepAlive_ != nullptr)
44
+ {
45
+ CFRunLoopTimerInvalidate(keepAlive_);
46
+ CFRelease(keepAlive_);
47
+ }
48
+ }
49
+
50
+ void run() override
51
+ {
52
+ running_ = true;
53
+ while (running_)
54
+ {
55
+ @autoreleasepool
56
+ {
57
+ if (ui_ && waiter_)
58
+ {
59
+ const auto wait = waiter_;
60
+ wait(0.25);
61
+ }
62
+ else if (ui_)
63
+ {
64
+ NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
65
+ untilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]
66
+ inMode:NSDefaultRunLoopMode
67
+ dequeue:YES];
68
+ if (event != nil)
69
+ {
70
+ [NSApp sendEvent:event];
71
+ [NSApp updateWindows];
72
+ }
73
+ }
74
+ else
75
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.25, true);
76
+ }
77
+ }
78
+ }
79
+
80
+ void quit() override { running_ = false; }
81
+
82
+ void post(std::function<void()> task) override
83
+ {
84
+ auto* heap = new std::function<void()>(std::move(task));
85
+ dispatch_async(dispatch_get_main_queue(), ^{
86
+ (*heap)();
87
+ delete heap;
88
+ });
89
+ CFRunLoopWakeUp(CFRunLoopGetMain());
90
+ }
91
+
92
+ Id addTimer(unsigned milliseconds, std::function<void()> fire) override
93
+ {
94
+ const Id id = ++nextId_;
95
+ auto timer = std::make_unique<Timer>();
96
+ timer->fire = std::move(fire);
97
+ const double interval = std::max(1U, milliseconds) / 1000.0;
98
+ CFRunLoopTimerContext context {};
99
+ context.info = timer.get();
100
+ timer->timer = CFRunLoopTimerCreate(
101
+ nullptr, CFAbsoluteTimeGetCurrent() + interval, interval, 0, 0,
102
+ [](CFRunLoopTimerRef, void* info)
103
+ {
104
+ // A copy: the callback may remove this timer.
105
+ const auto fire = static_cast<Timer*>(info)->fire;
106
+ fire();
107
+ },
108
+ &context);
109
+ CFRunLoopAddTimer(CFRunLoopGetMain(), timer->timer, kCFRunLoopCommonModes);
110
+ timers_.emplace(id, std::move(timer));
111
+ return id;
112
+ }
113
+
114
+ void removeTimer(Id id) override
115
+ {
116
+ const auto it = timers_.find(id);
117
+ if (it == timers_.end())
118
+ return;
119
+ CFRunLoopTimerInvalidate(it->second->timer);
120
+ CFRelease(it->second->timer);
121
+ timers_.erase(it);
122
+ }
123
+
124
+ void setWaiter(std::function<void(double seconds)> wait) override { waiter_ = std::move(wait); }
125
+
126
+ private:
127
+ struct Timer
128
+ {
129
+ CFRunLoopTimerRef timer = nullptr;
130
+ std::function<void()> fire;
131
+ };
132
+
133
+ bool ui_;
134
+ std::function<void(double seconds)> waiter_;
135
+ std::map<Id, std::unique_ptr<Timer>> timers_;
136
+ Id nextId_ = 0;
137
+ bool running_ = false;
138
+ CFRunLoopTimerRef keepAlive_ = nullptr;
139
+ };
140
+ } // namespace
141
+
142
+ std::unique_ptr<EventLoop> EventLoop::create(bool ui)
143
+ {
144
+ return std::make_unique<MacEventLoop>(ui);
145
+ }
146
+ } // namespace soundor::vst3host
@@ -0,0 +1,132 @@
1
+ #include "EventLoop.h"
2
+
3
+ #include <algorithm>
4
+ #include <deque>
5
+ #include <map>
6
+ #include <mutex>
7
+
8
+ #ifndef NOMINMAX
9
+ #define NOMINMAX
10
+ #endif
11
+ #include <windows.h>
12
+
13
+ namespace soundor::vst3host
14
+ {
15
+ namespace
16
+ {
17
+ constexpr UINT runTasksMessage = WM_APP + 1;
18
+
19
+ // Posted work goes through a message-only window rather than thread
20
+ // messages, which modal loops (a window being dragged or resized)
21
+ // would drop. Headless mode creates nothing visible.
22
+ class WindowsEventLoop final : public EventLoop
23
+ {
24
+ public:
25
+ WindowsEventLoop()
26
+ {
27
+ WNDCLASSEXW windowClass {};
28
+ windowClass.cbSize = sizeof windowClass;
29
+ windowClass.lpfnWndProc = &WindowsEventLoop::windowProc;
30
+ windowClass.hInstance = GetModuleHandleW(nullptr);
31
+ windowClass.lpszClassName = L"SoundorVst3HostMessages";
32
+ RegisterClassExW(&windowClass);
33
+ window_ = CreateWindowExW(0, windowClass.lpszClassName, L"", 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr,
34
+ windowClass.hInstance, nullptr);
35
+ SetWindowLongPtrW(window_, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
36
+ }
37
+
38
+ ~WindowsEventLoop() override
39
+ {
40
+ if (window_ != nullptr)
41
+ DestroyWindow(window_);
42
+ }
43
+
44
+ void run() override
45
+ {
46
+ running_ = true;
47
+ MSG message {};
48
+ while (running_ && GetMessageW(&message, nullptr, 0, 0) > 0)
49
+ {
50
+ TranslateMessage(&message);
51
+ DispatchMessageW(&message);
52
+ }
53
+ }
54
+
55
+ void quit() override
56
+ {
57
+ running_ = false;
58
+ PostMessageW(window_, WM_NULL, 0, 0);
59
+ }
60
+
61
+ void post(std::function<void()> task) override
62
+ {
63
+ {
64
+ const std::scoped_lock lock(mutex_);
65
+ tasks_.push_back(std::move(task));
66
+ }
67
+ PostMessageW(window_, runTasksMessage, 0, 0);
68
+ }
69
+
70
+ Id addTimer(unsigned milliseconds, std::function<void()> fire) override
71
+ {
72
+ const Id id = ++nextId_;
73
+ timers_.emplace(id, std::move(fire));
74
+ SetTimer(window_, static_cast<UINT_PTR>(id), std::max<UINT>(USER_TIMER_MINIMUM, milliseconds), nullptr);
75
+ return id;
76
+ }
77
+
78
+ void removeTimer(Id id) override
79
+ {
80
+ KillTimer(window_, static_cast<UINT_PTR>(id));
81
+ timers_.erase(id);
82
+ }
83
+
84
+ private:
85
+ static LRESULT CALLBACK windowProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
86
+ {
87
+ auto* self = reinterpret_cast<WindowsEventLoop*>(GetWindowLongPtrW(window, GWLP_USERDATA));
88
+ if (message == runTasksMessage)
89
+ {
90
+ if (self != nullptr)
91
+ self->runTasks();
92
+ return 0;
93
+ }
94
+ if (message == WM_TIMER && self != nullptr)
95
+ {
96
+ const auto it = self->timers_.find(static_cast<Id>(wParam));
97
+ if (it != self->timers_.end())
98
+ {
99
+ // A copy: the callback may remove this timer.
100
+ const auto fire = it->second;
101
+ fire();
102
+ }
103
+ return 0;
104
+ }
105
+ return DefWindowProcW(window, message, wParam, lParam);
106
+ }
107
+
108
+ void runTasks()
109
+ {
110
+ std::deque<std::function<void()>> tasks;
111
+ {
112
+ const std::scoped_lock lock(mutex_);
113
+ tasks.swap(tasks_);
114
+ }
115
+ for (auto& task : tasks)
116
+ task();
117
+ }
118
+
119
+ HWND window_ = nullptr;
120
+ bool running_ = false;
121
+ std::mutex mutex_;
122
+ std::deque<std::function<void()>> tasks_;
123
+ std::map<Id, std::function<void()>> timers_;
124
+ Id nextId_ = 0;
125
+ };
126
+ } // namespace
127
+
128
+ std::unique_ptr<EventLoop> EventLoop::create(bool /*ui*/)
129
+ {
130
+ return std::make_unique<WindowsEventLoop>();
131
+ }
132
+ } // namespace soundor::vst3host
@@ -0,0 +1,217 @@
1
+ #include "HostContext.h"
2
+
3
+ #include "pluginterfaces/base/ustring.h"
4
+
5
+ #if defined(__linux__)
6
+ #include <sys/socket.h>
7
+ #include <sys/un.h>
8
+
9
+ #include <cstddef>
10
+ #include <string_view>
11
+ #endif
12
+
13
+ namespace soundor::vst3host
14
+ {
15
+ #if defined(__linux__)
16
+ namespace
17
+ {
18
+ constexpr unsigned xRecheckMilliseconds = 16;
19
+
20
+ bool isXConnection(int fd)
21
+ {
22
+ sockaddr_un address {};
23
+ socklen_t size = sizeof address;
24
+ if (getpeername(fd, reinterpret_cast<sockaddr*>(&address), &size) != 0 || address.sun_family != AF_UNIX
25
+ || size <= offsetof(sockaddr_un, sun_path))
26
+ return false;
27
+ // Abstract sockets start with a NUL.
28
+ const std::string_view path(address.sun_path, size - offsetof(sockaddr_un, sun_path));
29
+ return path.find("/.X11-unix/") != std::string_view::npos;
30
+ }
31
+ } // namespace
32
+
33
+ // A handler that unregisters itself while it runs may drop the last other
34
+ // reference. It is released at once when it unregisters, never on a later
35
+ // turn: JUCE's handlers do not count references, and can be gone by then.
36
+ void RunLoop::fireFd(Linux::IEventHandler* handler, Linux::FileDescriptor fd)
37
+ {
38
+ const IPtr<Linux::IEventHandler> alive(handler);
39
+ handler->onFDIsSet(fd);
40
+ }
41
+
42
+ tresult PLUGIN_API RunLoop::registerEventHandler(Linux::IEventHandler* handler, Linux::FileDescriptor fd)
43
+ {
44
+ if (handler == nullptr)
45
+ return kInvalidArgument;
46
+ handler->addRef();
47
+ const auto id = loop_.addFd(fd, [handler, fd] { fireFd(handler, fd); });
48
+ handlers_.emplace(handler, id);
49
+ // Xlib reads whatever is on its connection during any round trip into a
50
+ // queue of its own, and then the connection has nothing to read: an
51
+ // event that arrived during one (a click) would wait for the next. So
52
+ // an X connection's handler is asked to look again, every frame. Only
53
+ // those: they must drain that queue anyway, and do not block with
54
+ // nothing there, as a handler of any other descriptor might.
55
+ if (isXConnection(fd))
56
+ rechecks_.emplace(handler, loop_.addTimer(xRecheckMilliseconds, [handler, fd] { fireFd(handler, fd); }));
57
+ return kResultTrue;
58
+ }
59
+
60
+ tresult PLUGIN_API RunLoop::unregisterEventHandler(Linux::IEventHandler* handler)
61
+ {
62
+ const auto [first, last] = handlers_.equal_range(handler);
63
+ if (first == last)
64
+ return kInvalidArgument;
65
+ const auto [recheck, lastRecheck] = rechecks_.equal_range(handler);
66
+ for (auto it = recheck; it != lastRecheck; ++it)
67
+ loop_.removeTimer(it->second);
68
+ rechecks_.erase(recheck, lastRecheck);
69
+ for (auto it = first; it != last; ++it)
70
+ {
71
+ loop_.removeFd(it->second);
72
+ handler->release();
73
+ }
74
+ handlers_.erase(first, last);
75
+ return kResultTrue;
76
+ }
77
+
78
+ tresult PLUGIN_API RunLoop::registerTimer(Linux::ITimerHandler* handler, Linux::TimerInterval milliseconds)
79
+ {
80
+ if (handler == nullptr)
81
+ return kInvalidArgument;
82
+ handler->addRef();
83
+ const auto id = loop_.addTimer(static_cast<unsigned>(milliseconds),
84
+ [handler]
85
+ {
86
+ const IPtr<Linux::ITimerHandler> alive(handler);
87
+ handler->onTimer();
88
+ });
89
+ timers_.emplace(handler, id);
90
+ return kResultTrue;
91
+ }
92
+
93
+ tresult PLUGIN_API RunLoop::unregisterTimer(Linux::ITimerHandler* handler)
94
+ {
95
+ const auto [first, last] = timers_.equal_range(handler);
96
+ if (first == last)
97
+ return kInvalidArgument;
98
+ for (auto it = first; it != last; ++it)
99
+ {
100
+ loop_.removeTimer(it->second);
101
+ handler->release();
102
+ }
103
+ timers_.erase(first, last);
104
+ return kResultTrue;
105
+ }
106
+
107
+ void RunLoop::clear()
108
+ {
109
+ // The module is about to go: release now, not on a later turn.
110
+ for (const auto& [handler, id] : rechecks_)
111
+ loop_.removeTimer(id);
112
+ rechecks_.clear();
113
+ for (const auto& [handler, id] : handlers_)
114
+ {
115
+ loop_.removeFd(id);
116
+ handler->release();
117
+ }
118
+ for (const auto& [handler, id] : timers_)
119
+ {
120
+ loop_.removeTimer(id);
121
+ handler->release();
122
+ }
123
+ handlers_.clear();
124
+ timers_.clear();
125
+ }
126
+ #endif
127
+
128
+ tresult PLUGIN_API HostApplication::getName(Vst::String128 name)
129
+ {
130
+ UString(name, 128).fromAscii("Soundor VST3 Host");
131
+ return kResultOk;
132
+ }
133
+
134
+ tresult PLUGIN_API HostApplication::queryInterface(const TUID iid, void** obj)
135
+ {
136
+ #if defined(__linux__)
137
+ if (runLoop_ != nullptr && FUnknownPrivate::iidEqual(iid, Linux::IRunLoop::iid))
138
+ return runLoop_->queryInterface(iid, obj);
139
+ #endif
140
+ return Vst::HostApplication::queryInterface(iid, obj);
141
+ }
142
+
143
+ tresult PLUGIN_API ComponentHandler::beginEdit(Vst::ParamID id)
144
+ {
145
+ listener_.beginEdit(id);
146
+ return kResultOk;
147
+ }
148
+
149
+ tresult PLUGIN_API ComponentHandler::performEdit(Vst::ParamID id, Vst::ParamValue valueNormalized)
150
+ {
151
+ listener_.performEdit(id, valueNormalized);
152
+ return kResultOk;
153
+ }
154
+
155
+ tresult PLUGIN_API ComponentHandler::endEdit(Vst::ParamID id)
156
+ {
157
+ listener_.endEdit(id);
158
+ return kResultOk;
159
+ }
160
+
161
+ tresult PLUGIN_API ComponentHandler::restartComponent(int32 flags)
162
+ {
163
+ listener_.restartComponent(flags);
164
+ return kResultOk;
165
+ }
166
+
167
+ tresult PLUGIN_API ComponentHandler::setDirty(TBool state)
168
+ {
169
+ listener_.setDirty(state != 0);
170
+ return kResultOk;
171
+ }
172
+
173
+ tresult PLUGIN_API ComponentHandler::requestOpenEditor(FIDString name)
174
+ {
175
+ if (name == nullptr || std::string_view(name) != Vst::ViewType::kEditor)
176
+ return kResultFalse;
177
+ listener_.requestOpenEditor();
178
+ return kResultOk;
179
+ }
180
+
181
+ tresult PLUGIN_API PlugFrame::resizeView(IPlugView* view, ViewRect* newSize)
182
+ {
183
+ if (view == nullptr || newSize == nullptr)
184
+ return kInvalidArgument;
185
+ return listener_.resizeView(view, newSize) ? kResultTrue : kResultFalse;
186
+ }
187
+
188
+ #if defined(__linux__)
189
+ namespace
190
+ {
191
+ IPtr<Linux::IRunLoop> runLoopOf(FUnknown* object)
192
+ {
193
+ return U::cast<Linux::IRunLoop>(object);
194
+ }
195
+ } // namespace
196
+
197
+ tresult PLUGIN_API PlugFrame::registerEventHandler(Linux::IEventHandler* handler, Linux::FileDescriptor fd)
198
+ {
199
+ return runLoopOf(runLoop_)->registerEventHandler(handler, fd);
200
+ }
201
+
202
+ tresult PLUGIN_API PlugFrame::unregisterEventHandler(Linux::IEventHandler* handler)
203
+ {
204
+ return runLoopOf(runLoop_)->unregisterEventHandler(handler);
205
+ }
206
+
207
+ tresult PLUGIN_API PlugFrame::registerTimer(Linux::ITimerHandler* handler, Linux::TimerInterval milliseconds)
208
+ {
209
+ return runLoopOf(runLoop_)->registerTimer(handler, milliseconds);
210
+ }
211
+
212
+ tresult PLUGIN_API PlugFrame::unregisterTimer(Linux::ITimerHandler* handler)
213
+ {
214
+ return runLoopOf(runLoop_)->unregisterTimer(handler);
215
+ }
216
+ #endif
217
+ } // namespace soundor::vst3host
@@ -0,0 +1,116 @@
1
+ // The host's side of VST3: what a plugin receives as its host context
2
+ // (IHostApplication, and on Linux IRunLoop), as its controller's component
3
+ // handler, and as its editor's frame.
4
+ #pragma once
5
+
6
+ #include "EventLoop.h"
7
+ #include "pluginterfaces/base/funknownimpl.h"
8
+ #include "pluginterfaces/gui/iplugview.h"
9
+ #include "pluginterfaces/vst/ivsteditcontroller.h"
10
+ #include "public.sdk/source/vst/hosting/hostclasses.h"
11
+
12
+ #include <map>
13
+ #include <vector>
14
+
15
+ namespace soundor::vst3host
16
+ {
17
+ using namespace Steinberg;
18
+
19
+ #if defined(__linux__)
20
+ /** Linux::IRunLoop over the main thread's EventLoop. A handler is held
21
+ while it runs, and released as soon as it unregisters. */
22
+ class RunLoop final : public U::Implements<U::Directly<Linux::IRunLoop>>
23
+ {
24
+ public:
25
+ explicit RunLoop(EventLoop& loop) : loop_(loop) {}
26
+ ~RunLoop() override { clear(); }
27
+
28
+ tresult PLUGIN_API registerEventHandler(Linux::IEventHandler* handler, Linux::FileDescriptor fd) override;
29
+ tresult PLUGIN_API unregisterEventHandler(Linux::IEventHandler* handler) override;
30
+ tresult PLUGIN_API registerTimer(Linux::ITimerHandler* handler, Linux::TimerInterval milliseconds) override;
31
+ tresult PLUGIN_API unregisterTimer(Linux::ITimerHandler* handler) override;
32
+
33
+ /** Drops every registration: before a plugin's module is unloaded. */
34
+ void clear();
35
+
36
+ private:
37
+ static void fireFd(Linux::IEventHandler* handler, Linux::FileDescriptor fd);
38
+
39
+ EventLoop& loop_;
40
+ std::multimap<Linux::IEventHandler*, EventLoop::Id> handlers_;
41
+ /** The timers that ask handlers of X connections to look again. */
42
+ std::multimap<Linux::IEventHandler*, EventLoop::Id> rechecks_;
43
+ std::multimap<Linux::ITimerHandler*, EventLoop::Id> timers_;
44
+ };
45
+ #endif
46
+
47
+ /** IHostApplication (and IPlugInterfaceSupport), plus IRunLoop on Linux,
48
+ which plugins may ask their host context for even without an editor. */
49
+ class HostApplication final : public Vst::HostApplication
50
+ {
51
+ public:
52
+ explicit HostApplication(FUnknown* runLoop) : runLoop_(runLoop) {}
53
+ tresult PLUGIN_API getName(Vst::String128 name) override;
54
+ tresult PLUGIN_API queryInterface(const TUID iid, void** obj) override;
55
+
56
+ private:
57
+ [[maybe_unused]] FUnknown* runLoop_; // Linux only
58
+ };
59
+
60
+ /** What the plugin's controller and editor ask of the host. */
61
+ class HostListener
62
+ {
63
+ public:
64
+ virtual ~HostListener() = default;
65
+ virtual void beginEdit(Vst::ParamID id) = 0;
66
+ virtual void performEdit(Vst::ParamID id, Vst::ParamValue value) = 0;
67
+ virtual void endEdit(Vst::ParamID id) = 0;
68
+ virtual void restartComponent(int32 flags) = 0;
69
+ virtual void setDirty(bool dirty) = 0;
70
+ virtual void requestOpenEditor() = 0;
71
+ /** IPlugFrame::resizeView. */
72
+ virtual bool resizeView(IPlugView* view, ViewRect* size) = 0;
73
+ };
74
+
75
+ class ComponentHandler final : public U::Implements<U::Directly<Vst::IComponentHandler, Vst::IComponentHandler2>>
76
+ {
77
+ public:
78
+ explicit ComponentHandler(HostListener& listener) : listener_(listener) {}
79
+
80
+ tresult PLUGIN_API beginEdit(Vst::ParamID id) override;
81
+ tresult PLUGIN_API performEdit(Vst::ParamID id, Vst::ParamValue valueNormalized) override;
82
+ tresult PLUGIN_API endEdit(Vst::ParamID id) override;
83
+ tresult PLUGIN_API restartComponent(int32 flags) override;
84
+
85
+ tresult PLUGIN_API setDirty(TBool state) override;
86
+ tresult PLUGIN_API requestOpenEditor(FIDString name) override;
87
+ tresult PLUGIN_API startGroupEdit() override { return kResultOk; }
88
+ tresult PLUGIN_API finishGroupEdit() override { return kResultOk; }
89
+
90
+ private:
91
+ HostListener& listener_;
92
+ };
93
+
94
+ #if defined(__linux__)
95
+ class PlugFrame final : public U::Implements<U::Directly<IPlugFrame, Linux::IRunLoop>>
96
+ #else
97
+ class PlugFrame final : public U::Implements<U::Directly<IPlugFrame>>
98
+ #endif
99
+ {
100
+ public:
101
+ PlugFrame(HostListener& listener, FUnknown* runLoop) : listener_(listener), runLoop_(runLoop) {}
102
+
103
+ tresult PLUGIN_API resizeView(IPlugView* view, ViewRect* newSize) override;
104
+
105
+ #if defined(__linux__)
106
+ tresult PLUGIN_API registerEventHandler(Linux::IEventHandler* handler, Linux::FileDescriptor fd) override;
107
+ tresult PLUGIN_API unregisterEventHandler(Linux::IEventHandler* handler) override;
108
+ tresult PLUGIN_API registerTimer(Linux::ITimerHandler* handler, Linux::TimerInterval milliseconds) override;
109
+ tresult PLUGIN_API unregisterTimer(Linux::ITimerHandler* handler) override;
110
+ #endif
111
+
112
+ private:
113
+ HostListener& listener_;
114
+ [[maybe_unused]] FUnknown* runLoop_; // Linux only
115
+ };
116
+ } // namespace soundor::vst3host