Browse Source

UI filebrowser saving mode, separate from pugl/DGL/Window (#349)

* Add UI::openFileBrowser that matches Window::openFileBrowser

* Add empty implementation so it builds

* Move file browser dialog implementation into its own file

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

* Fix warnings

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

* Fix tests; Add non-implemented saving flag

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

* Initial DBus/freedesktop file browser implementation

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

* Build fixes

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

* Fix window id

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

* More build fixes

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

* More file dialog tweaks

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

* Attempted fixes

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

* Fix C++98 build

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

* Fix windows build

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

* Really fix windows builds

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

* Fix for MSVC

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

* Yet another fix attempt

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

* Also fix macOS side

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

* More attempted fixes, this is getting annoying...

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

* FileBrowserDialog: Implement saving in Windows

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

* FileBrowserDialog: Implement saving on macOS

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

* Rework last commit

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

* One more macOS fix needed

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

* unref dbus connection on close

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

* More build fixes

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

* Hopefully final macOS fix

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

* Add libdbus-1-dev to CI

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

* Check that org.freedesktop.portal.Desktop exists before connecting

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

* Less indentation

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

* Fix macOS build
pull/351/head
Filipe Coelho GitHub 3 years ago
parent
commit
29709cbe4e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 856 additions and 569 deletions
  1. +1
    -0
      .github/workflows/cmake.yml
  2. +5
    -5
      .github/workflows/example-plugins.yml
  3. +1
    -1
      .github/workflows/makefile.yml
  4. +6
    -0
      Makefile.base.mk
  5. +9
    -48
      dgl/Window.hpp
  6. +33
    -308
      dgl/src/WindowPrivateData.cpp
  7. +4
    -24
      dgl/src/WindowPrivateData.hpp
  8. +9
    -141
      dgl/src/pugl.cpp
  9. +0
    -20
      dgl/src/pugl.hpp
  10. +23
    -3
      distrho/DistrhoUI.hpp
  11. +20
    -15
      distrho/DistrhoUI_macOS.mm
  12. +569
    -0
      distrho/extra/FileBrowserDialog.cpp
  13. +134
    -0
      distrho/extra/FileBrowserDialog.hpp
  14. +0
    -0
      distrho/extra/sofd/libsofd.c
  15. +0
    -0
      distrho/extra/sofd/libsofd.h
  16. +1
    -0
      distrho/src/DistrhoDefines.h
  17. +39
    -3
      distrho/src/DistrhoUI.cpp
  18. +1
    -1
      distrho/src/DistrhoUIPrivateData.hpp
  19. +1
    -0
      tests/FileBrowserDialog.cpp

+ 1
- 0
.github/workflows/cmake.yml View File

@@ -30,6 +30,7 @@ jobs:
liblo-dev \ liblo-dev \
libgl-dev \ libgl-dev \
libcairo2-dev \ libcairo2-dev \
libdbus-1-dev \
libx11-dev libx11-dev
- name: Create Build Environment - name: Create Build Environment
shell: bash shell: bash


+ 5
- 5
.github/workflows/example-plugins.yml View File

@@ -26,7 +26,7 @@ jobs:
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports bionic-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-arm64.list echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports bionic-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-arm64.list
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports bionic-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-arm64.list echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports bionic-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-arm64.list
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -yq g++-aarch64-linux-gnu libasound2-dev:arm64 libcairo2-dev:arm64 libgl1-mesa-dev:arm64 liblo-dev:arm64 libpulse-dev:arm64 libx11-dev:arm64 libxcursor-dev:arm64 libxext-dev:arm64 libxrandr-dev:arm64 qemu-user-static
sudo apt-get install -yq g++-aarch64-linux-gnu libasound2-dev:arm64 libcairo2-dev:arm64 libdbus-1-dev:arm64 libgl1-mesa-dev:arm64 liblo-dev:arm64 libpulse-dev:arm64 libx11-dev:arm64 libxcursor-dev:arm64 libxext-dev:arm64 libxrandr-dev:arm64 qemu-user-static
# fix broken Ubuntu packages missing pkg-config file in multi-arch package # fix broken Ubuntu packages missing pkg-config file in multi-arch package
sudo apt-get install -yq libasound2-dev libgl1-mesa-dev liblo-dev libpulse-dev libxcursor-dev libxrandr-dev sudo apt-get install -yq libasound2-dev libgl1-mesa-dev liblo-dev libpulse-dev libxcursor-dev libxrandr-dev
sudo ln -s /usr/lib/aarch64-linux-gnu/liblo.so.7 /usr/lib/aarch64-linux-gnu/liblo.so sudo ln -s /usr/lib/aarch64-linux-gnu/liblo.so.7 /usr/lib/aarch64-linux-gnu/liblo.so
@@ -63,7 +63,7 @@ jobs:
echo "deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports bionic-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-armhf.list echo "deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports bionic-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-armhf.list
echo "deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports bionic-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-armhf.list echo "deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports bionic-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-armhf.list
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -yq g++-arm-linux-gnueabihf libasound2-dev:armhf libcairo2-dev:armhf libgl1-mesa-dev:armhf liblo-dev:armhf libpulse-dev:armhf libx11-dev:armhf libxcursor-dev:armhf libxext-dev:armhf libxrandr-dev:armhf qemu-user-static
sudo apt-get install -yq g++-arm-linux-gnueabihf libasound2-dev:armhf libcairo2-dev:armhf libdbus-1-dev:armhf libgl1-mesa-dev:armhf liblo-dev:armhf libpulse-dev:armhf libx11-dev:armhf libxcursor-dev:armhf libxext-dev:armhf libxrandr-dev:armhf qemu-user-static
# fix broken Ubuntu packages missing pkg-config file in multi-arch package # fix broken Ubuntu packages missing pkg-config file in multi-arch package
sudo apt-get install -yq libasound2-dev libgl1-mesa-dev liblo-dev libpulse-dev libxcursor-dev libxrandr-dev sudo apt-get install -yq libasound2-dev libgl1-mesa-dev liblo-dev libpulse-dev libxcursor-dev libxrandr-dev
sudo ln -s /usr/lib/arm-linux-gnueabihf/liblo.so.7 /usr/lib/arm-linux-gnueabihf/liblo.so sudo ln -s /usr/lib/arm-linux-gnueabihf/liblo.so.7 /usr/lib/arm-linux-gnueabihf/liblo.so
@@ -96,7 +96,7 @@ jobs:
run: | run: |
sudo dpkg --add-architecture i386 sudo dpkg --add-architecture i386
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -yq g++-multilib libasound2-dev:i386 libcairo2-dev:i386 libgl1-mesa-dev:i386 liblo-dev:i386 libpulse-dev:i386 libx11-dev:i386 libxcursor-dev:i386 libxext-dev:i386 libxrandr-dev:i386
sudo apt-get install -yq g++-multilib libasound2-dev:i386 libcairo2-dev:i386 libdbus-1-dev:i386 libgl1-mesa-dev:i386 liblo-dev:i386 libpulse-dev:i386 libx11-dev:i386 libxcursor-dev:i386 libxext-dev:i386 libxrandr-dev:i386
- name: Build linux x86 - name: Build linux x86
env: env:
CFLAGS: -m32 CFLAGS: -m32
@@ -124,7 +124,7 @@ jobs:
- name: Set up dependencies - name: Set up dependencies
run: | run: |
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -yq libasound2-dev libcairo2-dev libgl1-mesa-dev liblo-dev libpulse-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev
sudo apt-get install -yq libasound2-dev libcairo2-dev libdbus-1-dev libgl1-mesa-dev liblo-dev libpulse-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev
- name: Build linux x86_64 - name: Build linux x86_64
env: env:
LDFLAGS: -static-libgcc -static-libstdc++ LDFLAGS: -static-libgcc -static-libstdc++
@@ -250,7 +250,7 @@ jobs:
sudo dpkg -i kxstudio-repos_10.0.3_all.deb sudo dpkg -i kxstudio-repos_10.0.3_all.deb
sudo apt-get update -qq sudo apt-get update -qq
# build-deps # build-deps
sudo apt-get install -yq libasound2-dev libcairo2-dev libgl1-mesa-dev liblo-dev libpulse-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev
sudo apt-get install -yq libasound2-dev libcairo2-dev libdbus-1-dev libgl1-mesa-dev liblo-dev libpulse-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev
# runtime testing # runtime testing
sudo apt-get install -yq carla-git lilv-utils lv2-dev lv2lint valgrind sudo apt-get install -yq carla-git lilv-utils lv2-dev lv2lint valgrind
- name: Build plugins - name: Build plugins


+ 1
- 1
.github/workflows/makefile.yml View File

@@ -20,7 +20,7 @@ jobs:
- name: Set up dependencies - name: Set up dependencies
run: | run: |
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -yq libasound2-dev libcairo2-dev libgl1-mesa-dev liblo-dev libpulse-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev xvfb
sudo apt-get install -yq libasound2-dev libcairo2-dev libdbus-1-dev libgl1-mesa-dev liblo-dev libpulse-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev xvfb
- name: Without any warnings - name: Without any warnings
env: env:
CFLAGS: -Werror CFLAGS: -Werror


+ 6
- 0
Makefile.base.mk View File

@@ -238,6 +238,7 @@ HAVE_OPENGL = true
else else
HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true)
ifneq ($(HAIKU),true) ifneq ($(HAIKU),true)
HAVE_DBUS = $(shell $(PKG_CONFIG) --exists dbus-1 && echo true)
HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true) HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true)
HAVE_XCURSOR = $(shell $(PKG_CONFIG) --exists xcursor && echo true) HAVE_XCURSOR = $(shell $(PKG_CONFIG) --exists xcursor && echo true)
HAVE_XEXT = $(shell $(PKG_CONFIG) --exists xext && echo true) HAVE_XEXT = $(shell $(PKG_CONFIG) --exists xext && echo true)
@@ -284,6 +285,10 @@ DGL_SYSTEM_LIBS += -lgdi32 -lcomdlg32
endif endif


ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true)
ifeq ($(HAVE_DBUS),true)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags dbus-1) -DHAVE_DBUS
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs dbus-1)
endif
ifeq ($(HAVE_X11),true) ifeq ($(HAVE_X11),true)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) -DHAVE_X11 DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) -DHAVE_X11
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11) DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11)
@@ -476,6 +481,7 @@ features:
$(call print_available,UNIX) $(call print_available,UNIX)
@echo === Detected features @echo === Detected features
$(call print_available,HAVE_ALSA) $(call print_available,HAVE_ALSA)
$(call print_available,HAVE_DBUS)
$(call print_available,HAVE_CAIRO) $(call print_available,HAVE_CAIRO)
$(call print_available,HAVE_DGL) $(call print_available,HAVE_DGL)
$(call print_available,HAVE_LIBLO) $(call print_available,HAVE_LIBLO)


