1 #include "SurgeLv2Ui.h"
2 #include "SurgeLv2Util.h"
3 #include "SurgeLv2Vstgui.h"
4 #include "SurgeLv2Wrapper.h"
5 #include "SurgeGUIEditor.h"
6 #include <cassert>
7 #include "DebugHelpers.h"
8
9 #include "CScalableBitmap.h"
10
SurgeLv2Ui(SurgeLv2Wrapper * instance,void * parentWindow,const LV2_URID_Map * uridMapper,const LV2UI_Resize * uiResizer,LV2UI_Write_Function writeFn,LV2UI_Controller controller,float uiScaleFactor)11 SurgeLv2Ui::SurgeLv2Ui(SurgeLv2Wrapper *instance, void *parentWindow,
12 const LV2_URID_Map *uridMapper, const LV2UI_Resize *uiResizer,
13 LV2UI_Write_Function writeFn, LV2UI_Controller controller,
14 float uiScaleFactor)
15 : _editor(new SurgeGUIEditor(instance, instance->synthesizer(), this)), _instance(instance),
16 _writeFn(writeFn), _controller(controller), _uiScaleFactor(uiScaleFactor),
17 _uiInitialized(false)
18 {
19 instance->setEditor(this);
20
21 if (uiResizer)
22 {
23 _editor->setZoomFactor(uiScaleFactor * 100);
24 _editor->setZoomCallback([this, uiResizer](SurgeGUIEditor *editor, bool resizeWindow) {
25 handleZoom(editor, uiResizer, resizeWindow);
26 });
27 }
28
29 _editor->open(parentWindow);
30
31 if (uiResizer)
32 {
33 VSTGUI::ERect *rect = nullptr;
34 _editor->getRect(&rect);
35 uiResizer->ui_resize(uiResizer->handle, (rect->right - rect->left) * uiScaleFactor,
36 (rect->bottom - rect->top) * uiScaleFactor);
37 }
38
39 _uiInitialized = true;
40 }
41
~SurgeLv2Ui()42 SurgeLv2Ui::~SurgeLv2Ui()
43 {
44 _instance->setEditor(nullptr);
45 _editor->close();
46 }
47
setParameterAutomated(int externalparam,float value)48 void SurgeLv2Ui::setParameterAutomated(int externalparam, float value)
49 {
50 _writeFn(_controller, externalparam, sizeof(float), 0, &value);
51 }
52
53 #if LINUX
associateIdleRunLoop(const VSTGUI::SharedPointer<Lv2IdleRunLoop> & runLoop)54 void SurgeLv2Ui::associateIdleRunLoop(const VSTGUI::SharedPointer<Lv2IdleRunLoop> &runLoop)
55 {
56 _runLoop = runLoop;
57 }
58 #endif
59
createDescriptor()60 LV2UI_Descriptor SurgeLv2Ui::createDescriptor()
61 {
62 LV2UI_Descriptor desc = {};
63 desc.URI = SURGE_UI_URI;
64 desc.instantiate = &instantiate;
65 desc.cleanup = &cleanup;
66 desc.port_event = &portEvent;
67 desc.extension_data = &extensionData;
68 return desc;
69 }
70
instantiate(const LV2UI_Descriptor * descriptor,const char * plugin_uri,const char * bundle_path,LV2UI_Write_Function write_function,LV2UI_Controller controller,LV2UI_Widget * widget,const LV2_Feature * const * features)71 LV2UI_Handle SurgeLv2Ui::instantiate(const LV2UI_Descriptor *descriptor, const char *plugin_uri,
72 const char *bundle_path, LV2UI_Write_Function write_function,
73 LV2UI_Controller controller, LV2UI_Widget *widget,
74 const LV2_Feature *const *features)
75 {
76 SurgeLv2Wrapper *instance =
77 (SurgeLv2Wrapper *)SurgeLv2::requireFeature(LV2_INSTANCE_ACCESS_URI, features);
78 void *parentWindow = (void *)SurgeLv2::findFeature(LV2_UI__parent, features);
79 auto *featureUridMap = (const LV2_URID_Map *)SurgeLv2::requireFeature(LV2_URID__map, features);
80 auto *featureResize = (const LV2UI_Resize *)SurgeLv2::findFeature(LV2_UI__resize, features);
81 auto *featureOptions =
82 (const LV2_Options_Option *)SurgeLv2::findFeature(LV2_OPTIONS__options, features);
83
84 auto uriScaleFactor = featureUridMap->map(featureUridMap->handle, LV2_UI__scaleFactor);
85 float uiScaleFactor = 1.0f;
86 for (int i = 0; featureOptions[i].key != 0; ++i)
87 {
88 if (featureOptions[i].key == uriScaleFactor)
89 {
90 if (featureOptions[i].type ==
91 featureUridMap->map(featureUridMap->handle, LV2_ATOM__Float))
92 uiScaleFactor = *(const float *)featureOptions[i].value;
93 break;
94 }
95 }
96
97 std::unique_ptr<SurgeLv2Ui> ui(new SurgeLv2Ui(instance, parentWindow, featureUridMap,
98 featureResize, write_function, controller,
99 uiScaleFactor));
100
101 // From LV2 documentation: The UI points this at its main widget, which has
102 // the type defined by the UI type in the data file.
103 if (widget != nullptr)
104 {
105 *widget = ui->_editor->getFrame()->getPlatformFrame()->getPlatformRepresentation();
106 }
107
108 return (LV2UI_Handle)ui.release();
109 }
110
cleanup(LV2UI_Handle ui)111 void SurgeLv2Ui::cleanup(LV2UI_Handle ui)
112 {
113 SurgeLv2Ui *self = (SurgeLv2Ui *)ui;
114 delete self;
115 }
116
portEvent(LV2UI_Handle ui,uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)117 void SurgeLv2Ui::portEvent(LV2UI_Handle ui, uint32_t port_index, uint32_t buffer_size,
118 uint32_t format, const void *buffer)
119 {
120 SurgeLv2Ui *self = (SurgeLv2Ui *)ui;
121 SurgeSynthesizer *s = self->_instance->synthesizer();
122
123 if (format == 0 && port_index < n_total_params)
124 {
125 assert(buffer_size == sizeof(float));
126
127 SurgeSynthesizer::ID did;
128 float value = *(const float *)buffer;
129 bool external = true;
130 if (s->fromDAWSideIndex(port_index, did))
131 s->setParameter01(did, value, external);
132 }
133 }
134
extensionData(const char * uri)135 const void *SurgeLv2Ui::extensionData(const char *uri)
136 {
137 if (!strcmp(uri, LV2_UI__idleInterface))
138 {
139 static const LV2UI_Idle_Interface idle = {&uiIdle};
140 return &idle;
141 }
142 // if (!strcmp(uri, LV2_UI__resize)) {
143 // static const LV2UI_Resize resize = { nullptr, &uiResize };
144 // return &resize;
145 // }
146 return nullptr;
147 }
148
uiIdle(LV2UI_Handle ui)149 int SurgeLv2Ui::uiIdle(LV2UI_Handle ui)
150 {
151 SurgeLv2Ui *self = (SurgeLv2Ui *)ui;
152 SurgeLv2Wrapper *instance = self->_instance;
153
154 // HACK to get parameters written to host
155 // if a patch was loaded from storage, the plugin atomically sets a bit to
156 // indicate it. In this case, write the control value again using UI's write
157 // interface, in order to update the host side.
158 if (instance->_editorMustReloadPatch.exchange(false))
159 {
160 SurgeSynthesizer *s = instance->synthesizer();
161 for (unsigned int i = 0; i < n_total_params; i++)
162 {
163 SurgeSynthesizer::ID did;
164 if (s->fromDAWSideIndex(i, did))
165 {
166 float value = s->getParameter01(did);
167 self->setParameterAutomated(did.getDawSideIndex(), value);
168 }
169 }
170 }
171
172 #if LINUX
173 self->_runLoop->execIdle();
174 #endif
175
176 // TODO maybe handle the case of closed window here
177 return 0;
178 }
179
handleZoom(SurgeGUIEditor * e,const LV2UI_Resize * resizer,bool resizeWindow)180 void SurgeLv2Ui::handleZoom(SurgeGUIEditor *e, const LV2UI_Resize *resizer, bool resizeWindow)
181 {
182 assert(e == _editor.get());
183
184 float fzf = e->getZoomFactor() / 100.0;
185 int newW = e->getWindowSizeX() * fzf;
186 int newH = e->getWindowSizeY() * fzf;
187
188 // identical implementation to VST2, except for here
189 if (resizer && resizeWindow && _uiInitialized)
190 resizer->ui_resize(resizer->handle, newW, newH);
191
192 VSTGUI::CFrame *frame = e->getFrame();
193 if (frame)
194 {
195 frame->setZoom(fzf);
196 /*
197 ** cframe::setZoom uses prior size and integer math which means repeated resets drift
198 ** look at the "oroginWidth" math around in CFrame::setZoom. So rather than let those
199 ** drifts accumulate, just reset the size here since we know it
200 */
201 frame->setSize(newW, newH);
202
203 setExtraScaleFactor(frame->getBackground(), e->getZoomFactor());
204
205 frame->setDirty(true);
206 frame->invalid();
207 }
208 }
209
setExtraScaleFactor(VSTGUI::CBitmap * bg,float zf)210 void SurgeLv2Ui::setExtraScaleFactor(VSTGUI::CBitmap *bg, float zf)
211 {
212 if (bg != NULL)
213 {
214 auto sbm = dynamic_cast<CScalableBitmap *>(bg); // dynamic casts are gross but better safe
215 if (sbm)
216 {
217 /*
218 ** VSTGUI has an error which is that the background bitmap doesn't get the frame
219 *transform
220 ** applied. Simply look at cviewcontainer::drawBackgroundRect. So we have to force the
221 *background
222 ** scale up using a backdoor API.
223 */
224 sbm->setExtraScaleFactor(zf);
225 }
226 }
227 }
228
229 ///
230 #if LINUX
231 #include "vstgui/lib/platform/platform_x11.h"
232 #include "vstgui/lib/platform/linux/x11platform.h"
233
234 namespace SurgeLv2
235 {
createRunLoop(void * ui)236 VSTGUI::SharedPointer<VSTGUI::X11::IRunLoop> createRunLoop(void *ui)
237 {
238 SurgeLv2Ui *self = (SurgeLv2Ui *)ui;
239 VSTGUI::SharedPointer<Lv2IdleRunLoop> runLoop(new Lv2IdleRunLoop);
240 self->associateIdleRunLoop(runLoop);
241 return runLoop;
242 }
243 } // namespace SurgeLv2
244 #endif // LINUX
245
246 ///
247 LV2_SYMBOL_EXPORT
lv2ui_descriptor(uint32_t index)248 const LV2UI_Descriptor *lv2ui_descriptor(uint32_t index)
249 {
250 #if LINUX
251 VSTGUI::initializeSoHandle();
252 #endif
253
254 if (index == 0)
255 {
256 static LV2UI_Descriptor desc = SurgeLv2Ui::createDescriptor();
257 return &desc;
258 }
259 return nullptr;
260 }
261