Skip to content
Draft
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
46 changes: 46 additions & 0 deletions include/SDL3/SDL_dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,52 @@ extern SDL_DECLSPEC void SDLCALL SDL_ShowFileDialogWithProperties(SDL_FileDialog
#define SDL_PROP_FILE_DIALOG_ACCEPT_STRING "SDL.filedialog.accept"
#define SDL_PROP_FILE_DIALOG_CANCEL_STRING "SDL.filedialog.cancel"

/**
* Callback used by input dialog functions.
*
* If `value` is NULL, an error occured. Details can be obtained with
* SDL_GetError().
*
* If the user cancels the dialog, the callback will be invoked with an empty
* string. It is not possible to differentiate cancelling and inputting an empty
* string.
*
* The value argument should not be freed; it will automatically be freed
* when the callback returns. If you need to keep the string after the callback
* returns, you must duplicate it with SDL_strdup() or some other means.
*
* \param userdata an app-provided pointer, for the callback's use.
* \param value the value provided by the user, or NULL if an error occured.
*
* \since This datatype is available since SDL 3.4.0.
*
* \sa SDL_ShowSimpleInputDialog
*/
typedef void (SDLCALL *SDL_DialogInputCallback)(void *userdata, const char *value);

/**
* Show a simple dialog prompting the user to input a string.
*
* \param callback a function pointer to be invoked when the user inputs a
* string and confirms, or cancels the dialog, or an error
* occurs.
* \param userdata an optional pointer to pass extra data to the callback when
* it will be invoked.
* \param title the title of the dialog. May be NULL.
* \param message the message of the dialog. May be NULL.
* \param value the default value of the dialog. May be NULL.
* \param window the window the dialog should be modal for. May be NULL.
*
* \threadsafety This function should be called only from the main thread. The
* callback may be invoked from the same thread or from a
* different one, depending on the OS's constraints.
*
* \since This function is available since SDL 3.4.0.
*
* \sa SDL_DialogInputCallback
*/
extern SDL_DECLSPEC void SDLCALL SDL_ShowSimpleInputDialog(SDL_DialogInputCallback callback, void *userdata, const char *title, const char *message, const char *value, SDL_Window *window);

/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
Expand Down
12 changes: 12 additions & 0 deletions src/dialog/unix/SDL_portaldialog.c
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,12 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog
SDL_free(location_folder);
}

void SDL_Portal_ShowSimpleInputDialog(SDL_DialogInputCallback callback, void *userdata, const char *title, const char *message, const char *value, SDL_Window *window)
{
SDL_Unsupported();
callback(userdata, NULL);
}

bool SDL_Portal_detect(void)
{
SDL_DBusContext *dbus = SDL_DBus_GetContext();
Expand Down Expand Up @@ -600,6 +606,12 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog
callback(userdata, NULL, -1);
}

void SDL_Portal_ShowSimpleInputDialog(SDL_DialogInputCallback callback, void *userdata, const char *title, const char *message, const char *value, SDL_Window *window)
{
SDL_Unsupported();
callback(userdata, NULL);
}

bool SDL_Portal_detect(void)
{
return false;
Expand Down
2 changes: 2 additions & 0 deletions src/dialog/unix/SDL_portaldialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,7 @@

void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props);

void SDL_Portal_ShowSimpleInputDialog(SDL_DialogInputCallback callback, void *userdata, const char *title, const char *message, const char *value, SDL_Window *window);

/** @returns non-zero if available, zero if unavailable */
bool SDL_Portal_detect(void);
25 changes: 20 additions & 5 deletions src/dialog/unix/SDL_unixdialog.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
#include "./SDL_portaldialog.h"
#include "./SDL_zenitydialog.h"

static void (*detected_function)(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props) = NULL;
static void (*detected_dialog)(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props) = NULL;
static void (*detected_input)(SDL_DialogInputCallback callback, void *userdata, const char *title, const char *message, const char *value, SDL_Window *window) = NULL;

void SDLCALL hint_callback(void *userdata, const char *name, const char *oldValue, const char *newValue);

Expand All @@ -47,14 +48,16 @@ static int detect_available_methods(const char *value)

if (driver == NULL || SDL_strcmp(driver, "portal") == 0) {
if (SDL_Portal_detect()) {
detected_function = SDL_Portal_ShowFileDialogWithProperties;
detected_dialog = SDL_Portal_ShowFileDialogWithProperties;
detected_input = SDL_Zenity_ShowSimpleInputDialog;
return 1;
}
}

