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