From 30f41c02aec763d32e62351452da9ef582bc3472 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 6 Mar 2026 13:30:59 -0800 Subject: Move contrib libraries to contrib repo --- .../src/dialog/windows/SDL_windowsdialog.c | 611 --------------------- 1 file changed, 611 deletions(-) delete mode 100644 contrib/SDL-3.2.8/src/dialog/windows/SDL_windowsdialog.c (limited to 'contrib/SDL-3.2.8/src/dialog/windows/SDL_windowsdialog.c') diff --git a/contrib/SDL-3.2.8/src/dialog/windows/SDL_windowsdialog.c b/contrib/SDL-3.2.8/src/dialog/windows/SDL_windowsdialog.c deleted file mode 100644 index 2de224f..0000000 --- a/contrib/SDL-3.2.8/src/dialog/windows/SDL_windowsdialog.c +++ /dev/null @@ -1,611 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" -#include "../SDL_dialog.h" -#include "../SDL_dialog_utils.h" - -#include -#include -#include -#include "../../core/windows/SDL_windows.h" -#include "../../thread/SDL_systhread.h" - -// If this number is too small, selecting too many files will give an error -#define SELECTLIST_SIZE 65536 - -typedef struct -{ - bool is_save; - wchar_t *filters_str; - char *default_file; - SDL_Window *parent; - DWORD flags; - SDL_DialogFileCallback callback; - void *userdata; - char *title; - char *accept; - char *cancel; -} winArgs; - -typedef struct -{ - SDL_Window *parent; - SDL_DialogFileCallback callback; - char *default_folder; - void *userdata; - char *title; - char *accept; - char *cancel; -} winFArgs; - -void freeWinArgs(winArgs *args) -{ - SDL_free(args->default_file); - SDL_free(args->filters_str); - SDL_free(args->title); - SDL_free(args->accept); - SDL_free(args->cancel); - - SDL_free(args); -} - -void freeWinFArgs(winFArgs *args) -{ - SDL_free(args->default_folder); - SDL_free(args->title); - SDL_free(args->accept); - SDL_free(args->cancel); - - SDL_free(args); -} - -/** Converts dialog.nFilterIndex to SDL-compatible value */ -int getFilterIndex(int as_reported_by_windows) -{ - return as_reported_by_windows - 1; -} - -char *clear_filt_names(const char *filt) -{ - char *cleared = SDL_strdup(filt); - - for (char *c = cleared; *c; c++) { - /* 0x01 bytes are used as temporary replacement for the various 0x00 - bytes required by Win32 (one null byte between each filter, two at - the end of the filters). Filter out these bytes from the filter names - to avoid early-ending the filters if someone puts two consecutive - 0x01 bytes in their filter names. */ - if (*c == '\x01') { - *c = ' '; - } - } - - return cleared; -} - -// TODO: The new version of file dialogs -void windows_ShowFileDialog(void *ptr) -{ - winArgs *args = (winArgs *) ptr; - bool is_save = args->is_save; - const char *default_file = args->default_file; - SDL_Window *parent = args->parent; - DWORD flags = args->flags; - SDL_DialogFileCallback callback = args->callback; - void *userdata = args->userdata; - const char *title = args->title; - wchar_t *filter_wchar = args->filters_str; - - /* GetOpenFileName and GetSaveFileName have the same signature - (yes, LPOPENFILENAMEW even for the save dialog) */ - typedef BOOL (WINAPI *pfnGetAnyFileNameW)(LPOPENFILENAMEW); - typedef DWORD (WINAPI *pfnCommDlgExtendedError)(void); - HMODULE lib = LoadLibraryW(L"Comdlg32.dll"); - pfnGetAnyFileNameW pGetAnyFileName = NULL; - pfnCommDlgExtendedError pCommDlgExtendedError = NULL; - - if (lib) { - pGetAnyFileName = (pfnGetAnyFileNameW) GetProcAddress(lib, is_save ? "GetSaveFileNameW" : "GetOpenFileNameW"); - pCommDlgExtendedError = (pfnCommDlgExtendedError) GetProcAddress(lib, "CommDlgExtendedError"); - } else { - SDL_SetError("Couldn't load Comdlg32.dll"); - callback(userdata, NULL, -1); - return; - } - - if (!pGetAnyFileName) { - SDL_SetError("Couldn't load GetOpenFileName/GetSaveFileName from library"); - callback(userdata, NULL, -1); - return; - } - - if (!pCommDlgExtendedError) { - SDL_SetError("Couldn't load CommDlgExtendedError from library"); - callback(userdata, NULL, -1); - return; - } - - HWND window = NULL; - - if (parent) { - window = (HWND) SDL_GetPointerProperty(SDL_GetWindowProperties(parent), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); - } - - wchar_t *filebuffer; // lpstrFile - wchar_t initfolder[MAX_PATH] = L""; // lpstrInitialDir - - /* If SELECTLIST_SIZE is too large, putting filebuffer on the stack might - cause an overflow */ - filebuffer = (wchar_t *) SDL_malloc(SELECTLIST_SIZE * sizeof(wchar_t)); - - // Necessary for the return code below - SDL_memset(filebuffer, 0, SELECTLIST_SIZE * sizeof(wchar_t)); - - if (default_file) { - /* On Windows 10, 11 and possibly others, lpstrFile can be initialized - with a path and the dialog will start at that location, but *only if - the path contains a filename*. If it ends with a folder (directory - separator), it fails with 0x3002 (12290) FNERR_INVALIDFILENAME. For - that specific case, lpstrInitialDir must be used instead, but just - for that case, because lpstrInitialDir doesn't support file names. - - On top of that, lpstrInitialDir hides a special algorithm that - decides which folder to actually use as starting point, which may or - may not be the one provided, or some other unrelated folder. Also, - the algorithm changes between platforms. Assuming the documentation - is correct, the algorithm is there under 'lpstrInitialDir': - - https://learn.microsoft.com/en-us/windows/win32/api/commdlg/ns-commdlg-openfilenamew - - Finally, lpstrFile does not support forward slashes. lpstrInitialDir - does, though. */ - - char last_c = default_file[SDL_strlen(default_file) - 1]; - - if (last_c == '\\' || last_c == '/') { - MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, default_file, -1, initfolder, MAX_PATH); - } else { - MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, default_file, -1, filebuffer, MAX_PATH); - - for (int i = 0; i < SELECTLIST_SIZE; i++) { - if (filebuffer[i] == L'/') { - filebuffer[i] = L'\\'; - } - } - } - } - - wchar_t *title_w = NULL; - - if (title) { - title_w = WIN_UTF8ToStringW(title); - if (!title_w) { - SDL_free(filebuffer); - callback(userdata, NULL, -1); - return; - } - } - - OPENFILENAMEW dialog; - dialog.lStructSize = sizeof(OPENFILENAME); - dialog.hwndOwner = window; - dialog.hInstance = 0; - dialog.lpstrFilter = filter_wchar; - dialog.lpstrCustomFilter = NULL; - dialog.nMaxCustFilter = 0; - dialog.nFilterIndex = 0; - dialog.lpstrFile = filebuffer; - dialog.nMaxFile = SELECTLIST_SIZE; - dialog.lpstrFileTitle = NULL; - dialog.lpstrInitialDir = *initfolder ? initfolder : NULL; - dialog.lpstrTitle = title_w; - dialog.Flags = flags | OFN_EXPLORER | OFN_HIDEREADONLY | OFN_NOCHANGEDIR; - dialog.nFileOffset = 0; - dialog.nFileExtension = 0; - dialog.lpstrDefExt = NULL; - dialog.lCustData = 0; - dialog.lpfnHook = NULL; - dialog.lpTemplateName = NULL; - // Skipped many mac-exclusive and reserved members - dialog.FlagsEx = 0; - - BOOL result = pGetAnyFileName(&dialog); - - SDL_free(title_w); - - if (result) { - if (!(flags & OFN_ALLOWMULTISELECT)) { - // File is a C string stored in dialog.lpstrFile - char *chosen_file = WIN_StringToUTF8W(dialog.lpstrFile); - const char *opts[2] = { chosen_file, NULL }; - callback(userdata, opts, getFilterIndex(dialog.nFilterIndex)); - SDL_free(chosen_file); - } else { - /* File is either a C string if the user chose a single file, else - it's a series of strings formatted like: - - "C:\\path\\to\\folder\0filename1.ext\0filename2.ext\0\0" - - The code below will only stop on a double NULL in all cases, so - it is important that the rest of the buffer has been zeroed. */ - char chosen_folder[MAX_PATH]; - char chosen_file[MAX_PATH]; - wchar_t *file_ptr = dialog.lpstrFile; - size_t nfiles = 0; - size_t chosen_folder_size; - char **chosen_files_list = (char **) SDL_malloc(sizeof(char *) * (nfiles + 1)); - - if (!chosen_files_list) { - callback(userdata, NULL, -1); - SDL_free(filebuffer); - return; - } - - chosen_files_list[nfiles] = NULL; - - if (WideCharToMultiByte(CP_UTF8, 0, file_ptr, -1, chosen_folder, MAX_PATH, NULL, NULL) >= MAX_PATH) { - SDL_SetError("Path too long or invalid character in path"); - SDL_free(chosen_files_list); - callback(userdata, NULL, -1); - SDL_free(filebuffer); - return; - } - - chosen_folder_size = SDL_strlen(chosen_folder); - SDL_strlcpy(chosen_file, chosen_folder, MAX_PATH); - chosen_file[chosen_folder_size] = '\\'; - - file_ptr += SDL_strlen(chosen_folder) + 1; - - while (*file_ptr) { - nfiles++; - char **new_cfl = (char **) SDL_realloc(chosen_files_list, sizeof(char*) * (nfiles + 1)); - - if (!new_cfl) { - for (size_t i = 0; i < nfiles - 1; i++) { - SDL_free(chosen_files_list[i]); - } - - SDL_free(chosen_files_list); - callback(userdata, NULL, -1); - SDL_free(filebuffer); - return; - } - - chosen_files_list = new_cfl; - chosen_files_list[nfiles] = NULL; - - int diff = ((int) chosen_folder_size) + 1; - - if (WideCharToMultiByte(CP_UTF8, 0, file_ptr, -1, chosen_file + diff, MAX_PATH - diff, NULL, NULL) >= MAX_PATH - diff) { - SDL_SetError("Path too long or invalid character in path"); - - for (size_t i = 0; i < nfiles - 1; i++) { - SDL_free(chosen_files_list[i]); - } - - SDL_free(chosen_files_list); - callback(userdata, NULL, -1); - SDL_free(filebuffer); - return; - } - - file_ptr += SDL_strlen(chosen_file) + 1 - diff; - - chosen_files_list[nfiles - 1] = SDL_strdup(chosen_file); - - if (!chosen_files_list[nfiles - 1]) { - for (size_t i = 0; i < nfiles - 1; i++) { - SDL_free(chosen_files_list[i]); - } - - SDL_free(chosen_files_list); - callback(userdata, NULL, -1); - SDL_free(filebuffer); - return; - } - } - - // If the user chose only one file, it's all just one string - if (nfiles == 0) { - nfiles++; - char **new_cfl = (char **) SDL_realloc(chosen_files_list, sizeof(char*) * (nfiles + 1)); - - if (!new_cfl) { - SDL_free(chosen_files_list); - callback(userdata, NULL, -1); - SDL_free(filebuffer); - return; - } - - chosen_files_list = new_cfl; - chosen_files_list[nfiles] = NULL; - chosen_files_list[nfiles - 1] = SDL_strdup(chosen_folder); - - if (!chosen_files_list[nfiles - 1]) { - SDL_free(chosen_files_list); - callback(userdata, NULL, -1); - SDL_free(filebuffer); - return; - } - } - - callback(userdata, (const char * const*) chosen_files_list, getFilterIndex(dialog.nFilterIndex)); - - for (size_t i = 0; i < nfiles; i++) { - SDL_free(chosen_files_list[i]); - } - - SDL_free(chosen_files_list); - } - } else { - DWORD error = pCommDlgExtendedError(); - // Error code 0 means the user clicked the cancel button. - if (error == 0) { - /* Unlike SDL's handling of errors, Windows does reset the error - code to 0 after calling GetOpenFileName if another Windows - function before set a different error code, so it's safe to - check for success. */ - const char *opts[1] = { NULL }; - callback(userdata, opts, getFilterIndex(dialog.nFilterIndex)); - } else { - SDL_SetError("Windows error, CommDlgExtendedError: %ld", pCommDlgExtendedError()); - callback(userdata, NULL, -1); - } - } - - SDL_free(filebuffer); -} - -int windows_file_dialog_thread(void *ptr) -{ - windows_ShowFileDialog(ptr); - freeWinArgs(ptr); - return 0; -} - -int CALLBACK browse_callback_proc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) -{ - switch (uMsg) { - case BFFM_INITIALIZED: - if (lpData) { - SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData); - } - break; - case BFFM_SELCHANGED: - break; - case BFFM_VALIDATEFAILED: - break; - default: - break; - } - return 0; -} - -void windows_ShowFolderDialog(void *ptr) -{ - winFArgs *args = (winFArgs *) ptr; - SDL_Window *window = args->parent; - SDL_DialogFileCallback callback = args->callback; - void *userdata = args->userdata; - HWND parent = NULL; - const char *title = args->title; - - if (window) { - parent = (HWND) SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); - } - - wchar_t *title_w = NULL; - - if (title) { - title_w = WIN_UTF8ToStringW(title); - if (!title_w) { - callback(userdata, NULL, -1); - return; - } - } - - wchar_t buffer[MAX_PATH]; - - BROWSEINFOW dialog; - dialog.hwndOwner = parent; - dialog.pidlRoot = NULL; - dialog.pszDisplayName = buffer; - dialog.lpszTitle = title_w; - dialog.ulFlags = BIF_USENEWUI; - dialog.lpfn = browse_callback_proc; - dialog.lParam = (LPARAM)args->default_folder; - dialog.iImage = 0; - - LPITEMIDLIST lpItem = SHBrowseForFolderW(&dialog); - - SDL_free(title_w); - - if (lpItem != NULL) { - SHGetPathFromIDListW(lpItem, buffer); - char *chosen_file = WIN_StringToUTF8W(buffer); - const char *files[2] = { chosen_file, NULL }; - callback(userdata, (const char * const*) files, -1); - SDL_free(chosen_file); - } else { - const char *files[1] = { NULL }; - callback(userdata, (const char * const*) files, -1); - } -} - -int windows_folder_dialog_thread(void *ptr) -{ - windows_ShowFolderDialog(ptr); - freeWinFArgs((winFArgs *)ptr); - return 0; -} - -wchar_t *win_get_filters(const SDL_DialogFileFilter *filters, int nfilters) -{ - wchar_t *filter_wchar = NULL; - - if (filters) { - // '\x01' is used in place of a null byte - // suffix needs two null bytes in case the filter list is empty - char *filterlist = convert_filters(filters, nfilters, clear_filt_names, - "", "", "\x01\x01", "", "\x01", - "\x01", "*.", ";*.", ""); - - if (!filterlist) { - return NULL; - } - - int filter_len = (int)SDL_strlen(filterlist); - - for (char *c = filterlist; *c; c++) { - if (*c == '\x01') { - *c = '\0'; - } - } - - int filter_wlen = MultiByteToWideChar(CP_UTF8, 0, filterlist, filter_len, NULL, 0); - filter_wchar = (wchar_t *)SDL_malloc(filter_wlen * sizeof(wchar_t)); - if (!filter_wchar) { - SDL_free(filterlist); - return NULL; - } - - MultiByteToWideChar(CP_UTF8, 0, filterlist, filter_len, filter_wchar, filter_wlen); - - SDL_free(filterlist); - } - - return filter_wchar; -} - -static void ShowFileDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const SDL_DialogFileFilter *filters, int nfilters, const char *default_location, bool allow_many, bool is_save, const char *title, const char *accept, const char *cancel) -{ - winArgs *args; - SDL_Thread *thread; - wchar_t *filters_str; - - if (SDL_GetHint(SDL_HINT_FILE_DIALOG_DRIVER) != NULL) { - SDL_SetError("File dialog driver unsupported"); - callback(userdata, NULL, -1); - return; - } - - args = (winArgs *)SDL_malloc(sizeof(*args)); - if (args == NULL) { - callback(userdata, NULL, -1); - return; - } - - filters_str = win_get_filters(filters, nfilters); - - if (!filters_str && filters) { - callback(userdata, NULL, -1); - SDL_free(args); - return; - } - - args->is_save = is_save; - args->filters_str = filters_str; - args->default_file = default_location ? SDL_strdup(default_location) : NULL; - args->parent = window; - args->flags = allow_many ? OFN_ALLOWMULTISELECT : 0; - args->callback = callback; - args->userdata = userdata; - args->title = title ? SDL_strdup(title) : NULL; - args->accept = accept ? SDL_strdup(accept) : NULL; - args->cancel = cancel ? SDL_strdup(cancel) : NULL; - - thread = SDL_CreateThread(windows_file_dialog_thread, "SDL_Windows_ShowFileDialog", (void *) args); - - if (thread == NULL) { - callback(userdata, NULL, -1); - // The thread won't have run, therefore the data won't have been freed - freeWinArgs(args); - return; - } - - SDL_DetachThread(thread); -} - -void ShowFolderDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const char *default_location, bool allow_many, const char *title, const char *accept, const char *cancel) -{ - winFArgs *args; - SDL_Thread *thread; - - if (SDL_GetHint(SDL_HINT_FILE_DIALOG_DRIVER) != NULL) { - SDL_SetError("File dialog driver unsupported"); - callback(userdata, NULL, -1); - return; - } - - args = (winFArgs *)SDL_malloc(sizeof(*args)); - if (args == NULL) { - callback(userdata, NULL, -1); - return; - } - - args->parent = window; - args->callback = callback; - args->default_folder = default_location ? SDL_strdup(default_location) : NULL; - args->userdata = userdata; - args->title = title ? SDL_strdup(title) : NULL; - args->accept = accept ? SDL_strdup(accept) : NULL; - args->cancel = cancel ? SDL_strdup(cancel) : NULL; - - thread = SDL_CreateThread(windows_folder_dialog_thread, "SDL_Windows_ShowFolderDialog", (void *) args); - - if (thread == NULL) { - callback(userdata, NULL, -1); - // The thread won't have run, therefore the data won't have been freed - freeWinFArgs(args); - return; - } - - SDL_DetachThread(thread); -} - -void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props) -{ - /* The internal functions will start threads, and the properties may be freed as soon as this function returns. - Save a copy of what we need before invoking the functions and starting the threads. */ - SDL_Window *window = SDL_GetPointerProperty(props, SDL_PROP_FILE_DIALOG_WINDOW_POINTER, NULL); - SDL_DialogFileFilter *filters = SDL_GetPointerProperty(props, SDL_PROP_FILE_DIALOG_FILTERS_POINTER, NULL); - int nfilters = (int) SDL_GetNumberProperty(props, SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER, 0); - bool allow_many = SDL_GetBooleanProperty(props, SDL_PROP_FILE_DIALOG_MANY_BOOLEAN, false); - const char *default_location = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_LOCATION_STRING, NULL); - const char *title = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_TITLE_STRING, NULL); - const char *accept = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_ACCEPT_STRING, NULL); - const char *cancel = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_CANCEL_STRING, NULL); - bool is_save = false; - - switch (type) { - case SDL_FILEDIALOG_SAVEFILE: - is_save = true; - SDL_FALLTHROUGH; - case SDL_FILEDIALOG_OPENFILE: - ShowFileDialog(callback, userdata, window, filters, nfilters, default_location, allow_many, is_save, title, accept, cancel); - break; - - case SDL_FILEDIALOG_OPENFOLDER: - ShowFolderDialog(callback, userdata, window, default_location, allow_many, title, accept, cancel); - break; - }; -} -- cgit v1.2.3