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