From cd269730eb4aa0518affc7b33b780d30e9a3ef1b Mon Sep 17 00:00:00 2001
From: Semphris <[EMAIL REDACTED]>
Date: Thu, 16 Jan 2025 18:09:45 -0500
Subject: [PATCH] Fix Windows dialog memory management
---
src/dialog/windows/SDL_windowsdialog.c | 163 ++++++++++++++++---------
1 file changed, 102 insertions(+), 61 deletions(-)
diff --git a/src/dialog/windows/SDL_windowsdialog.c b/src/dialog/windows/SDL_windowsdialog.c
index dbfac920e9c90..06a9b31c04788 100644
--- a/src/dialog/windows/SDL_windowsdialog.c
+++ b/src/dialog/windows/SDL_windowsdialog.c
@@ -34,29 +34,49 @@
typedef struct
{
bool is_save;
- const SDL_DialogFileFilter *filters;
- int nfilters;
- const char* default_file;
+ wchar_t *filters_str;
+ char* default_file;
SDL_Window* parent;
DWORD flags;
SDL_DialogFileCallback callback;
void* userdata;
- const char* title;
- const char* accept;
- const char* cancel;
+ char* title;
+ char* accept;
+ char* cancel;
} winArgs;
typedef struct
{
SDL_Window* parent;
SDL_DialogFileCallback callback;
- const char* default_folder;
+ char* default_folder;
void* userdata;
- const char* title;
- const char* accept;
- const char* cancel;
+ 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)
{
@@ -86,14 +106,13 @@ void windows_ShowFileDialog(void *ptr)
{
winArgs *args = (winArgs *) ptr;
bool is_save = args->is_save;
- const SDL_DialogFileFilter *filters = args->filters;
- int nfilters = args->nfilters;
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) */
@@ -109,18 +128,21 @@ void windows_ShowFileDialog(void *ptr)
} else {
SDL_SetError("Couldn't load Comdlg32.dll");
callback(userdata, NULL, -1);
+ freeWinArgs(args);
return;
}
if (!pGetAnyFileName) {
SDL_SetError("Couldn't load GetOpenFileName/GetSaveFileName from library");
callback(userdata, NULL, -1);
+ freeWinArgs(args);
return;
}
if (!pCommDlgExtendedError) {
SDL_SetError("Couldn't load CommDlgExtendedError from library");
callback(userdata, NULL, -1);
+ freeWinArgs(args);
return;
}
@@ -174,43 +196,6 @@ void windows_ShowFileDialog(void *ptr)
}
}
- 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) {
- callback(userdata, NULL, -1);
- SDL_free(filebuffer);
- return;
- }
-
- 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);
- callback(userdata, NULL, -1);
- SDL_free(filebuffer);
- return;
- }
-
- MultiByteToWideChar(CP_UTF8, 0, filterlist, filter_len, filter_wchar, filter_wlen);
-
- SDL_free(filterlist);
- }
-
wchar_t *title_w = NULL;
if (title) {
@@ -230,9 +215,9 @@ void windows_ShowFileDialog(void *ptr)
title_w = (wchar_t *)SDL_malloc(title_wlen * sizeof(wchar_t));
if (!title_w) {
- SDL_free(filter_wchar);
SDL_free(filebuffer);
callback(userdata, NULL, -1);
+ freeWinArgs(args);
return;
}
@@ -264,7 +249,6 @@ void windows_ShowFileDialog(void *ptr)
BOOL result = pGetAnyFileName(&dialog);
- SDL_free(filter_wchar);
SDL_free(title_w);
if (result) {
@@ -292,6 +276,7 @@ void windows_ShowFileDialog(void *ptr)
if (!chosen_files_list) {
callback(userdata, NULL, -1);
SDL_free(filebuffer);
+ freeWinArgs(args);
return;
}
@@ -302,6 +287,7 @@ void windows_ShowFileDialog(void *ptr)
SDL_free(chosen_files_list);
callback(userdata, NULL, -1);
SDL_free(filebuffer);
+ freeWinArgs(args);
return;
}
@@ -323,6 +309,7 @@ void windows_ShowFileDialog(void *ptr)
SDL_free(chosen_files_list);
callback(userdata, NULL, -1);
SDL_free(filebuffer);
+ freeWinArgs(args);
return;
}
@@ -341,6 +328,7 @@ void windows_ShowFileDialog(void *ptr)
SDL_free(chosen_files_list);
callback(userdata, NULL, -1);
SDL_free(filebuffer);
+ freeWinArgs(args);
return;
}
@@ -356,6 +344,7 @@ void windows_ShowFileDialog(void *ptr)
SDL_free(chosen_files_list);
callback(userdata, NULL, -1);
SDL_free(filebuffer);
+ freeWinArgs(args);
return;
}
}
@@ -369,6 +358,7 @@ void windows_ShowFileDialog(void *ptr)
SDL_free(chosen_files_list);
callback(userdata, NULL, -1);
SDL_free(filebuffer);
+ freeWinArgs(args);
return;
}
@@ -380,6 +370,7 @@ void windows_ShowFileDialog(void *ptr)
SDL_free(chosen_files_list);
callback(userdata, NULL, -1);
SDL_free(filebuffer);
+ freeWinArgs(args);
return;
}
}
@@ -409,6 +400,7 @@ void windows_ShowFileDialog(void *ptr)
}
SDL_free(filebuffer);
+ freeWinArgs(args);
}
int windows_file_dialog_thread(void* ptr)
@@ -469,6 +461,7 @@ void windows_ShowFolderDialog(void* ptr)
if (!title_w) {
callback(userdata, NULL, -1);
+ freeWinFArgs(args);
return;
}
@@ -501,6 +494,8 @@ void windows_ShowFolderDialog(void* ptr)
const char *files[1] = { NULL };
callback(userdata, (const char * const*) files, -1);
}
+
+ freeWinFArgs(args);
}
int windows_folder_dialog_thread(void* ptr)
@@ -510,10 +505,49 @@ int windows_folder_dialog_thread(void* 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");
@@ -527,17 +561,24 @@ static void ShowFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_
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 = filters;
- args->nfilters = nfilters;
- args->default_file = default_location;
+ 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;
- args->accept = accept;
- args->cancel = cancel;
+ 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);
@@ -569,11 +610,11 @@ void ShowFolderDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Windo
args->parent = window;
args->callback = callback;
- args->default_folder = default_location;
+ args->default_folder = default_location ? SDL_strdup(default_location) : NULL;
args->userdata = userdata;
- args->title = title;
- args->accept = accept;
- args->cancel = cancel;
+ 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);