Skip to content

Commit 29f86b7

Browse files
committed
dialog: Fix save file chooser with xdg portal
This correctly sets the xdg portal fields for targeting a specific new filename or existing file. "current_name" sets the dialogs placeholder name. "current_file" targets an existing file. "current_folder" for when the target is a folder.
1 parent 67e5130 commit 29f86b7

File tree

2 files changed

+83
-9
lines changed

2 files changed

+83
-9
lines changed

src/dialog/unix/SDL_portaldialog.c

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#ifdef SDL_USE_LIBDBUS
2727

2828
#include <errno.h>
29+
#include <libgen.h>
30+
#include <sys/stat.h>
2931
#include <sys/types.h>
3032
#include <sys/wait.h>
3133
#include <unistd.h>
@@ -294,7 +296,12 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog
294296
bool allow_many = SDL_GetBooleanProperty(props, SDL_PROP_FILE_DIALOG_MANY_BOOLEAN, false);
295297
const char *default_location = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_LOCATION_STRING, NULL);
296298
const char *accept = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_ACCEPT_STRING, NULL);
299+
char *location_name = NULL;
300+
char *location_folder = NULL;
301+
struct stat statbuf;
297302
bool open_folders = false;
303+
bool save_file_existing = false;
304+
bool save_file_new_named = false;
298305

299306
switch (type) {
300307
case SDL_FILEDIALOG_OPENFILE:
@@ -305,6 +312,28 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog
305312
case SDL_FILEDIALOG_SAVEFILE:
306313
method = "SaveFile";
307314
method_title = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_TITLE_STRING, "Save File");
315+
if (default_location) {
316+
if (stat(default_location, &statbuf) == 0) {
317+
save_file_existing = S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode);
318+
} else if (errno == ENOENT) {
319+
char *dirc = SDL_strdup(default_location);
320+
if (dirc) {
321+
location_folder = SDL_strdup(dirname(dirc));
322+
SDL_free(dirc);
323+
if (location_folder) {
324+
save_file_new_named = (stat(location_folder, &statbuf) == 0) && S_ISDIR(statbuf.st_mode);
325+
}
326+
}
327+
}
328+
329+
if (save_file_existing || save_file_new_named) {
330+
char *basec = SDL_strdup(default_location);
331+
if (basec) {
332+
location_name = SDL_strdup(basename(basec));
333+
SDL_free(basec);
334+
}
335+
}
336+
}
308337
break;
309338

310339
case SDL_FILEDIALOG_OPENFOLDER:
@@ -317,7 +346,7 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog
317346
/* This is already checked in ../SDL_dialog.c; this silences compiler warnings */
318347
SDL_SetError("Invalid file dialog type: %d", type);
319348
callback(userdata, NULL, -1);
320-
return;
349+
goto cleanup;
321350
}
322351

323352
SDL_DBusContext *dbus = SDL_DBus_GetContext();
@@ -335,20 +364,20 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog
335364
if (err_msg) {
336365
SDL_SetError("%s", err_msg);
337366
callback(userdata, NULL, -1);
338-
return;
367+
goto cleanup;
339368
}
340369

341370
if (dbus == NULL) {
342371
SDL_SetError("Failed to connect to DBus");
343372
callback(userdata, NULL, -1);
344-
return;
373+
goto cleanup;
345374
}
346375

347376
msg = dbus->message_new_method_call(PORTAL_DESTINATION, PORTAL_PATH, PORTAL_INTERFACE, method);
348377
if (msg == NULL) {
349378
SDL_SetError("Failed to send message to portal");
350379
callback(userdata, NULL, -1);
351-
return;
380+
goto cleanup;
352381
}
353382

354383
dbus->message_iter_init_append(msg, &params);
@@ -362,7 +391,7 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog
362391
handle_str = SDL_malloc(len * sizeof(char));
363392
if (!handle_str) {
364393
callback(userdata, NULL, -1);
365-
return;
394+
goto cleanup;
366395
}
367396

368397
SDL_snprintf(handle_str, len, "%s%s", WAYLAND_HANDLE_PREFIX, parent_handle);
@@ -373,7 +402,7 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog
373402
handle_str = SDL_malloc(len * sizeof(char));
374403
if (!handle_str) {
375404
callback(userdata, NULL, -1);
376-
return;
405+
goto cleanup;
377406
}
378407