+ 9
- 48
dgl/Window.hpp View File

@@ -19,9 +19,14 @@


#include "Geometry.hpp" #include "Geometry.hpp"


#ifndef DGL_FILE_BROWSER_DISABLED
# include "../distrho/extra/FileBrowserDialog.hpp"
#endif

START_NAMESPACE_DGL START_NAMESPACE_DGL


class Application; class Application;
class PluginWindow;
class TopLevelWidget; class TopLevelWidget;


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -53,53 +58,9 @@ class Window


public: public:
#ifndef DGL_FILE_BROWSER_DISABLED #ifndef DGL_FILE_BROWSER_DISABLED
/**
File browser options.
@see Window::openFileBrowser
*/
struct FileBrowserOptions {
/**
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,
};

/** 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 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()
: startDir(nullptr),
title(nullptr),
buttons() {}
};
#endif // DGL_FILE_BROWSER_DISABLED
typedef DISTRHO_NAMESPACE::FileBrowserHandle FileBrowserHandle;
typedef DISTRHO_NAMESPACE::FileBrowserOptions FileBrowserOptions;
#endif


/** /**
Window graphics context as a scoped struct. Window graphics context as a scoped struct.
@@ -361,7 +322,7 @@ public:


#ifndef DGL_FILE_BROWSER_DISABLED #ifndef DGL_FILE_BROWSER_DISABLED
/** /**
Open a file browser dialog with this window as parent.
Open a file browser dialog with this window as transient parent.
A few options can be specified to setup the dialog. A few options can be specified to setup the dialog.


If a path is selected, onFileSelected() will be called with the user chosen path. If a path is selected, onFileSelected() will be called with the user chosen path.


+ 33
- 308
dgl/src/WindowPrivateData.cpp View File

@@ -19,18 +19,6 @@


#include "pugl.hpp" #include "pugl.hpp"


#include "../../distrho/extra/String.hpp"

#ifdef DISTRHO_OS_WINDOWS
# include <direct.h>
# include <process.h>
# include <winsock2.h>
# include <windows.h>
# include <vector>
#else
# include <unistd.h>
#endif

// #define DGL_DEBUG_EVENTS // #define DGL_DEBUG_EVENTS


#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
@@ -64,11 +52,6 @@ START_NAMESPACE_DGL


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------


#ifdef DISTRHO_OS_WINDOWS
// static pointer used for direct comparisons
static const char* const kWin32SelectedFileCancelled = "__dpf_cancelled__";
#endif

static double getDesktopScaleFactor(const PuglView* const view) static double getDesktopScaleFactor(const PuglView* const view)
{ {
// allow custom scale for testing // allow custom scale for testing
@@ -83,154 +66,6 @@ static double getDesktopScaleFactor(const PuglView* const view)


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------


#ifdef DISTRHO_OS_WINDOWS
struct FileBrowserThread::PrivateData {
OPENFILENAMEW ofn;
volatile bool threadCancelled;
uintptr_t threadHandle;
std::vector<WCHAR> fileNameW;
std::vector<WCHAR> startDirW;
std::vector<WCHAR> titleW;
const bool isEmbed;
const char*& win32SelectedFile;

PrivateData(const bool embed, const char*& file)
: threadCancelled(false),
threadHandle(0),
fileNameW(32768),
isEmbed(embed),
win32SelectedFile(file)
{
std::memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.lpstrFile = fileNameW.data();
ofn.nMaxFile = (DWORD)fileNameW.size();
}

void setup(const char* const startDir,
const char* const title,
const uintptr_t winId,
const Window::FileBrowserOptions options)
{
ofn.hwndOwner = (HWND)winId;

ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
if (options.buttons.showHidden == Window::FileBrowserOptions::kButtonVisibleChecked)
ofn.Flags |= OFN_FORCESHOWHIDDEN;

ofn.FlagsEx = 0x0;
if (options.buttons.showPlaces == Window::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(title) + 1);
if (MultiByteToWideChar(CP_UTF8, 0, title, -1, titleW.data(), static_cast<int>(titleW.size())))
ofn.lpstrTitle = titleW.data();
}

void run()
{
const char* nextFile = nullptr;

if (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 = kWin32SelectedFileCancelled;

win32SelectedFile = nextFile;
threadHandle = 0;
}
};

FileBrowserThread::FileBrowserThread(const bool isEmbed, const char*& file)
: pData(new PrivateData(isEmbed, file)) {}

FileBrowserThread::~FileBrowserThread()
{
stop();
delete pData;
}

unsigned __stdcall FileBrowserThread__run(void* const arg)
{
// CoInitializeEx(nullptr, COINIT_MULTITHREADED);
static_cast<FileBrowserThread*>(arg)->pData->run();
// CoUninitialize();
_endthreadex(0);
return 0;
}

void FileBrowserThread::start(const char* const startDir,
const char* const title,
const uintptr_t winId,
const Window::FileBrowserOptions options)
{
pData->setup(startDir, title, winId, options);

uint threadId;
pData->threadCancelled = false;
pData->threadHandle = _beginthreadex(nullptr, 0, FileBrowserThread__run, this, 0, &threadId);
}

void FileBrowserThread::stop()
{
pData->threadCancelled = true;

if (pData->threadHandle == 0)
return;

// if previous dialog running, carefully close its window
const HWND owner = pData->isEmbed ? GetParent(pData->ofn.hwndOwner) : pData->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)pData->threadHandle, 5000);
}
}

// not good if thread still running, but let's close the handle anyway
if (pData->threadHandle != 0)
{
CloseHandle((HANDLE)pData->threadHandle);
pData->threadHandle = 0;
}
}

#endif // DISTRHO_OS_WINDOWS && !_MSC_VER

// -----------------------------------------------------------------------

Window::PrivateData::PrivateData(Application& a, Window* const s) Window::PrivateData::PrivateData(Application& a, Window* const s)
: app(a), : app(a),
appData(a.pData), appData(a.pData),
@@ -249,9 +84,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s)
keepAspectRatio(false), keepAspectRatio(false),
ignoreIdleCallbacks(false), ignoreIdleCallbacks(false),
filenameToRenderInto(nullptr), filenameToRenderInto(nullptr),
#ifdef DISTRHO_OS_WINDOWS
win32SelectedFile(nullptr),
win32FileThread(false, win32SelectedFile),
#ifndef DGL_FILE_BROWSER_DISABLED
fileBrowserHandle(nullptr),
#endif #endif
modal() modal()
{ {
@@ -276,9 +110,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c
keepAspectRatio(false), keepAspectRatio(false),
ignoreIdleCallbacks(false), ignoreIdleCallbacks(false),
filenameToRenderInto(nullptr), filenameToRenderInto(nullptr),
#ifdef DISTRHO_OS_WINDOWS
win32SelectedFile(nullptr),
win32FileThread(false, win32SelectedFile),
#ifndef DGL_FILE_BROWSER_DISABLED
fileBrowserHandle(nullptr),
#endif #endif
modal(ppData) modal(ppData)
{ {
@@ -307,9 +140,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s,
keepAspectRatio(false), keepAspectRatio(false),
ignoreIdleCallbacks(false), ignoreIdleCallbacks(false),
filenameToRenderInto(nullptr), filenameToRenderInto(nullptr),
#ifdef DISTRHO_OS_WINDOWS
win32SelectedFile(nullptr),
win32FileThread(isEmbed, win32SelectedFile),
#ifndef DGL_FILE_BROWSER_DISABLED
fileBrowserHandle(nullptr),
#endif #endif
modal() modal()
{ {
@@ -340,9 +172,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s,
keepAspectRatio(false), keepAspectRatio(false),
ignoreIdleCallbacks(false), ignoreIdleCallbacks(false),
filenameToRenderInto(nullptr), filenameToRenderInto(nullptr),
#ifdef DISTRHO_OS_WINDOWS
win32SelectedFile(nullptr),
win32FileThread(isEmbed, win32SelectedFile),
#ifndef DGL_FILE_BROWSER_DISABLED
fileBrowserHandle(nullptr),
#endif #endif
modal() modal()
{ {
@@ -363,8 +194,9 @@ Window::PrivateData::~PrivateData()


if (isEmbed) if (isEmbed)
{ {
#ifdef HAVE_X11
sofdFileDialogClose();
#ifndef DGL_FILE_BROWSER_DISABLED
if (fileBrowserHandle != nullptr)
fileBrowserClose(fileBrowserHandle);
#endif #endif
puglHide(view); puglHide(view);
appData->oneWindowClosed(); appData->oneWindowClosed();
@@ -372,13 +204,6 @@ Window::PrivateData::~PrivateData()
isVisible = false; isVisible = false;
} }


#ifdef DISTRHO_OS_WINDOWS
win32FileThread.stop();

if (win32SelectedFile != nullptr && win32SelectedFile != kWin32SelectedFileCancelled)
std::free(const_cast<char*>(win32SelectedFile));
#endif

puglFreeView(view); puglFreeView(view);
} }


@@ -522,9 +347,14 @@ void Window::PrivateData::hide()
if (modal.enabled) if (modal.enabled)
stopModal(); stopModal();


#ifdef HAVE_X11
sofdFileDialogClose();
#ifndef DGL_FILE_BROWSER_DISABLED
if (fileBrowserHandle != nullptr)
{
fileBrowserClose(fileBrowserHandle);
fileBrowserHandle = nullptr;
}
#endif #endif

puglHide(view); puglHide(view);


isVisible = false; isVisible = false;
@@ -566,20 +396,12 @@ void Window::PrivateData::setResizable(const bool resizable)
void Window::PrivateData::idleCallback() void Window::PrivateData::idleCallback()
{ {
#ifndef DGL_FILE_BROWSER_DISABLED #ifndef DGL_FILE_BROWSER_DISABLED
# ifdef DISTRHO_OS_WINDOWS
if (const char* path = win32SelectedFile)
if (fileBrowserHandle != nullptr && fileBrowserIdle(fileBrowserHandle))
{ {
win32SelectedFile = nullptr;
if (path == kWin32SelectedFileCancelled)
path = nullptr;
self->onFileSelected(path);
std::free(const_cast<char*>(path));
self->onFileSelected(fileBrowserGetPath(fileBrowserHandle));
fileBrowserClose(fileBrowserHandle);
fileBrowserHandle = nullptr;
} }
# endif
# ifdef HAVE_X11
if (sofdFileDialogIdle(view))
self->onFileSelected(sofdFileDialogGetPath());
# endif
#endif #endif
} }


@@ -619,120 +441,23 @@ bool Window::PrivateData::removeIdleCallback(IdleCallback* const callback)
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file handling // file handling


bool Window::PrivateData::openFileBrowser(const Window::FileBrowserOptions& options)
bool Window::PrivateData::openFileBrowser(const FileBrowserOptions& options)
{ {
using DISTRHO_NAMESPACE::String;

// --------------------------------------------------------------------------
// configure start dir

// TODO: get abspath if needed
// TODO: cross-platform

String startDir(options.startDir);

if (startDir.isEmpty())
{
// TESTING verify this whole thing...
# 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(), false);

if (! startDir.endsWith(DISTRHO_OS_SEP))
startDir += DISTRHO_OS_SEP_STR;

// --------------------------------------------------------------------------
// configure title

String title(options.title);

if (title.isEmpty())
{
title = puglGetWindowTitle(view);

if (title.isEmpty())
title = "FileBrowser";
}

// --------------------------------------------------------------------------
// show

# ifdef DISTRHO_OS_MAC
uint flags = 0x0;

if (options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleChecked)
flags |= 0x001;
else if (options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleUnchecked)
flags |= 0x002;

if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked)
flags |= 0x010;
else if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleUnchecked)
flags |= 0x020;
if (fileBrowserHandle != nullptr)
fileBrowserClose(fileBrowserHandle);


if (options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleChecked)
flags |= 0x100;
else if (options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleUnchecked)
flags |= 0x200;
FileBrowserOptions options2 = options;


return puglMacOSFilePanelOpen(view, startDir, title, flags, openPanelCallback);
# endif

# ifdef DISTRHO_OS_WINDOWS
// only one possible at a time
DISTRHO_SAFE_ASSERT_RETURN(win32FileThread.pData->threadHandle == 0, false);

if (win32SelectedFile != nullptr && win32SelectedFile != kWin32SelectedFileCancelled)
std::free(const_cast<char*>(win32SelectedFile));
win32SelectedFile = nullptr;

win32FileThread.start(startDir, title, puglGetNativeWindow(view), options);
return true;
# endif

# ifdef HAVE_X11
uint flags = 0x0;

if (options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleChecked)
flags |= 0x001;
else if (options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleUnchecked)
flags |= 0x002;
if (options2.title == nullptr)
options2.title = puglGetWindowTitle(view);


if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked)
flags |= 0x010;
else if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleUnchecked)
flags |= 0x020;
fileBrowserHandle = fileBrowserCreate(isEmbed,
puglGetNativeWindow(view),
autoScaling ? autoScaleFactor : scaleFactor,
options2);


if (options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleChecked)
flags |= 0x100;
else if (options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleUnchecked)
flags |= 0x200;

return sofdFileDialogShow(view, startDir, title, flags, autoScaling ? autoScaleFactor : scaleFactor);
# endif

return false;
return fileBrowserHandle != nullptr;
} }

# ifdef DISTRHO_OS_MAC
void Window::PrivateData::openPanelCallback(PuglView* const view, const char* const path)
{
((Window::PrivateData*)puglGetHandle(view))->self->onFileSelected(path);
}
# endif
#endif // ! DGL_FILE_BROWSER_DISABLED #endif // ! DGL_FILE_BROWSER_DISABLED


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------


+ 4
- 24
dgl/src/WindowPrivateData.hpp View File

@@ -31,21 +31,6 @@ class TopLevelWidget;


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------


#ifdef DISTRHO_OS_WINDOWS
struct FileBrowserThread
{
struct PrivateData;
PrivateData* const pData;

FileBrowserThread(bool isEmbed, const char*& win32SelectedFile);
~FileBrowserThread();
void start(const char* startDir, const char* title, uintptr_t winId, Window::FileBrowserOptions options);
void stop();
};
#endif

// -----------------------------------------------------------------------

struct Window::PrivateData : IdleCallback { struct Window::PrivateData : IdleCallback {
/** Reference to the DGL Application class this (private data) window associates with. */ /** Reference to the DGL Application class this (private data) window associates with. */
Application& app; Application& app;
@@ -95,11 +80,9 @@ struct Window::PrivateData : IdleCallback {
/** Render to a picture file when non-null, automatically free+unset after saving. */ /** Render to a picture file when non-null, automatically free+unset after saving. */
char* filenameToRenderInto; char* filenameToRenderInto;


#ifdef DISTRHO_OS_WINDOWS
/** Selected file for openFileBrowser on windows, stored for fake async operation. */
const char* win32SelectedFile;
/** Thread where the openFileBrowser runs. */
FileBrowserThread win32FileThread;
#ifndef DGL_FILE_BROWSER_DISABLED
/** Handle for file browser dialog operations. */
FileBrowserHandle fileBrowserHandle;
#endif #endif


/** Modal window setup. */ /** Modal window setup. */
@@ -176,10 +159,7 @@ struct Window::PrivateData : IdleCallback {


#ifndef DGL_FILE_BROWSER_DISABLED #ifndef DGL_FILE_BROWSER_DISABLED
// file handling // file handling
bool openFileBrowser(const Window::FileBrowserOptions& options);
# ifdef DISTRHO_OS_MAC
static void openPanelCallback(PuglView* view, const char* path);
# endif
bool openFileBrowser(const FileBrowserOptions& options);
#endif #endif


static void renderToPicture(const char* filename, const GraphicsContext& context, uint width, uint height); static void renderToPicture(const char* filename, const GraphicsContext& context, uint width, uint height);


+ 9
- 141
dgl/src/pugl.cpp View File

@@ -42,6 +42,7 @@
# endif # endif
#elif defined(DISTRHO_OS_WINDOWS) #elif defined(DISTRHO_OS_WINDOWS)
# include <wctype.h> # include <wctype.h>
# include <winsock2.h>
# include <windows.h> # include <windows.h>
# include <windowsx.h> # include <windowsx.h>
# ifdef DGL_CAIRO # ifdef DGL_CAIRO
@@ -57,6 +58,7 @@
# endif # endif
#else #else
# include <dlfcn.h> # include <dlfcn.h>
# include <unistd.h>
# include <sys/select.h> # include <sys/select.h>
# include <sys/time.h> # include <sys/time.h>
# include <X11/X.h> # include <X11/X.h>
@@ -90,10 +92,12 @@
# endif # endif
#endif #endif


#ifdef HAVE_X11
# define DBLCLKTME 400
# include "sofd/libsofd.h"
# include "sofd/libsofd.c"
#ifndef DGL_FILE_BROWSER_DISABLED
# ifdef DISTRHO_OS_MAC
# import "../../distrho/extra/FileBrowserDialog.cpp"
# else
# include "../../distrho/extra/FileBrowserDialog.cpp"
# endif
#endif #endif


#ifndef DISTRHO_OS_MAC #ifndef DISTRHO_OS_MAC
@@ -528,53 +532,6 @@ void puglMacOSShowCentered(PuglView* const view)
} }


// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
// macOS specific, setup file browser dialog

bool puglMacOSFilePanelOpen(PuglView* const view,
const char* const startDir, const char* const title, const uint flags,
openPanelCallback callback)
{
PuglInternals* impl = view->impl;

NSOpenPanel* const panel = [NSOpenPanel openPanel];

[panel setAllowsMultipleSelection:NO];
[panel setCanChooseDirectories:NO];
[panel setCanChooseFiles:YES];
[panel setDirectoryURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:startDir]]];

