Cleanup
[carla.git] / source / modules / distrho / src / DistrhoUI.cpp
blob3af6926f0e0466984eb8e422b09253d7b1ff3bda
1 /*
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"
20 #include <cstddef>
22 #ifdef DISTRHO_PROPER_CPP11_SUPPORT
23 # include <cstdint>
24 #else
25 # include <stdint.h>
26 #endif
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"
51 END_NAMESPACE_DISTRHO
52 # include "../extra/FileBrowserDialogImpl.cpp"
53 #endif
55 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
56 # if defined(DISTRHO_OS_WINDOWS)
57 # include <winsock2.h>
58 # include <windows.h>
59 # elif defined(HAVE_X11)
60 # include <X11/Xresource.h>
61 # endif
62 #else
63 # include "src/TopLevelWidgetPrivateData.hpp"
64 # include "src/WindowPrivateData.hpp"
65 #endif
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;
78 #endif
80 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
81 /* ------------------------------------------------------------------------------------------------------------
82 * get global scale factor */
84 #ifdef DISTRHO_OS_MAC
85 double getDesktopScaleFactor(uintptr_t parentWindowHandle);
86 #else
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"
102 # endif
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
109 # endif
111 DWORD dpiAware = 0;
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);
122 FreeLibrary(Shcore);
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);
129 XrmInitialize();
131 double dpi = 96.0;
132 if (char* const rms = XResourceManagerString(display))
134 if (const XrmDatabase db = XrmGetStringDatabase(rms))
136 char* type = nullptr;
137 XrmValue value = {};
139 if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)
140 && type != nullptr
141 && std::strcmp(type, "String") == 0
142 && value.addr != nullptr)
144 char* end = nullptr;
145 const double xftDpi = std::strtod(value.addr, &end);
146 if (xftDpi > 0.0 && xftDpi < HUGE_VAL)
147 dpi = xftDpi;
150 XrmDestroyDatabase(db);
154 XCloseDisplay(display);
155 return dpi / 96;
156 #endif
158 return 1.0;
160 // might be unused
161 (void)parentWindowHandle;
163 #endif // !DISTRHO_OS_MAC
165 #endif
167 /* ------------------------------------------------------------------------------------------------------------
168 * UI::PrivateData special handling */
170 UI::PrivateData* UI::PrivateData::s_nextPrivateData = nullptr;
172 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
173 ExternalWindow::PrivateData
174 #else
175 PluginWindow&
176 #endif
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;
189 return ewData;
190 #else
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();
198 #endif
201 /* ------------------------------------------------------------------------------------------------------------
202 * UI */
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);
216 #else
217 // unused
218 (void)automaticallyScaleAndSetAsMinimumSize;
219 #endif
222 UI::~UI()
226 /* ------------------------------------------------------------------------------------------------------------
227 * Host state */
229 bool UI::isResizable() const noexcept
231 #if DISTRHO_UI_USER_RESIZABLE
232 # if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
233 return true;
234 # else
235 return uiData->window->isResizable();
236 # endif
237 #else
238 return false;
239 #endif
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);
277 #endif
279 #if DISTRHO_PLUGIN_WANT_STATE
280 bool UI::requestStateFile(const char* key)
282 return uiData->fileRequestCallback(key);
284 #endif
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);
291 #endif
293 #if DISTRHO_UI_FILE_BROWSER
294 bool UI::openFileBrowser(const FileBrowserOptions& options)
296 return getWindow().openFileBrowser((DGL_NAMESPACE::FileBrowserOptions&)options);
298 #endif
300 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
301 /* ------------------------------------------------------------------------------------------------------------
302 * Direct DSP access */
304 void* UI::getPluginInstancePointer() const noexcept
306 return uiData->dspPtr;
308 #endif
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;
329 # endif
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)
360 return offer.id;
363 return 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*)
381 #endif
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);
393 #else
394 void UI::onResize(const ResizeEvent& ev)
396 UIWidget::onResize(ev);
398 #ifndef DISTRHO_PLUGIN_TARGET_VST3
399 if (uiData->initializing)
400 return;
402 const uint width = ev.size.getWidth();
403 const uint height = ev.size.getHeight();
404 uiData->setSizeCallback(width, height);
405 #endif
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);
414 else
415 uiData->setSizeCallback(width, height);
416 # else
417 // unused
418 (void)width;
419 (void)height;
420 # endif
422 #endif
424 // -----------------------------------------------------------------------------------------------------------
426 END_NAMESPACE_DISTRHO