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