// TODO file filter using allowedContentTypes: [UTType]

if (flags & 0x001)
[panel setAllowsOtherFileTypes:YES];
if (flags & 0x010)
[panel setShowsHiddenFiles:YES];

NSString* titleString = [[NSString alloc]
initWithBytes:title
length:strlen(title)
encoding:NSUTF8StringEncoding];
[panel setTitle:titleString];

dispatch_async(dispatch_get_main_queue(), ^
{
[panel beginSheetModalForWindow:(impl->window ? impl->window : [view->impl->wrapperView window])
completionHandler:^(NSModalResponse result)
{
if (result == NSModalResponseOK && [[panel URL] isFileURL])
{
NSString* const path = [[panel URL] path];
callback(view, [path UTF8String]);
}
else
{
callback(view, nullptr);
}
}];
});

return true;
}
#endif #endif


#ifdef DISTRHO_OS_WINDOWS #ifdef DISTRHO_OS_WINDOWS
@@ -680,96 +637,7 @@ void puglX11SetWindowTypeAndPID(const PuglView* const view)
} }


// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
// X11 specific stuff for sofd

static Display* sofd_display;
static char* sofd_filename;

// --------------------------------------------------------------------------------------------------------------------
// X11 specific, show file dialog via sofd

bool sofdFileDialogShow(PuglView* const view,
const char* const startDir, const char* const title,
const uint flags, const double scaleFactor)
{
// only one possible at a time
DISTRHO_SAFE_ASSERT_RETURN(sofd_display == nullptr, false);

sofd_display = XOpenDisplay(nullptr);
DISTRHO_SAFE_ASSERT_RETURN(sofd_display != nullptr, false);

DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false);
DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false);

