DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

commit 6c832980f6090a8bf1d2a4abf522b3ad3c709908
parent 4f3d99304f8d9cc583aa737e40a1716dff37e021
Author: falkTX <falktx@falktx.com>
Date:   Sat, 28 May 2022 14:48:35 +0100

Cleanup file dialog namespaces, add DISTRHO_UI_FILE_BROWSER

Signed-off-by: falkTX <falktx@falktx.com>

Diffstat:
Mdgl/Application.hpp | 12++++++++++--
Adgl/FileBrowserDialog.hpp | 28++++++++++++++++++++++++++++
Mdgl/Window.hpp | 20+++++++++++---------
Mdgl/src/WindowPrivateData.hpp | 7++-----
Mdgl/src/pugl.cpp | 6++++--
Mdistrho/DistrhoUI.hpp | 16+++++++---------
Mdistrho/DistrhoUI_macOS.mm | 2+-
Ddistrho/extra/FileBrowserDialog.cpp | 636-------------------------------------------------------------------------------
Mdistrho/extra/FileBrowserDialog.hpp | 110++-----------------------------------------------------------------------------
Adistrho/extra/FileBrowserDialogImpl.cpp | 643+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adistrho/extra/FileBrowserDialogImpl.hpp | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdistrho/src/DistrhoPluginChecks.h | 19++++++++++++++++++-
Mdistrho/src/DistrhoUI.cpp | 49++++++++++++++++++++++---------------------------
Mdistrho/src/DistrhoUIPrivateData.hpp | 51+++++++++++++--------------------------------------
Mexamples/Info/DistrhoPluginInfo.h | 1+
Mpugl-updates-notes.txt | 1-
16 files changed, 879 insertions(+), 839 deletions(-)

