diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 0b1b549b..3d43315f 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -82,6 +82,100 @@ static double getDesktopScaleFactor(const PuglView* const view) // ----------------------------------------------------------------------- +#ifdef DISTRHO_OS_WINDOWS +struct FileBrowserThread::PrivateData { + OPENFILENAMEW ofn; + std::vector fileNameW; + std::vector startDirW; + std::vector titleW; + + PrivateData() + : fileNameW(32768) + { + 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_PATHMUSTEXIST; + 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(startDirW.size()))) + ofn.lpstrInitialDir = startDirW.data(); + + titleW.resize(std::strlen(title) + 1); + if (MultiByteToWideChar(CP_UTF8, 0, title, -1, titleW.data(), static_cast(titleW.size()))) + ofn.lpstrTitle = titleW.data(); + } + + const char* run() + { + if (GetOpenFileNameW(&ofn)) + { + // back to UTF-8 + std::vector fileNameA(4 * 32768); + if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, + fileNameA.data(), (int)fileNameA.size(), + nullptr, nullptr)) + { + return strdup(fileNameA.data()); + } + } + + return nullptr; + } +}; + +FileBrowserThread::FileBrowserThread(const char*& file) + : pData(new PrivateData()), + win32SelectedFile(file) {} + +FileBrowserThread::~FileBrowserThread() +{ + stopThread(5000); + delete pData; +} + +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); + startThread(); +} + +void FileBrowserThread::run() +{ + const char* nextFile = pData->run(); + + if (shouldThreadExit()) + return; + + if (nextFile == nullptr) + nextFile = kWin32SelectedFileCancelled; + + d_stdout("WThread finished, final file '%s'", nextFile); + win32SelectedFile = nextFile; +} +#endif // DISTRHO_OS_WINDOWS + +// ----------------------------------------------------------------------- + Window::PrivateData::PrivateData(Application& a, Window* const s) : app(a), appData(a.pData), @@ -102,6 +196,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), + win32FileThread(win32SelectedFile), #endif modal() { @@ -128,6 +223,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), + win32FileThread(win32SelectedFile), #endif modal(ppData) { @@ -158,6 +254,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), + win32FileThread(win32SelectedFile), #endif modal() { @@ -190,6 +287,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), + win32FileThread(win32SelectedFile), #endif modal() { @@ -220,6 +318,9 @@ Window::PrivateData::~PrivateData() } #ifdef DISTRHO_OS_WINDOWS + if (win32FileThread.isThreadRunning()) + win32FileThread.stopThread(2000); + if (win32SelectedFile != nullptr && win32SelectedFile != kWin32SelectedFileCancelled) std::free(const_cast(win32SelectedFile)); #endif @@ -537,58 +638,15 @@ bool Window::PrivateData::openFileBrowser(const Window::FileBrowserOptions& opti # endif # ifdef DISTRHO_OS_WINDOWS - // the old and compatible dialog API - OPENFILENAMEW ofn; - memset(&ofn, 0, sizeof(ofn)); + // TODO signal to close + if (win32FileThread.isThreadRunning()) + win32FileThread.stopThread(1000); + if (win32SelectedFile != nullptr && win32SelectedFile != kWin32SelectedFileCancelled) std::free(const_cast(win32SelectedFile)); win32SelectedFile = nullptr; - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = (HWND)puglGetNativeWindow(view); - - // set start directory in UTF-16 encoding - std::vector startDirW; - startDirW.resize(startDir.length() + 1); - if (MultiByteToWideChar(CP_UTF8, 0, startDir.buffer(), -1, startDirW.data(), static_cast(startDirW.size()))) - ofn.lpstrInitialDir = startDirW.data(); - - // set title in UTF-16 encoding - std::vector titleW; - titleW.resize(title.length() + 1); - if (MultiByteToWideChar(CP_UTF8, 0, title.buffer(), -1, titleW.data(), static_cast(titleW.size()))) - ofn.lpstrTitle = titleW.data(); - - // prepare a buffer to receive the result - std::vector fileNameW(32768); // the Unicode maximum - ofn.lpstrFile = fileNameW.data(); - ofn.nMaxFile = (DWORD)fileNameW.size(); - - // flags - ofn.Flags = OFN_PATHMUSTEXIST; - if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked) - ofn.Flags |= OFN_FORCESHOWHIDDEN; - if (options.buttons.showPlaces == FileBrowserOptions::kButtonInvisible) - ofn.FlagsEx |= OFN_EX_NOPLACESBAR; - - // TODO synchronous only, can't do better with WinAPI native dialogs. - // threading might work, if someone is motivated to risk it. - if (GetOpenFileNameW(&ofn)) - { - // back to UTF-8 - std::vector fileNameA(4 * 32768); - if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, - fileNameA.data(), (int)fileNameA.size(), - nullptr, nullptr)) - { - // handle it during the next idle cycle (fake async) - win32SelectedFile = strdup(fileNameA.data()); - } - } - - if (win32SelectedFile == nullptr) - win32SelectedFile = kWin32SelectedFileCancelled; - + win32FileThread.start(startDir, title, puglGetNativeWindow(view), options); return true; # endif diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index d8e0f8cb..eff0d091 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -25,12 +25,35 @@ #include +#ifdef DISTRHO_OS_WINDOWS +# include "../distrho/extra/Thread.hpp" +#endif + START_NAMESPACE_DGL class TopLevelWidget; // ----------------------------------------------------------------------- +#ifdef DISTRHO_OS_WINDOWS +class FileBrowserThread : public Thread +{ + struct PrivateData; + PrivateData* const pData; + const char*& win32SelectedFile; + +public: + FileBrowserThread(const char*& win32SelectedFile); + ~FileBrowserThread() override; + void start(const char* startDir, const char* title, uintptr_t winId, Window::FileBrowserOptions options); + +protected: + void run() override; +}; +#endif + +// ----------------------------------------------------------------------- + struct Window::PrivateData : IdleCallback { /** Reference to the DGL Application class this (private data) window associates with. */ Application& app; @@ -83,6 +106,8 @@ struct Window::PrivateData : IdleCallback { #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; #endif /** Modal window setup. */