x_fib_cfg_buttons(3, flags & 0x001 ? 1 : flags & 0x002 ? 0 : -1);
x_fib_cfg_buttons(1, flags & 0x010 ? 1 : flags & 0x020 ? 0 : -1);
x_fib_cfg_buttons(2, flags & 0x100 ? 1 : flags & 0x200 ? 0 : -1);

return (x_fib_show(sofd_display, view->impl->win, 0, 0, scaleFactor + 0.5) == 0);
}

// --------------------------------------------------------------------------------------------------------------------
// X11 specific, idle sofd file dialog, returns true if dialog was closed (with or without a file selection)

bool sofdFileDialogIdle(PuglView* const view)
{
if (sofd_display == nullptr)
return false;

XEvent event;
while (XPending(sofd_display) > 0)
{
XNextEvent(sofd_display, &event);

if (x_fib_handle_events(sofd_display, &event) == 0)
continue;

if (sofd_filename != nullptr)
std::free(sofd_filename);

if (x_fib_status() > 0)
sofd_filename = x_fib_filename();
else
sofd_filename = nullptr;

x_fib_close(sofd_display);
XCloseDisplay(sofd_display);
sofd_display = nullptr;
return true;
}

return false;
}

// --------------------------------------------------------------------------------------------------------------------
// X11 specific, close sofd file dialog

