Mercurial > hg > pub > prymula > com
view DPF-Prymula-audioplugins/dpf/distrho/extra/FileBrowserDialogImpl.cpp @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
line wrap: on
line source
/* * 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_WASM # include <emscripten/emscripten.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 using DISTRHO_NAMESPACE::ScopedPointer; using DISTRHO_NAMESPACE::String; #else START_NAMESPACE_DISTRHO #endif // -------------------------------------------------------------------------------------------------------------------- // static pointer used for signal null/none action taken static const char* const kSelectedFileCancelled = "__dpf_cancelled__"; #ifdef HAVE_DBUS static constexpr bool isHexChar(const char c) noexcept { return c >= '0' && c <= 'f' && (c <= '9' || (c >= 'A' && c <= 'F') || c >= 'a'); } static constexpr int toHexChar(const char c) noexcept { return c >= '0' && c <= '9' ? c - '0' : (c >= 'A' && c <= 'F' ? c - 'A' : c - 'a') + 10; } #endif // -------------------------------------------------------------------------------------------------------------------- #ifdef DISTRHO_OS_WASM # define DISTRHO_WASM_NAMESPACE_MACRO_HELPER(NS, SEP, FUNCTION) NS ## SEP ## FUNCTION # define DISTRHO_WASM_NAMESPACE_MACRO(NS, FUNCTION) DISTRHO_WASM_NAMESPACE_MACRO_HELPER(NS, _, FUNCTION) # define DISTRHO_WASM_NAMESPACE_HELPER(NS) #NS # define DISTRHO_WASM_NAMESPACE(NS) DISTRHO_WASM_NAMESPACE_HELPER(NS) # define fileBrowserSetPathNamespaced DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, fileBrowserSetPath) # define fileBrowserSetPathFuncName DISTRHO_WASM_NAMESPACE(FILE_BROWSER_DIALOG_NAMESPACE) "_fileBrowserSetPath" // FIXME use world class name as prefix static bool openWebBrowserFileDialog(const char* const funcname, void* const handle) { const char* const nameprefix = DISTRHO_WASM_NAMESPACE(FILE_BROWSER_DIALOG_NAMESPACE); return EM_ASM_INT({ var canvasFileObjName = UTF8ToString($0) + "_file_open"; var canvasFileOpenElem = document.getElementById(canvasFileObjName); var jsfuncname = UTF8ToString($1); var jsfunc = Module.cwrap(jsfuncname, 'null', ['number', 'string']); if (canvasFileOpenElem) { document.body.removeChild(canvasFileOpenElem); } canvasFileOpenElem = document.createElement('input'); canvasFileOpenElem.type = 'file'; canvasFileOpenElem.id = canvasFileObjName; canvasFileOpenElem.style.display = 'none'; document.body.appendChild(canvasFileOpenElem); canvasFileOpenElem.onchange = function(e) { if (!canvasFileOpenElem.files) { jsfunc($2, ""); return; } var file = canvasFileOpenElem.files[0]; var filename = '/' + file.name; var reader = new FileReader(); reader.onloadend = function(e) { var content = new Uint8Array(reader.result); Module.FS.writeFile(filename, content); jsfunc($2, filename); }; reader.readAsArrayBuffer(file); }; canvasFileOpenElem.click(); return 1; }, nameprefix, funcname, handle) != 0; } static bool downloadWebBrowserFile(const char* const filename) { const char* const nameprefix = DISTRHO_WASM_NAMESPACE(FILE_BROWSER_DIALOG_NAMESPACE); return EM_ASM_INT({ var canvasFileObjName = UTF8ToString($0) + "_file_save"; var jsfilename = UTF8ToString($1); var canvasFileSaveElem = document.getElementById(canvasFileObjName); if (canvasFileSaveElem) { // only 1 file save allowed at once console.warn("One file save operation already in progress, refusing to open another"); return 0; } canvasFileSaveElem = document.createElement('a'); canvasFileSaveElem.download = jsfilename; canvasFileSaveElem.id = canvasFileObjName; canvasFileSaveElem.style.display = 'none'; document.body.appendChild(canvasFileSaveElem); var content = Module.FS.readFile('/' + jsfilename); canvasFileSaveElem.href = URL.createObjectURL(new Blob([content])); canvasFileSaveElem.click(); setTimeout(function() { URL.revokeObjectURL(canvasFileSaveElem.href); document.body.removeChild(canvasFileSaveElem); }, 2000); return 1; }, nameprefix, filename) != 0; } #endif // -------------------------------------------------------------------------------------------------------------------- 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_WASM char* defaultName; bool saving; #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 save) : selectedFile(nullptr) { #ifdef DISTRHO_OS_MAC if (save) { nsOpenPanel = nullptr; nsBasePanel = [[NSSavePanel savePanel]retain]; } else { nsOpenPanel = [[NSOpenPanel openPanel]retain]; nsBasePanel = nsOpenPanel; } #endif #ifdef DISTRHO_OS_WASM defaultName = nullptr; saving = save; #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)save; } ~FileBrowserData() { #ifdef DISTRHO_OS_MAC [nsBasePanel release]; #endif #ifdef DISTRHO_OS_WASM std::free(defaultName); #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_OS_WASM extern "C" { EMSCRIPTEN_KEEPALIVE void fileBrowserSetPathNamespaced(FileBrowserHandle handle, const char* filename) { handle->free(); if (filename != nullptr && filename[0] != '\0') handle->selectedFile = strdup(filename); else handle->selectedFile = kSelectedFileCancelled; } } #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)) #else if (char* const cwd = getcwd(nullptr, 0)) #endif { startDir = cwd; std::free(cwd); } } 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 d_stderr2("fileBrowserCreate is unsupported on macos < 10.8"); 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_WASM if (options.saving) { const size_t len = options.defaultName != nullptr ? strlen(options.defaultName) : 0; DISTRHO_SAFE_ASSERT_RETURN(len != 0, nullptr); char* const filename = static_cast<char*>(malloc(len + 2)); filename[0] = '/'; std::memcpy(filename + 1, options.defaultName, len + 1); handle->defaultName = strdup(options.defaultName); handle->selectedFile = filename; return handle.release(); } const char* const funcname = fileBrowserSetPathFuncName; if (openWebBrowserFileDialog(funcname, handle.get())) return handle.release(); return nullptr; #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, ¤t_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, ¤t_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:///")) { if (char* const decodedvalue = strdup(localvalue + 7)) { for (char* s = decodedvalue; (s = std::strchr(s, '%')) != nullptr; ++s) { if (! isHexChar(s[1]) || ! isHexChar(s[2])) continue; const int decodedNum = toHexChar(s[1]) * 0x10 + toHexChar(s[2]); char replacementChar; switch (decodedNum) { case 0x20: replacementChar = ' '; break; case 0x22: replacementChar = '\"'; break; case 0x23: replacementChar = '#'; break; case 0x25: replacementChar = '%'; break; case 0x3c: replacementChar = '<'; break; case 0x3e: replacementChar = '>'; break; case 0x5b: replacementChar = '['; break; case 0x5c: replacementChar = '\\'; break; case 0x5d: replacementChar = ']'; break; case 0x5e: replacementChar = '^'; break; case 0x60: replacementChar = '`'; break; case 0x7b: replacementChar = '{'; break; case 0x7c: replacementChar = '|'; break; case 0x7d: replacementChar = '}'; break; case 0x7e: replacementChar = '~'; break; default: continue; } s[0] = replacementChar; std::memmove(s + 1, s + 3, std::strlen(s) - 2); } handle->selectedFile = decodedvalue; } } } 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 DISTRHO_OS_WASM if (handle->saving && fileBrowserGetPath(handle) != nullptr) downloadWebBrowserFile(handle->defaultName); #endif #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 #undef FILE_BROWSER_DIALOG_NAMESPACE #undef fileBrowserSetPathNamespaced #undef fileBrowserSetPathFuncName