1 /*
2 * Stellarium
3 * Copyright (C) 2002 Fabien Chereau
4 * Copyright (C) 2012 Timothy Reaves
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
19 */
20
21 #include "StelMainView.hpp"
22 #include "StelSplashScreen.hpp"
23 #include "StelTranslator.hpp"
24 #include "StelLogger.hpp"
25 #include "StelFileMgr.hpp"
26 #include "CLIProcessor.hpp"
27 #include "StelIniParser.hpp"
28 #include "StelUtils.hpp"
29 #ifdef ENABLE_SCRIPTING
30 #include "StelScriptOutput.hpp"
31 #endif
32
33 #include <QDebug>
34
35 #ifndef USE_QUICKVIEW
36 #include <QApplication>
37 #include <QMessageBox>
38 #include <QStyleFactory>
39 #else
40 #include <QGuiApplication>
41 #endif
42 #include <QCoreApplication>
43 #include <QDir>
44 #include <QFile>
45 #include <QFileInfo>
46 #include <QFontDatabase>
47 #include <QGuiApplication>
48 #include <QSettings>
49 #include <QString>
50 #include <QStringList>
51 #include <QTextStream>
52 #include <QTranslator>
53 #include <QNetworkDiskCache>
54 #include <QThread>
55
56 #include <clocale>
57
58 #ifdef Q_OS_WIN
59 #include <Windows.h>
60 //we use WIN32_LEAN_AND_MEAN so this needs to be included
61 //to use timeBeginPeriod/timeEndPeriod
62 #include <mmsystem.h>
63
64 // Default to High Performance Mode on machines with hybrid graphics
65 // Details: https://stackoverflow.com/questions/44174859/how-to-give-an-option-to-select-graphics-adapter-in-a-directx-11-application
66 extern "C"
67 {
68 #ifdef _MSC_VER
69 __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
70 __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 0x00000001;
71 #else
72 __attribute__((dllexport)) DWORD NvOptimusEnablement = 0x00000001;
73 __attribute__((dllexport)) int AmdPowerXpressRequestHighPerformance = 0x00000001;
74 #endif
75 }
76 #else
77 extern "C"
78 {
79 int NvOptimusEnablement = 1;
80 int AmdPowerXpressRequestHighPerformance = 1;
81 }
82 #endif //Q_OS_WIN
83
84 //! @class CustomQTranslator
85 //! Provides custom i18n support.
86 class CustomQTranslator : public QTranslator
87 {
88 using QTranslator::translate;
89 public:
isEmpty() const90 virtual bool isEmpty() const { return false; }
91
92 //! Overrides QTranslator::translate().
93 //! Calls StelTranslator::qtranslate().
94 //! @param context Qt context string - IGNORED.
95 //! @param sourceText the source message.
96 //! @param comment optional parameter
translate(const char * context,const char * sourceText,const char * disambiguation=Q_NULLPTR,int n=-1) const97 virtual QString translate(const char *context, const char *sourceText, const char *disambiguation = Q_NULLPTR, int n = -1) const
98 {
99 Q_UNUSED(context);
100 Q_UNUSED(n);
101 return StelTranslator::globalTranslator->qtranslate(sourceText, disambiguation);
102 }
103 };
104
105
106 //! Copies the default configuration file.
107 //! This function copies the default_cfg.ini file to config.ini (or other
108 //! name specified on the command line located in the user data directory.
copyDefaultConfigFile(const QString & newPath)109 void copyDefaultConfigFile(const QString& newPath)
110 {
111 QString defaultConfigFilePath = StelFileMgr::findFile("data/default_cfg.ini");
112 if (defaultConfigFilePath.isEmpty())
113 qFatal("ERROR copyDefaultConfigFile failed to locate data/default_cfg.ini. Please check your installation.");
114 QFile::copy(defaultConfigFilePath, newPath);
115 if (!StelFileMgr::exists(newPath))
116 {
117 qFatal("ERROR copyDefaultConfigFile failed to copy file %s to %s. You could try to copy it by hand.",
118 qPrintable(defaultConfigFilePath), qPrintable(newPath));
119 }
120 QFile::setPermissions(newPath, QFile::permissions(newPath) | QFileDevice::WriteOwner);
121 }
122
123 //! Removes all items from the cache.
clearCache()124 void clearCache()
125 {
126 QNetworkDiskCache* cacheMgr = new QNetworkDiskCache();
127 cacheMgr->setCacheDirectory(StelFileMgr::getCacheDir());
128 cacheMgr->clear(); // Removes all items from the cache.
129 }
130
131 // Main stellarium procedure
main(int argc,char ** argv)132 int main(int argc, char **argv)
133 {
134 Q_INIT_RESOURCE(mainRes);
135 Q_INIT_RESOURCE(guiRes);
136
137 // Log command line arguments.
138 QString argStr;
139 QStringList argList;
140 for (int i=0; i<argc; ++i)
141 {
142 argList << argv[i];
143 argStr += QString("%1 ").arg(argv[i]);
144 }
145
146 #ifdef Q_OS_WIN
147 // Fix for the speeding system clock bug on systems that use ACPI
148 // See http://support.microsoft.com/kb/821893
149 UINT timerGrain = 1;
150 if (timeBeginPeriod(timerGrain) == TIMERR_NOCANDO)
151 {
152 // If this is too fine a grain, try the lowest value used by a timer
153 timerGrain = 5;
154 if (timeBeginPeriod(timerGrain) == TIMERR_NOCANDO)
155 timerGrain = 0;
156 }
157 #endif
158
159 // Seed the PRNG
160 qsrand(QDateTime::currentMSecsSinceEpoch());
161
162 QCoreApplication::setApplicationName("stellarium");
163 QCoreApplication::setApplicationVersion(StelUtils::getApplicationVersion());
164 QCoreApplication::setOrganizationDomain("stellarium.org");
165 QCoreApplication::setOrganizationName("stellarium");
166
167 // Support high DPI pixmaps and fonts
168 QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
169 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
170 if (argList.contains("--scale-gui")) // Variable QT_SCALE_FACTOR should be defined before app will be created!
171 qputenv("QT_SCALE_FACTOR", CLIProcessor::argsGetOptionWithArg(argList, "", "--scale-gui", "").toString().toLatin1());
172
173 #if defined(Q_OS_MAC)
174 QFileInfo appInfo(QString::fromUtf8(argv[0]));
175 QDir appDir(appInfo.absolutePath());
176 appDir.cdUp();
177 QCoreApplication::addLibraryPath(appDir.absoluteFilePath("PlugIns"));
178 #elif defined(Q_OS_WIN)
179 QFileInfo appInfo(QString::fromUtf8(argv[0]));
180 QCoreApplication::addLibraryPath(appInfo.absolutePath());
181 #endif
182
183 QGuiApplication::setDesktopSettingsAware(false);
184
185 #ifndef USE_QUICKVIEW
186 QApplication::setStyle(QStyleFactory::create("Fusion"));
187 // The QApplication MUST be created before the StelFileMgr is initialized.
188 QApplication app(argc, argv);
189 #else
190 QGuiApplication::setDesktopSettingsAware(false);
191 QGuiApplication app(argc, argv);
192 #endif
193
194 // QApplication sets current locale, but
195 // we need scanf()/printf() and friends to always work in the C locale,
196 // otherwise configuration/INI file parsing will be erroneous.
197 setlocale(LC_NUMERIC, "C");
198
199 // Solution for bug: https://bugs.launchpad.net/stellarium/+bug/1498616
200 qputenv("QT_HARFBUZZ", "old");
201
202 // Init the file manager
203 StelFileMgr::init();
204
205 SplashScreen::present();
206
207 // add contents of STEL_OPTS environment variable.
208 QString envStelOpts(qgetenv("STEL_OPTS").constData());
209 if (envStelOpts.length()>0)
210 {
211 argList+= envStelOpts.split(" ");
212 argStr += " " + envStelOpts;
213 }
214 //save the modified arg list as an app property for later use
215 qApp->setProperty("stelCommandLine", argList);
216
217 // Parse for first set of CLI arguments - stuff we want to process before other
218 // output, such as --help and --version
219 CLIProcessor::parseCLIArgsPreConfig(argList);
220
221 #ifdef Q_OS_WIN
222 if (qApp->property("onetime_angle_mode").isValid())
223 {
224 app.setAttribute(Qt::AA_UseOpenGLES, true);
225 }
226 if (qApp->property("onetime_mesa_mode").isValid())
227 {
228 app.setAttribute(Qt::AA_UseSoftwareOpenGL, true);
229 }
230 #endif
231
232 // Start logging.
233 StelLogger::init(StelFileMgr::getUserDir()+"/log.txt");
234 StelLogger::writeLog(argStr);
235
236 // OK we start the full program.
237 // Print the console splash and get on with loading the program
238 QString versionLine = QString("This is %1 - %2").arg(StelUtils::getApplicationName(), STELLARIUM_URL);
239 QString copyrightLine = QString("Copyright (C) %1 Fabien Chereau et al.").arg(COPYRIGHT_YEARS);
240 int maxLength = qMax(versionLine.size(), copyrightLine.size());
241 qDebug() << qPrintable(QString(" %1").arg(QString().fill('-', maxLength+2)));
242 qDebug() << qPrintable(QString("[ %1 ]").arg(versionLine.leftJustified(maxLength, ' ')));
243 qDebug() << qPrintable(QString("[ %1 ]").arg(copyrightLine.leftJustified(maxLength, ' ')));
244 qDebug() << qPrintable(QString(" %1").arg(QString().fill('-', maxLength+2)));
245 qDebug() << "Writing log file to:" << QDir::toNativeSeparators(StelLogger::getLogFileName());
246 qDebug() << "File search paths:";
247 int n=0;
248 for (const auto& i : StelFileMgr::getSearchPaths())
249 {
250 qDebug() << " " << n << ". " << QDir::toNativeSeparators(i);
251 ++n;
252 }
253
254 // Now manage the loading of the proper config file
255 QString configName;
256 try
257 {
258 configName = CLIProcessor::argsGetOptionWithArg(argList, "-c", "--config-file", "config.ini").toString();
259 }
260 catch (std::runtime_error& e)
261 {
262 qWarning() << "WARNING: while looking for --config-file option: " << e.what() << ". Using \"config.ini\"";
263 configName = "config.ini";
264 }
265
266 QString configFileFullPath = StelFileMgr::findFile(configName, StelFileMgr::Flags(StelFileMgr::Writable|StelFileMgr::File));
267 if (configFileFullPath.isEmpty())
268 {
269 configFileFullPath = StelFileMgr::findFile(configName, StelFileMgr::New);
270 if (configFileFullPath.isEmpty())
271 qFatal("Could not create configuration file %s.", qPrintable(configName));
272 }
273
274 QSettings* confSettings = Q_NULLPTR;
275 if (StelFileMgr::exists(configFileFullPath))
276 {
277 confSettings = new QSettings(configFileFullPath, StelIniFormat, Q_NULLPTR);
278 // Implement "restore default settings" feature.
279 bool restoreDefaultConfigFile = false;
280 if (CLIProcessor::argsGetOption(argList, "", "--restore-defaults"))
281 restoreDefaultConfigFile=true;
282 else
283 restoreDefaultConfigFile = confSettings->value("main/restore_defaults", false).toBool();
284
285 if (!restoreDefaultConfigFile)
286 {
287 QString version = confSettings->value("main/version", "0.0.0").toString();
288 if (version!=QString(PACKAGE_VERSION))
289 {
290 QTextStream istr(&version);
291 char tmp;
292 int v1=0;
293 int v2=0;
294 istr >> v1 >> tmp >> v2;
295 // Config versions less than 0.6.0 are not supported, otherwise we will try to use it
296 if (v1==0 && v2<6)
297 {
298 // The config file is too old to try an import
299 qDebug() << "The current config file is from a version too old for parameters to be imported ("
300 << (version.isEmpty() ? "<0.6.0" : version) << ").\n"
301 << "It will be replaced by the default config file.";
302 restoreDefaultConfigFile = true;
303 }
304 else
305 {
306 qDebug() << "Attempting to use an existing older config file.";
307 confSettings->setValue("main/version", QString(PACKAGE_VERSION)); // Upgrade version of config.ini
308 clearCache();
309 qDebug() << "Cleared cache and updated config.ini...";
310 }
311 }
312 }
313
314 if (restoreDefaultConfigFile)
315 {
316 if (confSettings)
317 delete confSettings;
318
319 QString backupFile(configFileFullPath.left(configFileFullPath.length()-3) + QString("old"));
320 if (QFileInfo(backupFile).exists())
321 QFile(backupFile).remove();
322
323 QFile(configFileFullPath).rename(backupFile);
324 copyDefaultConfigFile(configFileFullPath);
325 confSettings = new QSettings(configFileFullPath, StelIniFormat);
326 qWarning() << "Resetting defaults config file. Previous config file was backed up in " << QDir::toNativeSeparators(backupFile);
327 clearCache();
328 }
329 }
330 else
331 {
332 qDebug() << "Config file " << QDir::toNativeSeparators(configFileFullPath) << " does not exist. Copying the default file.";
333 copyDefaultConfigFile(configFileFullPath);
334 confSettings = new QSettings(configFileFullPath, StelIniFormat);
335 }
336
337 Q_ASSERT(confSettings);
338 qDebug() << "Config file is: " << QDir::toNativeSeparators(configFileFullPath);
339
340 #ifdef ENABLE_SCRIPTING
341 QString outputFile = StelFileMgr::getUserDir()+"/output.txt";
342 if (confSettings->value("main/use_separate_output_file", false).toBool())
343 outputFile = StelFileMgr::getUserDir()+"/output-"+QDateTime::currentDateTime().toString("yyyyMMdd-HHmmss")+".txt";
344 StelScriptOutput::init(outputFile);
345 #endif
346
347 // Override config file values from CLI.
348 CLIProcessor::parseCLIArgsPostConfig(argList, confSettings);
349
350 // Add the DejaVu font that we use everywhere in the program
351 const QString& fName = StelFileMgr::findFile("data/DejaVuSans.ttf");
352 if (!fName.isEmpty())
353 QFontDatabase::addApplicationFont(fName);
354
355 QString fileFont = confSettings->value("gui/base_font_file", "").toString();
356 if (!fileFont.isEmpty())
357 {
358 const QString& afName = StelFileMgr::findFile(QString("data/%1").arg(fileFont));
359 if (!afName.isEmpty() && !afName.contains("file not found"))
360 QFontDatabase::addApplicationFont(afName);
361 else
362 qWarning() << "ERROR while loading custom font " << QDir::toNativeSeparators(fileFont);
363 }
364
365 // Set the default application font and font size.
366 // Note that style sheet will possibly override this setting.
367 #ifdef Q_OS_WIN
368 // Let's try avoid ugly font rendering on Windows.
369 // Details: https://sourceforge.net/p/stellarium/discussion/278769/thread/810a1e5c/
370 QString baseFont = confSettings->value("gui/base_font_name", "Verdana").toString();
371 QFont tmpFont(baseFont);
372 tmpFont.setStyleHint(QFont::AnyStyle, QFont::OpenGLCompatible);
373 #else
374 QString baseFont = confSettings->value("gui/base_font_name", "DejaVu Sans").toString();
375 QFont tmpFont(baseFont);
376 #endif
377 tmpFont.setPixelSize(confSettings->value("gui/gui_font_size", 13).toInt());
378 QGuiApplication::setFont(tmpFont);
379
380 // Initialize translator feature
381 StelTranslator::init(StelFileMgr::getInstallationDir() + "/data/iso639-1.utf8");
382
383 // Use our custom translator for Qt translations as well
384 CustomQTranslator trans;
385 app.installTranslator(&trans);
386
387 StelMainView mainWin(confSettings);
388 mainWin.show();
389 SplashScreen::finish(&mainWin);
390 app.exec();
391 mainWin.deinit();
392
393 delete confSettings;
394 StelLogger::deinit();
395
396 #ifdef Q_OS_WIN
397 if(timerGrain)
398 timeEndPeriod(timerGrain);
399 #endif //Q_OS_WIN
400
401 return 0;
402 }
403
404