if (driver == NULL || SDL_strcmp(driver, "zenity") == 0) {
if (SDL_Zenity_detect()) {
detected_function = SDL_Zenity_ShowFileDialogWithProperties;
detected_dialog = SDL_Zenity_ShowFileDialogWithProperties;
detected_input = SDL_Zenity_ShowSimpleInputDialog;
return 2;
}
}
Expand All @@ -71,11 +74,23 @@ void SDLCALL hint_callback(void *userdata, const char *name, const char *oldValu
void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props)
{
// Call detect_available_methods() again each time in case the situation changed
if (!detected_function && !detect_available_methods(NULL)) {
if (!detected_dialog && !detect_available_methods(NULL)) {
// SetError() done by detect_available_methods()
callback(userdata, NULL, -1);
return;
}

detected_function(type, callback, userdata, props);
detected_dialog(type, callback, userdata, props);
}

void SDL_ShowSimpleInputDialog(SDL_DialogInputCallback callback, void *userdata, const char *title, const char *message, const char *value, SDL_Window *window)
{
// Call detect_available_methods() again each time in case the situation changed
if (!detected_input && !detect_available_methods(NULL)) {
// SetError() done by detect_available_methods()
callback(userdata, NULL);
return;
}

detected_input(callback, userdata, title, message, value, window);
}
86 changes: 86 additions & 0 deletions src/dialog/unix/SDL_zenitydialog.c
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,92 @@ void SDL_Zenity_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog
SDL_DetachThread(thread);
}

void SDL_Zenity_ShowSimpleInputDialog(SDL_DialogInputCallback callback, void *userdata, const char *title, const char *message, const char *value, SDL_Window *window)
{
SDL_Process *process = NULL;
SDL_Environment *env = NULL;
int status = -1;
size_t bytes_read = 0;
char *container = NULL;
bool result = false;
const char *argv[9];
int argc = 0;

argv[argc++] = "zenity";
argv[argc++] = "--entry";

if (title) {
argv[argc++] = "--title";
argv[argc++] = title;
}

if (message) {
argv[argc++] = "--text";
argv[argc++] = message;
}

if (value) {
argv[argc++] = "--entry-text";
argv[argc++] = value;
}

argv[argc] = NULL;

env = SDL_CreateEnvironment(true);
if (!env) {
goto done;
}

/* Recent versions of Zenity have different exit codes, but picks up
different codes from the environment */
SDL_SetEnvironmentVariable(env, "ZENITY_OK", "0", true);
SDL_SetEnvironmentVariable(env, "ZENITY_CANCEL", "1", true);
SDL_SetEnvironmentVariable(env, "ZENITY_ESC", "1", true);
SDL_SetEnvironmentVariable(env, "ZENITY_EXTRA", "2", true);
SDL_SetEnvironmentVariable(env, "ZENITY_ERROR", "2", true);
SDL_SetEnvironmentVariable(env, "ZENITY_TIMEOUT", "2", true);

SDL_PropertiesID props = SDL_CreateProperties();
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, argv);
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, env);
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_NULL);
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP);
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER, SDL_PROCESS_STDIO_NULL);
process = SDL_CreateProcessWithProperties(props);
SDL_DestroyProperties(props);
if (!process) {
goto done;
}

container = SDL_ReadProcess(process, &bytes_read, &status);
if (!container) {
goto done;
}

// Strings returned by Zenity finish with '\n'; swap that with a '\0'
container[bytes_read - 1] = '\0';

if (status == 0) {
callback(userdata, container);
} else if (status == 1) {
callback(userdata, "");
} else {
SDL_SetError("Could not run zenity: exit code %d", status);
callback(userdata, NULL);
}

result = true;

done:
SDL_free(container);
SDL_DestroyEnvironment(env);
SDL_DestroyProcess(process);

if (!result) {
callback(userdata, NULL);
}
}

