From 44c6cfda053d690462b7f396cd8a56a0ff6a77d4 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 13 Sep 2024 22:27:12 -0700
Subject: [PATCH] Switched zenity dialogs to use the new process API
---
src/dialog/unix/SDL_zenitydialog.c | 227 ++++++++++++-----------------
1 file changed, 96 insertions(+), 131 deletions(-)
diff --git a/src/dialog/unix/SDL_zenitydialog.c b/src/dialog/unix/SDL_zenitydialog.c
index b77593aef2b18..b816d02262318 100644
--- a/src/dialog/unix/SDL_zenitydialog.c
+++ b/src/dialog/unix/SDL_zenitydialog.c
@@ -191,128 +191,102 @@ static void run_zenity(zenityArgs* arg_struct)
{
SDL_DialogFileCallback callback = arg_struct->callback;
void* userdata = arg_struct->userdata;
-
- int out[2];
- pid_t process;
+ SDL_Process *process = NULL;
+ SDL_Environment *env = NULL;
+ char **process_env = NULL;
+ char **process_args = NULL;
int status = -1;
+ size_t bytes_read = 0;
+ char *container = NULL;
+ size_t narray = 1;
+ char **array = NULL;
+ bool result = false;
+
+ process_args = generate_args(arg_struct);
+ if (!process_args) {
+ goto done;
+ }
- if (pipe(out) < 0) {
- SDL_SetError("Could not create pipe: %s", strerror(errno));
- callback(userdata, NULL, -1);
- return;
+ env = SDL_CreateEnvironment(SDL_FALSE);
+ if (!env) {
+ goto done;
}
- /* Args are only needed in the forked process, but generating them early
- allows catching the error messages in the main process */
- char **args = generate_args(arg_struct);
+ /* Recent versions of Zenity have different exit codes, but picks up
+ different codes from the environment */
+ SDL_SetEnvironmentVariable(env, "ZENITY_OK", "0", SDL_TRUE);
+ SDL_SetEnvironmentVariable(env, "ZENITY_CANCEL", "1", SDL_TRUE);
+ SDL_SetEnvironmentVariable(env, "ZENITY_ESC", "1", SDL_TRUE);
+ SDL_SetEnvironmentVariable(env, "ZENITY_EXTRA", "2", SDL_TRUE);
+ SDL_SetEnvironmentVariable(env, "ZENITY_ERROR", "2", SDL_TRUE);
+ SDL_SetEnvironmentVariable(env, "ZENITY_TIMEOUT", "2", SDL_TRUE);
+
+ process_env = SDL_GetEnvironmentVariables(env);
+ if (!process_env) {
+ goto done;
+ }
- if (!args) {
- // SDL_SetError will have been called already
- callback(userdata, NULL, -1);
- return;
+ SDL_PropertiesID props = SDL_CreateProperties();
+ SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, process_args);
+ SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, process_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;
}
- process = fork();
+ container = SDL_ReadProcess(process, &bytes_read, &status);
+ if (!container) {
+ goto done;
+ }
- if (process < 0) {
- SDL_SetError("Could not fork process: %s", strerror(errno));
- close(out[0]);
- close(out[1]);
- free_args(args);
- callback(userdata, NULL, -1);
- return;
- } else if (process == 0){
- dup2(out[1], STDOUT_FILENO);
- close(STDERR_FILENO); // Hide errors from Zenity to stderr
- close(out[0]);
- close(out[1]);
-
- /* Recent versions of Zenity have different exit codes, but picks up
- different codes from the environment */
- SDL_setenv_unsafe("ZENITY_OK", "0", 1);
- SDL_setenv_unsafe("ZENITY_CANCEL", "1", 1);
- SDL_setenv_unsafe("ZENITY_ESC", "1", 1);
- SDL_setenv_unsafe("ZENITY_EXTRA", "2", 1);
- SDL_setenv_unsafe("ZENITY_ERROR", "2", 1);
- SDL_setenv_unsafe("ZENITY_TIMEOUT", "2", 1);
-
- execv(args[0], args);
-
- exit(errno + 128);
- } else {
- char readbuffer[2048];
- size_t bytes_read = 0, bytes_last_read;
- char *container = NULL;
- close(out[1]);
- free_args(args);
-
- while ((bytes_last_read = read(out[0], readbuffer, sizeof(readbuffer)))) {
- char *new_container = SDL_realloc(container, bytes_read + bytes_last_read);
- if (!new_container) {
- SDL_free(container);
- close(out[0]);
- callback(userdata, NULL, -1);
- return;
+ array = (char **)SDL_malloc((narray + 1) * sizeof(char *));
+ if (!array) {
+ goto done;
+ }
+ array[0] = container;
+ array[1] = NULL;
+
+ for (int i = 0; i < bytes_read; i++) {
+ if (container[i] == '\n') {
+ container[i] = '\0';
+ // Reading from a process often leaves a trailing \n, so ignore the last one
+ if (i < bytes_read - 1) {
+ array[narray] = container + i + 1;
+ narray++;
+ char **new_array = (char **) SDL_realloc(array, (narray + 1) * sizeof(char *));
+ if (!new_array) {
+ goto done;
+ }
+ array = new_array;
+ array[narray] = NULL;
}
- container = new_container;
- SDL_memcpy(container + bytes_read, readbuffer, bytes_last_read);
- bytes_read += bytes_last_read;
- }
- close(out[0]);
-
- if (waitpid(process, &status, 0) == -1) {
- SDL_SetError("waitpid failed");
- SDL_free(container);
- callback(userdata, NULL, -1);
- return;
- }
-
- if (WIFEXITED(status)) {
- status = WEXITSTATUS(status);
}
+ }
- size_t narray = 1;
- char **array = (char **) SDL_malloc((narray + 1) * sizeof(char *));
+ // 0 = the user chose one or more files, 1 = the user canceled the dialog
+ if (status == 0 || status == 1) {
+ callback(userdata, (const char * const*)array, -1);
+ } else {
+ SDL_SetError("Could not run zenity: exit code %d", status);
+ callback(userdata, NULL, -1);
+ }
- if (!array) {
- SDL_free(container);
- callback(userdata, NULL, -1);
- return;
- }
+ result = true;
- array[0] = container;
- array[1] = NULL;
-
- for (int i = 0; i < bytes_read; i++) {
- if (container[i] == '\n') {
- container[i] = '\0';
- // Reading from a process often leaves a trailing \n, so ignore the last one
- if (i < bytes_read - 1) {
- array[narray] = container + i + 1;
- narray++;
- char **new_array = (char **) SDL_realloc(array, (narray + 1) * sizeof(char *));
- if (!new_array) {
- SDL_free(container);
- SDL_free(array);
- callback(userdata, NULL, -1);
- return;
- }
- array = new_array;
- array[narray] = NULL;
- }
- }
- }
+done:
+ SDL_free(array);
+ SDL_free(container);
+ free_args(process_args);
+ SDL_free(process_env);
+ SDL_DestroyEnvironment(env);
+ SDL_DestroyProcess(process);
- // 0 = the user chose one or more files, 1 = the user canceled the dialog
- if (status == 0 || status == 1) {
- callback(userdata, (const char * const*) array, -1);
- } else {
- SDL_SetError("Could not run zenity: exit code %d (may be zenity or execv+128)", status);
- callback(userdata, NULL, -1);
- }
-
- SDL_free(array);
- SDL_free(container);
+ if (!result) {
+ callback(userdata, NULL, -1);
}
}
@@ -409,30 +383,21 @@ void SDL_Zenity_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* user
bool SDL_Zenity_detect(void)
{
- pid_t process;
+ const char *args[] = {
+ "/usr/bin/env", "zenity", "--version", NULL
+ };
int status = -1;
- process = fork();
-
- if (process < 0) {
- SDL_SetError("Could not fork process: %s", strerror(errno));
- return false;
- } else if (process == 0){
- // Disable output
- close(STDERR_FILENO);
- close(STDOUT_FILENO);
- execl("/usr/bin/env", "/usr/bin/env", "zenity", "--version", NULL);
- exit(errno + 128);
- } else {
- if (waitpid(process, &status, 0) == -1) {
- SDL_SetError("waitpid failed");
- return false;
- }
-
- if (WIFEXITED(status)) {
- status = WEXITSTATUS(status);
- }
-
- return (status == 0);
+ SDL_PropertiesID props = SDL_CreateProperties();
+ SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, args);
+ 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_NULL);
+ SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER, SDL_PROCESS_STDIO_NULL);
+ SDL_Process *process = SDL_CreateProcessWithProperties(props);
+ SDL_DestroyProperties(props);
+ if (process) {
+ SDL_WaitProcess(process, true, &status);
+ SDL_DestroyProcess(process);
}
+ return (status == 0);
}