Switched zenity dialogs to use the new process API

This commit is contained in:
Sam Lantinga
2024-09-13 22:27:12 -07:00
parent 3166a05c18
commit 44c6cfda05

View File

@@ -191,128 +191,102 @@ static void run_zenity(zenityArgs* arg_struct)
{ {
SDL_DialogFileCallback callback = arg_struct->callback; SDL_DialogFileCallback callback = arg_struct->callback;
void* userdata = arg_struct->userdata; void* userdata = arg_struct->userdata;
SDL_Process *process = NULL;
int out[2]; SDL_Environment *env = NULL;
pid_t process; char **process_env = NULL;
char **process_args = NULL;
int status = -1; int status = -1;
size_t bytes_read = 0;
char *container = NULL;
size_t narray = 1;
char **array = NULL;
bool result = false;
if (pipe(out) < 0) { process_args = generate_args(arg_struct);
SDL_SetError("Could not create pipe: %s", strerror(errno)); if (!process_args) {
callback(userdata, NULL, -1); goto done;
return;
} }
/* Args are only needed in the forked process, but generating them early env = SDL_CreateEnvironment(SDL_FALSE);
allows catching the error messages in the main process */ if (!env) {
char **args = generate_args(arg_struct); goto done;
if (!args) {
// SDL_SetError will have been called already
callback(userdata, NULL, -1);
return;
} }
process = fork(); /* 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);
if (process < 0) { process_env = SDL_GetEnvironmentVariables(env);
SDL_SetError("Could not fork process: %s", strerror(errno)); if (!process_env) {
close(out[0]); goto done;
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 SDL_PropertiesID props = SDL_CreateProperties();
different codes from the environment */ SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, process_args);
SDL_setenv_unsafe("ZENITY_OK", "0", 1); SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, process_env);
SDL_setenv_unsafe("ZENITY_CANCEL", "1", 1); SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_NULL);
SDL_setenv_unsafe("ZENITY_ESC", "1", 1); SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP);
SDL_setenv_unsafe("ZENITY_EXTRA", "2", 1); SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER, SDL_PROCESS_STDIO_NULL);
SDL_setenv_unsafe("ZENITY_ERROR", "2", 1); process = SDL_CreateProcessWithProperties(props);
SDL_setenv_unsafe("ZENITY_TIMEOUT", "2", 1); SDL_DestroyProperties(props);
if (!process) {
goto done;
}
execv(args[0], args); container = SDL_ReadProcess(process, &bytes_read, &status);
if (!container) {
goto done;
}
exit(errno + 128); array = (char **)SDL_malloc((narray + 1) * sizeof(char *));
} else { if (!array) {
char readbuffer[2048]; goto done;
size_t bytes_read = 0, bytes_last_read; }
char *container = NULL; array[0] = container;
close(out[1]); array[1] = NULL;
free_args(args);
while ((bytes_last_read = read(out[0], readbuffer, sizeof(readbuffer)))) { for (int i = 0; i < bytes_read; i++) {
char *new_container = SDL_realloc(container, bytes_read + bytes_last_read); if (container[i] == '\n') {
if (!new_container) { container[i] = '\0';
SDL_free(container); // Reading from a process often leaves a trailing \n, so ignore the last one
close(out[0]); if (i < bytes_read - 1) {
callback(userdata, NULL, -1); array[narray] = container + i + 1;
return; narray++;
} char **new_array = (char **) SDL_realloc(array, (narray + 1) * sizeof(char *));
container = new_container; if (!new_array) {
SDL_memcpy(container + bytes_read, readbuffer, bytes_last_read); goto done;
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 *));
if (!array) {
SDL_free(container);
callback(userdata, NULL, -1);
return;
}
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;
} }
array = new_array;
array[narray] = NULL;
} }
} }
}
// 0 = the user chose one or more files, 1 = the user canceled the dialog // 0 = the user chose one or more files, 1 = the user canceled the dialog
if (status == 0 || status == 1) { if (status == 0 || status == 1) {
callback(userdata, (const char * const*) array, -1); callback(userdata, (const char * const*)array, -1);
} else { } else {
SDL_SetError("Could not run zenity: exit code %d (may be zenity or execv+128)", status); SDL_SetError("Could not run zenity: exit code %d", status);
callback(userdata, NULL, -1); callback(userdata, NULL, -1);
} }
SDL_free(array); result = true;
SDL_free(container);
done:
SDL_free(array);
SDL_free(container);
free_args(process_args);
SDL_free(process_env);
SDL_DestroyEnvironment(env);
SDL_DestroyProcess(process);
if (!result) {
callback(userdata, NULL, -1);
} }
} }
@@ -409,30 +383,21 @@ void SDL_Zenity_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* user
bool SDL_Zenity_detect(void) bool SDL_Zenity_detect(void)
{ {
pid_t process; const char *args[] = {
"/usr/bin/env", "zenity", "--version", NULL
};
int status = -1; int status = -1;
process = fork(); SDL_PropertiesID props = SDL_CreateProperties();
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, args);
if (process < 0) { SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_NULL);
SDL_SetError("Could not fork process: %s", strerror(errno)); SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_NULL);
return false; SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER, SDL_PROCESS_STDIO_NULL);
} else if (process == 0){ SDL_Process *process = SDL_CreateProcessWithProperties(props);
// Disable output SDL_DestroyProperties(props);
close(STDERR_FILENO); if (process) {
close(STDOUT_FILENO); SDL_WaitProcess(process, true, &status);
execl("/usr/bin/env", "/usr/bin/env", "zenity", "--version", NULL); SDL_DestroyProcess(process);
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);
} }
return (status == 0);
} }