commit def5f2b47b4d0acec118b4ac87c074f0820e34d1
parent 497a831bd9ac0a9f28b581ea58fe8bfc0f95fff1
Author: falkTX <falktx@falktx.com>
Date: Wed, 27 Oct 2021 04:59:48 +0100
Run GetOpenFileNameW on secondary thread, tested on Windows 10
Signed-off-by: falkTX <falktx@falktx.com>
Diffstat:
2 files changed, 131 insertions(+), 48 deletions(-)
diff --git 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<WCHAR> fileNameW;
+ std::vector<WCHAR> startDirW;
+ std::vector<WCHAR> 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<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();
+ }
+
+ const char* run()
+ {
+ if (GetOpenFileNameW(&ofn))
+ {
+ // 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))
+ {
+ 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<char*>(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<char*>(win32SelectedFile));
win32SelectedFile = nullptr;
- ofn.lStructSize = sizeof(ofn);
- ofn.hwndOwner = (HWND)puglGetNativeWindow(view);
-
- // set start directory in UTF-16 encoding
- std::vector<WCHAR> startDirW;
- startDirW.resize(startDir.length() + 1);
- if (MultiByteToWideChar(CP_UTF8, 0, startDir.buffer(), -1, startDirW.data(), static_cast<int>(startDirW.size())))
- ofn.lpstrInitialDir = startDirW.data();
-
- // set title in UTF-16 encoding
- std::vector<WCHAR> titleW;
- titleW.resize(title.length() + 1);
- if (MultiByteToWideChar(CP_UTF8, 0, title.buffer(), -1, titleW.data(), static_cast<int>(titleW.size())))
- ofn.lpstrTitle = titleW.data();
-
- // prepare a buffer to receive the result
- std::vector<WCHAR> 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<char> 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
@@ -25,12 +25,35 @@
#include <list>
+#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. */