iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
IPlugVST3_View.h
1/*
2 ==============================================================================
3
4 This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5
6 See LICENSE.txt for more info.
7
8 ==============================================================================
9*/
10
11#pragma once
12#include "base/source/fstring.h"
13#include "pluginterfaces/gui/iplugviewcontentscalesupport.h"
14#include "pluginterfaces/base/keycodes.h"
15
16#include "IPlugStructs.h"
17
19template <class T>
20class IPlugVST3View : public Steinberg::CPluginView
21 , public Steinberg::IPlugViewContentScaleSupport
22{
23public:
24 IPlugVST3View(T& owner)
25 : mOwner(owner)
26 {
27 mOwner.addRef();
28 }
29
30 ~IPlugVST3View()
31 {
32 mOwner.release();
33 }
34
35 IPlugVST3View(const IPlugVST3View&) = delete;
36 IPlugVST3View& operator=(const IPlugVST3View&) = delete;
37
38 Steinberg::tresult PLUGIN_API isPlatformTypeSupported(Steinberg::FIDString type) override
39 {
40 if (mOwner.HasUI()) // for no editor plugins
41 {
42#ifdef OS_WIN
43 if (strcmp(type, Steinberg::kPlatformTypeHWND) == 0)
44 return Steinberg::kResultTrue;
45
46#elif defined OS_MAC
47 if (strcmp(type, Steinberg::kPlatformTypeNSView) == 0)
48 return Steinberg::kResultTrue;
49#endif
50 }
51
52 return Steinberg::kResultFalse;
53 }
54
55 Steinberg::tresult PLUGIN_API onSize(Steinberg::ViewRect* pSize) override
56 {
57 TRACE
58
59 if (pSize && mOwner.GetHostResizeEnabled())
60 {
61 rect = *pSize;
62 mOwner.OnParentWindowResize(rect.getWidth(), rect.getHeight());
63 return Steinberg::kResultTrue;
64 }
65
66 return Steinberg::kResultFalse;
67 }
68
69 Steinberg::tresult PLUGIN_API getSize(Steinberg::ViewRect* pSize) override
70 {
71 TRACE
72
73 if (mOwner.HasUI())
74 {
75 *pSize = Steinberg::ViewRect(0, 0, mOwner.GetEditorWidth(), mOwner.GetEditorHeight());
76
77 return Steinberg::kResultTrue;
78 }
79 else
80 {
81 return Steinberg::kResultFalse;
82 }
83 }
84
85 Steinberg::tresult PLUGIN_API canResize() override
86 {
87 if (mOwner.HasUI() && mOwner.GetHostResizeEnabled())
88 {
89 return Steinberg::kResultTrue;
90 }
91
92 return Steinberg::kResultFalse;
93 }
94
95 Steinberg::tresult PLUGIN_API checkSizeConstraint(Steinberg::ViewRect* pRect) override
96 {
97 int w = pRect->getWidth();
98 int h = pRect->getHeight();
99
100 if(!mOwner.ConstrainEditorResize(w, h))
101 {
102 pRect->right = pRect->left + w;
103 pRect->bottom = pRect->top + h;
104 }
105
106 return Steinberg::kResultTrue;
107 }
108
109 Steinberg::tresult PLUGIN_API attached(void* pParent, Steinberg::FIDString type) override
110 {
111 if (mOwner.HasUI())
112 {
113 void* pView = nullptr;
114#ifdef OS_WIN
115 if (strcmp(type, Steinberg::kPlatformTypeHWND) == 0)
116 pView = mOwner.OpenWindow(pParent);
117#elif defined OS_MAC
118 if (strcmp(type, Steinberg::kPlatformTypeNSView) == 0)
119 pView = mOwner.OpenWindow(pParent);
120 else // Carbon
121 return Steinberg::kResultFalse;
122#endif
123 return Steinberg::kResultTrue;
124 }
125
126 return Steinberg::kResultFalse;
127 }
128
129 Steinberg::tresult PLUGIN_API removed() override
130 {
131 if (mOwner.HasUI())
132 mOwner.CloseWindow();
133
134 return CPluginView::removed();
135 }
136
137 Steinberg::tresult PLUGIN_API setContentScaleFactor(ScaleFactor factor) override
138 {
139 mOwner.SetScreenScale(factor);
140
141 return Steinberg::kResultOk;
142 }
143
144 Steinberg::tresult PLUGIN_API queryInterface(const Steinberg::TUID _iid, void** obj) override
145 {
146 QUERY_INTERFACE(_iid, obj, IPlugViewContentScaleSupport::iid, IPlugViewContentScaleSupport)
147 *obj = 0;
148 return CPluginView::queryInterface(_iid, obj);
149 }
150
151 Steinberg::tresult PLUGIN_API onKeyDown (Steinberg::char16 key, Steinberg::int16 keyMsg, Steinberg::int16 modifiers) override
152 {
153 // Workaround for Reaper's funky key/keyMsg
154 if (mOwner.GetHost() == iplug::EHost::kHostReaper)
155 {
156 if (keyMsg == Steinberg::VirtualKeyCodes::KEY_SPACE)
157 {
158 iplug::IKeyPress keyPress { " ",
159 iplug::kVK_SPACE,
160 static_cast<bool>(modifiers & Steinberg::kShiftKey),
161 static_cast<bool>(modifiers & Steinberg::kCommandKey),
162 static_cast<bool>(modifiers & Steinberg::kAlternateKey)};
163
164 return mOwner.OnKeyDown(keyPress) ? Steinberg::kResultTrue : Steinberg::kResultFalse;
165 }
166 else
167 {
168 return Steinberg::kResultFalse;
169 }
170 }
171 else
172 {
173 return mOwner.OnKeyDown(TranslateKeyMessage(key, keyMsg, modifiers)) ? Steinberg::kResultTrue : Steinberg::kResultFalse;
174 }
175 }
176
177 Steinberg::tresult PLUGIN_API onKeyUp (Steinberg::char16 key, Steinberg::int16 keyMsg, Steinberg::int16 modifiers) override
178 {
179 // Workaround for Reaper's funky key/keyMsg
180 if (mOwner.GetHost() == iplug::EHost::kHostReaper)
181 {
182 if (keyMsg == Steinberg::VirtualKeyCodes::KEY_SPACE)
183 {
184 iplug::IKeyPress keyPress { " ", iplug::kVK_SPACE,
185 static_cast<bool>(modifiers & Steinberg::kShiftKey),
186 static_cast<bool>(modifiers & Steinberg::kCommandKey),
187 static_cast<bool>(modifiers & Steinberg::kAlternateKey)};
188
189 return mOwner.OnKeyUp(keyPress) ? Steinberg::kResultTrue : Steinberg::kResultFalse;
190 }
191 else
192 {
193 return Steinberg::kResultFalse;
194 }
195 }
196 else
197 {
198 return mOwner.OnKeyUp(TranslateKeyMessage(key, keyMsg, modifiers)) ? Steinberg::kResultTrue : Steinberg::kResultFalse;
199 }
200 }
201
202 DELEGATE_REFCOUNT(Steinberg::CPluginView)
203
204#pragma mark -
205
206 static int AsciiToVK(int ascii)
207 {
208 #ifdef OS_WIN
209 HKL layout = GetKeyboardLayout(0);
210 return VkKeyScanExA((CHAR)ascii, layout);
211 #else
212 // Numbers and uppercase alpha chars map directly to VK
213 if ((ascii >= 0x30 && ascii <= 0x39) || (ascii >= 0x41 && ascii <= 0x5A))
214 {
215 return ascii;
216 }
217
218 // Lowercase alpha chars map to VK but need shifting
219 if (ascii >= 0x61 && ascii <= 0x7A)
220 {
221 return ascii - 0x20;
222 }
223
224 return iplug::kVK_NONE;
225 #endif
226 }
227
228 static int VSTKeyCodeToVK (Steinberg::int16 code, char ascii)
229 {
230 // If the keycode provided by the host is 0, we can still calculate the VK from the ascii value
231 // NOTE: VKEY_EQUALS Doesn't seem to map to a Windows VK, so get the VK from the ascii char instead
232 if (code == 0 || code == Steinberg::KEY_EQUALS)
233 {
234 return AsciiToVK(ascii);
235 }
236
237 using namespace Steinberg;
238 using namespace iplug;
239
240 switch (code)
241 {
242 case KEY_BACK: return kVK_BACK;
243 case KEY_TAB: return kVK_TAB;
244 case KEY_CLEAR: return kVK_CLEAR;
245 case KEY_RETURN: return kVK_RETURN;
246 case KEY_PAUSE: return kVK_PAUSE;
247 case KEY_ESCAPE: return kVK_ESCAPE;
248 case KEY_SPACE: return kVK_SPACE;
249 case KEY_NEXT: return kVK_NEXT;
250 case KEY_END: return kVK_END;
251 case KEY_HOME: return kVK_HOME;
252 case KEY_LEFT: return kVK_LEFT;
253 case KEY_UP: return kVK_UP;
254 case KEY_RIGHT: return kVK_RIGHT;
255 case KEY_DOWN: return kVK_DOWN;
256 case KEY_PAGEUP: return kVK_PRIOR;
257 case KEY_PAGEDOWN: return kVK_NEXT;
258 case KEY_SELECT: return kVK_SELECT;
259 case KEY_PRINT: return kVK_PRINT;
260 case KEY_ENTER: return kVK_RETURN;
261 case KEY_SNAPSHOT: return kVK_SNAPSHOT;
262 case KEY_INSERT: return kVK_INSERT;
263 case KEY_DELETE: return kVK_DELETE;
264 case KEY_HELP: return kVK_HELP;
265 case KEY_NUMPAD0: return kVK_NUMPAD0;
266 case KEY_NUMPAD1: return kVK_NUMPAD1;
267 case KEY_NUMPAD2: return kVK_NUMPAD2;
268 case KEY_NUMPAD3: return kVK_NUMPAD3;
269 case KEY_NUMPAD4: return kVK_NUMPAD4;
270 case KEY_NUMPAD5: return kVK_NUMPAD5;
271 case KEY_NUMPAD6: return kVK_NUMPAD6;
272 case KEY_NUMPAD7: return kVK_NUMPAD7;
273 case KEY_NUMPAD8: return kVK_NUMPAD8;
274 case KEY_NUMPAD9: return kVK_NUMPAD9;
275 case KEY_MULTIPLY: return kVK_MULTIPLY;
276 case KEY_ADD: return kVK_ADD;
277 case KEY_SEPARATOR: return kVK_SEPARATOR;
278 case KEY_SUBTRACT: return kVK_SUBTRACT;
279 case KEY_DECIMAL: return kVK_DECIMAL;
280 case KEY_DIVIDE: return kVK_DIVIDE;
281 case KEY_F1: return kVK_F1;
282 case KEY_F2: return kVK_F2;
283 case KEY_F3: return kVK_F3;
284 case KEY_F4: return kVK_F4;
285 case KEY_F5: return kVK_F5;
286 case KEY_F6: return kVK_F6;
287 case KEY_F7: return kVK_F7;
288 case KEY_F8: return kVK_F8;
289 case KEY_F9: return kVK_F9;
290 case KEY_F10: return kVK_F10;
291 case KEY_F11: return kVK_F11;
292 case KEY_F12: return kVK_F12;
293 case KEY_NUMLOCK: return kVK_NUMLOCK;
294 case KEY_SCROLL: return kVK_SCROLL;
295 case KEY_SHIFT: return kVK_SHIFT;
296 case KEY_CONTROL: return kVK_CONTROL;
297 case KEY_ALT: return kVK_MENU;
298 case KEY_EQUALS: return kVK_NONE;
299 }
300
301 if(code >= VKEY_FIRST_ASCII)
302 return (code - VKEY_FIRST_ASCII + kVK_0);
303
304 return kVK_NONE;
305 };
306
307 static iplug::IKeyPress TranslateKeyMessage (Steinberg::char16 key, Steinberg::int16 keyMsg, Steinberg::int16 modifiers)
308 {
309 WDL_String str;
310
311 if (key == 0)
312 {
313 key = Steinberg::VirtualKeyCodeToChar((Steinberg::uint8) keyMsg);
314 }
315
316 if (key)
317 {
318 Steinberg::String keyStr(STR (" "));
319 keyStr.setChar16(0, key);
320 keyStr.toMultiByte(Steinberg::kCP_Utf8);
321 if (keyStr.length() == 1)
322 {
323 str.Set(keyStr.text8());
324 }
325 }
326
327 iplug::IKeyPress keyPress { str.Get(), VSTKeyCodeToVK(keyMsg, str.Get()[0]),
328 static_cast<bool>(modifiers & Steinberg::kShiftKey),
329 static_cast<bool>(modifiers & Steinberg::kCommandKey),
330 static_cast<bool>(modifiers & Steinberg::kAlternateKey)};
331
332 return keyPress;
333 }
334
335 void Resize(int w, int h)
336 {
337 TRACE
338
339 Steinberg::ViewRect newSize = Steinberg::ViewRect(0, 0, w, h);
340 plugFrame->resizeView(this, &newSize);
341 }
342
343 T& mOwner;
344};