Audacity 3.2.0
BasicUI.cpp
Go to the documentation of this file.
1/*!********************************************************************
2
3Audacity: A Digital Audio Editor
4
5@file BasicUI.cpp
6
7Paul Licameli
8
9**********************************************************************/
10#include "BasicUI.h"
11
12#include <mutex>
13#include <vector>
14
15#define HAS_XDG_OPEN_HELPER (defined(__linux__) && !defined __ANDROID__) || defined (__FreeBSD__) || defined (__NetBSD__) || defined(__OpenBSD__)
16
17#if HAS_XDG_OPEN_HELPER
18
19#include <sys/types.h>
20#include <sys/wait.h>
21#include <sys/resource.h>
22#include <unistd.h>
23#include <fcntl.h>
24#include <stddef.h>
25#include <string.h>
26#include <stdlib.h>
27#include <stdio.h>
28#include <errno.h>
29
30#include <string>
31
32namespace
33{
34
35const char* ENV_PREFIX = "APPIMAGE_PRESERVED_";
36
37bool IsPreservedEnvVar(const char* var)
38{
39 return strncmp(var, ENV_PREFIX, strlen(ENV_PREFIX)) == 0;
40}
41
42bool ResetEnv()
43{
44 for(auto envIterator = environ; *envIterator; ++envIterator)
45 {
46 auto envVar = *envIterator;
47
48 if (!IsPreservedEnvVar(envVar))
49 continue;
50
51 auto separator = strchr(envVar, '=');
52
53 // Why?
54 if (!separator)
55 continue;
56
57 auto varName = std::string(envVar + strlen(ENV_PREFIX), separator);
58
59 if (varName.empty())
60 continue;
61
62 auto varValue = separator + 1;
63
64 const auto result = *varValue ?
65 setenv(varName.c_str(), varValue, true) :
66 unsetenv(varName.c_str());
67
68 if (result != 0)
69 return false;
70 }
71
72 return true;
73}
74
75std::string FindXDGOpen()
76{
77 const char *path = getenv("PATH");
78
79 if (!path)
80 return {};
81
82 std::string result;
83
84 while (*path) {
85 const char *colon = strchr(path, ':');
86 if (!colon)
87 colon = path + strlen(path);
88
89 result.assign(path, colon);
90 result += "/xdg-open";
91
92 if (access(result.c_str(), X_OK) == 0)
93 return result;
94
95 path = colon;
96
97 if (*path == ':')
98 ++path;
99 }
100
101 return {};
102}
103
104bool RunXDGOpen(const std::string& uri)
105{
106 std::string xdgOpen = FindXDGOpen();
107
108 if (xdgOpen.empty())
109 return false;
110
111 pid_t pid = fork();
112
113 if (pid == -1)
114 return false;
115
116 if (pid == 0)
117 {
118 // Fork again. This way OS is now responsible for cleaning up the
119 // (grand)child process.
120 int secondFork = fork();
121
122 if (secondFork < 0)
123 return false;
124
125 if (secondFork == 0)
126 {// Close all file descriptors except stdin, stdout, stderr
127 struct rlimit rlim;
128 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0)
129 {
130 for (int fd = STDERR_FILENO + 1; fd < rlim.rlim_cur; ++fd)
131 close(fd);
132 }
133
134 // Open /dev/null as stdin, stdout, stderr
135 int fd = open("/dev/null", O_RDWR);
136
137 if (fd != -1)
138 {
139 dup2(fd, STDIN_FILENO);
140 dup2(fd, STDOUT_FILENO);
141 dup2(fd, STDERR_FILENO);
142
143 if (fd > STDERR_FILENO)
144 close(fd);
145 }
146
147 if(!ResetEnv())
148 _exit(1);
149
150 char* const args[] = {
151 const_cast<char*>(xdgOpen.c_str()),
152 const_cast<char*>(uri.c_str()),
153 nullptr
154 };
155 // Run xdg-open
156 execv(xdgOpen.c_str(), args);
157
158 // If we get here, something went wrong
159 _exit(1);
160 }
161 else
162 {
163 // Grandchild has started, so we can exit
164 _exit(0);
165 }
166 }
167 else
168 {
169 int status;
170 waitpid(pid, &status, 0);
171
172 return WIFEXITED(status) && WEXITSTATUS(status) == 0;
173 }
174
175 return false;
176}
177
178}
179
180
181#endif
182
183namespace BasicUI {
185
186Services::~Services() = default;
187
189
191
192static Services *theInstance = nullptr;
193
195
197{
198 auto result = theInstance;
199 theInstance = pInstance;
200 return result;
201}
202
203static std::recursive_mutex sActionsMutex;
204static std::vector<Action> sActions;
205
206void CallAfter(Action action)
207{
208 if (auto p = Get())
209 p->DoCallAfter(action);
210 else {
211 // No services yet -- but don't lose the action. Put it in a queue
212 auto guard = std::lock_guard{ sActionsMutex };
213 sActions.emplace_back(std::move(action));
214 }
215}
216
217void Yield()
218{
219 do {
220 // Dispatch anything in the queue, added while there were no Services
221 {
222 auto guard = std::lock_guard{ sActionsMutex };
223 std::vector<Action> actions;
224 actions.swap(sActions);
225 for (auto &action : actions)
226 action();
227 }
228
229 // Dispatch according to Services, if present
230 if (auto p = Get())
231 p->DoYield();
232 }
233 // Re-test for more actions that might have been enqueued by actions just
234 // dispatched
235 while ( !sActions.empty() );
236}
237
238bool OpenInDefaultBrowser(const wxString &url)
239{
240#if HAS_XDG_OPEN_HELPER
241 if (RunXDGOpen(url.ToStdString()))
242 return true;
243#endif
244
245 if(auto p = Get())
246 return p->DoOpenInDefaultBrowser(url);
247
248 return false;
249}
250
252{
253 return XO("Message");
254}
255}
Toolkit-neutral facade for basic user interface services.
#define XO(s)
Definition: Internat.h:31
Abstract class defines a few user interface services, not mentioning particular toolkits.
Definition: BasicUI.h:189
virtual ~Services()
Holds a msgid for the translation catalog; may also bind format arguments.
static Services * theInstance
Definition: BasicUI.cpp:192
TranslatableString DefaultCaption()
"Message", suitably translated
Definition: BasicUI.cpp:251
bool OpenInDefaultBrowser(const wxString &url)
Open an URL in default browser.
Definition: BasicUI.cpp:238
Services * Install(Services *pInstance)
Install an implementation; return the previously installed instance.
Definition: BasicUI.cpp:196
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:206
static std::vector< Action > sActions
Definition: BasicUI.cpp:204
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:194
std::function< void()> Action
Definition: BasicUI.h:24
static std::recursive_mutex sActionsMutex
Definition: BasicUI.cpp:203
void Yield()
Dispatch waiting events, including actions enqueued by CallAfter.
Definition: BasicUI.cpp:217