Skip to content

dialog: Fix save file chooser with xdg portal #13007

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion src/dialog/unix/SDL_portaldialog.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@

#ifdef SDL_USE_LIBDBUS

#include <libgen.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>

#define PORTAL_DESTINATION "org.freedesktop.portal.Desktop"
Expand Down Expand Up @@ -294,7 +296,12 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog
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* accept = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_ACCEPT_STRING, NULL);
char* location_name = NULL;
char* location_folder = NULL;
struct stat statbuf;
bool open_folders = false;
bool save_file_existing = false;
bool save_file_new_named = false;

switch (type) {
case SDL_FILEDIALOG_OPENFILE:
Expand All @@ -305,6 +312,22 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog
case SDL_FILEDIALOG_SAVEFILE:
method = "SaveFile";
method_title = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_TITLE_STRING, "Save File");
if (default_location) {
if (stat(default_location, &statbuf) == 0) {
save_file_existing = S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode);
} else if (errno == ENOENT) {
char *dirc = SDL_strdup(default_location);
location_folder = SDL_strdup(dirname(dirc));
SDL_free(dirc);
save_file_new_named = (stat(location_folder, &statbuf) == 0) && S_ISDIR(statbuf.st_mode);
}

if (save_file_existing || save_file_new_named) {
char *basec = SDL_strdup(default_location);
location_name = SDL_strdup(basename(basec));
SDL_free(basec);
}
}
break;

case SDL_FILEDIALOG_OPENFOLDER:
Expand Down Expand Up @@ -410,8 +433,26 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog
DBus_AppendFilters(dbus, &options, filters, nfilters);
}
if (default_location) {
DBus_AppendByteArray(dbus, &options, "current_folder", default_location);
if (save_file_existing) {
SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Create save dialog for existing file");
DBus_AppendByteArray(dbus, &options, "current_file", default_location);
/* Setting "current_name" should not be necessary however the kde-desktop-portal sets the filename without an extension.
* An alternative would be to match the extension to a filter and set "current_filter".
*/
DBus_AppendStringOption(dbus, &options, "current_name", location_name);
} else if (save_file_new_named) {
SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Create save dialog for new file");
DBus_AppendByteArray(dbus, &options, "current_folder", location_folder);
DBus_AppendStringOption(dbus, &options, "current_name", location_name);
} else {
SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Create dialog at folder");
DBus_AppendByteArray(dbus, &options, "current_folder", default_location);
}

SDL_free(location_name);
SDL_free(location_folder);
}

if (accept) {
DBus_AppendStringOption(dbus, &options, "accept_label", accept);
}
Expand Down
25 changes: 24 additions & 1 deletion test/testdialog.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_test.h>
#include <SDL3/SDL_iostream.h>

const SDL_DialogFileFilter filters[] = {
{ "All files", "*" },
Expand All @@ -23,6 +24,8 @@ const SDL_DialogFileFilter filters[] = {
};

static void SDLCALL callback(void *userdata, const char * const *files, int filter) {
char **saved_path = userdata;

if (files) {
const char* filter_name = "(filter fetching unsupported)";

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

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

if (*files && saved_path) {
*saved_path = SDL_strdup(*files);
/* Create the file */
SDL_IOStream* stream = SDL_IOFromFile(*saved_path, "w");
SDL_CloseIO(stream);
}

while (*files) {
SDL_Log("'%s'", *files);
files++;
Expand All @@ -55,6 +65,7 @@ int main(int argc, char *argv[])
const SDL_FRect open_folder_rect = { 370, 50, 220, 140 };
int i;
const char *initial_path = NULL;
char *saved_path = NULL;

/* Initialize test framework */
state = SDLTest_CommonCreateState(argv, 0);
Expand Down Expand Up @@ -116,7 +127,19 @@ int main(int argc, char *argv[])
} else if (SDL_PointInRectFloat(&p, &open_folder_rect)) {
SDL_ShowOpenFolderDialog(callback, NULL, w, initial_path, 1);
} else if (SDL_PointInRectFloat(&p, &save_file_rect)) {
SDL_ShowSaveFileDialog(callback, NULL, w, filters, SDL_arraysize(filters), initial_path);
const char *default_filename = "Untitled.index";
const size_t save_path_total_length = SDL_strlen(initial_path) + SDL_strlen(default_filename) + 1;
char *save_path;
if (saved_path) {
save_path = SDL_strdup(saved_path);
} else {
save_path = (char *)SDL_malloc(save_path_total_length);
*save_path = '\0';
SDL_strlcat(save_path, initial_path, save_path_total_length);
SDL_strlcat(save_path, default_filename, save_path_total_length);
}
SDL_ShowSaveFileDialog(callback, &saved_path, w, filters, SDL_arraysize(filters), save_path);
SDL_free(save_path);
}
}
}
Expand Down
Loading