From 757a86ce2767b67331d87cb8f3e624f044302a20 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 3 Apr 2024 22:28:49 -0400 Subject: [PATCH] drop patches that are on v0.10 --- T/TracyProfiler/build_tarballs.jl | 5 - .../TracyProfiler-filter-user-text.patch | 117 - .../TracyProfiler-nfd-extended-1.0.2.patch | 1987 ----------------- .../TracyProfiler-no-divide-zero.patch | 15 - .../patches/TracyProfiler-rr-nopl-seq.patch | 43 - 5 files changed, 2167 deletions(-) delete mode 100644 T/TracyProfiler/bundled/patches/TracyProfiler-filter-user-text.patch delete mode 100644 T/TracyProfiler/bundled/patches/TracyProfiler-nfd-extended-1.0.2.patch delete mode 100644 T/TracyProfiler/bundled/patches/TracyProfiler-no-divide-zero.patch delete mode 100644 T/TracyProfiler/bundled/patches/TracyProfiler-rr-nopl-seq.patch diff --git a/T/TracyProfiler/build_tarballs.jl b/T/TracyProfiler/build_tarballs.jl index 2279a1cf51b..a5fb031f7c3 100644 --- a/T/TracyProfiler/build_tarballs.jl +++ b/T/TracyProfiler/build_tarballs.jl @@ -41,11 +41,6 @@ if [[ "${target}" == x86_64-apple-darwin* ]]; then popd fi -atomic_patch -p1 ../patches/TracyProfiler-nfd-extended-1.0.2.patch -atomic_patch -p1 ../patches/TracyProfiler-filter-user-text.patch -atomic_patch -p1 ../patches/TracyProfiler-no-divide-zero.patch -atomic_patch -p1 ../patches/TracyProfiler-rr-nopl-seq.patch - # Build / install the profiler GUI make -e -j${nproc} -C profiler/build/unix LEGACY=1 IMAGE=tracy release cp -v ./profiler/build/unix/tracy* $bindir diff --git a/T/TracyProfiler/bundled/patches/TracyProfiler-filter-user-text.patch b/T/TracyProfiler/bundled/patches/TracyProfiler-filter-user-text.patch deleted file mode 100644 index cdb5f2c8d1e..00000000000 --- a/T/TracyProfiler/bundled/patches/TracyProfiler-filter-user-text.patch +++ /dev/null @@ -1,117 +0,0 @@ -diff --git a/server/TracyView.hpp b/server/TracyView.hpp -index f8f8d0f1..3a78367b 100644 ---- a/server/TracyView.hpp -+++ b/server/TracyView.hpp -@@ -458,6 +458,8 @@ private: - - ImGuiTextFilter m_statisticsFilter; - ImGuiTextFilter m_statisticsImageFilter; -+ ImGuiTextFilter m_userTextFilter; -+ unordered_flat_set m_filteredZones; - - Region m_highlight; - Region m_highlightZoom; -diff --git a/server/TracyView_FindZone.cpp b/server/TracyView_FindZone.cpp -index 7de5f561..1fadb376 100644 ---- a/server/TracyView_FindZone.cpp -+++ b/server/TracyView_FindZone.cpp -@@ -543,6 +543,7 @@ void View::DrawFindZone() - { - auto& ev = zones[i]; - if( ev.Zone()->End() > rangeMax || ev.Zone()->Start() < rangeMin ) continue; -+ if( m_filteredZones.contains( &ev ) ) continue; - if( selGroup == GetSelectionTarget( ev, groupBy ) ) - { - const auto ctx = m_worker.GetContextSwitchData( m_worker.DecompressThread( zones[i].Thread() ) ); -@@ -560,6 +561,7 @@ void View::DrawFindZone() - for( size_t i=m_findZone.selSortNum; iEnd() > rangeMax || ev.Zone()->Start() < rangeMin ) continue; -+ if( m_filteredZones.contains( &ev ) ) continue; - if( selGroup == GetSelectionTarget( ev, groupBy ) ) - { - const auto t = ev.Zone()->End() - ev.Zone()->Start() - GetZoneChildTimeFast( *ev.Zone() ); -@@ -595,6 +598,7 @@ void View::DrawFindZone() - for( size_t i=m_findZone.selSortNum; iEnd() - ev.Zone()->Start() - GetZoneChildTimeFast( *ev.Zone() ); -@@ -613,6 +617,7 @@ void View::DrawFindZone() - { - auto& ev = zones[i]; - if( ev.Zone()->End() > rangeMax || ev.Zone()->Start() < rangeMin ) continue; -+ if( m_filteredZones.contains( &ev ) ) continue; - if( selGroup == GetSelectionTarget( ev, groupBy ) ) - { - const auto t = ev.Zone()->End() - ev.Zone()->Start(); -@@ -627,6 +632,7 @@ void View::DrawFindZone() - for( size_t i=m_findZone.selSortNum; iEnd() - ev.Zone()->Start(); -@@ -1362,6 +1368,24 @@ void View::DrawFindZone() - SmallCheckbox( "Show zone time in frames", &m_findZone.showZoneInFrames ); - ImGui::Separator(); - -+ ImGui::AlignTextToFramePadding(); -+ TextDisabledUnformatted( "Filter user text:" ); -+ ImGui::SameLine(); -+ bool filterChanged = m_userTextFilter.Draw( ICON_FA_FILTER "###resultFilter", 200 ); -+ -+ ImGui::SameLine(); -+ if( ImGui::Button( ICON_FA_DELETE_LEFT " Clear###userText" ) ) -+ { -+ m_userTextFilter.Clear(); -+ filterChanged = true; -+ } -+ ImGui::Separator(); -+ if( filterChanged ) -+ { -+ m_filteredZones.clear(); -+ m_findZone.ResetGroups(); -+ } -+ - ImGui::TextUnformatted( "Found zones:" ); - ImGui::SameLine(); - DrawHelpMarker( "Left click to highlight entry." ); -@@ -1429,6 +1453,26 @@ void View::DrawFindZone() - zptr++; - continue; - } -+ -+ if( m_userTextFilter.IsActive() ) -+ { -+ bool keep = false; -+ if ( m_worker.HasZoneExtra( *ev.Zone() ) && m_worker.GetZoneExtra( *ev.Zone() ).text.Active() ) -+ { -+ auto text = m_worker.GetString( m_worker.GetZoneExtra( *ev.Zone() ).text ); -+ if( m_userTextFilter.PassFilter( text ) ) -+ { -+ keep = true; -+ } -+ } -+ if( !keep ) -+ { -+ m_filteredZones.insert( &ev ); -+ zptr++; -+ continue; -+ } -+ } -+ - auto timespan = end - start; - assert( timespan != 0 ); - if( m_findZone.selfTime ) diff --git a/T/TracyProfiler/bundled/patches/TracyProfiler-nfd-extended-1.0.2.patch b/T/TracyProfiler/bundled/patches/TracyProfiler-nfd-extended-1.0.2.patch deleted file mode 100644 index 717c3c0250e..00000000000 --- a/T/TracyProfiler/bundled/patches/TracyProfiler-nfd-extended-1.0.2.patch +++ /dev/null @@ -1,1987 +0,0 @@ -commit eb8f485f99f1ca23187edc72db7fe0ed54d12583 -Author: Bartosz Taudul -Date: Wed Apr 5 17:19:23 2023 +0200 - - Update nfd-extended to 1.0.2. - -diff --git a/nfd/nfd_cocoa.m b/nfd/nfd_cocoa.m -index f7024356..aeffb1c4 100644 ---- a/nfd/nfd_cocoa.m -+++ b/nfd/nfd_cocoa.m -@@ -180,6 +180,10 @@ const char* NFD_GetError(void) { - return g_errorstr; - } - -+void NFD_ClearError(void) { -+ NFDi_SetError(NULL); -+} -+ - void NFD_FreePathN(nfdnchar_t* filePath) { - NFDi_Free((void*)filePath); - } -diff --git a/nfd/nfd_portal.cpp b/nfd/nfd_portal.cpp -index 8ef9158f..f37e0916 100644 ---- a/nfd/nfd_portal.cpp -+++ b/nfd/nfd_portal.cpp -@@ -14,8 +14,16 @@ - #include - #include - #include --#include // for the random token string --#include // for access() -+#include // for access() -+ -+#if !defined(__has_include) || !defined(__linux__) -+#include // for getrandom() - the random token string -+#elif __has_include() -+#include -+#else // for GLIBC < 2.25 -+#include -+#define getrandom(buf, sz, flags) syscall(SYS_getrandom, buf, sz, flags) -+#endif - - #include "nfd.h" - -diff --git a/nfd/nfd_win.cpp b/nfd/nfd_win.cpp -index 991e5071..772dfb27 100644 ---- a/nfd/nfd_win.cpp -+++ b/nfd/nfd_win.cpp -@@ -1,969 +1,969 @@ --/* -- Native File Dialog Extended -- Repository: https://github.com/btzy/nativefiledialog-extended -- License: Zlib -- Author: Bernard Teo -- */ -- --/* only locally define UNICODE in this compilation unit */ --#ifndef UNICODE --#define UNICODE --#endif -- --#ifdef __MINGW32__ --// Explicitly setting NTDDI version, this is necessary for the MinGW compiler --#define NTDDI_VERSION NTDDI_VISTA --#define _WIN32_WINNT _WIN32_WINNT_VISTA --#endif -- --#if _MSC_VER --// see --// https://developercommunity.visualstudio.com/content/problem/185399/error-c2760-in-combaseapih-with-windows-sdk-81-and.html --struct IUnknown; // Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was -- // unexpected here" when using /permissive- --#endif -- --#include --#include --#include --#include --#include --#include "nfd.h" -- --namespace { -- --/* current error */ --const char* g_errorstr = nullptr; -- --void NFDi_SetError(const char* msg) { -- g_errorstr = msg; --} -- --template --T* NFDi_Malloc(size_t bytes) { -- void* ptr = malloc(bytes); -- if (!ptr) NFDi_SetError("NFDi_Malloc failed."); -- -- return static_cast(ptr); --} -- --template --void NFDi_Free(T* ptr) { -- assert(ptr); -- free(static_cast(ptr)); --} -- --/* guard objects */ --template --struct Release_Guard { -- T* data; -- Release_Guard(T* releasable) noexcept : data(releasable) {} -- ~Release_Guard() { data->Release(); } --}; -- --template --struct Free_Guard { -- T* data; -- Free_Guard(T* freeable) noexcept : data(freeable) {} -- ~Free_Guard() { NFDi_Free(data); } --}; -- --template --struct FreeCheck_Guard { -- T* data; -- FreeCheck_Guard(T* freeable = nullptr) noexcept : data(freeable) {} -- ~FreeCheck_Guard() { -- if (data) NFDi_Free(data); -- } --}; -- --/* helper functions */ --nfdresult_t AddFiltersToDialog(::IFileDialog* fileOpenDialog, -- const nfdnfilteritem_t* filterList, -- nfdfiltersize_t filterCount) { -- /* filterCount plus 1 because we hardcode the *.* wildcard after the while loop */ -- COMDLG_FILTERSPEC* specList = -- NFDi_Malloc(sizeof(COMDLG_FILTERSPEC) * (filterCount + 1)); -- if (!specList) { -- return NFD_ERROR; -- } -- -- /* ad-hoc RAII object to free memory when destructing */ -- struct COMDLG_FILTERSPEC_Guard { -- COMDLG_FILTERSPEC* _specList; -- nfdfiltersize_t index; -- COMDLG_FILTERSPEC_Guard(COMDLG_FILTERSPEC* specList) noexcept -- : _specList(specList), index(0) {} -- ~COMDLG_FILTERSPEC_Guard() { -- for (--index; index != static_cast(-1); --index) { -- NFDi_Free(const_cast(_specList[index].pszSpec)); -- } -- NFDi_Free(_specList); -- } -- }; -- -- COMDLG_FILTERSPEC_Guard specListGuard(specList); -- -- if (filterCount) { -- assert(filterList); -- -- // we have filters to add ... format and add them -- -- // use the index that comes from the RAII object (instead of making a copy), so the RAII -- // object will know which memory to free -- nfdfiltersize_t& index = specListGuard.index; -- -- for (; index != filterCount; ++index) { -- // set the friendly name of this filter -- specList[index].pszName = filterList[index].name; -- -- // set the specification of this filter... -- -- // count number of file extensions -- size_t sep = 1; -- for (const nfdnchar_t* p_spec = filterList[index].spec; *p_spec; ++p_spec) { -- if (*p_spec == L',') { -- ++sep; -- } -- } -- -- // calculate space needed (including the trailing '\0') -- size_t specSize = sep * 2 + wcslen(filterList[index].spec) + 1; -- -- // malloc the required memory and populate it -- nfdnchar_t* specBuf = NFDi_Malloc(sizeof(nfdnchar_t) * specSize); -- -- if (!specBuf) { -- // automatic freeing of memory via COMDLG_FILTERSPEC_Guard -- return NFD_ERROR; -- } -- -- // convert "png,jpg" to "*.png;*.jpg" as required by Windows ... -- nfdnchar_t* p_specBuf = specBuf; -- *p_specBuf++ = L'*'; -- *p_specBuf++ = L'.'; -- for (const nfdnchar_t* p_spec = filterList[index].spec; *p_spec; ++p_spec) { -- if (*p_spec == L',') { -- *p_specBuf++ = L';'; -- *p_specBuf++ = L'*'; -- *p_specBuf++ = L'.'; -- } else { -- *p_specBuf++ = *p_spec; -- } -- } -- *p_specBuf++ = L'\0'; -- -- // assert that we had allocated exactly the correct amount of memory that we used -- assert(static_cast(p_specBuf - specBuf) == specSize); -- -- // save the buffer to the guard object -- specList[index].pszSpec = specBuf; -- } -- } -- -- /* Add wildcard */ -- specList[filterCount].pszName = L"All files"; -- specList[filterCount].pszSpec = L"*.*"; -- -- // add the filter to the dialog -- if (!SUCCEEDED(fileOpenDialog->SetFileTypes(filterCount + 1, specList))) { -- NFDi_SetError("Failed to set the allowable file types for the drop-down menu."); -- return NFD_ERROR; -- } -- -- // automatic freeing of memory via COMDLG_FILTERSPEC_Guard -- return NFD_OKAY; --} -- --/* call after AddFiltersToDialog */ --nfdresult_t SetDefaultExtension(::IFileDialog* fileOpenDialog, -- const nfdnfilteritem_t* filterList, -- nfdfiltersize_t filterCount) { -- // if there are no filters, then don't set default extensions -- if (!filterCount) { -- return NFD_OKAY; -- } -- -- assert(filterList); -- -- // set the first item as the default index, and set the default extension -- if (!SUCCEEDED(fileOpenDialog->SetFileTypeIndex(1))) { -- NFDi_SetError("Failed to set the selected file type index."); -- return NFD_ERROR; -- } -- -- // set the first item as the default file extension -- const nfdnchar_t* p_spec = filterList[0].spec; -- for (; *p_spec; ++p_spec) { -- if (*p_spec == ',') { -- break; -- } -- } -- if (*p_spec) { -- // multiple file extensions for this type (need to allocate memory) -- size_t numChars = p_spec - filterList[0].spec; -- // allocate one more char space for the '\0' -- nfdnchar_t* extnBuf = NFDi_Malloc(sizeof(nfdnchar_t) * (numChars + 1)); -- if (!extnBuf) { -- return NFD_ERROR; -- } -- Free_Guard extnBufGuard(extnBuf); -- -- // copy the extension -- for (size_t i = 0; i != numChars; ++i) { -- extnBuf[i] = filterList[0].spec[i]; -- } -- // pad with trailing '\0' -- extnBuf[numChars] = L'\0'; -- -- if (!SUCCEEDED(fileOpenDialog->SetDefaultExtension(extnBuf))) { -- NFDi_SetError("Failed to set default extension."); -- return NFD_ERROR; -- } -- } else { -- // single file extension for this type (no need to allocate memory) -- if (!SUCCEEDED(fileOpenDialog->SetDefaultExtension(filterList[0].spec))) { -- NFDi_SetError("Failed to set default extension."); -- return NFD_ERROR; -- } -- } -- -- return NFD_OKAY; --} -- --nfdresult_t SetDefaultPath(IFileDialog* dialog, const nfdnchar_t* defaultPath) { -- if (!defaultPath || !*defaultPath) return NFD_OKAY; -- -- IShellItem* folder; -- HRESULT result = SHCreateItemFromParsingName(defaultPath, nullptr, IID_PPV_ARGS(&folder)); -- -- // Valid non results. -- if (result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || -- result == HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE)) { -- return NFD_OKAY; -- } -- -- if (!SUCCEEDED(result)) { -- NFDi_SetError("Failed to create ShellItem for setting the default path."); -- return NFD_ERROR; -- } -- -- Release_Guard folderGuard(folder); -- -- // SetDefaultFolder() might use another recently used folder if available, so the user doesn't -- // need to keep navigating back to the default folder (recommended by Windows). change to -- // SetFolder() if you always want to use the default folder -- if (!SUCCEEDED(dialog->SetDefaultFolder(folder))) { -- NFDi_SetError("Failed to set default path."); -- return NFD_ERROR; -- } -- -- return NFD_OKAY; --} -- --nfdresult_t SetDefaultName(IFileDialog* dialog, const nfdnchar_t* defaultName) { -- if (!defaultName || !*defaultName) return NFD_OKAY; -- -- if (!SUCCEEDED(dialog->SetFileName(defaultName))) { -- NFDi_SetError("Failed to set default file name."); -- return NFD_ERROR; -- } -- -- return NFD_OKAY; --} -- --nfdresult_t AddOptions(IFileDialog* dialog, FILEOPENDIALOGOPTIONS options) { -- FILEOPENDIALOGOPTIONS existingOptions; -- if (!SUCCEEDED(dialog->GetOptions(&existingOptions))) { -- NFDi_SetError("Failed to get options."); -- return NFD_ERROR; -- } -- if (!SUCCEEDED(dialog->SetOptions(existingOptions | options))) { -- NFDi_SetError("Failed to set options."); -- return NFD_ERROR; -- } -- return NFD_OKAY; --} --} // namespace -- --const char* NFD_GetError(void) { -- return g_errorstr; --} -- --void NFD_ClearError(void) { -- NFDi_SetError(nullptr); --} -- --/* public */ -- --namespace { --// The user might have initialized with COINIT_MULTITHREADED before, --// in which case we will fail to do CoInitializeEx(), but file dialogs will still work. --// See https://github.com/mlabbe/nativefiledialog/issues/72 for more information. --bool needs_uninitialize; --} // namespace -- --nfdresult_t NFD_Init(void) { -- // Init COM library. -- HRESULT result = -- ::CoInitializeEx(nullptr, ::COINIT_APARTMENTTHREADED | ::COINIT_DISABLE_OLE1DDE); -- -- if (SUCCEEDED(result)) { -- needs_uninitialize = true; -- return NFD_OKAY; -- } else if (result == RPC_E_CHANGED_MODE) { -- // If this happens, the user already initialized COM using COINIT_MULTITHREADED, -- // so COM will still work, but we shouldn't uninitialize it later. -- needs_uninitialize = false; -- return NFD_OKAY; -- } else { -- NFDi_SetError("Failed to initialize COM."); -- return NFD_ERROR; -- } --} --void NFD_Quit(void) { -- if (needs_uninitialize) ::CoUninitialize(); --} -- --void NFD_FreePathN(nfdnchar_t* filePath) { -- assert(filePath); -- ::CoTaskMemFree(filePath); --} -- --nfdresult_t NFD_OpenDialogN(nfdnchar_t** outPath, -- const nfdnfilteritem_t* filterList, -- nfdfiltersize_t filterCount, -- const nfdnchar_t* defaultPath) { -- ::IFileOpenDialog* fileOpenDialog; -- -- // Create dialog -- HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, -- nullptr, -- CLSCTX_ALL, -- ::IID_IFileOpenDialog, -- reinterpret_cast(&fileOpenDialog)); -- -- if (!SUCCEEDED(result)) { -- NFDi_SetError("Could not create dialog."); -- return NFD_ERROR; -- } -- -- // make sure we remember to free the dialog -- Release_Guard<::IFileOpenDialog> fileOpenDialogGuard(fileOpenDialog); -- -- // Build the filter list -- if (!AddFiltersToDialog(fileOpenDialog, filterList, filterCount)) { -- return NFD_ERROR; -- } -- -- // Set auto-completed default extension -- if (!SetDefaultExtension(fileOpenDialog, filterList, filterCount)) { -- return NFD_ERROR; -- } -- -- // Set the default path -- if (!SetDefaultPath(fileOpenDialog, defaultPath)) { -- return NFD_ERROR; -- } -- -- // Only show file system items -- if (!AddOptions(fileOpenDialog, ::FOS_FORCEFILESYSTEM)) { -- return NFD_ERROR; -- } -- -- // Show the dialog. -- result = fileOpenDialog->Show(nullptr); -- if (SUCCEEDED(result)) { -- // Get the file name -- ::IShellItem* psiResult; -- result = fileOpenDialog->GetResult(&psiResult); -- if (!SUCCEEDED(result)) { -- NFDi_SetError("Could not get shell item from dialog."); -- return NFD_ERROR; -- } -- Release_Guard<::IShellItem> psiResultGuard(psiResult); -- -- nfdnchar_t* filePath; -- result = psiResult->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); -- if (!SUCCEEDED(result)) { -- NFDi_SetError("Could not get file path from shell item returned by dialog."); -- return NFD_ERROR; -- } -- -- *outPath = filePath; -- -- return NFD_OKAY; -- } else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED)) { -- return NFD_CANCEL; -- } else { -- NFDi_SetError("File dialog box show failed."); -- return NFD_ERROR; -- } --} -- --nfdresult_t NFD_OpenDialogMultipleN(const nfdpathset_t** outPaths, -- const nfdnfilteritem_t* filterList, -- nfdfiltersize_t filterCount, -- const nfdnchar_t* defaultPath) { -- ::IFileOpenDialog* fileOpenDialog(nullptr); -- -- // Create dialog -- HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, -- nullptr, -- CLSCTX_ALL, -- ::IID_IFileOpenDialog, -- reinterpret_cast(&fileOpenDialog)); -- -- if (!SUCCEEDED(result)) { -- NFDi_SetError("Could not create dialog."); -- return NFD_ERROR; -- } -- -- // make sure we remember to free the dialog -- Release_Guard<::IFileOpenDialog> fileOpenDialogGuard(fileOpenDialog); -- -- // Build the filter list -- if (!AddFiltersToDialog(fileOpenDialog, filterList, filterCount)) { -- return NFD_ERROR; -- } -- -- // Set auto-completed default extension -- if (!SetDefaultExtension(fileOpenDialog, filterList, filterCount)) { -- return NFD_ERROR; -- } -- -- // Set the default path -- if (!SetDefaultPath(fileOpenDialog, defaultPath)) { -- return NFD_ERROR; -- } -- -- // Set a flag for multiple options and file system items only -- if (!AddOptions(fileOpenDialog, ::FOS_FORCEFILESYSTEM | ::FOS_ALLOWMULTISELECT)) { -- return NFD_ERROR; -- } -- -- // Show the dialog. -- result = fileOpenDialog->Show(nullptr); -- if (SUCCEEDED(result)) { -- ::IShellItemArray* shellItems; -- result = fileOpenDialog->GetResults(&shellItems); -- if (!SUCCEEDED(result)) { -- NFDi_SetError("Could not get shell items."); -- return NFD_ERROR; -- } -- -- // save the path set to the output -- *outPaths = static_cast(shellItems); -- -- return NFD_OKAY; -- } else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED)) { -- return NFD_CANCEL; -- } else { -- NFDi_SetError("File dialog box show failed."); -- return NFD_ERROR; -- } --} -- --nfdresult_t NFD_SaveDialogN(nfdnchar_t** outPath, -- const nfdnfilteritem_t* filterList, -- nfdfiltersize_t filterCount, -- const nfdnchar_t* defaultPath, -- const nfdnchar_t* defaultName) { -- ::IFileSaveDialog* fileSaveDialog; -- -- // Create dialog -- HRESULT result = ::CoCreateInstance(::CLSID_FileSaveDialog, -- nullptr, -- CLSCTX_ALL, -- ::IID_IFileSaveDialog, -- reinterpret_cast(&fileSaveDialog)); -- -- if (!SUCCEEDED(result)) { -- NFDi_SetError("Could not create dialog."); -- return NFD_ERROR; -- } -- -- // make sure we remember to free the dialog -- Release_Guard<::IFileSaveDialog> fileSaveDialogGuard(fileSaveDialog); -- -- // Build the filter list -- if (!AddFiltersToDialog(fileSaveDialog, filterList, filterCount)) { -- return NFD_ERROR; -- } -- -- // Set default extension -- if (!SetDefaultExtension(fileSaveDialog, filterList, filterCount)) { -- return NFD_ERROR; -- } -- -- // Set the default path -- if (!SetDefaultPath(fileSaveDialog, defaultPath)) { -- return NFD_ERROR; -- } -- -- // Set the default name -- if (!SetDefaultName(fileSaveDialog, defaultName)) { -- return NFD_ERROR; -- } -- -- // Only show file system items -- if (!AddOptions(fileSaveDialog, ::FOS_FORCEFILESYSTEM)) { -- return NFD_ERROR; -- } -- -- // Show the dialog. -- result = fileSaveDialog->Show(nullptr); -- if (SUCCEEDED(result)) { -- // Get the file name -- ::IShellItem* psiResult; -- result = fileSaveDialog->GetResult(&psiResult); -- if (!SUCCEEDED(result)) { -- NFDi_SetError("Could not get shell item from dialog."); -- return NFD_ERROR; -- } -- Release_Guard<::IShellItem> psiResultGuard(psiResult); -- -- nfdnchar_t* filePath; -- result = psiResult->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); -- if (!SUCCEEDED(result)) { -- NFDi_SetError("Could not get file path from shell item returned by dialog."); -- return NFD_ERROR; -- } -- -- *outPath = filePath; -- -- return NFD_OKAY; -- } else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED)) { -- return NFD_CANCEL; -- } else { -- NFDi_SetError("File dialog box show failed."); -- return NFD_ERROR; -- } --} -- --nfdresult_t NFD_PickFolderN(nfdnchar_t** outPath, const nfdnchar_t* defaultPath) { -- ::IFileOpenDialog* fileOpenDialog; -- -- // Create dialog -- if (!SUCCEEDED(::CoCreateInstance(::CLSID_FileOpenDialog, -- nullptr, -- CLSCTX_ALL, -- ::IID_IFileOpenDialog, -- reinterpret_cast(&fileOpenDialog)))) { -- NFDi_SetError("Could not create dialog."); -- return NFD_ERROR; -- } -- -- Release_Guard<::IFileOpenDialog> fileOpenDialogGuard(fileOpenDialog); -- -- // Set the default path -- if (!SetDefaultPath(fileOpenDialog, defaultPath)) { -- return NFD_ERROR; -- } -- -- // Only show items that are folders and on the file system -- if (!AddOptions(fileOpenDialog, ::FOS_FORCEFILESYSTEM | ::FOS_PICKFOLDERS)) { -- return NFD_ERROR; -- } -- -- // Show the dialog to the user -- const HRESULT result = fileOpenDialog->Show(nullptr); -- if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED)) { -- return NFD_CANCEL; -- } else if (!SUCCEEDED(result)) { -- NFDi_SetError("File dialog box show failed."); -- return NFD_ERROR; -- } -- -- // Get the shell item result -- ::IShellItem* psiResult; -- if (!SUCCEEDED(fileOpenDialog->GetResult(&psiResult))) { -- return NFD_ERROR; -- } -- -- Release_Guard<::IShellItem> psiResultGuard(psiResult); -- -- // Finally get the path -- nfdnchar_t* filePath; -- // Why are we not using SIGDN_FILESYSPATH? -- if (!SUCCEEDED(psiResult->GetDisplayName(::SIGDN_DESKTOPABSOLUTEPARSING, &filePath))) { -- NFDi_SetError("Could not get file path from shell item returned by dialog."); -- return NFD_ERROR; -- } -- -- *outPath = filePath; -- -- return NFD_OKAY; --} -- --nfdresult_t NFD_PathSet_GetCount(const nfdpathset_t* pathSet, nfdpathsetsize_t* count) { -- assert(pathSet); -- // const_cast because methods on IShellItemArray aren't const, but it should act like const to -- // the caller -- ::IShellItemArray* psiaPathSet = -- const_cast<::IShellItemArray*>(static_cast(pathSet)); -- -- DWORD numPaths; -- if (!SUCCEEDED(psiaPathSet->GetCount(&numPaths))) { -- NFDi_SetError("Could not get path count."); -- return NFD_ERROR; -- } -- *count = numPaths; -- return NFD_OKAY; --} -- --nfdresult_t NFD_PathSet_GetPathN(const nfdpathset_t* pathSet, -- nfdpathsetsize_t index, -- nfdnchar_t** outPath) { -- assert(pathSet); -- // const_cast because methods on IShellItemArray aren't const, but it should act like const to -- // the caller -- ::IShellItemArray* psiaPathSet = -- const_cast<::IShellItemArray*>(static_cast(pathSet)); -- -- ::IShellItem* psiPath; -- if (!SUCCEEDED(psiaPathSet->GetItemAt(index, &psiPath))) { -- NFDi_SetError("Could not get shell item."); -- return NFD_ERROR; -- } -- -- Release_Guard<::IShellItem> psiPathGuard(psiPath); -- -- nfdnchar_t* name; -- if (!SUCCEEDED(psiPath->GetDisplayName(::SIGDN_FILESYSPATH, &name))) { -- NFDi_SetError("Could not get file path from shell item."); -- return NFD_ERROR; -- } -- -- *outPath = name; -- return NFD_OKAY; --} -- --nfdresult_t NFD_PathSet_GetEnum(const nfdpathset_t* pathSet, nfdpathsetenum_t* outEnumerator) { -- assert(pathSet); -- // const_cast because methods on IShellItemArray aren't const, but it should act like const to -- // the caller -- ::IShellItemArray* psiaPathSet = -- const_cast<::IShellItemArray*>(static_cast(pathSet)); -- -- ::IEnumShellItems* pesiPaths; -- if (!SUCCEEDED(psiaPathSet->EnumItems(&pesiPaths))) { -- NFDi_SetError("Could not get enumerator."); -- return NFD_ERROR; -- } -- -- outEnumerator->ptr = static_cast(pesiPaths); -- return NFD_OKAY; --} -- --void NFD_PathSet_FreeEnum(nfdpathsetenum_t* enumerator) { -- assert(enumerator->ptr); -- -- ::IEnumShellItems* pesiPaths = static_cast<::IEnumShellItems*>(enumerator->ptr); -- -- // free the enumerator memory -- pesiPaths->Release(); --} -- --nfdresult_t NFD_PathSet_EnumNextN(nfdpathsetenum_t* enumerator, nfdnchar_t** outPath) { -- assert(enumerator->ptr); -- -- ::IEnumShellItems* pesiPaths = static_cast<::IEnumShellItems*>(enumerator->ptr); -- -- ::IShellItem* psiPath; -- HRESULT res = pesiPaths->Next(1, &psiPath, NULL); -- if (!SUCCEEDED(res)) { -- NFDi_SetError("Could not get next item of enumerator."); -- return NFD_ERROR; -- } -- if (res != S_OK) { -- *outPath = nullptr; -- return NFD_OKAY; -- } -- -- Release_Guard<::IShellItem> psiPathGuard(psiPath); -- -- nfdnchar_t* name; -- if (!SUCCEEDED(psiPath->GetDisplayName(::SIGDN_FILESYSPATH, &name))) { -- NFDi_SetError("Could not get file path from shell item."); -- return NFD_ERROR; -- } -- -- *outPath = name; -- return NFD_OKAY; --} -- --void NFD_PathSet_Free(const nfdpathset_t* pathSet) { -- assert(pathSet); -- // const_cast because methods on IShellItemArray aren't const, but it should act like const to -- // the caller -- ::IShellItemArray* psiaPathSet = -- const_cast<::IShellItemArray*>(static_cast(pathSet)); -- -- // free the path set memory -- psiaPathSet->Release(); --} -- --namespace { --// allocs the space in outStr -- call NFDi_Free() --nfdresult_t CopyCharToWChar(const nfdu8char_t* inStr, nfdnchar_t*& outStr) { -- int charsNeeded = MultiByteToWideChar(CP_UTF8, 0, inStr, -1, nullptr, 0); -- assert(charsNeeded); -- -- nfdnchar_t* tmp_outStr = NFDi_Malloc(sizeof(nfdnchar_t) * charsNeeded); -- if (!tmp_outStr) { -- return NFD_ERROR; -- } -- -- int ret = MultiByteToWideChar(CP_UTF8, 0, inStr, -1, tmp_outStr, charsNeeded); -- assert(ret && ret == charsNeeded); -- (void)ret; // prevent warning in release build -- outStr = tmp_outStr; -- return NFD_OKAY; --} -- --// allocs the space in outPath -- call NFDi_Free() --nfdresult_t CopyWCharToNFDChar(const nfdnchar_t* inStr, nfdu8char_t*& outStr) { -- int bytesNeeded = WideCharToMultiByte(CP_UTF8, 0, inStr, -1, nullptr, 0, nullptr, nullptr); -- assert(bytesNeeded); -- -- nfdu8char_t* tmp_outStr = NFDi_Malloc(sizeof(nfdu8char_t) * bytesNeeded); -- if (!tmp_outStr) { -- return NFD_ERROR; -- } -- -- int ret = WideCharToMultiByte(CP_UTF8, 0, inStr, -1, tmp_outStr, bytesNeeded, nullptr, nullptr); -- assert(ret && ret == bytesNeeded); -- (void)ret; // prevent warning in release build -- outStr = tmp_outStr; -- return NFD_OKAY; --} -- --struct FilterItem_Guard { -- nfdnfilteritem_t* data; -- nfdfiltersize_t index; -- FilterItem_Guard() noexcept : data(nullptr), index(0) {} -- ~FilterItem_Guard() { -- assert(data || index == 0); -- for (--index; index != static_cast(-1); --index) { -- NFDi_Free(const_cast(data[index].spec)); -- NFDi_Free(const_cast(data[index].name)); -- } -- if (data) NFDi_Free(data); -- } --}; -- --nfdresult_t CopyFilterItem(const nfdu8filteritem_t* filterList, -- nfdfiltersize_t count, -- FilterItem_Guard& filterItemsNGuard) { -- if (count) { -- nfdnfilteritem_t*& filterItemsN = filterItemsNGuard.data; -- filterItemsN = NFDi_Malloc(sizeof(nfdnfilteritem_t) * count); -- if (!filterItemsN) { -- return NFD_ERROR; -- } -- -- nfdfiltersize_t& index = filterItemsNGuard.index; -- for (; index != count; ++index) { -- nfdresult_t res = CopyCharToWChar(filterList[index].name, -- const_cast(filterItemsN[index].name)); -- if (!res) { -- return NFD_ERROR; -- } -- res = CopyCharToWChar(filterList[index].spec, -- const_cast(filterItemsN[index].spec)); -- if (!res) { -- // remember to free the name, because we also created it (and it won't be protected -- // by the guard, because we have not incremented the index) -- NFDi_Free(const_cast(filterItemsN[index].name)); -- return NFD_ERROR; -- } -- } -- } -- return NFD_OKAY; --} --nfdresult_t ConvertU8ToNative(const nfdu8char_t* u8Text, FreeCheck_Guard& nativeText) { -- if (u8Text) { -- nfdresult_t res = CopyCharToWChar(u8Text, nativeText.data); -- if (!res) { -- return NFD_ERROR; -- } -- } -- return NFD_OKAY; --} --void NormalizePathSeparator(nfdnchar_t* path) { -- if (path) { -- for (; *path; ++path) { -- if (*path == L'/') *path = L'\\'; -- } -- } --} --} // namespace -- --void NFD_FreePathU8(nfdu8char_t* outPath) { -- NFDi_Free(outPath); --} -- --nfdresult_t NFD_OpenDialogU8(nfdu8char_t** outPath, -- const nfdu8filteritem_t* filterList, -- nfdfiltersize_t count, -- const nfdu8char_t* defaultPath) { -- // populate the real nfdnfilteritem_t -- FilterItem_Guard filterItemsNGuard; -- if (!CopyFilterItem(filterList, count, filterItemsNGuard)) { -- return NFD_ERROR; -- } -- -- // convert and normalize the default path, but only if it is not nullptr -- FreeCheck_Guard defaultPathNGuard; -- ConvertU8ToNative(defaultPath, defaultPathNGuard); -- NormalizePathSeparator(defaultPathNGuard.data); -- -- // call the native function -- nfdnchar_t* outPathN; -- nfdresult_t res = -- NFD_OpenDialogN(&outPathN, filterItemsNGuard.data, count, defaultPathNGuard.data); -- -- if (res != NFD_OKAY) { -- return res; -- } -- -- // convert the outPath to UTF-8 -- res = CopyWCharToNFDChar(outPathN, *outPath); -- -- // free the native out path, and return the result -- NFD_FreePathN(outPathN); -- return res; --} -- --/* multiple file open dialog */ --/* It is the caller's responsibility to free `outPaths` via NFD_PathSet_Free() if this function -- * returns NFD_OKAY */ --nfdresult_t NFD_OpenDialogMultipleU8(const nfdpathset_t** outPaths, -- const nfdu8filteritem_t* filterList, -- nfdfiltersize_t count, -- const nfdu8char_t* defaultPath) { -- // populate the real nfdnfilteritem_t -- FilterItem_Guard filterItemsNGuard; -- if (!CopyFilterItem(filterList, count, filterItemsNGuard)) { -- return NFD_ERROR; -- } -- -- // convert and normalize the default path, but only if it is not nullptr -- FreeCheck_Guard defaultPathNGuard; -- ConvertU8ToNative(defaultPath, defaultPathNGuard); -- NormalizePathSeparator(defaultPathNGuard.data); -- -- // call the native function -- return NFD_OpenDialogMultipleN(outPaths, filterItemsNGuard.data, count, defaultPathNGuard.data); --} -- --/* save dialog */ --/* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function returns -- * NFD_OKAY */ --nfdresult_t NFD_SaveDialogU8(nfdu8char_t** outPath, -- const nfdu8filteritem_t* filterList, -- nfdfiltersize_t count, -- const nfdu8char_t* defaultPath, -- const nfdu8char_t* defaultName) { -- // populate the real nfdnfilteritem_t -- FilterItem_Guard filterItemsNGuard; -- if (!CopyFilterItem(filterList, count, filterItemsNGuard)) { -- return NFD_ERROR; -- } -- -- // convert and normalize the default path, but only if it is not nullptr -- FreeCheck_Guard defaultPathNGuard; -- ConvertU8ToNative(defaultPath, defaultPathNGuard); -- NormalizePathSeparator(defaultPathNGuard.data); -- -- // convert the default name, but only if it is not nullptr -- FreeCheck_Guard defaultNameNGuard; -- ConvertU8ToNative(defaultName, defaultNameNGuard); -- -- // call the native function -- nfdnchar_t* outPathN; -- nfdresult_t res = NFD_SaveDialogN( -- &outPathN, filterItemsNGuard.data, count, defaultPathNGuard.data, defaultNameNGuard.data); -- -- if (res != NFD_OKAY) { -- return res; -- } -- -- // convert the outPath to UTF-8 -- res = CopyWCharToNFDChar(outPathN, *outPath); -- -- // free the native out path, and return the result -- NFD_FreePathN(outPathN); -- return res; --} -- --/* select folder dialog */ --/* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function returns -- * NFD_OKAY */ --nfdresult_t NFD_PickFolderU8(nfdu8char_t** outPath, const nfdu8char_t* defaultPath) { -- // convert and normalize the default path, but only if it is not nullptr -- FreeCheck_Guard defaultPathNGuard; -- ConvertU8ToNative(defaultPath, defaultPathNGuard); -- NormalizePathSeparator(defaultPathNGuard.data); -- -- // call the native function -- nfdnchar_t* outPathN; -- nfdresult_t res = NFD_PickFolderN(&outPathN, defaultPathNGuard.data); -- -- if (res != NFD_OKAY) { -- return res; -- } -- -- // convert the outPath to UTF-8 -- res = CopyWCharToNFDChar(outPathN, *outPath); -- -- // free the native out path, and return the result -- NFD_FreePathN(outPathN); -- return res; --} -- --/* Get the UTF-8 path at offset index */ --/* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function returns -- * NFD_OKAY */ --nfdresult_t NFD_PathSet_GetPathU8(const nfdpathset_t* pathSet, -- nfdpathsetsize_t index, -- nfdu8char_t** outPath) { -- // call the native function -- nfdnchar_t* outPathN; -- nfdresult_t res = NFD_PathSet_GetPathN(pathSet, index, &outPathN); -- -- if (res != NFD_OKAY) { -- return res; -- } -- -- // convert the outPath to UTF-8 -- res = CopyWCharToNFDChar(outPathN, *outPath); -- -- // free the native out path, and return the result -- NFD_FreePathN(outPathN); -- return res; --} -- --nfdresult_t NFD_PathSet_EnumNextU8(nfdpathsetenum_t* enumerator, nfdu8char_t** outPath) { -- // call the native function -- nfdnchar_t* outPathN; -- nfdresult_t res = NFD_PathSet_EnumNextN(enumerator, &outPathN); -- -- if (res != NFD_OKAY) { -- return res; -- } -- -- if (outPathN) { -- // convert the outPath to UTF-8 -- res = CopyWCharToNFDChar(outPathN, *outPath); -- -- // free the native out path, and return the result -- NFD_FreePathN(outPathN); -- } else { -- *outPath = nullptr; -- res = NFD_OKAY; -- } -- -- return res; --} -+/* -+ Native File Dialog Extended -+ Repository: https://github.com/btzy/nativefiledialog-extended -+ License: Zlib -+ Author: Bernard Teo -+ */ -+ -+/* only locally define UNICODE in this compilation unit */ -+#ifndef UNICODE -+#define UNICODE -+#endif -+ -+#ifdef __MINGW32__ -+// Explicitly setting NTDDI version, this is necessary for the MinGW compiler -+#define NTDDI_VERSION NTDDI_VISTA -+#define _WIN32_WINNT _WIN32_WINNT_VISTA -+#endif -+ -+#if _MSC_VER -+// see -+// https://developercommunity.visualstudio.com/content/problem/185399/error-c2760-in-combaseapih-with-windows-sdk-81-and.html -+struct IUnknown; // Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was -+ // unexpected here" when using /permissive- -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include "nfd.h" -+ -+namespace { -+ -+/* current error */ -+const char* g_errorstr = nullptr; -+ -+void NFDi_SetError(const char* msg) { -+ g_errorstr = msg; -+} -+ -+template -+T* NFDi_Malloc(size_t bytes) { -+ void* ptr = malloc(bytes); -+ if (!ptr) NFDi_SetError("NFDi_Malloc failed."); -+ -+ return static_cast(ptr); -+} -+ -+template -+void NFDi_Free(T* ptr) { -+ assert(ptr); -+ free(static_cast(ptr)); -+} -+ -+/* guard objects */ -+template -+struct Release_Guard { -+ T* data; -+ Release_Guard(T* releasable) noexcept : data(releasable) {} -+ ~Release_Guard() { data->Release(); } -+}; -+ -+template -+struct Free_Guard { -+ T* data; -+ Free_Guard(T* freeable) noexcept : data(freeable) {} -+ ~Free_Guard() { NFDi_Free(data); } -+}; -+ -+template -+struct FreeCheck_Guard { -+ T* data; -+ FreeCheck_Guard(T* freeable = nullptr) noexcept : data(freeable) {} -+ ~FreeCheck_Guard() { -+ if (data) NFDi_Free(data); -+ } -+}; -+ -+/* helper functions */ -+nfdresult_t AddFiltersToDialog(::IFileDialog* fileOpenDialog, -+ const nfdnfilteritem_t* filterList, -+ nfdfiltersize_t filterCount) { -+ /* filterCount plus 1 because we hardcode the *.* wildcard after the while loop */ -+ COMDLG_FILTERSPEC* specList = -+ NFDi_Malloc(sizeof(COMDLG_FILTERSPEC) * (filterCount + 1)); -+ if (!specList) { -+ return NFD_ERROR; -+ } -+ -+ /* ad-hoc RAII object to free memory when destructing */ -+ struct COMDLG_FILTERSPEC_Guard { -+ COMDLG_FILTERSPEC* _specList; -+ nfdfiltersize_t index; -+ COMDLG_FILTERSPEC_Guard(COMDLG_FILTERSPEC* specList) noexcept -+ : _specList(specList), index(0) {} -+ ~COMDLG_FILTERSPEC_Guard() { -+ for (--index; index != static_cast(-1); --index) { -+ NFDi_Free(const_cast(_specList[index].pszSpec)); -+ } -+ NFDi_Free(_specList); -+ } -+ }; -+ -+ COMDLG_FILTERSPEC_Guard specListGuard(specList); -+ -+ if (filterCount) { -+ assert(filterList); -+ -+ // we have filters to add ... format and add them -+ -+ // use the index that comes from the RAII object (instead of making a copy), so the RAII -+ // object will know which memory to free -+ nfdfiltersize_t& index = specListGuard.index; -+ -+ for (; index != filterCount; ++index) { -+ // set the friendly name of this filter -+ specList[index].pszName = filterList[index].name; -+ -+ // set the specification of this filter... -+ -+ // count number of file extensions -+ size_t sep = 1; -+ for (const nfdnchar_t* p_spec = filterList[index].spec; *p_spec; ++p_spec) { -+ if (*p_spec == L',') { -+ ++sep; -+ } -+ } -+ -+ // calculate space needed (including the trailing '\0') -+ size_t specSize = sep * 2 + wcslen(filterList[index].spec) + 1; -+ -+ // malloc the required memory and populate it -+ nfdnchar_t* specBuf = NFDi_Malloc(sizeof(nfdnchar_t) * specSize); -+ -+ if (!specBuf) { -+ // automatic freeing of memory via COMDLG_FILTERSPEC_Guard -+ return NFD_ERROR; -+ } -+ -+ // convert "png,jpg" to "*.png;*.jpg" as required by Windows ... -+ nfdnchar_t* p_specBuf = specBuf; -+ *p_specBuf++ = L'*'; -+ *p_specBuf++ = L'.'; -+ for (const nfdnchar_t* p_spec = filterList[index].spec; *p_spec; ++p_spec) { -+ if (*p_spec == L',') { -+ *p_specBuf++ = L';'; -+ *p_specBuf++ = L'*'; -+ *p_specBuf++ = L'.'; -+ } else { -+ *p_specBuf++ = *p_spec; -+ } -+ } -+ *p_specBuf++ = L'\0'; -+ -+ // assert that we had allocated exactly the correct amount of memory that we used -+ assert(static_cast(p_specBuf - specBuf) == specSize); -+ -+ // save the buffer to the guard object -+ specList[index].pszSpec = specBuf; -+ } -+ } -+ -+ /* Add wildcard */ -+ specList[filterCount].pszName = L"All files"; -+ specList[filterCount].pszSpec = L"*.*"; -+ -+ // add the filter to the dialog -+ if (!SUCCEEDED(fileOpenDialog->SetFileTypes(filterCount + 1, specList))) { -+ NFDi_SetError("Failed to set the allowable file types for the drop-down menu."); -+ return NFD_ERROR; -+ } -+ -+ // automatic freeing of memory via COMDLG_FILTERSPEC_Guard -+ return NFD_OKAY; -+} -+ -+/* call after AddFiltersToDialog */ -+nfdresult_t SetDefaultExtension(::IFileDialog* fileOpenDialog, -+ const nfdnfilteritem_t* filterList, -+ nfdfiltersize_t filterCount) { -+ // if there are no filters, then don't set default extensions -+ if (!filterCount) { -+ return NFD_OKAY; -+ } -+ -+ assert(filterList); -+ -+ // set the first item as the default index, and set the default extension -+ if (!SUCCEEDED(fileOpenDialog->SetFileTypeIndex(1))) { -+ NFDi_SetError("Failed to set the selected file type index."); -+ return NFD_ERROR; -+ } -+ -+ // set the first item as the default file extension -+ const nfdnchar_t* p_spec = filterList[0].spec; -+ for (; *p_spec; ++p_spec) { -+ if (*p_spec == ',') { -+ break; -+ } -+ } -+ if (*p_spec) { -+ // multiple file extensions for this type (need to allocate memory) -+ size_t numChars = p_spec - filterList[0].spec; -+ // allocate one more char space for the '\0' -+ nfdnchar_t* extnBuf = NFDi_Malloc(sizeof(nfdnchar_t) * (numChars + 1)); -+ if (!extnBuf) { -+ return NFD_ERROR; -+ } -+ Free_Guard extnBufGuard(extnBuf); -+ -+ // copy the extension -+ for (size_t i = 0; i != numChars; ++i) { -+ extnBuf[i] = filterList[0].spec[i]; -+ } -+ // pad with trailing '\0' -+ extnBuf[numChars] = L'\0'; -+ -+ if (!SUCCEEDED(fileOpenDialog->SetDefaultExtension(extnBuf))) { -+ NFDi_SetError("Failed to set default extension."); -+ return NFD_ERROR; -+ } -+ } else { -+ // single file extension for this type (no need to allocate memory) -+ if (!SUCCEEDED(fileOpenDialog->SetDefaultExtension(filterList[0].spec))) { -+ NFDi_SetError("Failed to set default extension."); -+ return NFD_ERROR; -+ } -+ } -+ -+ return NFD_OKAY; -+} -+ -+nfdresult_t SetDefaultPath(IFileDialog* dialog, const nfdnchar_t* defaultPath) { -+ if (!defaultPath || !*defaultPath) return NFD_OKAY; -+ -+ IShellItem* folder; -+ HRESULT result = SHCreateItemFromParsingName(defaultPath, nullptr, IID_PPV_ARGS(&folder)); -+ -+ // Valid non results. -+ if (result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || -+ result == HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE)) { -+ return NFD_OKAY; -+ } -+ -+ if (!SUCCEEDED(result)) { -+ NFDi_SetError("Failed to create ShellItem for setting the default path."); -+ return NFD_ERROR; -+ } -+ -+ Release_Guard folderGuard(folder); -+ -+ // SetDefaultFolder() might use another recently used folder if available, so the user doesn't -+ // need to keep navigating back to the default folder (recommended by Windows). change to -+ // SetFolder() if you always want to use the default folder -+ if (!SUCCEEDED(dialog->SetDefaultFolder(folder))) { -+ NFDi_SetError("Failed to set default path."); -+ return NFD_ERROR; -+ } -+ -+ return NFD_OKAY; -+} -+ -+nfdresult_t SetDefaultName(IFileDialog* dialog, const nfdnchar_t* defaultName) { -+ if (!defaultName || !*defaultName) return NFD_OKAY; -+ -+ if (!SUCCEEDED(dialog->SetFileName(defaultName))) { -+ NFDi_SetError("Failed to set default file name."); -+ return NFD_ERROR; -+ } -+ -+ return NFD_OKAY; -+} -+ -+nfdresult_t AddOptions(IFileDialog* dialog, FILEOPENDIALOGOPTIONS options) { -+ FILEOPENDIALOGOPTIONS existingOptions; -+ if (!SUCCEEDED(dialog->GetOptions(&existingOptions))) { -+ NFDi_SetError("Failed to get options."); -+ return NFD_ERROR; -+ } -+ if (!SUCCEEDED(dialog->SetOptions(existingOptions | options))) { -+ NFDi_SetError("Failed to set options."); -+ return NFD_ERROR; -+ } -+ return NFD_OKAY; -+} -+} // namespace -+ -+const char* NFD_GetError(void) { -+ return g_errorstr; -+} -+ -+void NFD_ClearError(void) { -+ NFDi_SetError(nullptr); -+} -+ -+/* public */ -+ -+namespace { -+// The user might have initialized with COINIT_MULTITHREADED before, -+// in which case we will fail to do CoInitializeEx(), but file dialogs will still work. -+// See https://github.com/mlabbe/nativefiledialog/issues/72 for more information. -+bool needs_uninitialize; -+} // namespace -+ -+nfdresult_t NFD_Init(void) { -+ // Init COM library. -+ HRESULT result = -+ ::CoInitializeEx(nullptr, ::COINIT_APARTMENTTHREADED | ::COINIT_DISABLE_OLE1DDE); -+ -+ if (SUCCEEDED(result)) { -+ needs_uninitialize = true; -+ return NFD_OKAY; -+ } else if (result == RPC_E_CHANGED_MODE) { -+ // If this happens, the user already initialized COM using COINIT_MULTITHREADED, -+ // so COM will still work, but we shouldn't uninitialize it later. -+ needs_uninitialize = false; -+ return NFD_OKAY; -+ } else { -+ NFDi_SetError("Failed to initialize COM."); -+ return NFD_ERROR; -+ } -+} -+void NFD_Quit(void) { -+ if (needs_uninitialize) ::CoUninitialize(); -+} -+ -+void NFD_FreePathN(nfdnchar_t* filePath) { -+ assert(filePath); -+ ::CoTaskMemFree(filePath); -+} -+ -+nfdresult_t NFD_OpenDialogN(nfdnchar_t** outPath, -+ const nfdnfilteritem_t* filterList, -+ nfdfiltersize_t filterCount, -+ const nfdnchar_t* defaultPath) { -+ ::IFileOpenDialog* fileOpenDialog; -+ -+ // Create dialog -+ HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, -+ nullptr, -+ CLSCTX_ALL, -+ ::IID_IFileOpenDialog, -+ reinterpret_cast(&fileOpenDialog)); -+ -+ if (!SUCCEEDED(result)) { -+ NFDi_SetError("Could not create dialog."); -+ return NFD_ERROR; -+ } -+ -+ // make sure we remember to free the dialog -+ Release_Guard<::IFileOpenDialog> fileOpenDialogGuard(fileOpenDialog); -+ -+ // Build the filter list -+ if (!AddFiltersToDialog(fileOpenDialog, filterList, filterCount)) { -+ return NFD_ERROR; -+ } -+ -+ // Set auto-completed default extension -+ if (!SetDefaultExtension(fileOpenDialog, filterList, filterCount)) { -+ return NFD_ERROR; -+ } -+ -+ // Set the default path -+ if (!SetDefaultPath(fileOpenDialog, defaultPath)) { -+ return NFD_ERROR; -+ } -+ -+ // Only show file system items -+ if (!AddOptions(fileOpenDialog, ::FOS_FORCEFILESYSTEM)) { -+ return NFD_ERROR; -+ } -+ -+ // Show the dialog. -+ result = fileOpenDialog->Show(nullptr); -+ if (SUCCEEDED(result)) { -+ // Get the file name -+ ::IShellItem* psiResult; -+ result = fileOpenDialog->GetResult(&psiResult); -+ if (!SUCCEEDED(result)) { -+ NFDi_SetError("Could not get shell item from dialog."); -+ return NFD_ERROR; -+ } -+ Release_Guard<::IShellItem> psiResultGuard(psiResult); -+ -+ nfdnchar_t* filePath; -+ result = psiResult->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); -+ if (!SUCCEEDED(result)) { -+ NFDi_SetError("Could not get file path from shell item returned by dialog."); -+ return NFD_ERROR; -+ } -+ -+ *outPath = filePath; -+ -+ return NFD_OKAY; -+ } else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED)) { -+ return NFD_CANCEL; -+ } else { -+ NFDi_SetError("File dialog box show failed."); -+ return NFD_ERROR; -+ } -+} -+ -+nfdresult_t NFD_OpenDialogMultipleN(const nfdpathset_t** outPaths, -+ const nfdnfilteritem_t* filterList, -+ nfdfiltersize_t filterCount, -+ const nfdnchar_t* defaultPath) { -+ ::IFileOpenDialog* fileOpenDialog(nullptr); -+ -+ // Create dialog -+ HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, -+ nullptr, -+ CLSCTX_ALL, -+ ::IID_IFileOpenDialog, -+ reinterpret_cast(&fileOpenDialog)); -+ -+ if (!SUCCEEDED(result)) { -+ NFDi_SetError("Could not create dialog."); -+ return NFD_ERROR; -+ } -+ -+ // make sure we remember to free the dialog -+ Release_Guard<::IFileOpenDialog> fileOpenDialogGuard(fileOpenDialog); -+ -+ // Build the filter list -+ if (!AddFiltersToDialog(fileOpenDialog, filterList, filterCount)) { -+ return NFD_ERROR; -+ } -+ -+ // Set auto-completed default extension -+ if (!SetDefaultExtension(fileOpenDialog, filterList, filterCount)) { -+ return NFD_ERROR; -+ } -+ -+ // Set the default path -+ if (!SetDefaultPath(fileOpenDialog, defaultPath)) { -+ return NFD_ERROR; -+ } -+ -+ // Set a flag for multiple options and file system items only -+ if (!AddOptions(fileOpenDialog, ::FOS_FORCEFILESYSTEM | ::FOS_ALLOWMULTISELECT)) { -+ return NFD_ERROR; -+ } -+ -+ // Show the dialog. -+ result = fileOpenDialog->Show(nullptr); -+ if (SUCCEEDED(result)) { -+ ::IShellItemArray* shellItems; -+ result = fileOpenDialog->GetResults(&shellItems); -+ if (!SUCCEEDED(result)) { -+ NFDi_SetError("Could not get shell items."); -+ return NFD_ERROR; -+ } -+ -+ // save the path set to the output -+ *outPaths = static_cast(shellItems); -+ -+ return NFD_OKAY; -+ } else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED)) { -+ return NFD_CANCEL; -+ } else { -+ NFDi_SetError("File dialog box show failed."); -+ return NFD_ERROR; -+ } -+} -+ -+nfdresult_t NFD_SaveDialogN(nfdnchar_t** outPath, -+ const nfdnfilteritem_t* filterList, -+ nfdfiltersize_t filterCount, -+ const nfdnchar_t* defaultPath, -+ const nfdnchar_t* defaultName) { -+ ::IFileSaveDialog* fileSaveDialog; -+ -+ // Create dialog -+ HRESULT result = ::CoCreateInstance(::CLSID_FileSaveDialog, -+ nullptr, -+ CLSCTX_ALL, -+ ::IID_IFileSaveDialog, -+ reinterpret_cast(&fileSaveDialog)); -+ -+ if (!SUCCEEDED(result)) { -+ NFDi_SetError("Could not create dialog."); -+ return NFD_ERROR; -+ } -+ -+ // make sure we remember to free the dialog -+ Release_Guard<::IFileSaveDialog> fileSaveDialogGuard(fileSaveDialog); -+ -+ // Build the filter list -+ if (!AddFiltersToDialog(fileSaveDialog, filterList, filterCount)) { -+ return NFD_ERROR; -+ } -+ -+ // Set default extension -+ if (!SetDefaultExtension(fileSaveDialog, filterList, filterCount)) { -+ return NFD_ERROR; -+ } -+ -+ // Set the default path -+ if (!SetDefaultPath(fileSaveDialog, defaultPath)) { -+ return NFD_ERROR; -+ } -+ -+ // Set the default name -+ if (!SetDefaultName(fileSaveDialog, defaultName)) { -+ return NFD_ERROR; -+ } -+ -+ // Only show file system items -+ if (!AddOptions(fileSaveDialog, ::FOS_FORCEFILESYSTEM)) { -+ return NFD_ERROR; -+ } -+ -+ // Show the dialog. -+ result = fileSaveDialog->Show(nullptr); -+ if (SUCCEEDED(result)) { -+ // Get the file name -+ ::IShellItem* psiResult; -+ result = fileSaveDialog->GetResult(&psiResult); -+ if (!SUCCEEDED(result)) { -+ NFDi_SetError("Could not get shell item from dialog."); -+ return NFD_ERROR; -+ } -+ Release_Guard<::IShellItem> psiResultGuard(psiResult); -+ -+ nfdnchar_t* filePath; -+ result = psiResult->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); -+ if (!SUCCEEDED(result)) { -+ NFDi_SetError("Could not get file path from shell item returned by dialog."); -+ return NFD_ERROR; -+ } -+ -+ *outPath = filePath; -+ -+ return NFD_OKAY; -+ } else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED)) { -+ return NFD_CANCEL; -+ } else { -+ NFDi_SetError("File dialog box show failed."); -+ return NFD_ERROR; -+ } -+} -+ -+nfdresult_t NFD_PickFolderN(nfdnchar_t** outPath, const nfdnchar_t* defaultPath) { -+ ::IFileOpenDialog* fileOpenDialog; -+ -+ // Create dialog -+ if (!SUCCEEDED(::CoCreateInstance(::CLSID_FileOpenDialog, -+ nullptr, -+ CLSCTX_ALL, -+ ::IID_IFileOpenDialog, -+ reinterpret_cast(&fileOpenDialog)))) { -+ NFDi_SetError("Could not create dialog."); -+ return NFD_ERROR; -+ } -+ -+ Release_Guard<::IFileOpenDialog> fileOpenDialogGuard(fileOpenDialog); -+ -+ // Set the default path -+ if (!SetDefaultPath(fileOpenDialog, defaultPath)) { -+ return NFD_ERROR; -+ } -+ -+ // Only show items that are folders and on the file system -+ if (!AddOptions(fileOpenDialog, ::FOS_FORCEFILESYSTEM | ::FOS_PICKFOLDERS)) { -+ return NFD_ERROR; -+ } -+ -+ // Show the dialog to the user -+ const HRESULT result = fileOpenDialog->Show(nullptr); -+ if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED)) { -+ return NFD_CANCEL; -+ } else if (!SUCCEEDED(result)) { -+ NFDi_SetError("File dialog box show failed."); -+ return NFD_ERROR; -+ } -+ -+ // Get the shell item result -+ ::IShellItem* psiResult; -+ if (!SUCCEEDED(fileOpenDialog->GetResult(&psiResult))) { -+ return NFD_ERROR; -+ } -+ -+ Release_Guard<::IShellItem> psiResultGuard(psiResult); -+ -+ // Finally get the path -+ nfdnchar_t* filePath; -+ // Why are we not using SIGDN_FILESYSPATH? -+ if (!SUCCEEDED(psiResult->GetDisplayName(::SIGDN_DESKTOPABSOLUTEPARSING, &filePath))) { -+ NFDi_SetError("Could not get file path from shell item returned by dialog."); -+ return NFD_ERROR; -+ } -+ -+ *outPath = filePath; -+ -+ return NFD_OKAY; -+} -+ -+nfdresult_t NFD_PathSet_GetCount(const nfdpathset_t* pathSet, nfdpathsetsize_t* count) { -+ assert(pathSet); -+ // const_cast because methods on IShellItemArray aren't const, but it should act like const to -+ // the caller -+ ::IShellItemArray* psiaPathSet = -+ const_cast<::IShellItemArray*>(static_cast(pathSet)); -+ -+ DWORD numPaths; -+ if (!SUCCEEDED(psiaPathSet->GetCount(&numPaths))) { -+ NFDi_SetError("Could not get path count."); -+ return NFD_ERROR; -+ } -+ *count = numPaths; -+ return NFD_OKAY; -+} -+ -+nfdresult_t NFD_PathSet_GetPathN(const nfdpathset_t* pathSet, -+ nfdpathsetsize_t index, -+ nfdnchar_t** outPath) { -+ assert(pathSet); -+ // const_cast because methods on IShellItemArray aren't const, but it should act like const to -+ // the caller -+ ::IShellItemArray* psiaPathSet = -+ const_cast<::IShellItemArray*>(static_cast(pathSet)); -+ -+ ::IShellItem* psiPath; -+ if (!SUCCEEDED(psiaPathSet->GetItemAt(index, &psiPath))) { -+ NFDi_SetError("Could not get shell item."); -+ return NFD_ERROR; -+ } -+ -+ Release_Guard<::IShellItem> psiPathGuard(psiPath); -+ -+ nfdnchar_t* name; -+ if (!SUCCEEDED(psiPath->GetDisplayName(::SIGDN_FILESYSPATH, &name))) { -+ NFDi_SetError("Could not get file path from shell item."); -+ return NFD_ERROR; -+ } -+ -+ *outPath = name; -+ return NFD_OKAY; -+} -+ -+nfdresult_t NFD_PathSet_GetEnum(const nfdpathset_t* pathSet, nfdpathsetenum_t* outEnumerator) { -+ assert(pathSet); -+ // const_cast because methods on IShellItemArray aren't const, but it should act like const to -+ // the caller -+ ::IShellItemArray* psiaPathSet = -+ const_cast<::IShellItemArray*>(static_cast(pathSet)); -+ -+ ::IEnumShellItems* pesiPaths; -+ if (!SUCCEEDED(psiaPathSet->EnumItems(&pesiPaths))) { -+ NFDi_SetError("Could not get enumerator."); -+ return NFD_ERROR; -+ } -+ -+ outEnumerator->ptr = static_cast(pesiPaths); -+ return NFD_OKAY; -+} -+ -+void NFD_PathSet_FreeEnum(nfdpathsetenum_t* enumerator) { -+ assert(enumerator->ptr); -+ -+ ::IEnumShellItems* pesiPaths = static_cast<::IEnumShellItems*>(enumerator->ptr); -+ -+ // free the enumerator memory -+ pesiPaths->Release(); -+} -+ -+nfdresult_t NFD_PathSet_EnumNextN(nfdpathsetenum_t* enumerator, nfdnchar_t** outPath) { -+ assert(enumerator->ptr); -+ -+ ::IEnumShellItems* pesiPaths = static_cast<::IEnumShellItems*>(enumerator->ptr); -+ -+ ::IShellItem* psiPath; -+ HRESULT res = pesiPaths->Next(1, &psiPath, NULL); -+ if (!SUCCEEDED(res)) { -+ NFDi_SetError("Could not get next item of enumerator."); -+ return NFD_ERROR; -+ } -+ if (res != S_OK) { -+ *outPath = nullptr; -+ return NFD_OKAY; -+ } -+ -+ Release_Guard<::IShellItem> psiPathGuard(psiPath); -+ -+ nfdnchar_t* name; -+ if (!SUCCEEDED(psiPath->GetDisplayName(::SIGDN_FILESYSPATH, &name))) { -+ NFDi_SetError("Could not get file path from shell item."); -+ return NFD_ERROR; -+ } -+ -+ *outPath = name; -+ return NFD_OKAY; -+} -+ -+void NFD_PathSet_Free(const nfdpathset_t* pathSet) { -+ assert(pathSet); -+ // const_cast because methods on IShellItemArray aren't const, but it should act like const to -+ // the caller -+ ::IShellItemArray* psiaPathSet = -+ const_cast<::IShellItemArray*>(static_cast(pathSet)); -+ -+ // free the path set memory -+ psiaPathSet->Release(); -+} -+ -+namespace { -+// allocs the space in outStr -- call NFDi_Free() -+nfdresult_t CopyCharToWChar(const nfdu8char_t* inStr, nfdnchar_t*& outStr) { -+ int charsNeeded = MultiByteToWideChar(CP_UTF8, 0, inStr, -1, nullptr, 0); -+ assert(charsNeeded); -+ -+ nfdnchar_t* tmp_outStr = NFDi_Malloc(sizeof(nfdnchar_t) * charsNeeded); -+ if (!tmp_outStr) { -+ return NFD_ERROR; -+ } -+ -+ int ret = MultiByteToWideChar(CP_UTF8, 0, inStr, -1, tmp_outStr, charsNeeded); -+ assert(ret && ret == charsNeeded); -+ (void)ret; // prevent warning in release build -+ outStr = tmp_outStr; -+ return NFD_OKAY; -+} -+ -+// allocs the space in outPath -- call NFDi_Free() -+nfdresult_t CopyWCharToNFDChar(const nfdnchar_t* inStr, nfdu8char_t*& outStr) { -+ int bytesNeeded = WideCharToMultiByte(CP_UTF8, 0, inStr, -1, nullptr, 0, nullptr, nullptr); -+ assert(bytesNeeded); -+ -+ nfdu8char_t* tmp_outStr = NFDi_Malloc(sizeof(nfdu8char_t) * bytesNeeded); -+ if (!tmp_outStr) { -+ return NFD_ERROR; -+ } -+ -+ int ret = WideCharToMultiByte(CP_UTF8, 0, inStr, -1, tmp_outStr, bytesNeeded, nullptr, nullptr); -+ assert(ret && ret == bytesNeeded); -+ (void)ret; // prevent warning in release build -+ outStr = tmp_outStr; -+ return NFD_OKAY; -+} -+ -+struct FilterItem_Guard { -+ nfdnfilteritem_t* data; -+ nfdfiltersize_t index; -+ FilterItem_Guard() noexcept : data(nullptr), index(0) {} -+ ~FilterItem_Guard() { -+ assert(data || index == 0); -+ for (--index; index != static_cast(-1); --index) { -+ NFDi_Free(const_cast(data[index].spec)); -+ NFDi_Free(const_cast(data[index].name)); -+ } -+ if (data) NFDi_Free(data); -+ } -+}; -+ -+nfdresult_t CopyFilterItem(const nfdu8filteritem_t* filterList, -+ nfdfiltersize_t count, -+ FilterItem_Guard& filterItemsNGuard) { -+ if (count) { -+ nfdnfilteritem_t*& filterItemsN = filterItemsNGuard.data; -+ filterItemsN = NFDi_Malloc(sizeof(nfdnfilteritem_t) * count); -+ if (!filterItemsN) { -+ return NFD_ERROR; -+ } -+ -+ nfdfiltersize_t& index = filterItemsNGuard.index; -+ for (; index != count; ++index) { -+ nfdresult_t res = CopyCharToWChar(filterList[index].name, -+ const_cast(filterItemsN[index].name)); -+ if (!res) { -+ return NFD_ERROR; -+ } -+ res = CopyCharToWChar(filterList[index].spec, -+ const_cast(filterItemsN[index].spec)); -+ if (!res) { -+ // remember to free the name, because we also created it (and it won't be protected -+ // by the guard, because we have not incremented the index) -+ NFDi_Free(const_cast(filterItemsN[index].name)); -+ return NFD_ERROR; -+ } -+ } -+ } -+ return NFD_OKAY; -+} -+nfdresult_t ConvertU8ToNative(const nfdu8char_t* u8Text, FreeCheck_Guard& nativeText) { -+ if (u8Text) { -+ nfdresult_t res = CopyCharToWChar(u8Text, nativeText.data); -+ if (!res) { -+ return NFD_ERROR; -+ } -+ } -+ return NFD_OKAY; -+} -+void NormalizePathSeparator(nfdnchar_t* path) { -+ if (path) { -+ for (; *path; ++path) { -+ if (*path == L'/') *path = L'\\'; -+ } -+ } -+} -+} // namespace -+ -+void NFD_FreePathU8(nfdu8char_t* outPath) { -+ NFDi_Free(outPath); -+} -+ -+nfdresult_t NFD_OpenDialogU8(nfdu8char_t** outPath, -+ const nfdu8filteritem_t* filterList, -+ nfdfiltersize_t count, -+ const nfdu8char_t* defaultPath) { -+ // populate the real nfdnfilteritem_t -+ FilterItem_Guard filterItemsNGuard; -+ if (!CopyFilterItem(filterList, count, filterItemsNGuard)) { -+ return NFD_ERROR; -+ } -+ -+ // convert and normalize the default path, but only if it is not nullptr -+ FreeCheck_Guard defaultPathNGuard; -+ ConvertU8ToNative(defaultPath, defaultPathNGuard); -+ NormalizePathSeparator(defaultPathNGuard.data); -+ -+ // call the native function -+ nfdnchar_t* outPathN; -+ nfdresult_t res = -+ NFD_OpenDialogN(&outPathN, filterItemsNGuard.data, count, defaultPathNGuard.data); -+ -+ if (res != NFD_OKAY) { -+ return res; -+ } -+ -+ // convert the outPath to UTF-8 -+ res = CopyWCharToNFDChar(outPathN, *outPath); -+ -+ // free the native out path, and return the result -+ NFD_FreePathN(outPathN); -+ return res; -+} -+ -+/* multiple file open dialog */ -+/* It is the caller's responsibility to free `outPaths` via NFD_PathSet_Free() if this function -+ * returns NFD_OKAY */ -+nfdresult_t NFD_OpenDialogMultipleU8(const nfdpathset_t** outPaths, -+ const nfdu8filteritem_t* filterList, -+ nfdfiltersize_t count, -+ const nfdu8char_t* defaultPath) { -+ // populate the real nfdnfilteritem_t -+ FilterItem_Guard filterItemsNGuard; -+ if (!CopyFilterItem(filterList, count, filterItemsNGuard)) { -+ return NFD_ERROR; -+ } -+ -+ // convert and normalize the default path, but only if it is not nullptr -+ FreeCheck_Guard defaultPathNGuard; -+ ConvertU8ToNative(defaultPath, defaultPathNGuard); -+ NormalizePathSeparator(defaultPathNGuard.data); -+ -+ // call the native function -+ return NFD_OpenDialogMultipleN(outPaths, filterItemsNGuard.data, count, defaultPathNGuard.data); -+} -+ -+/* save dialog */ -+/* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function returns -+ * NFD_OKAY */ -+nfdresult_t NFD_SaveDialogU8(nfdu8char_t** outPath, -+ const nfdu8filteritem_t* filterList, -+ nfdfiltersize_t count, -+ const nfdu8char_t* defaultPath, -+ const nfdu8char_t* defaultName) { -+ // populate the real nfdnfilteritem_t -+ FilterItem_Guard filterItemsNGuard; -+ if (!CopyFilterItem(filterList, count, filterItemsNGuard)) { -+ return NFD_ERROR; -+ } -+ -+ // convert and normalize the default path, but only if it is not nullptr -+ FreeCheck_Guard defaultPathNGuard; -+ ConvertU8ToNative(defaultPath, defaultPathNGuard); -+ NormalizePathSeparator(defaultPathNGuard.data); -+ -+ // convert the default name, but only if it is not nullptr -+ FreeCheck_Guard defaultNameNGuard; -+ ConvertU8ToNative(defaultName, defaultNameNGuard); -+ -+ // call the native function -+ nfdnchar_t* outPathN; -+ nfdresult_t res = NFD_SaveDialogN( -+ &outPathN, filterItemsNGuard.data, count, defaultPathNGuard.data, defaultNameNGuard.data); -+ -+ if (res != NFD_OKAY) { -+ return res; -+ } -+ -+ // convert the outPath to UTF-8 -+ res = CopyWCharToNFDChar(outPathN, *outPath); -+ -+ // free the native out path, and return the result -+ NFD_FreePathN(outPathN); -+ return res; -+} -+ -+/* select folder dialog */ -+/* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function returns -+ * NFD_OKAY */ -+nfdresult_t NFD_PickFolderU8(nfdu8char_t** outPath, const nfdu8char_t* defaultPath) { -+ // convert and normalize the default path, but only if it is not nullptr -+ FreeCheck_Guard defaultPathNGuard; -+ ConvertU8ToNative(defaultPath, defaultPathNGuard); -+ NormalizePathSeparator(defaultPathNGuard.data); -+ -+ // call the native function -+ nfdnchar_t* outPathN; -+ nfdresult_t res = NFD_PickFolderN(&outPathN, defaultPathNGuard.data); -+ -+ if (res != NFD_OKAY) { -+ return res; -+ } -+ -+ // convert the outPath to UTF-8 -+ res = CopyWCharToNFDChar(outPathN, *outPath); -+ -+ // free the native out path, and return the result -+ NFD_FreePathN(outPathN); -+ return res; -+} -+ -+/* Get the UTF-8 path at offset index */ -+/* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function returns -+ * NFD_OKAY */ -+nfdresult_t NFD_PathSet_GetPathU8(const nfdpathset_t* pathSet, -+ nfdpathsetsize_t index, -+ nfdu8char_t** outPath) { -+ // call the native function -+ nfdnchar_t* outPathN; -+ nfdresult_t res = NFD_PathSet_GetPathN(pathSet, index, &outPathN); -+ -+ if (res != NFD_OKAY) { -+ return res; -+ } -+ -+ // convert the outPath to UTF-8 -+ res = CopyWCharToNFDChar(outPathN, *outPath); -+ -+ // free the native out path, and return the result -+ NFD_FreePathN(outPathN); -+ return res; -+} -+ -+nfdresult_t NFD_PathSet_EnumNextU8(nfdpathsetenum_t* enumerator, nfdu8char_t** outPath) { -+ // call the native function -+ nfdnchar_t* outPathN; -+ nfdresult_t res = NFD_PathSet_EnumNextN(enumerator, &outPathN); -+ -+ if (res != NFD_OKAY) { -+ return res; -+ } -+ -+ if (outPathN) { -+ // convert the outPath to UTF-8 -+ res = CopyWCharToNFDChar(outPathN, *outPath); -+ -+ // free the native out path, and return the result -+ NFD_FreePathN(outPathN); -+ } else { -+ *outPath = nullptr; -+ res = NFD_OKAY; -+ } -+ -+ return res; -+} diff --git a/T/TracyProfiler/bundled/patches/TracyProfiler-no-divide-zero.patch b/T/TracyProfiler/bundled/patches/TracyProfiler-no-divide-zero.patch deleted file mode 100644 index 5065ee1afe9..00000000000 --- a/T/TracyProfiler/bundled/patches/TracyProfiler-no-divide-zero.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/csvexport/src/csvexport.cpp b/csvexport/src/csvexport.cpp -index c0654c44..1ce0478a 100644 ---- a/csvexport/src/csvexport.cpp -+++ b/csvexport/src/csvexport.cpp -@@ -299,7 +299,9 @@ int main(int argc, char** argv) - const auto ss = zone_data.sumSq - - 2. * zone_data.total * avg - + avg * avg * sz; -- const auto std = sqrt(ss / (sz - 1)); -+ double std = 0; -+ if( sz > 1 ) -+ std = sqrt(ss / (sz - 1)); - values[9] = std::to_string(std); - - std::string row = join(values, args.separator); diff --git a/T/TracyProfiler/bundled/patches/TracyProfiler-rr-nopl-seq.patch b/T/TracyProfiler/bundled/patches/TracyProfiler-rr-nopl-seq.patch deleted file mode 100644 index 8ecd9193bf3..00000000000 --- a/T/TracyProfiler/bundled/patches/TracyProfiler-rr-nopl-seq.patch +++ /dev/null @@ -1,43 +0,0 @@ -commit 21a65e08372a37342e598422a2bd58263857807e -Author: Keno Fischer -Date: Sat Aug 19 01:40:18 2023 +0000 - - Use patchable rdtsc sequence to avoid slowdowns under rr - - We (Julia) ship both support for using tracy to trace julia applications, - as well as using `rr` (https://github.com/rr-debugger/rr) for record-replay debugging. - After our most recent rebuild of tracy, users have been reporting signfificant performance - slowdowns when `rr` recording a session that happens to also load the tracy library - (even if tracing is not enabled). Upon further examination, the recompile happened - to trigger a protective heuristic that disabled rr's patching of tracy's use of - `rdtsc` because an earlier part of the same function happened to look like a - conditional branch into the patch region. See https://github.com/rr-debugger/rr/pull/3580 - for details. To avoid this issue occurring again in future rebuilds of tracy, - adjust tracy's `rdtsc` sequence to be `nopl; rdtsc`, which (as of of the - linked PR) is a sequence that is guaranteed to bypass this heuristic - and not incur the additional overhead when run under rr. - -diff --git a/public/client/TracyProfiler.hpp b/public/client/TracyProfiler.hpp -index 1b825ea3..27f6bbbd 100644 ---- a/public/client/TracyProfiler.hpp -+++ b/public/client/TracyProfiler.hpp -@@ -209,7 +209,18 @@ public: - if( HardwareSupportsInvariantTSC() ) - { - uint64_t rax, rdx; -- asm volatile ( "rdtsc" : "=a" (rax), "=d" (rdx) ); -+ // Some external tooling (such as rr) wants to patch our rdtsc and replace it by a -+ // branch to control the external input seen by a program. This kind of patching is -+ // not generally possible depending on the surrounding code and can lead to significant -+ // slowdowns if the compiler generated unlucky code and rr and tracy are used together. -+ // To avoid this, use the rr-safe `nopl 0(%rax, %rax, 1); rdtsc` instruction sequence, -+ // which rr promises will be patchable independent of the surrounding code. -+ asm volatile ( -+ // This is nopl 0(%rax, %rax, 1), but assemblers are inconsistent about whether -+ // they emit that as a 4 or 5 byte sequence and we need to be guaranteed to use -+ // the 5 byte one. -+ ".byte 0x0f, 0x1f, 0x44, 0x00, 0x00\n\t" -+ "rdtsc" : "=a" (rax), "=d" (rdx) ); - return (int64_t)(( rdx << 32 ) + rax); - } - # else