void sofdFileDialogClose()
{
if (sofd_display != nullptr)
{
x_fib_close(sofd_display);
XCloseDisplay(sofd_display);
sofd_display = nullptr;
}

if (sofd_filename != nullptr)
{
std::free(sofd_filename);
sofd_filename = nullptr;
}
}

// --------------------------------------------------------------------------------------------------------------------
// X11 specific, get path chosen via sofd file dialog

const char* sofdFileDialogGetPath()
{
return sofd_filename;
}
#endif

// --------------------------------------------------------------------------------------------------------------------
#endif // HAVE_X11


#ifndef DISTRHO_OS_MAC #ifndef DISTRHO_OS_MAC
END_NAMESPACE_DGL END_NAMESPACE_DGL


+ 0
- 20
dgl/src/pugl.hpp View File

@@ -111,10 +111,6 @@ puglMacOSRemoveChildWindow(PuglView* view, PuglView* child);
// macOS specific, center view based on parent coordinates (if there is one) // macOS specific, center view based on parent coordinates (if there is one)
PUGL_API void PUGL_API void
puglMacOSShowCentered(PuglView* view); puglMacOSShowCentered(PuglView* view);

// macOS specific, setup file browser dialog
typedef void (*openPanelCallback)(PuglView* view, const char* path);
bool puglMacOSFilePanelOpen(PuglView* view, const char* startDir, const char* title, uint flags, openPanelCallback callback);
#endif #endif


