1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #define FORBIDDEN_SYMBOL_EXCEPTION_getenv
24 #define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
25 #define FORBIDDEN_SYMBOL_EXCEPTION_exit
26 #define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
27 #define FORBIDDEN_SYMBOL_EXCEPTION_time_h //On IRIX, sys/stat.h includes sys/time.h
28 #define FORBIDDEN_SYMBOL_EXCEPTION_system
29
30 #include "common/scummsys.h"
31
32 #ifdef POSIX
33
34 #include "backends/platform/sdl/posix/posix.h"
35 #include "backends/saves/posix/posix-saves.h"
36 #include "backends/fs/posix/posix-fs-factory.h"
37 #include "backends/fs/posix/posix-fs.h"
38 #include "backends/taskbar/unity/unity-taskbar.h"
39
40 #ifdef USE_LINUXCD
41 #include "backends/audiocd/linux/linux-audiocd.h"
42 #endif
43
44 #include "common/textconsole.h"
45
46 #include <stdlib.h>
47 #include <errno.h>
48 #include <sys/stat.h>
49 #include <sys/wait.h>
50 #include <unistd.h>
51
OSystem_POSIX(Common::String baseConfigName)52 OSystem_POSIX::OSystem_POSIX(Common::String baseConfigName)
53 :
54 _baseConfigName(baseConfigName) {
55 }
56
init()57 void OSystem_POSIX::init() {
58 // Initialze File System Factory
59 _fsFactory = new POSIXFilesystemFactory();
60
61 #if defined(USE_TASKBAR) && defined(USE_UNITY)
62 // Initialize taskbar manager
63 _taskbarManager = new UnityTaskbarManager();
64 #endif
65
66 // Invoke parent implementation of this method
67 OSystem_SDL::init();
68 }
69
initBackend()70 void OSystem_POSIX::initBackend() {
71 // Create the savefile manager
72 if (_savefileManager == 0)
73 _savefileManager = new POSIXSaveFileManager();
74
75 // Invoke parent implementation of this method
76 OSystem_SDL::initBackend();
77
78 #if defined(USE_TASKBAR) && defined(USE_UNITY)
79 // Register the taskbar manager as an event source (this is necessary for the glib event loop to be run)
80 _eventManager->getEventDispatcher()->registerSource((UnityTaskbarManager *)_taskbarManager, false);
81 #endif
82 }
83
hasFeature(Feature f)84 bool OSystem_POSIX::hasFeature(Feature f) {
85 if (f == kFeatureDisplayLogFile || f == kFeatureOpenUrl)
86 return true;
87 return OSystem_SDL::hasFeature(f);
88 }
89
getDefaultConfigFileName()90 Common::String OSystem_POSIX::getDefaultConfigFileName() {
91 Common::String configFile;
92
93 Common::String prefix;
94 #ifdef MACOSX
95 prefix = getenv("HOME");
96 #elif !defined(SAMSUNGTV)
97 const char *envVar;
98 // Our old configuration file path for POSIX systems was ~/.residualvmrc.
99 // If that file exists, we still use it.
100 envVar = getenv("HOME");
101 if (envVar && *envVar) {
102 configFile = envVar;
103 configFile += '/';
104 configFile += ".residualvmrc";
105
106 if (configFile.size() < MAXPATHLEN) {
107 struct stat sb;
108 if (stat(configFile.c_str(), &sb) == 0) {
109 return configFile;
110 }
111 }
112 }
113
114 // On POSIX systems we follow the XDG Base Directory Specification for
115 // where to store files. The version we based our code upon can be found
116 // over here: http://standards.freedesktop.org/basedir-spec/basedir-spec-0.8.html
117 envVar = getenv("XDG_CONFIG_HOME");
118 if (!envVar || !*envVar) {
119 envVar = getenv("HOME");
120 if (!envVar) {
121 return 0;
122 }
123
124 if (Posix::assureDirectoryExists(".config", envVar)) {
125 prefix = envVar;
126 prefix += "/.config";
127 }
128 } else {
129 prefix = envVar;
130 }
131
132 if (!prefix.empty() && Posix::assureDirectoryExists("residualvm", prefix.c_str())) {
133 prefix += "/residualvm";
134 }
135 #endif
136
137 if (!prefix.empty() && (prefix.size() + 1 + _baseConfigName.size()) < MAXPATHLEN) {
138 configFile = prefix;
139 configFile += '/';
140 configFile += _baseConfigName;
141 } else {
142 configFile = _baseConfigName;
143 }
144
145 return configFile;
146 }
147
addSysArchivesToSearchSet(Common::SearchSet & s,int priority)148 void OSystem_POSIX::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
149 #ifdef DATA_PATH
150 const char *snap = getenv("SNAP");
151 if (snap) {
152 Common::String dataPath = Common::String(snap) + DATA_PATH;
153 Common::FSNode dataNode(dataPath);
154 if (dataNode.exists() && dataNode.isDirectory()) {
155 // This is the same priority which is used for the data path (below),
156 // but we insert this one first, so it will be searched first.
157 s.add(dataPath, new Common::FSDirectory(dataNode, 4), priority);
158 }
159 }
160 #endif
161
162 // For now, we always add the data path, just in case SNAP doesn't make sense.
163 OSystem_SDL::addSysArchivesToSearchSet(s, priority);
164 }
165
createLogFile()166 Common::WriteStream *OSystem_POSIX::createLogFile() {
167 // Start out by resetting _logFilePath, so that in case
168 // of a failure, we know that no log file is open.
169 _logFilePath.clear();
170
171 const char *prefix = nullptr;
172 Common::String logFile;
173 #ifdef MACOSX
174 prefix = getenv("HOME");
175 if (prefix == nullptr) {
176 return 0;
177 }
178
179 logFile = "Library/Logs";
180 #elif SAMSUNGTV
181 prefix = nullptr;
182 logFile = "/mtd_ram";
183 #else
184 // On POSIX systems we follow the XDG Base Directory Specification for
185 // where to store files. The version we based our code upon can be found
186 // over here: http://standards.freedesktop.org/basedir-spec/basedir-spec-0.8.html
187 prefix = getenv("XDG_CACHE_HOME");
188 if (prefix == nullptr || !*prefix) {
189 prefix = getenv("HOME");
190 if (prefix == nullptr) {
191 return 0;
192 }
193
194 logFile = ".cache/";
195 }
196
197 logFile += "residualvm/logs";
198 #endif
199
200 if (!Posix::assureDirectoryExists(logFile, prefix)) {
201 return 0;
202 }
203
204 if (prefix) {
205 logFile = Common::String::format("%s/%s", prefix, logFile.c_str());
206 }
207
208 logFile += "/residualvm.log";
209
210 Common::FSNode file(logFile);
211 Common::WriteStream *stream = file.createWriteStream();
212 if (stream)
213 _logFilePath = logFile;
214 return stream;
215 }
216
displayLogFile()217 bool OSystem_POSIX::displayLogFile() {
218 if (_logFilePath.empty())
219 return false;
220
221 // FIXME: This may not work perfectly when in fullscreen mode.
222 // On my system it drops from fullscreen without ScummVM noticing,
223 // so the next Alt-Enter does nothing, going from windowed to windowed.
224 // (wjp, 20110604)
225
226 pid_t pid = fork();
227 if (pid < 0) {
228 // failed to fork
229 return false;
230 } else if (pid == 0) {
231
232 // Try xdg-open first
233 execlp("xdg-open", "xdg-open", _logFilePath.c_str(), (char *)0);
234
235 // If we're here, that clearly failed.
236
237 // TODO: We may also want to try detecting the case where
238 // xdg-open is successfully executed but returns an error code.
239
240 // Try xterm+less next
241
242 execlp("xterm", "xterm", "-e", "less", _logFilePath.c_str(), (char *)0);
243
244 // TODO: If less does not exist we could fall back to 'more'.
245 // However, we'll have to use 'xterm -hold' for that to prevent the
246 // terminal from closing immediately (for short log files) or
247 // unexpectedly.
248
249 exit(127);
250 }
251
252 int status;
253 // Wait for viewer to close.
254 // (But note that xdg-open may have spawned a viewer in the background.)
255
256 // FIXME: We probably want the viewer to always open in the background.
257 // This may require installing a SIGCHLD handler.
258 pid = waitpid(pid, &status, 0);
259
260 if (pid < 0) {
261 // Probably nothing sensible to do in this error situation
262 return false;
263 }
264
265 return WIFEXITED(status) && WEXITSTATUS(status) == 0;
266 }
267
openUrl(const Common::String & url)268 bool OSystem_POSIX::openUrl(const Common::String &url) {
269 // inspired by Qt's "qdesktopservices_x11.cpp"
270
271 // try "standards"
272 if (launchBrowser("xdg-open", url))
273 return true;
274 if (launchBrowser(getenv("DEFAULT_BROWSER"), url))
275 return true;
276 if (launchBrowser(getenv("BROWSER"), url))
277 return true;
278
279 // try desktop environment specific tools
280 if (launchBrowser("gnome-open", url)) // gnome
281 return true;
282 if (launchBrowser("kfmclient openURL", url)) // kde
283 return true;
284 if (launchBrowser("exo-open", url)) // xfce
285 return true;
286
287 // try browser names
288 if (launchBrowser("firefox", url))
289 return true;
290 if (launchBrowser("mozilla", url))
291 return true;
292 if (launchBrowser("netscape", url))
293 return true;
294 if (launchBrowser("opera", url))
295 return true;
296 if (launchBrowser("chromium-browser", url))
297 return true;
298 if (launchBrowser("google-chrome", url))
299 return true;
300
301 warning("openUrl() (POSIX) failed to open URL");
302 return false;
303 }
304
launchBrowser(const Common::String & client,const Common::String & url)305 bool OSystem_POSIX::launchBrowser(const Common::String& client, const Common::String &url) {
306 // FIXME: system's input must be heavily escaped
307 // well, when url's specified by user
308 // it's OK now (urls are hardcoded somewhere in GUI)
309 Common::String cmd = client + " " + url;
310 return (system(cmd.c_str()) != -1);
311 }
312
313
createAudioCDManager()314 AudioCDManager *OSystem_POSIX::createAudioCDManager() {
315 #ifdef USE_LINUXCD
316 return createLinuxAudioCDManager();
317 #else
318 return OSystem_SDL::createAudioCDManager();
319 #endif
320 }
321
322 #endif
323