1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org> |
4 | |
5 | This software is provided 'as-is', without any express or implied |
6 | warranty. In no event will the authors be held liable for any damages |
7 | arising from the use of this software. |
8 | |
9 | Permission is granted to anyone to use this software for any purpose, |
10 | including commercial applications, and to alter it and redistribute it |
11 | freely, subject to the following restrictions: |
12 | |
13 | 1. The origin of this software must not be misrepresented; you must not |
14 | claim that you wrote the original software. If you use this software |
15 | in a product, an acknowledgment in the product documentation would be |
16 | appreciated but is not required. |
17 | 2. Altered source versions must be plainly marked as such, and must not be |
18 | misrepresented as being the original software. |
19 | 3. This notice may not be removed or altered from any source distribution. |
20 | */ |
21 | |
22 | #include "../../SDL_internal.h" |
23 | |
24 | #if SDL_VIDEO_DRIVER_WAYLAND |
25 | |
26 | #include "SDL.h" |
27 | #include <stdlib.h> /* popen/pclose/fgets */ |
28 | |
29 | int |
30 | Wayland_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) |
31 | { |
32 | #define ZENITY_CONST(name, str) \ |
33 | const char *name = str; \ |
34 | const size_t name##_len = SDL_strlen(name); |
35 | ZENITY_CONST(zenity, "zenity --question --switch --no-wrap --icon-name=dialog-" ) |
36 | ZENITY_CONST(title, "--title=" ) |
37 | ZENITY_CONST(message, "--text=" ) |
38 | ZENITY_CONST(, "--extra-button=" ) |
39 | ZENITY_CONST(icon_info, "information" ) |
40 | ZENITY_CONST(icon_warn, "warning" ) |
41 | ZENITY_CONST(icon_error, "error" ) |
42 | #undef ZENITY_CONST |
43 | |
44 | char *command, *output; |
45 | size_t command_len, output_len; |
46 | const char *icon; |
47 | char *tmp; |
48 | FILE *process; |
49 | int i; |
50 | |
51 | /* Start by calculating the lengths of the strings. These commands can get |
52 | * pretty long, so we need to dynamically allocate this. |
53 | */ |
54 | command_len = zenity_len; |
55 | output_len = 0; |
56 | switch (messageboxdata->flags) |
57 | { |
58 | case SDL_MESSAGEBOX_ERROR: |
59 | icon = icon_error; |
60 | command_len += icon_error_len; |
61 | break; |
62 | case SDL_MESSAGEBOX_WARNING: |
63 | icon = icon_warn; |
64 | command_len += icon_warn_len; |
65 | break; |
66 | case SDL_MESSAGEBOX_INFORMATION: |
67 | default: |
68 | icon = icon_info; |
69 | command_len += icon_info_len; |
70 | break; |
71 | } |
72 | #define ADD_ARGUMENT(arg, value) \ |
73 | command_len += arg + 3; /* Two " and a space */ \ |
74 | if (messageboxdata->value != NULL) { \ |
75 | command_len += SDL_strlen(messageboxdata->value); \ |
76 | } |
77 | ADD_ARGUMENT(title_len, title) |
78 | ADD_ARGUMENT(message_len, message) |
79 | #undef ADD_ARGUMENT |
80 | for (i = 0; i < messageboxdata->numbuttons; i += 1) { |
81 | command_len += extrabutton_len + 3; /* Two " and a space */ |
82 | if (messageboxdata->buttons[i].text != NULL) { |
83 | const size_t button_len = SDL_strlen(messageboxdata->buttons[i].text); |
84 | command_len += button_len; |
85 | if (button_len > output_len) { |
86 | output_len = button_len; |
87 | } |
88 | } |
89 | } |
90 | |
91 | /* Don't forget null terminators! */ |
92 | command_len += 1; |
93 | output_len += 1; |
94 | |
95 | /* Now that we know the length of the command, allocate! */ |
96 | command = (char*) SDL_malloc(command_len + output_len); |
97 | if (command == NULL) { |
98 | return SDL_OutOfMemory(); |
99 | } |
100 | output = command + command_len; |
101 | command[0] = '\0'; |
102 | output[0] = '\0'; |
103 | |
104 | /* Now we can build the command... */ |
105 | SDL_strlcpy(command, zenity, command_len); |
106 | SDL_strlcat(command, icon, command_len); |
107 | #define ADD_ARGUMENT(arg, value) \ |
108 | SDL_strlcat(command, " ", command_len); \ |
109 | SDL_strlcat(command, arg, command_len); \ |
110 | SDL_strlcat(command, "\"", command_len); \ |
111 | if (value != NULL) { \ |
112 | SDL_strlcat(command, value, command_len); \ |
113 | } \ |
114 | SDL_strlcat(command, "\"", command_len) |
115 | ADD_ARGUMENT(title, messageboxdata->title); |
116 | ADD_ARGUMENT(message, messageboxdata->message); |
117 | for (i = 0; i < messageboxdata->numbuttons; i += 1) { |
118 | ADD_ARGUMENT(extrabutton, messageboxdata->buttons[i].text); |
119 | } |
120 | #undef ADD_ARGUMENT |
121 | |
122 | /* ... then run it, finally. */ |
123 | process = popen(command, "r" ); |
124 | if (process == NULL) { |
125 | SDL_free(command); |
126 | return SDL_SetError("zenity failed to run" ); |
127 | } |
128 | |
129 | /* At this point, if no button ID is needed, we can just bail as soon as the |
130 | * process has completed. |
131 | */ |
132 | if (buttonid == NULL) { |
133 | pclose(process); |
134 | goto end; |
135 | } |
136 | *buttonid = -1; |
137 | |
138 | /* Read the result from stdout */ |
139 | tmp = fgets(output, output_len, process); |
140 | pclose(process); |
141 | if ((tmp == NULL) || (*tmp == '\0') || (*tmp == '\n')) { |
142 | goto end; /* User simply closed the dialog */ |
143 | } |
144 | |
145 | /* It likes to add a newline... */ |
146 | tmp = SDL_strrchr(output, '\n'); |
147 | if (tmp != NULL) { |
148 | *tmp = '\0'; |
149 | } |
150 | |
151 | /* Check which button got pressed */ |
152 | for (i = 0; i < messageboxdata->numbuttons; i += 1) { |
153 | if (messageboxdata->buttons[i].text != NULL) { |
154 | if (SDL_strcmp(output, messageboxdata->buttons[i].text) == 0) { |
155 | *buttonid = i; |
156 | break; |
157 | } |
158 | } |
159 | } |
160 | |
161 | end: |
162 | SDL_free(command); |
163 | return 0; |
164 | } |
165 | |
166 | #endif /* SDL_VIDEO_DRIVER_WAYLAND */ |
167 | |
168 | /* vi: set ts=4 sw=4 expandtab: */ |
169 | |