#ifdef DISTRHO_OS_WINDOWS #ifdef DISTRHO_OS_WINDOWS
@@ -139,22 +135,6 @@ puglX11GrabFocus(const PuglView* view);
// X11 specific, set dialog window type and pid hints // X11 specific, set dialog window type and pid hints
PUGL_API void PUGL_API void
puglX11SetWindowTypeAndPID(const PuglView* view); puglX11SetWindowTypeAndPID(const PuglView* view);

// X11 specific, show file dialog via sofd
PUGL_API bool
sofdFileDialogShow(PuglView* view, const char* startDir, const char* title, uint flags, double scaleFactor);

// X11 specific, idle sofd file dialog, returns true if dialog was closed (with or without a file selection)
PUGL_API bool
sofdFileDialogIdle(PuglView* const view);

// X11 specific, close sofd file dialog
PUGL_API void
sofdFileDialogClose();

// X11 specific, get path chosen via sofd file dialog
PUGL_API const char*
sofdFileDialogGetPath();
#endif #endif


PUGL_END_DECLS PUGL_END_DECLS


+ 23
- 3
distrho/DistrhoUI.hpp View File

@@ -48,6 +48,10 @@ typedef DGL_NAMESPACE::NanoTopLevelWidget UIWidget;
typedef DGL_NAMESPACE::TopLevelWidget UIWidget; typedef DGL_NAMESPACE::TopLevelWidget UIWidget;
#endif #endif


