ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

posix.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #define FORBIDDEN_SYMBOL_EXCEPTION_getenv
00024 #define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
00025 #define FORBIDDEN_SYMBOL_EXCEPTION_exit
00026 #define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
00027 #define FORBIDDEN_SYMBOL_EXCEPTION_time_h   //On IRIX, sys/stat.h includes sys/time.h
00028 #define FORBIDDEN_SYMBOL_EXCEPTION_system
00029 #define FORBIDDEN_SYMBOL_EXCEPTION_random
00030 #define FORBIDDEN_SYMBOL_EXCEPTION_srandom
00031 
00032 #include "common/scummsys.h"
00033 
00034 #ifdef POSIX
00035 
00036 #include "backends/platform/sdl/posix/posix.h"
00037 #include "backends/saves/posix/posix-saves.h"
00038 #include "backends/fs/posix/posix-fs-factory.h"
00039 #include "backends/fs/posix/posix-fs.h"
00040 #include "backends/taskbar/unity/unity-taskbar.h"
00041 
00042 #ifdef USE_LINUXCD
00043 #include "backends/audiocd/linux/linux-audiocd.h"
00044 #endif
00045 
00046 #include "common/textconsole.h"
00047 
00048 #include <stdlib.h>
00049 #include <errno.h>
00050 #include <sys/stat.h>
00051 #include <sys/wait.h>
00052 #include <unistd.h>
00053 
00054 #ifdef HAS_POSIX_SPAWN
00055 #include <spawn.h>
00056 #endif
00057 extern char **environ;
00058 
00059 OSystem_POSIX::OSystem_POSIX(Common::String baseConfigName)
00060     :
00061     _baseConfigName(baseConfigName) {
00062 }
00063 
00064 void OSystem_POSIX::init() {
00065     // Initialze File System Factory
00066     _fsFactory = new POSIXFilesystemFactory();
00067 
00068 #if defined(USE_TASKBAR) && defined(USE_UNITY)
00069     // Initialize taskbar manager
00070     _taskbarManager = new UnityTaskbarManager();
00071 #endif
00072 
00073     // Invoke parent implementation of this method
00074     OSystem_SDL::init();
00075 }
00076 
00077 void OSystem_POSIX::initBackend() {
00078     // Create the savefile manager
00079     if (_savefileManager == 0)
00080         _savefileManager = new POSIXSaveFileManager();
00081 
00082     // Invoke parent implementation of this method
00083     OSystem_SDL::initBackend();
00084 
00085 #if defined(USE_TASKBAR) && defined(USE_UNITY)
00086     // Register the taskbar manager as an event source (this is necessary for the glib event loop to be run)
00087     _eventManager->getEventDispatcher()->registerSource((UnityTaskbarManager *)_taskbarManager, false);
00088 #endif
00089 }
00090 
00091 bool OSystem_POSIX::hasFeature(Feature f) {
00092     if (f == kFeatureDisplayLogFile)
00093         return true;
00094 #ifdef HAS_POSIX_SPAWN
00095     if (f == kFeatureOpenUrl)
00096         return true;
00097 #endif
00098     return OSystem_SDL::hasFeature(f);
00099 }
00100 
00101 Common::String OSystem_POSIX::getDefaultConfigFileName() {
00102     Common::String configFile;
00103 
00104     Common::String prefix;
00105 #ifdef MACOSX
00106     prefix = getenv("HOME");
00107 #elif !defined(SAMSUNGTV)
00108     const char *envVar;
00109     // Our old configuration file path for POSIX systems was ~/.residualvmrc.
00110     // If that file exists, we still use it.
00111     envVar = getenv("HOME");
00112     if (envVar && *envVar) {
00113         configFile = envVar;
00114         configFile += '/';
00115         configFile += ".residualvmrc";
00116 
00117         if (configFile.size() < MAXPATHLEN) {
00118             struct stat sb;
00119             if (stat(configFile.c_str(), &sb) == 0) {
00120                 return configFile;
00121             }
00122         }
00123     }
00124 
00125     // On POSIX systems we follow the XDG Base Directory Specification for
00126     // where to store files. The version we based our code upon can be found
00127     // over here: http://standards.freedesktop.org/basedir-spec/basedir-spec-0.8.html
00128     envVar = getenv("XDG_CONFIG_HOME");
00129     if (!envVar || !*envVar) {
00130         envVar = getenv("HOME");
00131         if (!envVar) {
00132             return 0;
00133         }
00134 
00135         if (Posix::assureDirectoryExists(".config", envVar)) {
00136             prefix = envVar;
00137             prefix += "/.config";
00138         }
00139     } else {
00140         prefix = envVar;
00141     }
00142 
00143     if (!prefix.empty() && Posix::assureDirectoryExists("residualvm", prefix.c_str())) {
00144         prefix += "/residualvm";
00145     }
00146 #endif
00147 
00148     if (!prefix.empty() && (prefix.size() + 1 + _baseConfigName.size()) < MAXPATHLEN) {
00149         configFile = prefix;
00150         configFile += '/';
00151         configFile += _baseConfigName;
00152     } else {
00153         configFile = _baseConfigName;
00154     }
00155 
00156     return configFile;
00157 }
00158 
00159 Common::String OSystem_POSIX::getXdgUserDir(const char *name) {
00160     // The xdg-user-dirs configuration path is stored in the XDG config
00161     // home directory. We start by retrieving this value.
00162     Common::String configHome = getenv("XDG_CONFIG_HOME");
00163     if (configHome.empty()) {
00164         const char *home = getenv("HOME");
00165         if (!home) {
00166             return "";
00167         }
00168 
00169         configHome = Common::String::format("%s/.config", home);
00170     }
00171 
00172     // Find the requested directory line in the xdg-user-dirs configuration file
00173     //   Example line value: XDG_PICTURES_DIR="$HOME/Pictures"
00174     Common::FSNode userDirsFile(configHome + "/user-dirs.dirs");
00175     if (!userDirsFile.exists() || !userDirsFile.isReadable() || userDirsFile.isDirectory()) {
00176         return "";
00177     }
00178 
00179     Common::SeekableReadStream *userDirsStream = userDirsFile.createReadStream();
00180     if (!userDirsStream) {
00181         return "";
00182     }
00183 
00184     Common::String dirLinePrefix = Common::String::format("XDG_%s_DIR=", name);
00185 
00186     Common::String directoryValue;
00187     while (!userDirsStream->eos() && !userDirsStream->err()) {
00188         Common::String userDirsLine = userDirsStream->readLine();
00189         userDirsLine.trim();
00190 
00191         if (userDirsLine.hasPrefix(dirLinePrefix)) {
00192             directoryValue = Common::String(userDirsLine.c_str() + dirLinePrefix.size());
00193             break;
00194         }
00195     }
00196 
00197     delete userDirsStream;
00198 
00199     // Extract the path from the value
00200     //   Example value: "$HOME/Pictures"
00201     if (directoryValue.empty() || directoryValue[0] != '"') {
00202         return "";
00203     }
00204 
00205     if (directoryValue[directoryValue.size() - 1] != '"') {
00206         return "";
00207     }
00208 
00209     // According to the spec the value is shell-escaped, and would need to be
00210     // unescaped to be used, but neither the GTK+ nor the Qt implementation seem to
00211     // properly perform that step, it's probably fine if we don't do it either.
00212     Common::String directoryPath(directoryValue.c_str() + 1, directoryValue.size() - 2);
00213 
00214     if (directoryPath.hasPrefix("$HOME/")) {
00215         const char *home = getenv("HOME");
00216         directoryPath = Common::String::format("%s%s", home, directoryPath.c_str() + 5);
00217     }
00218 
00219     // At this point, the path must be absolute
00220     if (directoryPath.empty() || directoryPath[0] != '/') {
00221         return "";
00222     }
00223 
00224     return directoryPath;
00225 }
00226 
00227 Common::String OSystem_POSIX::getScreenshotsPath() {
00228     // If the user has configured a screenshots path, use it
00229     const Common::String path = OSystem_SDL::getScreenshotsPath();
00230     if (!path.empty()) {
00231         return path;
00232     }
00233 
00234     // Otherwise, the default screenshots path is the "ResidualVM Screenshots"
00235     // directory in the XDG "Pictures" user directory, as defined in the
00236     // xdg-user-dirs spec: https://www.freedesktop.org/wiki/Software/xdg-user-dirs/
00237     Common::String picturesPath = getXdgUserDir("PICTURES");
00238     if (picturesPath.empty()) {
00239         return "";
00240     }
00241 
00242     if (!picturesPath.hasSuffix("/")) {
00243         picturesPath += "/";
00244     }
00245 
00246     static const char *SCREENSHOTS_DIR_NAME = "ResidualVM Screenshots";
00247     if (!Posix::assureDirectoryExists(SCREENSHOTS_DIR_NAME, picturesPath.c_str())) {
00248         return "";
00249     }
00250 
00251     return picturesPath + SCREENSHOTS_DIR_NAME + "/";
00252 }
00253 
00254 void OSystem_POSIX::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
00255 #ifdef DATA_PATH
00256     const char *snap = getenv("SNAP");
00257     if (snap) {
00258         Common::String dataPath = Common::String(snap) + DATA_PATH;
00259         Common::FSNode dataNode(dataPath);
00260         if (dataNode.exists() && dataNode.isDirectory()) {
00261             // This is the same priority which is used for the data path (below),
00262             // but we insert this one first, so it will be searched first.
00263             s.add(dataPath, new Common::FSDirectory(dataNode, 4), priority);
00264         }
00265     }
00266 #endif
00267 
00268     // For now, we always add the data path, just in case SNAP doesn't make sense.
00269     OSystem_SDL::addSysArchivesToSearchSet(s, priority);
00270 }
00271 
00272 Common::WriteStream *OSystem_POSIX::createLogFile() {
00273     // Start out by resetting _logFilePath, so that in case
00274     // of a failure, we know that no log file is open.
00275     _logFilePath.clear();
00276 
00277     const char *prefix = nullptr;
00278     Common::String logFile;
00279 #ifdef MACOSX
00280     prefix = getenv("HOME");
00281     if (prefix == nullptr) {
00282         return 0;
00283     }
00284 
00285     logFile = "Library/Logs";
00286 #elif SAMSUNGTV
00287     prefix = nullptr;
00288     logFile = "/mtd_ram";
00289 #else
00290     // On POSIX systems we follow the XDG Base Directory Specification for
00291     // where to store files. The version we based our code upon can be found
00292     // over here: http://standards.freedesktop.org/basedir-spec/basedir-spec-0.8.html
00293     prefix = getenv("XDG_CACHE_HOME");
00294     if (prefix == nullptr || !*prefix) {
00295         prefix = getenv("HOME");
00296         if (prefix == nullptr) {
00297             return 0;
00298         }
00299 
00300         logFile = ".cache/";
00301     }
00302 
00303     logFile += "residualvm/logs";
00304 #endif
00305 
00306     if (!Posix::assureDirectoryExists(logFile, prefix)) {
00307         return 0;
00308     }
00309 
00310     if (prefix) {
00311         logFile = Common::String::format("%s/%s", prefix, logFile.c_str());
00312     }
00313 
00314     logFile += "/residualvm.log";
00315 
00316     Common::FSNode file(logFile);
00317     Common::WriteStream *stream = file.createWriteStream();
00318     if (stream)
00319         _logFilePath = logFile;
00320     return stream;
00321 }
00322 
00323 bool OSystem_POSIX::displayLogFile() {
00324     if (_logFilePath.empty())
00325         return false;
00326 
00327     // FIXME: This may not work perfectly when in fullscreen mode.
00328     // On my system it drops from fullscreen without ScummVM noticing,
00329     // so the next Alt-Enter does nothing, going from windowed to windowed.
00330     // (wjp, 20110604)
00331 
00332     pid_t pid = fork();
00333     if (pid < 0) {
00334         // failed to fork
00335         return false;
00336     } else if (pid == 0) {
00337 
00338         // Try xdg-open first
00339         execlp("xdg-open", "xdg-open", _logFilePath.c_str(), (char *)0);
00340 
00341         // If we're here, that clearly failed.
00342 
00343         // TODO: We may also want to try detecting the case where
00344         // xdg-open is successfully executed but returns an error code.
00345 
00346         // Try xterm+less next
00347 
00348         execlp("xterm", "xterm", "-e", "less", _logFilePath.c_str(), (char *)0);
00349 
00350         // TODO: If less does not exist we could fall back to 'more'.
00351         // However, we'll have to use 'xterm -hold' for that to prevent the
00352         // terminal from closing immediately (for short log files) or
00353         // unexpectedly.
00354 
00355         exit(127);
00356     }
00357 
00358     int status;
00359     // Wait for viewer to close.
00360     // (But note that xdg-open may have spawned a viewer in the background.)
00361 
00362     // FIXME: We probably want the viewer to always open in the background.
00363     // This may require installing a SIGCHLD handler.
00364     pid = waitpid(pid, &status, 0);
00365 
00366     if (pid < 0) {
00367         // Probably nothing sensible to do in this error situation
00368         return false;
00369     }
00370 
00371     return WIFEXITED(status) && WEXITSTATUS(status) == 0;
00372 }
00373 
00374 bool OSystem_POSIX::openUrl(const Common::String &url) {
00375 #ifdef HAS_POSIX_SPAWN
00376     // inspired by Qt's "qdesktopservices_x11.cpp"
00377 
00378     // try "standards"
00379     if (launchBrowser("xdg-open", url))
00380         return true;
00381     if (launchBrowser(getenv("DEFAULT_BROWSER"), url))
00382         return true;
00383     if (launchBrowser(getenv("BROWSER"), url))
00384         return true;
00385 
00386     // try desktop environment specific tools
00387     if (launchBrowser("gnome-open", url)) // gnome
00388         return true;
00389     if (launchBrowser("kfmclient", url)) // kde
00390         return true;
00391     if (launchBrowser("exo-open", url)) // xfce
00392         return true;
00393 
00394     // try browser names
00395     if (launchBrowser("firefox", url))
00396         return true;
00397     if (launchBrowser("mozilla", url))
00398         return true;
00399     if (launchBrowser("netscape", url))
00400         return true;
00401     if (launchBrowser("opera", url))
00402         return true;
00403     if (launchBrowser("chromium-browser", url))
00404         return true;
00405     if (launchBrowser("google-chrome", url))
00406         return true;
00407 
00408     warning("openUrl() (POSIX) failed to open URL");
00409     return false;
00410 #else
00411     return false;
00412 #endif
00413 }
00414 
00415 bool OSystem_POSIX::launchBrowser(const Common::String &client, const Common::String &url) {
00416 #ifdef HAS_POSIX_SPAWN
00417     pid_t pid;
00418     const char *argv[] = {
00419         client.c_str(),
00420         url.c_str(),
00421         NULL,
00422         NULL
00423     };
00424     if (client == "kfmclient") {
00425         argv[2] = argv[1];
00426         argv[1] = "openURL";
00427     }
00428     if (posix_spawnp(&pid, client.c_str(), NULL, NULL, const_cast<char **>(argv), environ) != 0) {
00429         return false;
00430     }
00431     return (waitpid(pid, NULL, WNOHANG) != -1);
00432 #else
00433     return false;
00434 #endif
00435 }
00436 
00437 AudioCDManager *OSystem_POSIX::createAudioCDManager() {
00438 #ifdef USE_LINUXCD
00439     return createLinuxAudioCDManager();
00440 #else
00441     return OSystem_SDL::createAudioCDManager();
00442 #endif
00443 }
00444 
00445 #endif


Generated on Sat May 18 2019 05:01:13 for ResidualVM by doxygen 1.7.1
curved edge   curved edge