diff --git a/dgl/Application.hpp b/dgl/Application.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> + * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -19,6 +19,12 @@ #include "Base.hpp" +#ifdef DISTRHO_NAMESPACE +START_NAMESPACE_DISTRHO +class PluginApplication; +END_NAMESPACE_DISTRHO +#endif + START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- @@ -116,8 +122,10 @@ public: private: struct PrivateData; PrivateData* const pData; - friend class PluginApplication; friend class Window; + #ifdef DISTRHO_NAMESPACE + friend class DISTRHO_NAMESPACE::PluginApplication; + #endif DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Application) }; diff --git a/dgl/FileBrowserDialog.hpp b/dgl/FileBrowserDialog.hpp @@ -0,0 +1,28 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED +#define DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED + +#include "Base.hpp" + +START_NAMESPACE_DGL + +#include "../distrho/extra/FileBrowserDialogImpl.hpp" + +END_NAMESPACE_DGL + +#endif // DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED diff --git a/dgl/Window.hpp b/dgl/Window.hpp @@ -20,15 +20,20 @@ #include "Geometry.hpp" #ifndef DGL_FILE_BROWSER_DISABLED -# include "../distrho/extra/FileBrowserDialog.hpp" +# include "FileBrowserDialog.hpp" #endif #include <vector> +#ifdef DISTRHO_NAMESPACE +START_NAMESPACE_DISTRHO +class PluginWindow; +END_NAMESPACE_DISTRHO +#endif + START_NAMESPACE_DGL class Application; -class PluginWindow; class TopLevelWidget; // ----------------------------------------------------------------------- @@ -59,11 +64,6 @@ class DISTRHO_API Window struct PrivateData; public: -#ifndef DGL_FILE_BROWSER_DISABLED - typedef DISTRHO_NAMESPACE::FileBrowserHandle FileBrowserHandle; - typedef DISTRHO_NAMESPACE::FileBrowserOptions FileBrowserOptions; -#endif - /** Window graphics context as a scoped struct. This class gives graphics context drawing time to a window's widgets. @@ -400,7 +400,7 @@ public: This function does not block the event loop. */ - bool openFileBrowser(const FileBrowserOptions& options = FileBrowserOptions()); + bool openFileBrowser(const DGL_NAMESPACE::FileBrowserOptions& options = FileBrowserOptions()); #endif /** @@ -521,8 +521,10 @@ protected: private: PrivateData* const pData; friend class Application; - friend class PluginWindow; friend class TopLevelWidget; + #ifdef DISTRHO_NAMESPACE + friend class DISTRHO_NAMESPACE::PluginWindow; + #endif /** @internal */ explicit Window(Application& app, diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp @@ -44,9 +44,6 @@ struct Window::PrivateData : IdleCallback { /** Pugl view instance. */ PuglView* view; - /** Pugl view instance of the transient parent window. */ -// PuglView* const transientParentView; - /** Reserved space for graphics context. */ mutable uint8_t graphicsContext[sizeof(void*)]; @@ -91,7 +88,7 @@ struct Window::PrivateData : IdleCallback { #ifndef DGL_FILE_BROWSER_DISABLED /** Handle for file browser dialog operations. */ - FileBrowserHandle fileBrowserHandle; + DGL_NAMESPACE::FileBrowserHandle fileBrowserHandle; #endif /** Modal window setup. */ @@ -168,7 +165,7 @@ struct Window::PrivateData : IdleCallback { #ifndef DGL_FILE_BROWSER_DISABLED // file handling - bool openFileBrowser(const FileBrowserOptions& options); + bool openFileBrowser(const DGL_NAMESPACE::FileBrowserOptions& options); #endif static void renderToPicture(const char* filename, const GraphicsContext& context, uint width, uint height); diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp @@ -101,10 +101,12 @@ #endif #ifndef DGL_FILE_BROWSER_DISABLED +# define FILE_BROWSER_DIALOG_DGL_NAMESPACE +# include "../FileBrowserDialog.hpp" # ifdef DISTRHO_OS_MAC -# import "../../distrho/extra/FileBrowserDialog.cpp" +# import "../../distrho/extra/FileBrowserDialogImpl.cpp" # else -# include "../../distrho/extra/FileBrowserDialog.cpp" +# include "../../distrho/extra/FileBrowserDialogImpl.cpp" # endif #endif diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp @@ -48,16 +48,14 @@ typedef DGL_NAMESPACE::NanoTopLevelWidget UIWidget; typedef DGL_NAMESPACE::TopLevelWidget UIWidget; #endif -#ifndef DGL_FILE_BROWSER_DISABLED +#if DISTRHO_UI_FILE_BROWSER # include "extra/FileBrowserDialog.hpp" #endif -START_NAMESPACE_DGL -class PluginWindow; -END_NAMESPACE_DGL - START_NAMESPACE_DISTRHO +class PluginWindow; + /* ------------------------------------------------------------------------------------------------------------ * DPF UI */ @@ -185,7 +183,7 @@ public: void sendNote(uint8_t channel, uint8_t note, uint8_t velocity); #endif -#ifndef DGL_FILE_BROWSER_DISABLED +#if DISTRHO_UI_FILE_BROWSER /** Open a file browser dialog with this window as transient parent.@n A few options can be specified to setup the dialog. @@ -198,7 +196,7 @@ public: @note This is exactly the same API as provided by the Window class, but redeclared here so that non-embed/DGL based UIs can still use file browser related functions. */ - bool openFileBrowser(const FileBrowserOptions& options = FileBrowserOptions()); + bool openFileBrowser(const DISTRHO_NAMESPACE::FileBrowserOptions& options = FileBrowserOptions()); #endif #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS @@ -334,7 +332,7 @@ protected: virtual void uiReshape(uint width, uint height); #endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI -#ifndef DGL_FILE_BROWSER_DISABLED +#if DISTRHO_UI_FILE_BROWSER /** Window file selected function, called when a path is selected by the user, as triggered by openFileBrowser(). This function is for plugin UIs to be able to override Window::onFileSelected(const char*). @@ -371,7 +369,7 @@ protected: private: struct PrivateData; PrivateData* const uiData; - friend class DGL_NAMESPACE::PluginWindow; + friend class PluginWindow; friend class UIExporter; #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI /** @internal */ diff --git a/distrho/DistrhoUI_macOS.mm b/distrho/DistrhoUI_macOS.mm @@ -26,7 +26,7 @@ # import <Cocoa/Cocoa.h> # include <algorithm> # include <cmath> -# ifndef DGL_FILE_BROWSER_DISABLED +# if DISTRHO_UI_FILE_BROWSER # import "extra/FileBrowserDialog.cpp" # endif diff --git a/distrho/extra/FileBrowserDialog.cpp b/distrho/extra/FileBrowserDialog.cpp @@ -1,636 +0,0 @@ -/* - * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> - * - * Permission to use, copy, modify, and/or distribute this software for any purpose with - * or without fee is hereby granted, provided that the above copyright notice and this - * permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD - * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL - * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "FileBrowserDialog.hpp" -#include "ScopedPointer.hpp" -#include "String.hpp" - -#ifdef DISTRHO_OS_MAC -# import <Cocoa/Cocoa.h> -#endif -#ifdef DISTRHO_OS_WINDOWS -# include <direct.h> -# include <process.h> -# include <winsock2.h> -# include <windows.h> -# include <commdlg.h> -# include <vector> -#else -# include <unistd.h> -#endif -#ifdef HAVE_DBUS -# include <dbus/dbus.h> -#endif -#ifdef HAVE_X11 -# define DBLCLKTME 400 -# include "sofd/libsofd.h" -# include "sofd/libsofd.c" -#endif - -START_NAMESPACE_DISTRHO - -// -------------------------------------------------------------------------------------------------------------------- - -// static pointer used for signal null/none action taken -static const char* const kSelectedFileCancelled = "__dpf_cancelled__"; - -struct FileBrowserData { - const char* selectedFile; - -#ifdef DISTRHO_OS_MAC - NSSavePanel* nsBasePanel; - NSOpenPanel* nsOpenPanel; -#endif -#ifdef HAVE_DBUS - DBusConnection* dbuscon; -#endif -#ifdef HAVE_X11 - Display* x11display; -#endif - -#ifdef DISTRHO_OS_WINDOWS - OPENFILENAMEW ofn; - volatile bool threadCancelled; - uintptr_t threadHandle; - std::vector<WCHAR> fileNameW; - std::vector<WCHAR> startDirW; - std::vector<WCHAR> titleW; - const bool saving; - bool isEmbed; - - FileBrowserData(const bool save) - : selectedFile(nullptr), - threadCancelled(false), - threadHandle(0), - fileNameW(32768), - saving(save), - isEmbed(false) - { - std::memset(&ofn, 0, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.lpstrFile = fileNameW.data(); - ofn.nMaxFile = (DWORD)fileNameW.size(); - } - - ~FileBrowserData() - { - if (cancelAndStop()) - free(); - } - - void setupAndStart(const bool embed, - const char* const startDir, - const char* const windowTitle, - const uintptr_t winId, - const FileBrowserOptions options) - { - isEmbed = embed; - - ofn.hwndOwner = (HWND)winId; - - ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; - if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked) - ofn.Flags |= OFN_FORCESHOWHIDDEN; - - ofn.FlagsEx = 0x0; - if (options.buttons.showPlaces == FileBrowserOptions::kButtonInvisible) - ofn.FlagsEx |= OFN_EX_NOPLACESBAR; - - startDirW.resize(std::strlen(startDir) + 1); - if (MultiByteToWideChar(CP_UTF8, 0, startDir, -1, startDirW.data(), static_cast<int>(startDirW.size()))) - ofn.lpstrInitialDir = startDirW.data(); - - titleW.resize(std::strlen(windowTitle) + 1); - if (MultiByteToWideChar(CP_UTF8, 0, windowTitle, -1, titleW.data(), static_cast<int>(titleW.size()))) - ofn.lpstrTitle = titleW.data(); - - uint threadId; - threadCancelled = false; - threadHandle = _beginthreadex(nullptr, 0, _run, this, 0, &threadId); - } - - bool cancelAndStop() - { - threadCancelled = true; - - if (threadHandle == 0) - return true; - - // if previous dialog running, carefully close its window - const HWND owner = isEmbed ? GetParent(ofn.hwndOwner) : ofn.hwndOwner; - - if (owner != nullptr && owner != INVALID_HANDLE_VALUE) - { - const HWND window = GetWindow(owner, GW_HWNDFIRST); - - if (window != nullptr && window != INVALID_HANDLE_VALUE) - { - SendMessage(window, WM_SYSCOMMAND, SC_CLOSE, 0); - SendMessage(window, WM_CLOSE, 0, 0); - WaitForSingleObject((HANDLE)threadHandle, 5000); - } - } - - if (threadHandle == 0) - return true; - - // not good if thread still running, but let's close the handle anyway - CloseHandle((HANDLE)threadHandle); - threadHandle = 0; - return false; - } - - void run() - { - const char* nextFile = nullptr; - - if (saving ? GetSaveFileNameW(&ofn) : GetOpenFileNameW(&ofn)) - { - if (threadCancelled) - { - threadHandle = 0; - return; - } - - // back to UTF-8 - std::vector<char> fileNameA(4 * 32768); - if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, - fileNameA.data(), (int)fileNameA.size(), - nullptr, nullptr)) - { - nextFile = strdup(fileNameA.data()); - } - } - - if (threadCancelled) - { - threadHandle = 0; - return; - } - - if (nextFile == nullptr) - nextFile = kSelectedFileCancelled; - - selectedFile = nextFile; - threadHandle = 0; - } - - static unsigned __stdcall _run(void* const arg) - { - // CoInitializeEx(nullptr, COINIT_MULTITHREADED); - static_cast<FileBrowserData*>(arg)->run(); - // CoUninitialize(); - _endthreadex(0); - return 0; - } -#else // DISTRHO_OS_WINDOWS - FileBrowserData(const bool saving) - : selectedFile(nullptr) - { -#ifdef DISTRHO_OS_MAC - if (saving) - { - nsOpenPanel = nullptr; - nsBasePanel = [[NSSavePanel savePanel]retain]; - } - else - { - nsOpenPanel = [[NSOpenPanel openPanel]retain]; - nsBasePanel = nsOpenPanel; - } -#endif -#ifdef HAVE_DBUS - if ((dbuscon = dbus_bus_get(DBUS_BUS_SESSION, nullptr)) != nullptr) - dbus_connection_set_exit_on_disconnect(dbuscon, false); -#endif -#ifdef HAVE_X11 - x11display = XOpenDisplay(nullptr); -#endif - - // maybe unused - return; (void)saving; - } - - ~FileBrowserData() - { -#ifdef DISTRHO_OS_MAC - [nsBasePanel release]; -#endif -#ifdef HAVE_DBUS - if (dbuscon != nullptr) - dbus_connection_unref(dbuscon); -#endif -#ifdef HAVE_X11 - if (x11display != nullptr) - XCloseDisplay(x11display); -#endif - - free(); - } -#endif - - void free() - { - if (selectedFile == nullptr) - return; - - if (selectedFile == kSelectedFileCancelled || std::strcmp(selectedFile, kSelectedFileCancelled) == 0) - { - selectedFile = nullptr; - return; - } - - std::free(const_cast<char*>(selectedFile)); - selectedFile = nullptr; - } -}; - -// -------------------------------------------------------------------------------------------------------------------- - -#ifdef DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE -namespace DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE { -#endif - -// -------------------------------------------------------------------------------------------------------------------- - -FileBrowserHandle fileBrowserCreate(const bool isEmbed, - const uintptr_t windowId, - const double scaleFactor, - const FileBrowserOptions& options) -{ - String startDir(options.startDir); - - if (startDir.isEmpty()) - { -#ifdef DISTRHO_OS_WINDOWS - if (char* const cwd = _getcwd(nullptr, 0)) - { - startDir = cwd; - std::free(cwd); - } -#else - if (char* const cwd = getcwd(nullptr, 0)) - { - startDir = cwd; - std::free(cwd); - } -#endif - } - - DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), nullptr); - - if (! startDir.endsWith(DISTRHO_OS_SEP)) - startDir += DISTRHO_OS_SEP_STR; - - String windowTitle(options.title); - - if (windowTitle.isEmpty()) - windowTitle = "FileBrowser"; - - ScopedPointer<FileBrowserData> handle(new FileBrowserData(options.saving)); - -#ifdef DISTRHO_OS_MAC -# if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_8 - // unsupported - return nullptr; -# else - NSSavePanel* const nsBasePanel = handle->nsBasePanel; - DISTRHO_SAFE_ASSERT_RETURN(nsBasePanel != nullptr, nullptr); - - if (! options.saving) - { - NSOpenPanel* const nsOpenPanel = handle->nsOpenPanel; - DISTRHO_SAFE_ASSERT_RETURN(nsOpenPanel != nullptr, nullptr); - - [nsOpenPanel setAllowsMultipleSelection:NO]; - [nsOpenPanel setCanChooseDirectories:NO]; - [nsOpenPanel setCanChooseFiles:YES]; - } - - [nsBasePanel setDirectoryURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:startDir]]]; - - // TODO file filter using allowedContentTypes: [UTType] - - if (options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleChecked) - [nsBasePanel setAllowsOtherFileTypes:YES]; - if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked) - [nsBasePanel setShowsHiddenFiles:YES]; - - NSString* const titleString = [[NSString alloc] - initWithBytes:windowTitle - length:strlen(windowTitle) - encoding:NSUTF8StringEncoding]; - [nsBasePanel setTitle:titleString]; - - FileBrowserData* const handleptr = handle.get(); - - dispatch_async(dispatch_get_main_queue(), ^ - { - [nsBasePanel beginSheetModalForWindow:[(NSView*)windowId window] - completionHandler:^(NSModalResponse result) - { - if (result == NSModalResponseOK && [[nsBasePanel URL] isFileURL]) - { - NSString* const path = [[nsBasePanel URL] path]; - handleptr->selectedFile = strdup([path UTF8String]); - } - else - { - handleptr->selectedFile = kSelectedFileCancelled; - } - }]; - }); -# endif -#endif - -#ifdef DISTRHO_OS_WINDOWS - handle->setupAndStart(isEmbed, startDir, windowTitle, windowId, options); -#endif - -#ifdef HAVE_DBUS - // optional, can be null - DBusConnection* const dbuscon = handle->dbuscon; - - // https://flatpak.github.io/xdg-desktop-portal/portal-docs.html#gdbus-org.freedesktop.portal.FileChooser - if (dbuscon != nullptr) - { - // if this is the first time we are calling into DBus, check if things are working - static bool checkAvailable = !dbus_bus_name_has_owner(dbuscon, "org.freedesktop.portal.Desktop", nullptr); - - if (checkAvailable) - { - checkAvailable = false; - - if (DBusMessage* const msg = dbus_message_new_method_call("org.freedesktop.portal.Desktop", - "/org/freedesktop/portal/desktop", - "org.freedesktop.portal.FileChooser", - "version")) - { - if (DBusMessage* const reply = dbus_connection_send_with_reply_and_block(dbuscon, msg, 250, nullptr)) - dbus_message_unref(reply); - - dbus_message_unref(msg); - } - } - - // Any subsquent calls should have this DBus service active - if (dbus_bus_name_has_owner(dbuscon, "org.freedesktop.portal.Desktop", nullptr)) - { - if (DBusMessage* const msg = dbus_message_new_method_call("org.freedesktop.portal.Desktop", - "/org/freedesktop/portal/desktop", - "org.freedesktop.portal.FileChooser", - options.saving ? "SaveFile" : "OpenFile")) - { - #ifdef HAVE_X11 - char windowIdStr[32]; - memset(windowIdStr, 0, sizeof(windowIdStr)); - snprintf(windowIdStr, sizeof(windowIdStr)-1, "x11:%llx", (ulonglong)windowId); - const char* windowIdStrPtr = windowIdStr; - #endif - - dbus_message_append_args(msg, - #ifdef HAVE_X11 - DBUS_TYPE_STRING, &windowIdStrPtr, - #endif - DBUS_TYPE_STRING, &windowTitle, - DBUS_TYPE_INVALID); - - DBusMessageIter iter, array; - dbus_message_iter_init_append(msg, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array); - - { - DBusMessageIter dict, variant, variantArray; - const char* const current_folder_key = "current_folder"; - const char* const current_folder_val = startDir.buffer(); - - dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, nullptr, &dict); - dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &current_folder_key); - dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "ay", &variant); - dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &variantArray); - dbus_message_iter_append_fixed_array(&variantArray, DBUS_TYPE_BYTE, - &current_folder_val, startDir.length()+1); - dbus_message_iter_close_container(&variant, &variantArray); - dbus_message_iter_close_container(&dict, &variant); - dbus_message_iter_close_container(&array, &dict); - } - - dbus_message_iter_close_container(&iter, &array); - - dbus_connection_send(dbuscon, msg, nullptr); - - dbus_message_unref(msg); - return handle.release(); - } - } - } -#endif - -#ifdef HAVE_X11 - Display* const x11display = handle->x11display; - DISTRHO_SAFE_ASSERT_RETURN(x11display != nullptr, nullptr); - - // unsupported at the moment - if (options.saving) - return nullptr; - - DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, nullptr); - DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, windowTitle) == 0, nullptr); - - const int button1 = options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked ? 1 - : options.buttons.showHidden == FileBrowserOptions::kButtonVisibleUnchecked ? 0 : -1; - const int button2 = options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleChecked ? 1 - : options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleUnchecked ? 0 : -1; - const int button3 = options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleChecked ? 1 - : options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleUnchecked ? 0 : -1; - - x_fib_cfg_buttons(1, button1); - x_fib_cfg_buttons(2, button2); - x_fib_cfg_buttons(3, button3); - - if (x_fib_show(x11display, windowId, 0, 0, scaleFactor + 0.5) != 0) - return nullptr; -#endif - - return handle.release(); - - // might be unused - (void)isEmbed; - (void)windowId; - (void)scaleFactor; -} - -// -------------------------------------------------------------------------------------------------------------------- -// returns true if dialog was closed (with or without a file selection) - -bool fileBrowserIdle(const FileBrowserHandle handle) -{ -#ifdef HAVE_DBUS - if (DBusConnection* dbuscon = handle->dbuscon) - { - while (dbus_connection_dispatch(dbuscon) == DBUS_DISPATCH_DATA_REMAINS) {} - dbus_connection_read_write_dispatch(dbuscon, 0); - - if (DBusMessage* const message = dbus_connection_pop_message(dbuscon)) - { - const char* const interface = dbus_message_get_interface(message); - const char* const member = dbus_message_get_member(message); - - if (interface != nullptr && std::strcmp(interface, "org.freedesktop.portal.Request") == 0 - && member != nullptr && std::strcmp(member, "Response") == 0) - { - do { - DBusMessageIter iter; - dbus_message_iter_init(message, &iter); - - // starts with uint32 for return/exit code - DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32); - - uint32_t ret = 1; - dbus_message_iter_get_basic(&iter, &ret); - - if (ret != 0) - break; - - // next must be array - dbus_message_iter_next(&iter); - DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY); - - // open dict array - DBusMessageIter dictArray; - dbus_message_iter_recurse(&iter, &dictArray); - DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dictArray) == DBUS_TYPE_DICT_ENTRY); - - // open containing dict - DBusMessageIter dict; - dbus_message_iter_recurse(&dictArray, &dict); - - // look for dict with string "uris" - DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRING); - - const char* key = nullptr; - dbus_message_iter_get_basic(&dict, &key); - DISTRHO_SAFE_ASSERT_BREAK(key != nullptr); - - // keep going until we find it - while (std::strcmp(key, "uris") != 0) - { - key = nullptr; - dbus_message_iter_next(&dictArray); - DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dictArray) == DBUS_TYPE_DICT_ENTRY); - - dbus_message_iter_recurse(&dictArray, &dict); - DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRING); - - dbus_message_iter_get_basic(&dict, &key); - DISTRHO_SAFE_ASSERT_BREAK(key != nullptr); - } - - if (key == nullptr) - break; - - // then comes variant - dbus_message_iter_next(&dict); - DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_VARIANT); - - DBusMessageIter variant; - dbus_message_iter_recurse(&dict, &variant); - DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_ARRAY); - - // open variant array (variant type is string) - DBusMessageIter variantArray; - dbus_message_iter_recurse(&variant, &variantArray); - DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&variantArray) == DBUS_TYPE_STRING); - - const char* value = nullptr; - dbus_message_iter_get_basic(&variantArray, &value); - - // and finally we have our dear value, just make sure it is local - DISTRHO_SAFE_ASSERT_BREAK(value != nullptr); - - if (const char* const localvalue = std::strstr(value, "file:///")) - handle->selectedFile = strdup(localvalue + 7); - - } while(false); - - if (handle->selectedFile == nullptr) - handle->selectedFile = kSelectedFileCancelled; - } - } - } -#endif - -#ifdef HAVE_X11 - Display* const x11display = handle->x11display; - - if (x11display == nullptr) - return false; - - XEvent event; - while (XPending(x11display) > 0) - { - XNextEvent(x11display, &event); - - if (x_fib_handle_events(x11display, &event) == 0) - continue; - - if (x_fib_status() > 0) - handle->selectedFile = x_fib_filename(); - else - handle->selectedFile = kSelectedFileCancelled; - - x_fib_close(x11display); - XCloseDisplay(x11display); - handle->x11display = nullptr; - break; - } -#endif - - return handle->selectedFile != nullptr; -} - -// -------------------------------------------------------------------------------------------------------------------- -// close sofd file dialog - -void fileBrowserClose(const FileBrowserHandle handle) -{ -#ifdef HAVE_X11 - if (Display* const x11display = handle->x11display) - x_fib_close(x11display); -#endif - - delete handle; -} - -// -------------------------------------------------------------------------------------------------------------------- -// get path chosen via sofd file dialog - -const char* fileBrowserGetPath(const FileBrowserHandle handle) -{ - if (const char* const selectedFile = handle->selectedFile) - if (selectedFile != kSelectedFileCancelled && std::strcmp(selectedFile, kSelectedFileCancelled) != 0) - return selectedFile; - - return nullptr; -} - -// -------------------------------------------------------------------------------------------------------------------- - -#ifdef DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE -} -#endif - -END_NAMESPACE_DISTRHO diff --git a/distrho/extra/FileBrowserDialog.hpp b/distrho/extra/FileBrowserDialog.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> + * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -21,113 +21,7 @@ START_NAMESPACE_DISTRHO -// -------------------------------------------------------------------------------------------------------------------- -// File Browser Dialog stuff - -struct FileBrowserData; -typedef FileBrowserData* FileBrowserHandle; - -// -------------------------------------------------------------------------------------------------------------------- - -/** - File browser options, for customizing the file browser dialog.@n - By default the file browser dialog will be work as "open file" in the current working directory. -*/ -struct FileBrowserOptions { - /** Whether we are saving, opening files otherwise (default) */ - bool saving; - - /** Start directory, uses current working directory if null */ - const char* startDir; - - /** File browser dialog window title, uses "FileBrowser" if null */ - const char* title; - - // TODO file filter - - /** - File browser button state. - This allows to customize the behaviour of the file browse dialog buttons. - Note these are merely hints, not all systems support them. - */ - enum ButtonState { - kButtonInvisible, - kButtonVisibleUnchecked, - kButtonVisibleChecked, - }; - - /** - File browser buttons. - */ - struct Buttons { - /** Whether to list all files vs only those with matching file extension */ - ButtonState listAllFiles; - /** Whether to show hidden files */ - ButtonState showHidden; - /** Whether to show list of places (bookmarks) */ - ButtonState showPlaces; - - /** Constructor for default values */ - Buttons() - : listAllFiles(kButtonVisibleChecked), - showHidden(kButtonVisibleUnchecked), - showPlaces(kButtonVisibleChecked) {} - } buttons; - - /** Constructor for default values */ - FileBrowserOptions() - : saving(false), - startDir(nullptr), - title(nullptr), - buttons() {} -}; - -// -------------------------------------------------------------------------------------------------------------------- - -#ifdef DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE -namespace DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE { -#endif - -/** - Create a new file browser dialog. - - @p isEmbed: Whether the window this dialog belongs to is an embed/child window (needed to close dialog on Windows) - @p windowId: The native window id to attach this dialog to as transient parent (X11 Window, HWND or NSView*) - @p scaleFactor: Scale factor to use (only used on X11) - @p options: Extra options, optional - By default the file browser dialog will be work as "open file" in the current working directory. -*/ -FileBrowserHandle fileBrowserCreate(bool isEmbed, - uintptr_t windowId, - double scaleFactor, - const FileBrowserOptions& options = FileBrowserOptions()); - -/** - Idle the file browser dialog handle.@n - Returns true if dialog was closed (with or without a file selection), - in which case the handle must not be used afterwards. - You can then call fileBrowserGetPath to know the selected file (or null if cancelled). -*/ -bool fileBrowserIdle(const FileBrowserHandle handle); - -/** - Close the file browser dialog, handle must not be used afterwards. -*/ -void fileBrowserClose(const FileBrowserHandle handle); - -/** - Get the path chosen by the user or null.@n - Should only be called after fileBrowserIdle returns true. -*/ -const char* fileBrowserGetPath(const FileBrowserHandle handle); - -// -------------------------------------------------------------------------------------------------------------------- - -#ifdef DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE -} -#endif - -// -------------------------------------------------------------------------------------------------------------------- +#include "FileBrowserDialogImpl.hpp" END_NAMESPACE_DISTRHO diff --git a/distrho/extra/FileBrowserDialogImpl.cpp b/distrho/extra/FileBrowserDialogImpl.cpp @@ -0,0 +1,643 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED) && !defined(DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED) +# error bad include +#endif +#if !defined(FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE) && !defined(FILE_BROWSER_DIALOG_DGL_NAMESPACE) +# error bad usage +#endif + +#include "ScopedPointer.hpp" +#include "String.hpp" + +#ifdef DISTRHO_OS_MAC +# import <Cocoa/Cocoa.h> +#endif +#ifdef DISTRHO_OS_WINDOWS +# include <direct.h> +# include <process.h> +# include <winsock2.h> +# include <windows.h> +# include <commdlg.h> +# include <vector> +#else +# include <unistd.h> +#endif +#ifdef HAVE_DBUS +# include <dbus/dbus.h> +#endif +#ifdef HAVE_X11 +# define DBLCLKTME 400 +# include "sofd/libsofd.h" +# include "sofd/libsofd.c" +#endif + +#ifdef FILE_BROWSER_DIALOG_DGL_NAMESPACE +START_NAMESPACE_DGL +#else +START_NAMESPACE_DISTRHO +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +// static pointer used for signal null/none action taken +static const char* const kSelectedFileCancelled = "__dpf_cancelled__"; + +struct FileBrowserData { + const char* selectedFile; + +#ifdef DISTRHO_OS_MAC + NSSavePanel* nsBasePanel; + NSOpenPanel* nsOpenPanel; +#endif +#ifdef HAVE_DBUS + DBusConnection* dbuscon; +#endif +#ifdef HAVE_X11 + Display* x11display; +#endif + +#ifdef DISTRHO_OS_WINDOWS + OPENFILENAMEW ofn; + volatile bool threadCancelled; + uintptr_t threadHandle; + std::vector<WCHAR> fileNameW; + std::vector<WCHAR> startDirW; + std::vector<WCHAR> titleW; + const bool saving; + bool isEmbed; + + FileBrowserData(const bool save) + : selectedFile(nullptr), + threadCancelled(false), + threadHandle(0), + fileNameW(32768), + saving(save), + isEmbed(false) + { + std::memset(&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.lpstrFile = fileNameW.data(); + ofn.nMaxFile = (DWORD)fileNameW.size(); + } + + ~FileBrowserData() + { + if (cancelAndStop()) + free(); + } + + void setupAndStart(const bool embed, + const char* const startDir, + const char* const windowTitle, + const uintptr_t winId, + const FileBrowserOptions options) + { + isEmbed = embed; + + ofn.hwndOwner = (HWND)winId; + + ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; + if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked) + ofn.Flags |= OFN_FORCESHOWHIDDEN; + + ofn.FlagsEx = 0x0; + if (options.buttons.showPlaces == FileBrowserOptions::kButtonInvisible) + ofn.FlagsEx |= OFN_EX_NOPLACESBAR; + + startDirW.resize(std::strlen(startDir) + 1); + if (MultiByteToWideChar(CP_UTF8, 0, startDir, -1, startDirW.data(), static_cast<int>(startDirW.size()))) + ofn.lpstrInitialDir = startDirW.data(); + + titleW.resize(std::strlen(windowTitle) + 1); + if (MultiByteToWideChar(CP_UTF8, 0, windowTitle, -1, titleW.data(), static_cast<int>(titleW.size()))) + ofn.lpstrTitle = titleW.data(); + + uint threadId; + threadCancelled = false; + threadHandle = _beginthreadex(nullptr, 0, _run, this, 0, &threadId); + } + + bool cancelAndStop() + { + threadCancelled = true; + + if (threadHandle == 0) + return true; + + // if previous dialog running, carefully close its window + const HWND owner = isEmbed ? GetParent(ofn.hwndOwner) : ofn.hwndOwner; + + if (owner != nullptr && owner != INVALID_HANDLE_VALUE) + { + const HWND window = GetWindow(owner, GW_HWNDFIRST); + + if (window != nullptr && window != INVALID_HANDLE_VALUE) + { + SendMessage(window, WM_SYSCOMMAND, SC_CLOSE, 0); + SendMessage(window, WM_CLOSE, 0, 0); + WaitForSingleObject((HANDLE)threadHandle, 5000); + } + } + + if (threadHandle == 0) + return true; + + // not good if thread still running, but let's close the handle anyway + CloseHandle((HANDLE)threadHandle); + threadHandle = 0; + return false; + } + + void run() + { + const char* nextFile = nullptr; + + if (saving ? GetSaveFileNameW(&ofn) : GetOpenFileNameW(&ofn)) + { + if (threadCancelled) + { + threadHandle = 0; + return; + } + + // back to UTF-8 + std::vector<char> fileNameA(4 * 32768); + if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, + fileNameA.data(), (int)fileNameA.size(), + nullptr, nullptr)) + { + nextFile = strdup(fileNameA.data()); + } + } + + if (threadCancelled) + { + threadHandle = 0; + return; + } + + if (nextFile == nullptr) + nextFile = kSelectedFileCancelled; + + selectedFile = nextFile; + threadHandle = 0; + } + + static unsigned __stdcall _run(void* const arg) + { + // CoInitializeEx(nullptr, COINIT_MULTITHREADED); + static_cast<FileBrowserData*>(arg)->run(); + // CoUninitialize(); + _endthreadex(0); + return 0; + } +#else // DISTRHO_OS_WINDOWS + FileBrowserData(const bool saving) + : selectedFile(nullptr) + { +#ifdef DISTRHO_OS_MAC + if (saving) + { + nsOpenPanel = nullptr; + nsBasePanel = [[NSSavePanel savePanel]retain]; + } + else + { + nsOpenPanel = [[NSOpenPanel openPanel]retain]; + nsBasePanel = nsOpenPanel; + } +#endif +#ifdef HAVE_DBUS + if ((dbuscon = dbus_bus_get(DBUS_BUS_SESSION, nullptr)) != nullptr) + dbus_connection_set_exit_on_disconnect(dbuscon, false); +#endif +#ifdef HAVE_X11 + x11display = XOpenDisplay(nullptr); +#endif + + // maybe unused + return; (void)saving; + } + + ~FileBrowserData() + { +#ifdef DISTRHO_OS_MAC + [nsBasePanel release]; +#endif +#ifdef HAVE_DBUS + if (dbuscon != nullptr) + dbus_connection_unref(dbuscon); +#endif +#ifdef HAVE_X11 + if (x11display != nullptr) + XCloseDisplay(x11display); +#endif + + free(); + } +#endif + + void free() + { + if (selectedFile == nullptr) + return; + + if (selectedFile == kSelectedFileCancelled || std::strcmp(selectedFile, kSelectedFileCancelled) == 0) + { + selectedFile = nullptr; + return; + } + + std::free(const_cast<char*>(selectedFile)); + selectedFile = nullptr; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +FileBrowserHandle fileBrowserCreate(const bool isEmbed, + const uintptr_t windowId, + const double scaleFactor, + const FileBrowserOptions& options) +{ + String startDir(options.startDir); + + if (startDir.isEmpty()) + { +#ifdef DISTRHO_OS_WINDOWS + if (char* const cwd = _getcwd(nullptr, 0)) + { + startDir = cwd; + std::free(cwd); + } +#else + if (char* const cwd = getcwd(nullptr, 0)) + { + startDir = cwd; + std::free(cwd); + } +#endif + } + + DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), nullptr); + + if (! startDir.endsWith(DISTRHO_OS_SEP)) + startDir += DISTRHO_OS_SEP_STR; + + String windowTitle(options.title); + + if (windowTitle.isEmpty()) + windowTitle = "FileBrowser"; + + ScopedPointer<FileBrowserData> handle(new FileBrowserData(options.saving)); + +#ifdef DISTRHO_OS_MAC +# if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_8 + // unsupported + return nullptr; +# else + NSSavePanel* const nsBasePanel = handle->nsBasePanel; + DISTRHO_SAFE_ASSERT_RETURN(nsBasePanel != nullptr, nullptr); + + if (! options.saving) + { + NSOpenPanel* const nsOpenPanel = handle->nsOpenPanel; + DISTRHO_SAFE_ASSERT_RETURN(nsOpenPanel != nullptr, nullptr); + + [nsOpenPanel setAllowsMultipleSelection:NO]; + [nsOpenPanel setCanChooseDirectories:NO]; + [nsOpenPanel setCanChooseFiles:YES]; + } + + [nsBasePanel setDirectoryURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:startDir]]]; + + // TODO file filter using allowedContentTypes: [UTType] + + if (options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleChecked) + [nsBasePanel setAllowsOtherFileTypes:YES]; + if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked) + [nsBasePanel setShowsHiddenFiles:YES]; + + NSString* const titleString = [[NSString alloc] + initWithBytes:windowTitle + length:strlen(windowTitle) + encoding:NSUTF8StringEncoding]; + [nsBasePanel setTitle:titleString]; + + FileBrowserData* const handleptr = handle.get(); + + dispatch_async(dispatch_get_main_queue(), ^ + { + [nsBasePanel beginSheetModalForWindow:[(NSView*)windowId window] + completionHandler:^(NSModalResponse result) + { + if (result == NSModalResponseOK && [[nsBasePanel URL] isFileURL]) + { + NSString* const path = [[nsBasePanel URL] path]; + handleptr->selectedFile = strdup([path UTF8String]); + } + else + { + handleptr->selectedFile = kSelectedFileCancelled; + } + }]; + }); +# endif +#endif + +#ifdef DISTRHO_OS_WINDOWS + handle->setupAndStart(isEmbed, startDir, windowTitle, windowId, options); +#endif + +#ifdef HAVE_DBUS + // optional, can be null + DBusConnection* const dbuscon = handle->dbuscon; + + // https://flatpak.github.io/xdg-desktop-portal/portal-docs.html#gdbus-org.freedesktop.portal.FileChooser + if (dbuscon != nullptr) + { + // if this is the first time we are calling into DBus, check if things are working + static bool checkAvailable = !dbus_bus_name_has_owner(dbuscon, "org.freedesktop.portal.Desktop", nullptr); + + if (checkAvailable) + { + checkAvailable = false; + + if (DBusMessage* const msg = dbus_message_new_method_call("org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.FileChooser", + "version")) + { + if (DBusMessage* const reply = dbus_connection_send_with_reply_and_block(dbuscon, msg, 250, nullptr)) + dbus_message_unref(reply); + + dbus_message_unref(msg); + } + } + + // Any subsquent calls should have this DBus service active + if (dbus_bus_name_has_owner(dbuscon, "org.freedesktop.portal.Desktop", nullptr)) + { + if (DBusMessage* const msg = dbus_message_new_method_call("org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.FileChooser", + options.saving ? "SaveFile" : "OpenFile")) + { + #ifdef HAVE_X11 + char windowIdStr[32]; + memset(windowIdStr, 0, sizeof(windowIdStr)); + snprintf(windowIdStr, sizeof(windowIdStr)-1, "x11:%llx", (ulonglong)windowId); + const char* windowIdStrPtr = windowIdStr; + #endif + + dbus_message_append_args(msg, + #ifdef HAVE_X11 + DBUS_TYPE_STRING, &windowIdStrPtr, + #endif + DBUS_TYPE_STRING, &windowTitle, + DBUS_TYPE_INVALID); + + DBusMessageIter iter, array; + dbus_message_iter_init_append(msg, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array); + + { + DBusMessageIter dict, variant, variantArray; + const char* const current_folder_key = "current_folder"; + const char* const current_folder_val = startDir.buffer(); + + dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, nullptr, &dict); + dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &current_folder_key); + dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "ay", &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &variantArray); + dbus_message_iter_append_fixed_array(&variantArray, DBUS_TYPE_BYTE, + &current_folder_val, startDir.length()+1); + dbus_message_iter_close_container(&variant, &variantArray); + dbus_message_iter_close_container(&dict, &variant); + dbus_message_iter_close_container(&array, &dict); + } + + dbus_message_iter_close_container(&iter, &array); + + dbus_connection_send(dbuscon, msg, nullptr); + + dbus_message_unref(msg); + return handle.release(); + } + } + } +#endif + +#ifdef HAVE_X11 + Display* const x11display = handle->x11display; + DISTRHO_SAFE_ASSERT_RETURN(x11display != nullptr, nullptr); + + // unsupported at the moment + if (options.saving) + return nullptr; + + DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, nullptr); + DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, windowTitle) == 0, nullptr); + + const int button1 = options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked ? 1 + : options.buttons.showHidden == FileBrowserOptions::kButtonVisibleUnchecked ? 0 : -1; + const int button2 = options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleChecked ? 1 + : options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleUnchecked ? 0 : -1; + const int button3 = options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleChecked ? 1 + : options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleUnchecked ? 0 : -1; + + x_fib_cfg_buttons(1, button1); + x_fib_cfg_buttons(2, button2); + x_fib_cfg_buttons(3, button3); + + if (x_fib_show(x11display, windowId, 0, 0, scaleFactor + 0.5) != 0) + return nullptr; +#endif + + return handle.release(); + + // might be unused + (void)isEmbed; + (void)windowId; + (void)scaleFactor; +} + +// -------------------------------------------------------------------------------------------------------------------- +// returns true if dialog was closed (with or without a file selection) + +bool fileBrowserIdle(const FileBrowserHandle handle) +{ +#ifdef HAVE_DBUS + if (DBusConnection* dbuscon = handle->dbuscon) + { + while (dbus_connection_dispatch(dbuscon) == DBUS_DISPATCH_DATA_REMAINS) {} + dbus_connection_read_write_dispatch(dbuscon, 0); + + if (DBusMessage* const message = dbus_connection_pop_message(dbuscon)) + { + const char* const interface = dbus_message_get_interface(message); + const char* const member = dbus_message_get_member(message); + + if (interface != nullptr && std::strcmp(interface, "org.freedesktop.portal.Request") == 0 + && member != nullptr && std::strcmp(member, "Response") == 0) + { + do { + DBusMessageIter iter; + dbus_message_iter_init(message, &iter); + + // starts with uint32 for return/exit code + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32); + + uint32_t ret = 1; + dbus_message_iter_get_basic(&iter, &ret); + + if (ret != 0) + break; + + // next must be array + dbus_message_iter_next(&iter); + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY); + + // open dict array + DBusMessageIter dictArray; + dbus_message_iter_recurse(&iter, &dictArray); + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dictArray) == DBUS_TYPE_DICT_ENTRY); + + // open containing dict + DBusMessageIter dict; + dbus_message_iter_recurse(&dictArray, &dict); + + // look for dict with string "uris" + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRING); + + const char* key = nullptr; + dbus_message_iter_get_basic(&dict, &key); + DISTRHO_SAFE_ASSERT_BREAK(key != nullptr); + + // keep going until we find it + while (std::strcmp(key, "uris") != 0) + { + key = nullptr; + dbus_message_iter_next(&dictArray); + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dictArray) == DBUS_TYPE_DICT_ENTRY); + + dbus_message_iter_recurse(&dictArray, &dict); + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRING); + + dbus_message_iter_get_basic(&dict, &key); + DISTRHO_SAFE_ASSERT_BREAK(key != nullptr); + } + + if (key == nullptr) + break; + + // then comes variant + dbus_message_iter_next(&dict); + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_VARIANT); + + DBusMessageIter variant; + dbus_message_iter_recurse(&dict, &variant); + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_ARRAY); + + // open variant array (variant type is string) + DBusMessageIter variantArray; + dbus_message_iter_recurse(&variant, &variantArray); + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&variantArray) == DBUS_TYPE_STRING); + + const char* value = nullptr; + dbus_message_iter_get_basic(&variantArray, &value); + + // and finally we have our dear value, just make sure it is local + DISTRHO_SAFE_ASSERT_BREAK(value != nullptr); + + if (const char* const localvalue = std::strstr(value, "file:///")) + handle->selectedFile = strdup(localvalue + 7); + + } while(false); + + if (handle->selectedFile == nullptr) + handle->selectedFile = kSelectedFileCancelled; + } + } + } +#endif + +#ifdef HAVE_X11 + Display* const x11display = handle->x11display; + + if (x11display == nullptr) + return false; + + XEvent event; + while (XPending(x11display) > 0) + { + XNextEvent(x11display, &event); + + if (x_fib_handle_events(x11display, &event) == 0) + continue; + + if (x_fib_status() > 0) + handle->selectedFile = x_fib_filename(); + else + handle->selectedFile = kSelectedFileCancelled; + + x_fib_close(x11display); + XCloseDisplay(x11display); + handle->x11display = nullptr; + break; + } +#endif + + return handle->selectedFile != nullptr; +} + +// -------------------------------------------------------------------------------------------------------------------- +// close sofd file dialog + +void fileBrowserClose(const FileBrowserHandle handle) +{ +#ifdef HAVE_X11 + if (Display* const x11display = handle->x11display) + x_fib_close(x11display); +#endif + + delete handle; +} + +// -------------------------------------------------------------------------------------------------------------------- +// get path chosen via sofd file dialog + +const char* fileBrowserGetPath(const FileBrowserHandle handle) +{ + if (const char* const selectedFile = handle->selectedFile) + if (selectedFile != kSelectedFileCancelled && std::strcmp(selectedFile, kSelectedFileCancelled) != 0) + return selectedFile; + + return nullptr; +} + +// -------------------------------------------------------------------------------------------------------------------- + +#ifdef FILE_BROWSER_DIALOG_DGL_NAMESPACE +END_NAMESPACE_DGL +#else +END_NAMESPACE_DISTRHO +#endif + +#undef FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE +#undef FILE_BROWSER_DIALOG_DGL_NAMESPACE diff --git a/distrho/extra/FileBrowserDialogImpl.hpp b/distrho/extra/FileBrowserDialogImpl.hpp @@ -0,0 +1,117 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED) && !defined(DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED) +# error bad include +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// File Browser Dialog stuff + +struct FileBrowserData; +typedef FileBrowserData* FileBrowserHandle; + +// -------------------------------------------------------------------------------------------------------------------- + +/** + File browser options, for customizing the file browser dialog.@n + By default the file browser dialog will be work as "open file" in the current working directory. +*/ +struct FileBrowserOptions { + /** Whether we are saving, opening files otherwise (default) */ + bool saving; + + /** Start directory, uses current working directory if null */ + const char* startDir; + + /** File browser dialog window title, uses "FileBrowser" if null */ + const char* title; + + // TODO file filter + + /** + File browser button state. + This allows to customize the behaviour of the file browse dialog buttons. + Note these are merely hints, not all systems support them. + */ + enum ButtonState { + kButtonInvisible, + kButtonVisibleUnchecked, + kButtonVisibleChecked, + }; + + /** + File browser buttons. + */ + struct Buttons { + /** Whether to list all files vs only those with matching file extension */ + ButtonState listAllFiles; + /** Whether to show hidden files */ + ButtonState showHidden; + /** Whether to show list of places (bookmarks) */ + ButtonState showPlaces; + + /** Constructor for default values */ + Buttons() + : listAllFiles(kButtonVisibleChecked), + showHidden(kButtonVisibleUnchecked), + showPlaces(kButtonVisibleChecked) {} + } buttons; + + /** Constructor for default values */ + FileBrowserOptions() + : saving(false), + startDir(nullptr), + title(nullptr), + buttons() {} +}; + +// -------------------------------------------------------------------------------------------------------------------- + +/** + Create a new file browser dialog. + + @p isEmbed: Whether the window this dialog belongs to is an embed/child window (needed to close dialog on Windows) + @p windowId: The native window id to attach this dialog to as transient parent (X11 Window, HWND or NSView*) + @p scaleFactor: Scale factor to use (only used on X11) + @p options: Extra options, optional + By default the file browser dialog will be work as "open file" in the current working directory. +*/ +FileBrowserHandle fileBrowserCreate(bool isEmbed, + uintptr_t windowId, + double scaleFactor, + const FileBrowserOptions& options = FileBrowserOptions()); + +/** + Idle the file browser dialog handle.@n + Returns true if dialog was closed (with or without a file selection), + in which case the handle must not be used afterwards. + You can then call fileBrowserGetPath to know the selected file (or null if cancelled). +*/ +bool fileBrowserIdle(const FileBrowserHandle handle); + +/** + Close the file browser dialog, handle must not be used afterwards. +*/ +void fileBrowserClose(const FileBrowserHandle handle); + +/** + Get the path chosen by the user or null.@n + Should only be called after fileBrowserIdle returns true. +*/ +const char* fileBrowserGetPath(const FileBrowserHandle handle); + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginChecks.h b/distrho/src/DistrhoPluginChecks.h @@ -90,6 +90,14 @@ # define DISTRHO_PLUGIN_WANT_TIMEPOS 0 #endif +#ifndef DISTRHO_UI_FILE_BROWSER +# if defined(DGL_FILE_BROWSER_DISABLED) || DISTRHO_PLUGIN_HAS_EXTERNAL_UI +# define DISTRHO_UI_FILE_BROWSER 0 +# else +# define DISTRHO_UI_FILE_BROWSER 1 +# endif +#endif + #ifndef DISTRHO_UI_USER_RESIZABLE # define DISTRHO_UI_USER_RESIZABLE 0 #endif @@ -154,7 +162,16 @@ #endif // ----------------------------------------------------------------------- -// Disable UI if DGL or External UI is not available +// Disable file browser if using external UI + +#if DISTRHO_UI_FILE_BROWSER && DISTRHO_PLUGIN_HAS_EXTERNAL_UI +# warning file browser APIs do not work for external UIs +# undef DISTRHO_UI_FILE_BROWSER 0 +# define DISTRHO_UI_FILE_BROWSER 0 +#endif + +// ----------------------------------------------------------------------- +// Disable UI if DGL or external UI is not available #if (defined(DGL_CAIRO) && ! defined(HAVE_CAIRO)) || (defined(DGL_OPENGL) && ! defined(HAVE_OPENGL)) # undef DISTRHO_PLUGIN_HAS_EMBED_UI diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp @@ -17,26 +17,27 @@ #include "src/DistrhoPluginChecks.h" #include "src/DistrhoDefines.h" -#if !defined(DGL_FILE_BROWSER_DISABLED) && !defined(DISTRHO_UI_FILE_BROWSER) && !defined(DISTRHO_OS_MAC) +#if DISTRHO_UI_FILE_BROWSER && !defined(DISTRHO_OS_MAC) # define DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, SEP, FUNCTION) NS ## SEP ## FUNCTION # define DISTRHO_PUGL_NAMESPACE_MACRO(NS, FUNCTION) DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, _, FUNCTION) -# define DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE Plugin -# define x_fib_add_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_add_recent) -# define x_fib_cfg_buttons DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_cfg_buttons) -# define x_fib_cfg_filter_callback DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_cfg_filter_callback) -# define x_fib_close DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_close) -# define x_fib_configure DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_configure) -# define x_fib_filename DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_filename) -# define x_fib_free_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_free_recent) -# define x_fib_handle_events DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_handle_events) -# define x_fib_load_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_load_recent) -# define x_fib_recent_at DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_recent_at) -# define x_fib_recent_count DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_recent_count) -# define x_fib_recent_file DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_recent_file) -# define x_fib_save_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_save_recent) -# define x_fib_show DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_show) -# define x_fib_status DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_status) -# include "../extra/FileBrowserDialog.cpp" +# define x_fib_add_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_add_recent) +# define x_fib_cfg_buttons DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_cfg_buttons) +# define x_fib_cfg_filter_callback DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_cfg_filter_callback) +# define x_fib_close DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_close) +# define x_fib_configure DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_configure) +# define x_fib_filename DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_filename) +# define x_fib_free_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_free_recent) +# define x_fib_handle_events DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_handle_events) +# define x_fib_load_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_load_recent) +# define x_fib_recent_at DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_recent_at) +# define x_fib_recent_count DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_recent_count) +# define x_fib_recent_file DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_recent_file) +# define x_fib_save_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_save_recent) +# define x_fib_show DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_show) +# define x_fib_status DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_status) +# define FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE +# include "../extra/FileBrowserDialog.hpp" +# include "../extra/FileBrowserDialogImpl.cpp" #endif #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI @@ -278,16 +279,10 @@ void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity) } #endif -#ifndef DGL_FILE_BROWSER_DISABLED +#if DISTRHO_UI_FILE_BROWSER bool UI::openFileBrowser(const FileBrowserOptions& options) { -# if DISTRHO_PLUGIN_HAS_EXTERNAL_UI - // TODO - return false; - (void)options; -# else - return getWindow().openFileBrowser(options); -# endif + return getWindow().openFileBrowser((DGL_NAMESPACE::FileBrowserOptions&)options); } #endif @@ -368,7 +363,7 @@ void UI::uiReshape(uint, uint) } #endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI -#ifndef DGL_FILE_BROWSER_DISABLED +#if DISTRHO_UI_FILE_BROWSER void UI::uiFileBrowserSelected(const char*) { } diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp @@ -48,13 +48,7 @@ # define DISTRHO_UI_USER_RESIZABLE 0 #endif -// ----------------------------------------------------------------------- - -#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI START_NAMESPACE_DISTRHO -#else -START_NAMESPACE_DGL -#endif // ----------------------------------------------------------------------- // Plugin Application, will set class name based on plugin details @@ -107,11 +101,11 @@ struct PluginApplication DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication) }; #else -class PluginApplication : public Application +class PluginApplication : public DGL_NAMESPACE::Application { public: explicit PluginApplication() - : Application(DISTRHO_UI_IS_STANDALONE) + : DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE) { const char* const className = ( #ifdef DISTRHO_PLUGIN_BRAND @@ -172,14 +166,14 @@ public: DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow) }; #else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI -class PluginWindow : public Window +class PluginWindow : public DGL_NAMESPACE::Window { - DISTRHO_NAMESPACE::UI* const ui; + UI* const ui; bool initializing; bool receivedReshapeDuringInit; public: - explicit PluginWindow(DISTRHO_NAMESPACE::UI* const uiPtr, + explicit PluginWindow(UI* const uiPtr, PluginApplication& app, const uintptr_t parentWindowHandle, const uint width, @@ -238,7 +232,7 @@ public: } #endif - std::vector<ClipboardDataOffer> getClipboardDataOfferTypes() + std::vector<DGL_NAMESPACE::ClipboardDataOffer> getClipboardDataOfferTypes() { return Window::getClipboardDataOfferTypes(); } @@ -287,7 +281,7 @@ protected: ui->uiScaleFactorChanged(scaleFactor); } -# ifndef DGL_FILE_BROWSER_DISABLED +# if DISTRHO_UI_FILE_BROWSER void onFileSelected(const char* filename) override; # endif @@ -295,21 +289,6 @@ protected: }; #endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI -#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI -END_NAMESPACE_DISTRHO -#else -END_NAMESPACE_DGL -#endif - -// ----------------------------------------------------------------------- - -START_NAMESPACE_DISTRHO - -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI -using DGL_NAMESPACE::PluginApplication; -using DGL_NAMESPACE::PluginWindow; -#endif - // ----------------------------------------------------------------------- // UI callbacks @@ -465,7 +444,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) snprintf(title, sizeof(title)-1u, DISTRHO_PLUGIN_NAME ": %s", key); title[sizeof(title)-1u] = '\0'; - FileBrowserOptions opts; + DGL_NAMESPACE::FileBrowserOptions opts; opts.title = title; return window->openFileBrowser(opts); #endif @@ -473,14 +452,10 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) return false; } -END_NAMESPACE_DISTRHO - // ----------------------------------------------------------------------- // PluginWindow onFileSelected that require UI::PrivateData definitions -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) -START_NAMESPACE_DGL - +#if DISTRHO_UI_FILE_BROWSER inline void PluginWindow::onFileSelected(const char* const filename) { DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); @@ -488,7 +463,7 @@ inline void PluginWindow::onFileSelected(const char* const filename) if (initializing) return; -# if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE if (char* const key = ui->uiData->uiStateFileKeyRequest) { ui->uiData->uiStateFileKeyRequest = nullptr; @@ -502,14 +477,14 @@ inline void PluginWindow::onFileSelected(const char* const filename) std::free(key); return; } -# endif + #endif ui->uiFileBrowserSelected(filename); } - -END_NAMESPACE_DGL #endif // ----------------------------------------------------------------------- +END_NAMESPACE_DISTRHO + #endif // DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED diff --git a/examples/Info/DistrhoPluginInfo.h b/examples/Info/DistrhoPluginInfo.h @@ -26,6 +26,7 @@ #define DISTRHO_PLUGIN_NUM_INPUTS 2 #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 #define DISTRHO_PLUGIN_WANT_TIMEPOS 1 +#define DISTRHO_UI_FILE_BROWSER 0 #define DISTRHO_UI_USER_RESIZABLE 1 #define DISTRHO_UI_USE_NANOVG 1 diff --git a/pugl-updates-notes.txt b/pugl-updates-notes.txt @@ -1,6 +1,5 @@ puglClearMinSize needed? puglSetWindowSize was used on first show, still needed? -transientParentView needed? remove from WindowPrivateData update distrhoui.cpp get scale factor to match new parent request setup and pugl