#ifndef DGL_FILE_BROWSER_DISABLED
# include "extra/FileBrowserDialog.hpp"
#endif

START_NAMESPACE_DGL START_NAMESPACE_DGL
class PluginWindow; class PluginWindow;
END_NAMESPACE_DGL END_NAMESPACE_DGL
@@ -183,6 +187,22 @@ public:
void sendNote(uint8_t channel, uint8_t note, uint8_t velocity); void sendNote(uint8_t channel, uint8_t note, uint8_t velocity);
#endif #endif


#ifndef DGL_FILE_BROWSER_DISABLED
/**
Open a file browser dialog with this window as transient parent.@n
A few options can be specified to setup the dialog.

If a path is selected, onFileSelected() will be called with the user chosen path.
If the user cancels or does not pick a file, onFileSelected() will be called with nullptr as filename.

This function does not block the event loop.

@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());
#endif

#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
/* -------------------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------------------
* Direct DSP access - DO NOT USE THIS UNLESS STRICTLY NECESSARY!! */ * Direct DSP access - DO NOT USE THIS UNLESS STRICTLY NECESSARY!! */
@@ -297,8 +317,9 @@ protected:
The most common exception is custom OpenGL setup, but only really needed for custom OpenGL drawing code. The most common exception is custom OpenGL setup, but only really needed for custom OpenGL drawing code.
*/ */
virtual void uiReshape(uint width, uint height); virtual void uiReshape(uint width, uint height);
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI


# ifndef DGL_FILE_BROWSER_DISABLED
#ifndef DGL_FILE_BROWSER_DISABLED
/** /**
Window file selected function, called when a path is selected by the user, as triggered by openFileBrowser(). 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*). This function is for plugin UIs to be able to override Window::onFileSelected(const char*).
@@ -309,8 +330,7 @@ protected:
If you need to use files as plugin state, please setup and use DISTRHO_PLUGIN_WANT_STATEFILES instead. If you need to use files as plugin state, please setup and use DISTRHO_PLUGIN_WANT_STATEFILES instead.
*/ */
virtual void uiFileBrowserSelected(const char* filename); virtual void uiFileBrowserSelected(const char* filename);
# endif
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#endif


/* -------------------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------------------
* UI Resize Handling, internal */ * UI Resize Handling, internal */


+ 20
- 15
distrho/DistrhoUI_macOS.mm View File

@@ -22,10 +22,15 @@
#include "src/DistrhoDefines.h" #include "src/DistrhoDefines.h"


#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#import <Cocoa/Cocoa.h>
#include <algorithm>
#include <cmath>


# import <Cocoa/Cocoa.h>
# include <algorithm>
# include <cmath>
# ifndef DGL_FILE_BROWSER_DISABLED
# import "extra/FileBrowserDialog.cpp"
# endif

// Declared in DistrhoUI.cpp but defined here because it uses Obj-C
START_NAMESPACE_DISTRHO START_NAMESPACE_DISTRHO
double getDesktopScaleFactor(const uintptr_t parentWindowHandle) double getDesktopScaleFactor(const uintptr_t parentWindowHandle)
{ {
@@ -40,19 +45,19 @@ double getDesktopScaleFactor(const uintptr_t parentWindowHandle)
return [NSScreen mainScreen].backingScaleFactor; return [NSScreen mainScreen].backingScaleFactor;
} }
END_NAMESPACE_DISTRHO END_NAMESPACE_DISTRHO
#else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#include "../dgl/Base.hpp"


#define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, SEP, PUGL_NS, INTERFACE) DGL_NS ## SEP ## PUGL_NS ## SEP ## INTERFACE
#define DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NS, PUGL_NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, _, PUGL_NS, INTERFACE)
#else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI


#define PuglCairoView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, CairoView)
#define PuglOpenGLView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, OpenGLView)
#define PuglStubView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, StubView)
#define PuglVulkanView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, VulkanView)
#define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, Window)
#define PuglWindowDelegate DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WindowDelegate)
#define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WrapperView)
# include "../dgl/Base.hpp"
# define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, SEP, PUGL_NS, INTERFACE) DGL_NS ## SEP ## PUGL_NS ## SEP ## INTERFACE
# define DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NS, PUGL_NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, _, PUGL_NS, INTERFACE)
# define PuglCairoView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, CairoView)
# define PuglOpenGLView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, OpenGLView)
# define PuglStubView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, StubView)
# define PuglVulkanView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, VulkanView)
# define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, Window)
# define PuglWindowDelegate DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WindowDelegate)
# define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WrapperView)
# import "src/pugl.mm"


#import "src/pugl.mm"
#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI #endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI

+ 569
- 0
distrho/extra/FileBrowserDialog.cpp View File

