2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
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.
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.
17 #include "src/DistrhoPluginChecks.h"
18 #include "src/DistrhoDefines.h"
22 #ifdef DISTRHO_PROPER_CPP11_SUPPORT
28 #if DISTRHO_UI_FILE_BROWSER && !defined(DISTRHO_OS_MAC)
29 # define DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, SEP, FUNCTION) NS ## SEP ## FUNCTION
30 # define DISTRHO_PUGL_NAMESPACE_MACRO(NS, FUNCTION) DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, _, FUNCTION)
31 # define x_fib_add_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_add_recent)
32 # define x_fib_cfg_buttons DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_cfg_buttons)
33 # define x_fib_cfg_filter_callback DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_cfg_filter_callback)
34 # define x_fib_close DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_close)
35 # define x_fib_configure DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_configure)
36 # define x_fib_filename DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_filename)
37 # define x_fib_free_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_free_recent)
38 # define x_fib_handle_events DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_handle_events)
39 # define x_fib_load_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_load_recent)
40 # define x_fib_recent_at DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_recent_at)
41 # define x_fib_recent_count DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_recent_count)
42 # define x_fib_recent_file DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_recent_file)
43 # define x_fib_save_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_save_recent)
44 # define x_fib_show DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_show)
45 # define x_fib_status DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_status)
46 # define DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED
47 # define FILE_BROWSER_DIALOG_NAMESPACE DISTRHO_NAMESPACE
48 # define FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE
49 START_NAMESPACE_DISTRHO
50 # include "../extra/FileBrowserDialogImpl.hpp"
52 # include "../extra/FileBrowserDialogImpl.cpp"
55 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
56 # if defined(DISTRHO_OS_WINDOWS)
57 # include <winsock2.h>
59 # elif defined(HAVE_X11)
60 # include <X11/Xresource.h>
63 # include "src/TopLevelWidgetPrivateData.hpp"
64 # include "src/WindowPrivateData.hpp"
67 #include "DistrhoUIPrivateData.hpp"
69 START_NAMESPACE_DISTRHO
71 /* ------------------------------------------------------------------------------------------------------------
72 * Static data, see DistrhoUIInternal.hpp */
74 const char* g_nextBundlePath
= nullptr;
75 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
76 uintptr_t g_nextWindowId
= 0;
77 double g_nextScaleFactor
= 1.0;
80 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
81 /* ------------------------------------------------------------------------------------------------------------
82 * get global scale factor */
85 double getDesktopScaleFactor(uintptr_t parentWindowHandle
);
87 static double getDesktopScaleFactor(const uintptr_t parentWindowHandle
)
89 // allow custom scale for testing
90 if (const char* const scale
= getenv("DPF_SCALE_FACTOR"))
91 return std::max(1.0, std::atof(scale
));
93 #if defined(DISTRHO_OS_WINDOWS)
94 if (const HMODULE Shcore
= LoadLibraryA("Shcore.dll"))
96 typedef HRESULT(WINAPI
* PFN_GetProcessDpiAwareness
)(HANDLE
, DWORD
*);
97 typedef HRESULT(WINAPI
* PFN_GetScaleFactorForMonitor
)(HMONITOR
, DWORD
*);
99 # if defined(__GNUC__) && (__GNUC__ >= 9)
100 # pragma GCC diagnostic push
101 # pragma GCC diagnostic ignored "-Wcast-function-type"
103 const PFN_GetProcessDpiAwareness GetProcessDpiAwareness
104 = (PFN_GetProcessDpiAwareness
)GetProcAddress(Shcore
, "GetProcessDpiAwareness");
105 const PFN_GetScaleFactorForMonitor GetScaleFactorForMonitor
106 = (PFN_GetScaleFactorForMonitor
)GetProcAddress(Shcore
, "GetScaleFactorForMonitor");
107 # if defined(__GNUC__) && (__GNUC__ >= 9)
108 # pragma GCC diagnostic pop
112 DWORD scaleFactor
= 100;
113 if (GetProcessDpiAwareness
&& GetScaleFactorForMonitor
114 && GetProcessDpiAwareness(nullptr, &dpiAware
) == 0 && dpiAware
!= 0)
116 const HMONITOR hMon
= parentWindowHandle
!= 0
117 ? MonitorFromWindow((HWND
)parentWindowHandle
, MONITOR_DEFAULTTOPRIMARY
)
118 : MonitorFromPoint(POINT
{0,0}, MONITOR_DEFAULTTOPRIMARY
);
119 GetScaleFactorForMonitor(hMon
, &scaleFactor
);
123 return static_cast<double>(scaleFactor
) / 100.0;
125 #elif defined(HAVE_X11)
126 ::Display
* const display
= XOpenDisplay(nullptr);
127 DISTRHO_SAFE_ASSERT_RETURN(display
!= nullptr, 1.0);
132 if (char* const rms
= XResourceManagerString(display
))
134 if (const XrmDatabase db
= XrmGetStringDatabase(rms
))
136 char* type
= nullptr;
139 if (XrmGetResource(db
, "Xft.dpi", "Xft.Dpi", &type
, &value
)
141 && std::strcmp(type
, "String") == 0
142 && value
.addr
!= nullptr)
145 const double xftDpi
= std::strtod(value
.addr
, &end
);
146 if (xftDpi
> 0.0 && xftDpi
< HUGE_VAL
)
150 XrmDestroyDatabase(db
);
154 XCloseDisplay(display
);
161 (void)parentWindowHandle
;
163 #endif // !DISTRHO_OS_MAC
167 /* ------------------------------------------------------------------------------------------------------------
168 * UI::PrivateData special handling */
170 UI::PrivateData
* UI::PrivateData::s_nextPrivateData
= nullptr;
172 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
173 ExternalWindow::PrivateData
177 UI::PrivateData::createNextWindow(UI
* const ui
, const uint width
, const uint height
)
179 UI::PrivateData
* const pData
= s_nextPrivateData
;
180 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
181 pData
->window
= new PluginWindow(ui
, pData
->app
);
182 ExternalWindow::PrivateData ewData
;
183 ewData
.parentWindowHandle
= pData
->winId
;
184 ewData
.width
= width
;
185 ewData
.height
= height
;
186 ewData
.scaleFactor
= pData
->scaleFactor
!= 0.0 ? pData
->scaleFactor
: getDesktopScaleFactor(pData
->winId
);
187 ewData
.title
= DISTRHO_PLUGIN_NAME
;
188 ewData
.isStandalone
= DISTRHO_UI_IS_STANDALONE
;
191 pData
->window
= new PluginWindow(ui
, pData
->app
, pData
->winId
, width
, height
, pData
->scaleFactor
);
193 // If there are no callbacks, this is most likely a temporary window, so ignore idle callbacks
194 if (pData
->callbacksPtr
== nullptr)
195 pData
->window
->setIgnoreIdleCallbacks();
197 return pData
->window
.getObject();
201 /* ------------------------------------------------------------------------------------------------------------
204 UI::UI(const uint width
, const uint height
, const bool automaticallyScaleAndSetAsMinimumSize
)
205 : UIWidget(UI::PrivateData::createNextWindow(this, width
, height
)),
206 uiData(UI::PrivateData::s_nextPrivateData
)
208 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
209 if (width
!= 0 && height
!= 0)
211 Widget::setSize(width
, height
);
213 if (automaticallyScaleAndSetAsMinimumSize
)
214 setGeometryConstraints(width
, height
, true, true, true);
218 (void)automaticallyScaleAndSetAsMinimumSize
;
226 /* ------------------------------------------------------------------------------------------------------------
229 bool UI::isResizable() const noexcept
231 #if DISTRHO_UI_USER_RESIZABLE
232 # if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
235 return uiData
->window
->isResizable();
242 uint
UI::getBackgroundColor() const noexcept
244 return uiData
->bgColor
;
247 uint
UI::getForegroundColor() const noexcept
249 return uiData
->fgColor
;
252 double UI::getSampleRate() const noexcept
254 return uiData
->sampleRate
;
257 const char* UI::getBundlePath() const noexcept
259 return uiData
->bundlePath
;
262 void UI::editParameter(uint32_t index
, bool started
)
264 uiData
->editParamCallback(index
+ uiData
->parameterOffset
, started
);
267 void UI::setParameterValue(uint32_t index
, float value
)
269 uiData
->setParamCallback(index
+ uiData
->parameterOffset
, value
);
272 #if DISTRHO_PLUGIN_WANT_STATE
273 void UI::setState(const char* key
, const char* value
)
275 uiData
->setStateCallback(key
, value
);
279 #if DISTRHO_PLUGIN_WANT_STATE
280 bool UI::requestStateFile(const char* key
)
282 return uiData
->fileRequestCallback(key
);
286 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
287 void UI::sendNote(uint8_t channel
, uint8_t note
, uint8_t velocity
)
289 uiData
->sendNoteCallback(channel
, note
, velocity
);
293 #if DISTRHO_UI_FILE_BROWSER
294 bool UI::openFileBrowser(const FileBrowserOptions
& options
)
296 return getWindow().openFileBrowser((DGL_NAMESPACE::FileBrowserOptions
&)options
);
300 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
301 /* ------------------------------------------------------------------------------------------------------------
302 * Direct DSP access */
304 void* UI::getPluginInstancePointer() const noexcept
306 return uiData
->dspPtr
;
310 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
311 /* ------------------------------------------------------------------------------------------------------------
312 * External UI helpers (static calls) */
314 const char* UI::getNextBundlePath() noexcept
316 return g_nextBundlePath
;
319 double UI::getNextScaleFactor() noexcept
321 return g_nextScaleFactor
;
324 # if DISTRHO_PLUGIN_HAS_EMBED_UI
325 uintptr_t UI::getNextWindowId() noexcept
327 return g_nextWindowId
;
330 #endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
332 /* ------------------------------------------------------------------------------------------------------------
333 * DSP/Plugin Callbacks (optional) */
335 void UI::sampleRateChanged(double)
339 /* ------------------------------------------------------------------------------------------------------------
340 * UI Callbacks (optional) */
342 void UI::uiScaleFactorChanged(double)
346 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
347 std::vector
<DGL_NAMESPACE::ClipboardDataOffer
> UI::getClipboardDataOfferTypes()
349 return uiData
->window
->getClipboardDataOfferTypes();
352 uint32_t UI::uiClipboardDataOffer()
354 std::vector
<DGL_NAMESPACE::ClipboardDataOffer
> offers(uiData
->window
->getClipboardDataOfferTypes());
356 for (std::vector
<DGL_NAMESPACE::ClipboardDataOffer
>::iterator it
=offers
.begin(), end
=offers
.end(); it
!= end
;++it
)
358 const DGL_NAMESPACE::ClipboardDataOffer offer
= *it
;
359 if (std::strcmp(offer
.type
, "text/plain") == 0)
366 void UI::uiFocus(bool, DGL_NAMESPACE::CrossingMode
)
370 void UI::uiReshape(uint
, uint
)
372 // NOTE this must be the same as Window::onReshape
373 pData
->fallbackOnResize();
375 #endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
377 #if DISTRHO_UI_FILE_BROWSER
378 void UI::uiFileBrowserSelected(const char*)
383 /* ------------------------------------------------------------------------------------------------------------
384 * UI Resize Handling, internal */
386 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
387 void UI::sizeChanged(const uint width
, const uint height
)
389 UIWidget::sizeChanged(width
, height
);
391 uiData
->setSizeCallback(width
, height
);
394 void UI::onResize(const ResizeEvent
& ev
)
396 UIWidget::onResize(ev
);
398 #ifndef DISTRHO_PLUGIN_TARGET_VST3
399 if (uiData
->initializing
)
402 const uint width
= ev
.size
.getWidth();
403 const uint height
= ev
.size
.getHeight();
404 uiData
->setSizeCallback(width
, height
);
408 // NOTE: only used for VST3
409 void UI::requestSizeChange(const uint width
, const uint height
)
411 # ifdef DISTRHO_PLUGIN_TARGET_VST3
412 if (uiData
->initializing
)
413 uiData
->window
->setSizeForVST3(width
, height
);
415 uiData
->setSizeCallback(width
, height
);
424 // -----------------------------------------------------------------------------------------------------------
426 END_NAMESPACE_DISTRHO