1 /*
2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any purpose with
6 * or without fee is hereby granted, provided that the above copyright notice and this
7 * permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
10 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
11 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "src/DistrhoPluginChecks.h"
18
19 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
20 # if defined(DISTRHO_OS_WINDOWS)
21 # define WIN32_LEAN_AND_MEAN
22 # include <windows.h>
23 # elif defined(HAVE_X11)
24 # include <X11/Xresource.h>
25 # endif
26 #else
27 # include "src/TopLevelWidgetPrivateData.hpp"
28 # include "src/WindowPrivateData.hpp"
29 #endif
30
31 #include "DistrhoUIPrivateData.hpp"
32
33 START_NAMESPACE_DISTRHO
34
35 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
36 /* ------------------------------------------------------------------------------------------------------------
37 * Static data, see DistrhoUIInternal.hpp */
38
39 uintptr_t g_nextWindowId = 0;
40 double g_nextScaleFactor = 1.0;
41 const char* g_nextBundlePath = nullptr;
42
43 /* ------------------------------------------------------------------------------------------------------------
44 * get global scale factor */
45
46 #ifdef DISTRHO_OS_MAC
47 double getDesktopScaleFactor(uintptr_t parentWindowHandle);
48 #else
49 static double getDesktopScaleFactor(const uintptr_t parentWindowHandle)
50 {
51 // allow custom scale for testing
52 if (const char* const scale = getenv("DPF_SCALE_FACTOR"))
53 return std::max(1.0, std::atof(scale));
54
55 #if defined(DISTRHO_OS_WINDOWS)
56 if (const HMODULE Shcore = LoadLibraryA("Shcore.dll"))
57 {
58 typedef HRESULT(WINAPI* PFN_GetProcessDpiAwareness)(HANDLE, DWORD*);
59 typedef HRESULT(WINAPI* PFN_GetScaleFactorForMonitor)(HMONITOR, DWORD*);
60
61 # if defined(__GNUC__) && (__GNUC__ >= 9)
62 # pragma GCC diagnostic push
63 # pragma GCC diagnostic ignored "-Wcast-function-type"
64 # endif
65 const PFN_GetProcessDpiAwareness GetProcessDpiAwareness
66 = (PFN_GetProcessDpiAwareness)GetProcAddress(Shcore, "GetProcessDpiAwareness");
67 const PFN_GetScaleFactorForMonitor GetScaleFactorForMonitor
68 = (PFN_GetScaleFactorForMonitor)GetProcAddress(Shcore, "GetScaleFactorForMonitor");
69 # if defined(__GNUC__) && (__GNUC__ >= 9)
70 # pragma GCC diagnostic pop
71 # endif
72
73 DWORD dpiAware = 0;
74 if (GetProcessDpiAwareness && GetScaleFactorForMonitor
75 && GetProcessDpiAwareness(NULL, &dpiAware) == 0 && dpiAware != 0)
76 {
77 const HMONITOR hMon = parentWindowHandle != 0
78 ? MonitorFromWindow((HWND)parentWindowHandle, MONITOR_DEFAULTTOPRIMARY)
79 : MonitorFromPoint(POINT{0,0}, MONITOR_DEFAULTTOPRIMARY);
80
81 DWORD scaleFactor = 0;
82 if (GetScaleFactorForMonitor(hMon, &scaleFactor) == 0 && scaleFactor != 0)
83 {
84 FreeLibrary(Shcore);
strncpy(char * const dst,const char * const src,const size_t size)85 return static_cast<double>(scaleFactor) / 100.0;
86 }
87 }
88
89 FreeLibrary(Shcore);
90 }
91 #elif defined(HAVE_X11)
92 ::Display* const display = XOpenDisplay(nullptr);
93 DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, 1.0);
94
95 XrmInitialize();
96
97 if (char* const rms = XResourceManagerString(display))
98 {
99 if (const XrmDatabase sdb = XrmGetStringDatabase(rms))
snprintf_param(char * const dst,const float value,const size_t size)100 {
101 char* type = nullptr;
102 XrmValue ret;
103
104 if (XrmGetResource(sdb, "Xft.dpi", "String", &type, &ret)
105 && ret.addr != nullptr
106 && type != nullptr
107 && std::strncmp("String", type, 6) == 0)
108 {
109 if (const double dpi = std::atof(ret.addr))
110 {
111 XCloseDisplay(display);
112 return dpi / 96;
113 }
114 }
115 }
116 }
117
118 XCloseDisplay(display);
119 #endif
120
121 return 1.0;
122
123 // might be unused
124 (void)parentWindowHandle;
125 }
ParameterAndNotesHelperParameterAndNotesHelper126 #endif // !DISTRHO_OS_MAC
127
128 #endif
129
130 /* ------------------------------------------------------------------------------------------------------------
131 * UI::PrivateData special handling */
132
133 UI::PrivateData* UI::PrivateData::s_nextPrivateData = nullptr;
134
135 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
136 ExternalWindow::PrivateData
137 #else
138 PluginWindow&
139 #endif
~ParameterAndNotesHelperParameterAndNotesHelper140 UI::PrivateData::createNextWindow(UI* const ui, const uint width, const uint height)
141 {
142 UI::PrivateData* const pData = s_nextPrivateData;
143 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
144 pData->window = new PluginWindow(ui, pData->app);
145 ExternalWindow::PrivateData ewData;
146 ewData.parentWindowHandle = pData->winId;
147 ewData.width = width;
148 ewData.height = height;
149 ewData.scaleFactor = pData->scaleFactor != 0.0 ? pData->scaleFactor : getDesktopScaleFactor(pData->winId);
150 ewData.title = DISTRHO_PLUGIN_NAME;
151 ewData.isStandalone = DISTRHO_UI_IS_STANDALONE;
152 return ewData;
153 #else
154 pData->window = new PluginWindow(ui, pData->app, pData->winId, width, height, pData->scaleFactor);
155
156 // If there are no callbacks, this is most likely a temporary window, so ignore idle callbacks
157 if (pData->callbacksPtr == nullptr)
158 pData->window->setIgnoreIdleCallbacks();
159
160 return pData->window.getObject();
161 #endif
162 }
163
164 /* ------------------------------------------------------------------------------------------------------------
165 * UI */
166
167 UI::UI(const uint width, const uint height, const bool automaticallyScale)
168 : UIWidget(UI::PrivateData::createNextWindow(this, width, height)),
169 uiData(UI::PrivateData::s_nextPrivateData)
170 {
171 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
172 if (width > 0 && height > 0)
173 {
UIVst(const audioMasterCallback audioMaster,AEffect * const effect,ParameterAndNotesHelper * const uiHelper,PluginExporter * const plugin,const intptr_t winId,const float scaleFactor)174 Widget::setSize(width, height);
175
176 if (automaticallyScale)
177 setGeometryConstraints(width, height, true, true);
178 }
179 #else
180 // unused
181 return; (void)automaticallyScale;
182 #endif
183 }
184
185 UI::~UI()
186 {
187 }
188
189 /* ------------------------------------------------------------------------------------------------------------
190 * Host state */
191
192 bool UI::isResizable() const noexcept
193 {
194 #if DISTRHO_UI_USER_RESIZABLE
195 # if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
196 return true;
197 # else
198 return uiData->window->isResizable();
199 # endif
200 #else
201 return false;
202 #endif
203 }
204
205 uint UI::getBackgroundColor() const noexcept
206 {
idle()207 return uiData->bgColor;
208 }
209
210 uint UI::getForegroundColor() const noexcept
211 {
212 return uiData->fgColor;
213 }
214
215 double UI::getSampleRate() const noexcept
216 {
217 return uiData->sampleRate;
218 }
219
220 void UI::editParameter(uint32_t index, bool started)
getWidth() const221 {
222 uiData->editParamCallback(index + uiData->parameterOffset, started);
223 }
224
225 void UI::setParameterValue(uint32_t index, float value)
getHeight() const226 {
227 uiData->setParamCallback(index + uiData->parameterOffset, value);
228 }
229
230 #if DISTRHO_PLUGIN_WANT_STATE
getScaleFactor() const231 void UI::setState(const char* key, const char* value)
232 {
233 uiData->setStateCallback(key, value);
234 }
235 #endif
setSampleRate(const double newSampleRate)236
237 #if DISTRHO_PLUGIN_WANT_STATEFILES
238 bool UI::requestStateFile(const char* key)
239 {
240 return uiData->fileRequestCallback(key);
241 }
242 #endif
243
244 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
245 void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity)
246 {
247 uiData->sendNoteCallback(channel, note, velocity);
248 }
249 #endif
setStateFromPlugin(const char * const key,const char * const value)250
251 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
252 /* ------------------------------------------------------------------------------------------------------------
253 * Direct DSP access */
254
255 void* UI::getPluginInstancePointer() const noexcept
256 {
257 return uiData->dspPtr;
258 }
259 #endif
260
261 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
262 /* ------------------------------------------------------------------------------------------------------------
263 * External UI helpers */
264
265 const char* UI::getNextBundlePath() noexcept
266 {
267 return g_nextBundlePath;
268 }
269
270 double UI::getNextScaleFactor() noexcept
271 {
272 return g_nextScaleFactor;
273 }
274
275 # if DISTRHO_PLUGIN_HAS_EMBED_UI
276 uintptr_t UI::getNextWindowId() noexcept
277 {
278 return g_nextWindowId;
279 }
280 # endif
281 #endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
282
283 /* ------------------------------------------------------------------------------------------------------------
284 * DSP/Plugin Callbacks (optional) */
285
286 void UI::sampleRateChanged(double)
287 {
288 }
289
290 /* ------------------------------------------------------------------------------------------------------------
291 * UI Callbacks (optional) */
292
293 void UI::uiScaleFactorChanged(double)
294 {
295 }
296
297 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
298 void UI::uiFocus(bool, DGL_NAMESPACE::CrossingMode)
299 {
300 }
301
302 void UI::uiReshape(uint, uint)
303 {
304 // NOTE this must be the same as Window::onReshape
305 pData->fallbackOnResize();
306 }
307
308 # ifndef DGL_FILE_BROWSER_DISABLED
309 void UI::uiFileBrowserSelected(const char*)
310 {
311 }
312 # endif
313 #endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
314
315 /* ------------------------------------------------------------------------------------------------------------
316 * UI Resize Handling, internal */
317
318 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
319 void UI::sizeChanged(const uint width, const uint height)
320 {
321 UIWidget::sizeChanged(width, height);
322
323 uiData->setSizeCallback(width, height);
324 }
325 #else
326 void UI::onResize(const ResizeEvent& ev)
327 {
328 UIWidget::onResize(ev);
329
330 const uint width = ev.size.getWidth();
331 const uint height = ev.size.getHeight();
332 uiData->setSizeCallback(width, height);
333 }
334 #endif
335
336 // -----------------------------------------------------------------------------------------------------------
337
338 END_NAMESPACE_DISTRHO
339