From fed0149172effb00f6c8a3cb09756f474ed9438a Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 14 Sep 2024 09:54:50 -0700
Subject: [PATCH] Switched wayland messageboxes to use the new process API
---
src/video/wayland/SDL_waylandmessagebox.c | 207 +++++++---------------
1 file changed, 65 insertions(+), 142 deletions(-)
diff --git a/src/video/wayland/SDL_waylandmessagebox.c b/src/video/wayland/SDL_waylandmessagebox.c
index 40edb3e78269b..e32e65c5ab43e 100644
--- a/src/video/wayland/SDL_waylandmessagebox.c
+++ b/src/video/wayland/SDL_waylandmessagebox.c
@@ -23,122 +23,66 @@
#ifdef SDL_VIDEO_DRIVER_WAYLAND
-#include <stdlib.h> // fgets
-#include <stdio.h> // FILE, STDOUT_FILENO, fdopen, fclose
-#include <unistd.h> // pid_t, pipe, fork, close, dup2, execvp, _exit
-#include <sys/wait.h> // waitpid, WIFEXITED, WEXITSTATUS
-#include <string.h> // strerr
-#include <errno.h>
-
#include "SDL_waylandmessagebox.h"
#define ZENITY_VERSION_LEN 32 // Number of bytes to read from zenity --version (including NUL)
#define MAX_BUTTONS 8 // Maximum number of buttons supported
-static bool run_zenity(const char **args, int fd_pipe[2])
+static bool parse_zenity_version(const char *version, int *major, int *minor)
{
- int status;
- pid_t pid1;
-
- pid1 = fork();
- if (pid1 == 0) { // child process
- close(fd_pipe[0]); // no reading from pipe
- // write stdout in pipe
- if (dup2(fd_pipe[1], STDOUT_FILENO) == -1) {
- _exit(128);
- }
- close(fd_pipe[1]);
-
- /* const casting argv is fine:
- * https://pubs.opengroup.org/onlinepubs/9699919799/functions/fexecve.html -> rational
- */
- execvp("zenity", (char **)args);
- _exit(129);
- } else if (pid1 < 0) { // fork() failed
- return SDL_SetError("fork() failed: %s", strerror(errno));
- } else { // parent process
- if (waitpid(pid1, &status, 0) != pid1) {
- return SDL_SetError("Waiting on zenity failed: %s", strerror(errno));
- }
-
- if (!WIFEXITED(status)) {
- return SDL_SetError("zenity failed for some reason");
- }
+ /* We expect the version string is in the form of MAJOR.MINOR.MICRO
+ * as described in meson.build. We'll ignore everything after that.
+ */
+ const char *version_ptr = version;
+ char *end_ptr = NULL;
+ int tmp = (int) SDL_strtol(version_ptr, &end_ptr, 10);
+ if (tmp == 0 && end_ptr == version_ptr) {
+ return SDL_SetError("failed to get zenity major version number");
+ }
+ *major = tmp;
- if (WEXITSTATUS(status) >= 128) {
- return SDL_SetError("zenity reported error or failed to launch: %d", WEXITSTATUS(status));
+ if (*end_ptr == '.') {
+ version_ptr = end_ptr + 1; // skip the dot
+ tmp = (int) SDL_strtol(version_ptr, &end_ptr, 10);
+ if (tmp == 0 && end_ptr == version_ptr) {
+ return SDL_SetError("failed to get zenity minor version number");
}
-
- return true; // success!
+ *minor = tmp;
+ } else {
+ *minor = 0;
}
+ return true;
}
static bool get_zenity_version(int *major, int *minor)
{
- int fd_pipe[2]; // fd_pipe[0]: read end of pipe, fd_pipe[1]: write end of pipe
const char *argv[] = { "zenity", "--version", NULL };
+ bool result = false;
- if (pipe(fd_pipe) != 0) { // create a pipe
- return SDL_SetError("pipe() failed: %s", strerror(errno));
+ SDL_Process *process = SDL_CreateProcess(argv, true);
+ if (!process) {
+ return false;
}
- if (run_zenity(argv, fd_pipe)) {
- FILE *outputfp = NULL;
- char version_str[ZENITY_VERSION_LEN];
- char *version_ptr = NULL, *end_ptr = NULL;
- int tmp;
-
- close(fd_pipe[1]);
- outputfp = fdopen(fd_pipe[0], "r");
- if (!outputfp) {
- close(fd_pipe[0]);
- return SDL_SetError("failed to open pipe for reading: %s", strerror(errno));
- }
-
- version_ptr = fgets(version_str, ZENITY_VERSION_LEN, outputfp);
- (void)fclose(outputfp); // will close underlying fd
-
- if (!version_ptr) {
- return SDL_SetError("failed to read zenity version string");
- }
-
- /* we expect the version string is in the form of MAJOR.MINOR.MICRO
- * as described in meson.build. We'll ignore everything after that.
- */
- tmp = (int) SDL_strtol(version_ptr, &end_ptr, 10);
- if (tmp == 0 && end_ptr == version_ptr) {
- return SDL_SetError("failed to get zenity major version number");
- }
- *major = tmp;
-
- if (*end_ptr == '.') {
- version_ptr = end_ptr + 1; // skip the dot
- tmp = (int) SDL_strtol(version_ptr, &end_ptr, 10);
- if (tmp == 0 && end_ptr == version_ptr) {
- return SDL_SetError("failed to get zenity minor version number");
- }
- *minor = tmp;
- } else {
- *minor = 0;
- }
-
- return true; // success
+ char *output = SDL_ReadProcess(process, NULL, NULL);
+ if (output) {
+ result = parse_zenity_version(output, major, minor);
+ SDL_free(output);
}
+ SDL_DestroyProcess(process);
- close(fd_pipe[0]);
- close(fd_pipe[1]);
- return false; // run_zenity should've called SDL_SetError()
+ return result;
}
bool Wayland_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
{
- int fd_pipe[2]; // fd_pipe[0]: read end of pipe, fd_pipe[1]: write end of pipe
int zenity_major = 0, zenity_minor = 0, output_len = 0;
int argc = 5, i;
const char *argv[5 + 2 /* icon name */ + 2 /* title */ + 2 /* message */ + 2 * MAX_BUTTONS + 1 /* NULL */] = {
"zenity", "--question", "--switch", "--no-wrap", "--no-markup"
};
+ SDL_Process *process;
// Are we trying to connect to or are currently in a Wayland session?
if (!SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "WAYLAND_DISPLAY")) {
@@ -157,10 +101,6 @@ bool Wayland_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *butto
return false; // get_zenity_version() calls SDL_SetError(), so message is already set
}
- if (pipe(fd_pipe) != 0) { // create a pipe
- return SDL_SetError("pipe() failed: %s", strerror(errno));
- }
-
/* https://gitlab.gnome.org/GNOME/zenity/-/commit/c686bdb1b45e95acf010efd9ca0c75527fbb4dea
* This commit removed --icon-name without adding a deprecation notice.
* We need to handle it gracefully, otherwise no message box will be shown.
@@ -208,63 +148,46 @@ bool Wayland_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *butto
}
argv[argc] = NULL;
- if (run_zenity(argv, fd_pipe)) {
- FILE *outputfp = NULL;
- char *output = NULL;
- char *tmp = NULL;
- close(fd_pipe[1]);
-
- if (!buttonID) {
- // if we don't need buttonID, we can return immediately
- close(fd_pipe[0]);
- return true; // success
- }
- *buttonID = -1;
-
- output = SDL_malloc(output_len + 1);
- if (!output) {
- close(fd_pipe[0]);
- return false;
- }
- output[0] = '\0';
-
- outputfp = fdopen(fd_pipe[0], "r");
- if (!outputfp) {
- SDL_free(output);
- close(fd_pipe[0]);
- return SDL_SetError("Couldn't open pipe for reading: %s", strerror(errno));
- }
- tmp = fgets(output, output_len + 1, outputfp);
- (void)fclose(outputfp);
-
- if ((!tmp) || (*tmp == '\0') || (*tmp == '\n')) {
- SDL_free(output);
- return true; // User simply closed the dialog
- }
-
- // It likes to add a newline...
- tmp = SDL_strrchr(output, '\n');
- if (tmp) {
- *tmp = '\0';
- }
+ SDL_PropertiesID props = SDL_CreateProperties();
+ if (!props) {
+ return false;
+ }
+ SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, argv);
+ // If buttonID is set we need to wait and read the results
+ if (buttonID) {
+ SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP);
+ } else {
+ SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_NULL);
+ }
+ process = SDL_CreateProcessWithProperties(props);
+ SDL_DestroyProperties(props);
+ if (!process) {
+ return false;
+ }
+ if (buttonID) {
+ char *output = SDL_ReadProcess(process, NULL, NULL);
+ if (output) {
+ // It likes to add a newline...
+ char *tmp = SDL_strrchr(output, '\n');
+ if (tmp) {
+ *tmp = '\0';
+ }
- // Check which button got pressed
- for (i = 0; i < messageboxdata->numbuttons; i += 1) {
- if (messageboxdata->buttons[i].text) {
- if (SDL_strcmp(output, messageboxdata->buttons[i].text) == 0) {
- *buttonID = messageboxdata->buttons[i].buttonID;
- break;
+ // Check which button got pressed
+ for (i = 0; i < messageboxdata->numbuttons; i += 1) {
+ if (messageboxdata->buttons[i].text) {
+ if (SDL_strcmp(output, messageboxdata->buttons[i].text) == 0) {
+ *buttonID = messageboxdata->buttons[i].buttonID;
+ break;
+ }
}
}
+ SDL_free(output);
}
-
- SDL_free(output);
- return true; // success!
}
+ SDL_DestroyProcess(process);
- close(fd_pipe[0]);
- close(fd_pipe[1]);
- return false; // run_zenity() calls SDL_SetError(), so message is already set
+ return true;
}
#endif // SDL_VIDEO_DRIVER_WAYLAND