@@ -0,0 +1,569 @@
/*
* 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 <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() && selectedFile != nullptr && selectedFile != kSelectedFileCancelled)
std::free(const_cast<char*>(selectedFile));
}

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

if (selectedFile != nullptr && selectedFile != kSelectedFileCancelled)
std::free(const_cast<char*>(selectedFile));
}
#endif
};

// --------------------------------------------------------------------------------------------------------------------

#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
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

#ifdef DISTRHO_OS_WINDOWS
handle->setupAndStart(isEmbed, startDir, windowTitle, windowId, options);
#endif

#ifdef HAVE_DBUS
// optional, can be null
DBusConnection* const dbuscon = handle->dbuscon;

if (dbuscon != nullptr && dbus_bus_name_has_owner(dbuscon, "org.freedesktop.portal.Desktop", nullptr))
{
// https://flatpak.github.io/xdg-desktop-portal/portal-docs.html#gdbus-org.freedesktop.portal.FileChooser
if (DBusMessage* const message = dbus_message_new_method_call("org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.FileChooser",
options.saving ? "SaveFile" : "OpenFile"))
{
char windowIdStr[32];
memset(windowIdStr, 0, sizeof(windowIdStr));
# ifdef HAVE_X11
snprintf(windowIdStr, sizeof(windowIdStr)-1, "x11:%llx", (ulonglong)windowId);
# endif
const char* windowIdStrPtr = windowIdStr;

dbus_message_append_args(message,
DBUS_TYPE_STRING, &windowIdStrPtr,
DBUS_TYPE_STRING, &windowTitle,
DBUS_TYPE_INVALID);

DBusMessageIter iter, array;
dbus_message_iter_init_append(message, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array);

/* here merely as example, in case we need to configure it
{
DBusMessageIter dict, variant;
const char* const property = "property";
const char* const value = "value";

dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, nullptr, &dict);
dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &property);
dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "s", &variant);
dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &value);
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, message, nullptr);

dbus_message_unref(message);
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);

// start with the 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 && std::strcmp(key, "uris") == 0);

// 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)
{
return handle->selectedFile != kSelectedFileCancelled ? handle->selectedFile : nullptr;
}

// --------------------------------------------------------------------------------------------------------------------

#ifdef DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE
}
#endif

END_NAMESPACE_DISTRHO

+ 134
- 0
distrho/extra/FileBrowserDialog.hpp View File

@@ -0,0 +1,134 @@
/*
* 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.
*/

#ifndef DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED
#define DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED

#include "../DistrhoUtils.hpp"

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

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DISTRHO

#endif // DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED

dgl/src/sofd/libsofd.c → distrho/extra/sofd/libsofd.c View File


dgl/src/sofd/libsofd.h → distrho/extra/sofd/libsofd.h View File


+ 1
- 0
distrho/src/DistrhoDefines.h View File

@@ -207,6 +207,7 @@ typedef unsigned char uchar;
typedef unsigned short int ushort; typedef unsigned short int ushort;
typedef unsigned int uint; typedef unsigned int uint;
typedef unsigned long int ulong; typedef unsigned long int ulong;
typedef unsigned long long int ulonglong;


/* Deprecated macros */ /* Deprecated macros */
#define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) DISTRHO_DECLARE_NON_COPYABLE(ClassName) #define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) DISTRHO_DECLARE_NON_COPYABLE(ClassName)


+ 39
- 3
distrho/src/DistrhoUI.cpp View File

@@ -15,6 +15,29 @@
*/ */


#include "src/DistrhoPluginChecks.h" #include "src/DistrhoPluginChecks.h"
#include "src/DistrhoDefines.h"

#if !defined(DGL_FILE_BROWSER_DISABLED) && !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"
#endif


#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# if defined(DISTRHO_OS_WINDOWS) # if defined(DISTRHO_OS_WINDOWS)
@@ -255,6 +278,19 @@ void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity)
} }
#endif #endif


#ifndef DGL_FILE_BROWSER_DISABLED
bool UI::openFileBrowser(const FileBrowserOptions& options)
{
# if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// TODO
return false;
(void)options;
# else
return getWindow().openFileBrowser(options);
# endif
}
#endif

#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
/* ------------------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------------------
* Direct DSP access */ * Direct DSP access */
@@ -311,13 +347,13 @@ void UI::uiReshape(uint, uint)
// NOTE this must be the same as Window::onReshape // NOTE this must be the same as Window::onReshape
pData->fallbackOnResize(); pData->fallbackOnResize();
} }
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI


# ifndef DGL_FILE_BROWSER_DISABLED
#ifndef DGL_FILE_BROWSER_DISABLED
void UI::uiFileBrowserSelected(const char*) void UI::uiFileBrowserSelected(const char*)
{ {
} }
# endif
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#endif


/* ------------------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------------------
* UI Resize Handling, internal */ * UI Resize Handling, internal */


+ 1
- 1
distrho/src/DistrhoUIPrivateData.hpp View File

@@ -433,7 +433,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key)
snprintf(title, sizeof(title)-1u, DISTRHO_PLUGIN_NAME ": %s", key); snprintf(title, sizeof(title)-1u, DISTRHO_PLUGIN_NAME ": %s", key);
title[sizeof(title)-1u] = '\0'; title[sizeof(title)-1u] = '\0';


DGL_NAMESPACE::Window::FileBrowserOptions opts;
FileBrowserOptions opts;
opts.title = title; opts.title = title;
return window->openFileBrowser(opts); return window->openFileBrowser(opts);
#endif #endif


+ 1
- 0
tests/FileBrowserDialog.cpp View File

@@ -125,6 +125,7 @@ protected:
repaint(); repaint();


FileBrowserOptions opts; FileBrowserOptions opts;
// opts.saving = true;
opts.title = "Look at me"; opts.title = "Look at me";
if (! openFileBrowser(opts)) if (! openFileBrowser(opts))
{ {


Loading…
Cancel
Save