379408
// The portal wants X11 window ID numbers in hex.
@@ -393,7 +422,7 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog
393422
handle_str = SDL_malloc(sizeof(char) * (HANDLE_LEN + 1));
394423
if (!handle_str) {
395424
callback(userdata, NULL, -1);
396-
return;
425+
goto cleanup;
397426
}
398427
SDL_snprintf(handle_str, HANDLE_LEN, "%u", ++handle_id);
399428
DBus_AppendStringOption(dbus, &options, "handle_token", handle_str);
@@ -410,7 +439,20 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog
410439
DBus_AppendFilters(dbus, &options, filters, nfilters);
411440
}
412441
if (default_location) {
413-
DBus_AppendByteArray(dbus, &options, "current_folder", default_location);
442+
if (save_file_existing && location_name) {
443+
/* Open a save dialog at an existing file */
444+
DBus_AppendByteArray(dbus, &options, "current_file", default_location);
445+
/* Setting "current_name" should not be necessary however the kde-desktop-portal sets the filename without an extension.
446+
* An alternative would be to match the extension to a filter and set "current_filter".
447+
*/
448+
DBus_AppendStringOption(dbus, &options, "current_name", location_name);
449+
} else if (save_file_new_named && location_folder && location_name) {
450+
/* Open a save dialog at a location with a suggested name */
451+
DBus_AppendByteArray(dbus, &options, "current_folder", location_folder);
452+
DBus_AppendStringOption(dbus, &options, "current_name", location_name);
453+
} else {
454+
DBus_AppendByteArray(dbus, &options, "current_folder", default_location);
455+
}
414456
}
415457
if (accept) {
416458
DBus_AppendStringOption(dbus, &options, "accept_label", accept);
@@ -469,6 +511,10 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog
469511

470512
incorrect_type:
471513
dbus->message_unref(reply);
514+
515+
cleanup:
516+
SDL_free(location_name);
517+
SDL_free(location_folder);
472518
}
473519

474520
bool SDL_Portal_detect(void)

test/testdialog.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
/* Sample program: Create open and save dialogs. */
1313

1414
#include <SDL3/SDL.h>
15+
#include <SDL3/SDL_iostream.h>
1516
#include <SDL3/SDL_main.h>
1617
#include <SDL3/SDL_test.h>
1718

@@ -23,6 +24,8 @@ const SDL_DialogFileFilter filters[] = {
2324
};
2425

2526
static void SDLCALL callback(void *userdata, const char * const *files, int filter) {
27+
char **saved_path = userdata;
28+
2629
if (files) {
2730
const char* filter_name = "(filter fetching unsupported)";
2831

@@ -36,6 +39,13 @@ static void SDLCALL callback(void *userdata, const char * const *files, int filt
3639

3740
SDL_Log("Filter used: '%s'", filter_name);
3841

42+
if (*files && saved_path) {
43+
*saved_path = SDL_strdup(*files);
44+
/* Create the file */
45+
SDL_IOStream *stream = SDL_IOFromFile(*saved_path, "w");
46+
SDL_CloseIO(stream);
47+
}
48+
3949
while (*files) {
4050
SDL_Log("'%s'", *files);
4151
files++;
@@ -55,6 +65,7 @@ int main(int argc, char *argv[])
5565
const SDL_FRect open_folder_rect = { 370, 50, 220, 140 };
5666
int i;
5767
const char *initial_path = NULL;
68+
char *saved_path = NULL;
5869

5970
/* Initialize test framework */
6071
state = SDLTest_CommonCreateState(argv, 0);
@@ -116,7 +127,24 @@ int main(int argc, char *argv[])
116127
} else if (SDL_PointInRectFloat(&p, &open_folder_rect)) {
117128
SDL_ShowOpenFolderDialog(callback, NULL, w, initial_path, 1);
118129
} else if (SDL_PointInRectFloat(&p, &save_file_rect)) {
119-
SDL_ShowSaveFileDialog(callback, NULL, w, filters, SDL_arraysize(filters), initial_path);
130+
const char *default_filename = "Untitled.index";
131+
char *save_path = NULL;
132+
if (saved_path) {
133+
save_path = SDL_strdup(saved_path);
134+
} else if (initial_path != NULL) {
135+
const size_t save_path_total_length = SDL_strlen(initial_path) + SDL_strlen(default_filename) + 1;
136+
save_path = (char *)SDL_malloc(save_path_total_length);
137+
if (save_path) {
138+
*save_path = '\0';
139+
SDL_strlcat(save_path, initial_path, save_path_total_length);
140+
SDL_strlcat(save_path, default_filename, save_path_total_length);
141+
}
142+
}
143+
if (save_path == NULL) {
144+
save_path = SDL_strdup(default_filename);
145+
}
146+
SDL_ShowSaveFileDialog(callback, &saved_path, w, filters, SDL_arraysize(filters), save_path);
147+
SDL_free(save_path);
120148
}
121149
}
122150
}

0 commit comments

Comments
 (0)