bool SDL_Zenity_detect(void)
{
const char *args[] = {
Expand Down
2 changes: 2 additions & 0 deletions src/dialog/unix/SDL_zenitydialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,7 @@

extern void SDL_Zenity_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props);

extern void SDL_Zenity_ShowSimpleInputDialog(SDL_DialogInputCallback callback, void *userdata, const char *title, const char *message, const char *value, SDL_Window *window);

/** @returns non-zero if available, zero if unavailable */
extern bool SDL_Zenity_detect(void);
1 change: 1 addition & 0 deletions src/dynapi/SDL_dynapi.sym
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,7 @@ SDL3_0.0.0 {
SDL_hid_get_properties;
SDL_GetPixelFormatFromGPUTextureFormat;
SDL_GetGPUTextureFormatFromPixelFormat;
SDL_ShowSimpleInputDialog;
# extra symbols go here (don't modify this line)
local: *;
};
1 change: 1 addition & 0 deletions src/dynapi/SDL_dynapi_overrides.h
Original file line number Diff line number Diff line change
Expand Up @@ -1282,3 +1282,4 @@
#define SDL_hid_get_properties SDL_hid_get_properties_REAL
#define SDL_GetPixelFormatFromGPUTextureFormat SDL_GetPixelFormatFromGPUTextureFormat_REAL
#define SDL_GetGPUTextureFormatFromPixelFormat SDL_GetGPUTextureFormatFromPixelFormat_REAL
#define SDL_ShowSimpleInputDialog SDL_ShowSimpleInputDialog_REAL
1 change: 1 addition & 0 deletions src/dynapi/SDL_dynapi_procs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1290,3 +1290,4 @@ SDL_DYNAPI_PROC(Uint32,SDL_AddAtomicU32,(SDL_AtomicU32 *a,int b),(a,b),return)
SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_hid_get_properties,(SDL_hid_device *a),(a),return)
SDL_DYNAPI_PROC(SDL_PixelFormat,SDL_GetPixelFormatFromGPUTextureFormat,(SDL_GPUTextureFormat a),(a),return)
SDL_DYNAPI_PROC(SDL_GPUTextureFormat,SDL_GetGPUTextureFormatFromPixelFormat,(SDL_PixelFormat a),(a),return)
SDL_DYNAPI_PROC(void,SDL_ShowSimpleInputDialog,(SDL_DialogInputCallback a,void *b,const char *c,const char *d,const char *e,SDL_Window *f),(a,b,c,d,e,f),)
11 changes: 11 additions & 0 deletions test/testdialog.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ static void SDLCALL callback(void *userdata, const char * const *files, int filt
}
}

static void SDLCALL input_callback(void *userdata, const char *input) {
SDL_Log("Input: %s\n", input);
}

char *concat_strings(const char *a, const char *b)
{
char *out = NULL;
Expand All @@ -80,6 +84,7 @@ int main(int argc, char *argv[])
const SDL_FRect open_file_rect = { 50, 50, 220, 140 };
const SDL_FRect save_file_rect = { 50, 290, 220, 140 };
const SDL_FRect open_folder_rect = { 370, 50, 220, 140 };
const SDL_FRect input_rect = { 370, 290, 220, 140 };
int i;
const char *default_filename = "Untitled.index";
const char *initial_path = NULL;
Expand Down Expand Up @@ -153,6 +158,8 @@ int main(int argc, char *argv[])
}
SDL_ShowSaveFileDialog(callback, &last_saved_path, w, filters, SDL_arraysize(filters), save_path ? save_path : default_filename);
SDL_free(save_path);
} else if (SDL_PointInRectFloat(&p, &input_rect)) {
SDL_ShowSimpleInputDialog(input_callback, NULL, NULL, NULL, NULL, w);
}
}
}
Expand All @@ -173,10 +180,14 @@ int main(int argc, char *argv[])
SDL_SetRenderDrawColor(r, 0, 0, 255, SDL_ALPHA_OPAQUE);
SDL_RenderFillRect(r, &open_folder_rect);

SDL_SetRenderDrawColor(r, 255, 255, 0, SDL_ALPHA_OPAQUE);
SDL_RenderFillRect(r, &input_rect);

SDL_SetRenderDrawColor(r, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDLTest_DrawString(r, open_file_rect.x+5, open_file_rect.y+open_file_rect.h/2, "Open File...");
SDLTest_DrawString(r, save_file_rect.x+5, save_file_rect.y+save_file_rect.h/2, "Save File...");
SDLTest_DrawString(r, open_folder_rect.x+5, open_folder_rect.y+open_folder_rect.h/2, "Open Folder...");
SDLTest_DrawString(r, input_rect.x+5, input_rect.y+input_rect.h/2, "Input text...");

SDL_RenderPresent(r);
}
Expand Down
Loading