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