1 /*
2  * Stellarium
3  * Copyright (C) 2006 Fabien Chereau
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
18  */
19 
20 #include "StelApp.hpp"
21 
22 #include "StelCore.hpp"
23 #include "StelMainView.hpp"
24 #include "StelSplashScreen.hpp"
25 #include "StelUtils.hpp"
26 #include "StelTextureMgr.hpp"
27 #include "StelObjectMgr.hpp"
28 #include "ConstellationMgr.hpp"
29 #include "AsterismMgr.hpp"
30 #include "HipsMgr.hpp"
31 #include "NebulaMgr.hpp"
32 #include "LandscapeMgr.hpp"
33 #include "CustomObjectMgr.hpp"
34 #include "HighlightMgr.hpp"
35 #include "GridLinesMgr.hpp"
36 #include "MilkyWay.hpp"
37 #include "ZodiacalLight.hpp"
38 #include "LabelMgr.hpp"
39 #include "MarkerMgr.hpp"
40 #include "SolarSystem.hpp"
41 #include "NomenclatureMgr.hpp"
42 #include "SporadicMeteorMgr.hpp"
43 #include "StarMgr.hpp"
44 #include "StelIniParser.hpp"
45 #include "StelProjector.hpp"
46 #include "StelLocationMgr.hpp"
47 #include "ToastMgr.hpp"
48 #include "StelActionMgr.hpp"
49 #include "StelPropertyMgr.hpp"
50 #include "StelProgressController.hpp"
51 #include "StelModuleMgr.hpp"
52 #include "StelLocaleMgr.hpp"
53 #include "StelSkyCultureMgr.hpp"
54 #include "StelFileMgr.hpp"
55 #include "StelJsonParser.hpp"
56 #include "StelSkyLayerMgr.hpp"
57 #include "StelAudioMgr.hpp"
58 #include "StelVideoMgr.hpp"
59 #include "SpecialMarkersMgr.hpp"
60 #include "StelViewportEffect.hpp"
61 #include "StelGuiBase.hpp"
62 #include "StelPainter.hpp"
63 #ifdef ENABLE_SCRIPTING
64  #include "StelScriptMgr.hpp"
65  #include "StelMainScriptAPIProxy.hpp"
66 #endif
67 
68 
69 #include <cstdlib>
70 #include <iostream>
71 #include <QDebug>
72 #include <QFile>
73 #include <QFileInfo>
74 #include <QMouseEvent>
75 #include <QNetworkAccessManager>
76 #include <QNetworkDiskCache>
77 #include <QNetworkProxy>
78 #include <QNetworkReply>
79 #include <QOpenGLContext>
80 #include <QOpenGLFramebufferObject>
81 #include <QString>
82 #include <QStringList>
83 #include <QSysInfo>
84 #include <QTextStream>
85 #include <QTimer>
86 #include <QDir>
87 #include <QCoreApplication>
88 #include <QGuiApplication>
89 #include <QScreen>
90 #include <QDateTime>
91 #include <QRegularExpression>
92 #ifdef ENABLE_SPOUT
93 #include <QMessageBox>
94 #include "SpoutSender.hpp"
95 #endif
96 
97 #ifdef USE_STATIC_PLUGIN_HELLOSTELMODULE
98 Q_IMPORT_PLUGIN(HelloStelModuleStelPluginInterface)
99 #endif
100 
101 #ifdef USE_STATIC_PLUGIN_SIMPLEDRAWLINE
102 Q_IMPORT_PLUGIN(SimpleDrawLineStelPluginInterface)
103 #endif
104 
105 #ifdef USE_STATIC_PLUGIN_ANGLEMEASURE
106 Q_IMPORT_PLUGIN(AngleMeasureStelPluginInterface)
107 #endif
108 
109 #ifdef USE_STATIC_PLUGIN_ARCHAEOLINES
110 Q_IMPORT_PLUGIN(ArchaeoLinesStelPluginInterface)
111 #endif
112 
113 #ifdef USE_STATIC_PLUGIN_CALENDARS
114 Q_IMPORT_PLUGIN(CalendarsStelPluginInterface)
115 #endif
116 
117 #ifdef USE_STATIC_PLUGIN_SATELLITES
118 Q_IMPORT_PLUGIN(SatellitesStelPluginInterface)
119 #endif
120 
121 #ifdef USE_STATIC_PLUGIN_TEXTUSERINTERFACE
122 Q_IMPORT_PLUGIN(TextUserInterfaceStelPluginInterface)
123 #endif
124 
125 #ifdef USE_STATIC_PLUGIN_OCULARS
126 Q_IMPORT_PLUGIN(OcularsStelPluginInterface)
127 #endif
128 
129 #ifdef USE_STATIC_PLUGIN_OCULUS
130 Q_IMPORT_PLUGIN(OculusStelPluginInterface)
131 #endif
132 
133 #ifdef USE_STATIC_PLUGIN_TELESCOPECONTROL
134 Q_IMPORT_PLUGIN(TelescopeControlStelPluginInterface)
135 #endif
136 
137 #ifdef USE_STATIC_PLUGIN_SOLARSYSTEMEDITOR
138 Q_IMPORT_PLUGIN(SolarSystemEditorStelPluginInterface)
139 #endif
140 
141 #ifdef USE_STATIC_PLUGIN_METEORSHOWERS
142 Q_IMPORT_PLUGIN(MeteorShowersStelPluginInterface)
143 #endif
144 
145 #ifdef USE_STATIC_PLUGIN_NAVSTARS
146 Q_IMPORT_PLUGIN(NavStarsStelPluginInterface)
147 #endif
148 
149 #ifdef USE_STATIC_PLUGIN_NOVAE
150 Q_IMPORT_PLUGIN(NovaeStelPluginInterface)
151 #endif
152 
153 #ifdef USE_STATIC_PLUGIN_SUPERNOVAE
154 Q_IMPORT_PLUGIN(SupernovaeStelPluginInterface)
155 #endif
156 
157 #ifdef USE_STATIC_PLUGIN_QUASARS
158 Q_IMPORT_PLUGIN(QuasarsStelPluginInterface)
159 #endif
160 
161 #ifdef USE_STATIC_PLUGIN_PULSARS
162 Q_IMPORT_PLUGIN(PulsarsStelPluginInterface)
163 #endif
164 
165 #ifdef USE_STATIC_PLUGIN_EXOPLANETS
166 Q_IMPORT_PLUGIN(ExoplanetsStelPluginInterface)
167 #endif
168 
169 #ifdef USE_STATIC_PLUGIN_EQUATIONOFTIME
170 Q_IMPORT_PLUGIN(EquationOfTimeStelPluginInterface)
171 #endif
172 
173 #ifdef USE_STATIC_PLUGIN_POINTERCOORDINATES
174 Q_IMPORT_PLUGIN(PointerCoordinatesStelPluginInterface)
175 #endif
176 
177 #ifdef USE_STATIC_PLUGIN_OBSERVABILITY
178 Q_IMPORT_PLUGIN(ObservabilityStelPluginInterface)
179 #endif
180 
181 #ifdef USE_STATIC_PLUGIN_SCENERY3D
182 Q_IMPORT_PLUGIN(Scenery3dStelPluginInterface)
183 #endif
184 
185 #ifdef USE_STATIC_PLUGIN_REMOTECONTROL
186 Q_IMPORT_PLUGIN(RemoteControlStelPluginInterface)
187 #endif
188 
189 #ifdef USE_STATIC_PLUGIN_REMOTESYNC
190 Q_IMPORT_PLUGIN(RemoteSyncStelPluginInterface)
191 #endif
192 
193 #ifdef USE_STATIC_PLUGIN_VTS
194 Q_IMPORT_PLUGIN(VtsStelPluginInterface)
195 #endif
196 
197 #ifdef USE_STATIC_PLUGIN_ONLINEQUERIES
198 Q_IMPORT_PLUGIN(OnlineQueriesPluginInterface)
199 #endif
200 
201 // Initialize static variables
202 StelApp* StelApp::singleton = Q_NULLPTR;
203 qint64 StelApp::startMSecs = 0;
204 double StelApp::animationScale = 1.;
205 
initStatic()206 void StelApp::initStatic()
207 {
208 	StelApp::startMSecs = QDateTime::currentMSecsSinceEpoch();
209 }
210 
deinitStatic()211 void StelApp::deinitStatic()
212 {
213 	StelApp::startMSecs = 0;
214 }
215 
216 /*************************************************************************
217  Create and initialize the main Stellarium application.
218 *************************************************************************/
StelApp(StelMainView * parent)219 StelApp::StelApp(StelMainView *parent)
220 	: QObject(parent)
221 	, mainWin(parent)
222 	, core(Q_NULLPTR)
223 	, moduleMgr(Q_NULLPTR)
224 	, localeMgr(Q_NULLPTR)
225 	, skyCultureMgr(Q_NULLPTR)
226 	, actionMgr(Q_NULLPTR)
227 	, propMgr(Q_NULLPTR)
228 	, textureMgr(Q_NULLPTR)
229 	, stelObjectMgr(Q_NULLPTR)
230 	, planetLocationMgr(Q_NULLPTR)
231 	, networkAccessManager(Q_NULLPTR)
232 	, audioMgr(Q_NULLPTR)
233 	, videoMgr(Q_NULLPTR)
234 	, skyImageMgr(Q_NULLPTR)
235 #ifdef ENABLE_SCRIPTING
236 	, scriptAPIProxy(Q_NULLPTR)
237 	, scriptMgr(Q_NULLPTR)
238 #endif
239 	, stelGui(Q_NULLPTR)
240 	, devicePixelsPerPixel(1.)
241 	, globalScalingRatio(1.f)
242 	, fps(0)
243 	, frame(0)
244 	, frameTimeAccum(0.)
245 	, flagNightVision(false)
246 	, confSettings(Q_NULLPTR)
247 	, initialized(false)
248 	, saveProjW(-1.)
249 	, saveProjH(-1.)
250 	, nbDownloadedFiles(0)
251 	, totalDownloadedSize(0)
252 	, nbUsedCache(0)
253 	, totalUsedCacheSize(0)
254 	, screenFontSize(13)
255 	, renderBuffer(Q_NULLPTR)
256 	, viewportEffect(Q_NULLPTR)
257 	, gl(Q_NULLPTR)
258 	, flagShowDecimalDegrees(false)
259 	, flagUseAzimuthFromSouth(false)
260 	, flagUseFormattingOutput(false)
261 	, flagUseCCSDesignation(false)
262 	, flagOverwriteInfoColor(false)
263 	, overwriteInfoColor(Vec3f(1.f))
264 	, daylightInfoColor(Vec3f(0.f))
265 	#ifdef ENABLE_SPOUT
266 	, spoutSender(Q_NULLPTR)
267 	#endif
268 	, currentFbo(0)
269 {
270 	setObjectName("StelApp");
271 
272 	// Can't create 2 StelApp instances
273 	Q_ASSERT(!singleton);
274 	singleton = this;
275 
276 	moduleMgr = new StelModuleMgr();
277 }
278 
279 /*************************************************************************
280  Deinitialize and destroy the main Stellarium application.
281 *************************************************************************/
~StelApp()282 StelApp::~StelApp()
283 {
284 	qDebug() << qPrintable(QString("Downloaded %1 files (%2 kbytes) in a session of %3 sec (average of %4 kB/s + %5 files from cache (%6 kB)).").arg(nbDownloadedFiles).arg(totalDownloadedSize/1024).arg(getTotalRunTime()).arg(static_cast<double>(totalDownloadedSize/1024)/getTotalRunTime()).arg(nbUsedCache).arg(totalUsedCacheSize/1024));
285 
286 	stelObjectMgr->unSelect();
287 	moduleMgr->unloadModule("StelVideoMgr", false);  // We need to delete it afterward
288 	moduleMgr->unloadModule("StelSkyLayerMgr", false);  // We need to delete it afterward
289 	moduleMgr->unloadModule("StelObjectMgr", false);// We need to delete it afterward
290 	StelModuleMgr* tmp = moduleMgr;
291 	moduleMgr = new StelModuleMgr(); // Create a secondary instance to avoid crashes at other deinit
292 	delete tmp; tmp=Q_NULLPTR;
293 	delete skyImageMgr; skyImageMgr=Q_NULLPTR;
294 	delete core; core=Q_NULLPTR;
295 	delete skyCultureMgr; skyCultureMgr=Q_NULLPTR;
296 	delete localeMgr; localeMgr=Q_NULLPTR;
297 	delete audioMgr; audioMgr=Q_NULLPTR;
298 	delete videoMgr; videoMgr=Q_NULLPTR;
299 	delete stelObjectMgr; stelObjectMgr=Q_NULLPTR; // Delete the module by hand afterward
300 	delete textureMgr; textureMgr=Q_NULLPTR;
301 	delete planetLocationMgr; planetLocationMgr=Q_NULLPTR;
302 	delete moduleMgr; moduleMgr=Q_NULLPTR; // Delete the secondary instance
303 	delete actionMgr; actionMgr = Q_NULLPTR;
304 	delete propMgr; propMgr = Q_NULLPTR;
305 	delete renderBuffer; renderBuffer = Q_NULLPTR;
306 
307 	Q_ASSERT(singleton);
308 	singleton = Q_NULLPTR;
309 }
310 
setupNetworkProxy()311 void StelApp::setupNetworkProxy()
312 {
313 	QString proxyHost = confSettings->value("proxy/host_name").toString();
314 	QString proxyPort = confSettings->value("proxy/port").toString();
315 	QString proxyUser = confSettings->value("proxy/user").toString();
316 	QString proxyPass = confSettings->value("proxy/password").toString();
317 	QString proxyType = confSettings->value("proxy/type").toString();
318 
319 	bool useSocksProxy = proxyType.contains("socks", Qt::CaseInsensitive);
320 
321 	// If proxy settings not found in config, use environment variable
322 	// if it is defined.  (Config file over-rides environment).
323 	if (proxyHost.isEmpty() && proxyUser.isEmpty() && proxyPass.isEmpty() && proxyPort.isEmpty())
324 	{
325 		char *httpProxyEnv;
326 		httpProxyEnv = std::getenv("http_proxy");
327 		if (!httpProxyEnv)
328 		{
329 			httpProxyEnv = std::getenv("HTTP_PROXY");
330 		}
331 		if (httpProxyEnv)
332 		{
333 			QString proxyString = QString(httpProxyEnv);
334 			if (!proxyString.isEmpty())
335 			{
336 				// Handle http_proxy of the form
337 				// proto://username:password@fqdn:port
338 				// e.g.:
339 				// http://usr:pass@proxy.loc:3128/
340 				// http://proxy.loc:3128/
341 				// http://2001:62a:4:203:6ab5:99ff:fef2:560b:3128/
342 				// http://foo:bar@2001:62a:4:203:6ab5:99ff:fef2:560b:3128/
343 				QRegularExpression pre("^([^:]+://)?(?:([^:]+):([^@]*)@)?(.+):([\\d]+)");
344 				QRegularExpressionMatch preMatch=pre.match(proxyString);
345 				if (proxyString.indexOf(pre) >= 0)
346 				{
347 					proxyUser = preMatch.captured(2);
348 					proxyPass = preMatch.captured(3);
349 					proxyHost = preMatch.captured(4);
350 					proxyPort = preMatch.captured(5);
351 				}
352 				else
353 				{
354 					qDebug() << "indecipherable environment variable http_proxy:" << proxyString;
355 					return;
356 				}
357 			}
358 		}
359 	}
360 
361 	if (!proxyHost.isEmpty())
362 	{
363 		QNetworkProxy proxy;
364 		if (useSocksProxy)
365 			proxy.setType(QNetworkProxy::Socks5Proxy);
366 		else
367 			proxy.setType(QNetworkProxy::HttpProxy);
368 		proxy.setHostName(proxyHost);
369 		if (!proxyPort.isEmpty())
370 			proxy.setPort(proxyPort.toUShort());
371 
372 		if (!proxyUser.isEmpty())
373 			proxy.setUser(proxyUser);
374 
375 		if (!proxyPass.isEmpty())
376 			proxy.setPassword(proxyPass);
377 
378 		QString ppDisp = proxyPass;
379 		ppDisp.fill('*');
380 		if (useSocksProxy)
381 			qDebug() << "Using SOCKS proxy:" << proxyUser << ppDisp << proxyHost << proxyPort;
382 		else
383 			qDebug() << "Using HTTP proxy:" << proxyUser << ppDisp << proxyHost << proxyPort;
384 		QNetworkProxy::setApplicationProxy(proxy);
385 	}
386 }
387 
388 #ifdef ENABLE_SCRIPTING
initScriptMgr()389 void StelApp::initScriptMgr()
390 {
391 	scriptMgr->addModules();
392 	QString startupScript;
393 	if (qApp->property("onetime_startup_script").isValid())
394 		startupScript = qApp->property("onetime_startup_script").toString();
395 	else
396 		startupScript = confSettings->value("scripts/startup_script", "startup.ssc").toString();
397 	// Use a queued slot call to start the script only once the main qApp event loop is running...
398 	QMetaObject::invokeMethod(scriptMgr,
399 				  "runScript",
400 				  Qt::QueuedConnection,
401 				  Q_ARG(QString, startupScript));
402 }
403 #else
initScriptMgr()404 void StelApp::initScriptMgr() {}
405 #endif
406 
getCommandlineArguments()407 QStringList StelApp::getCommandlineArguments()
408 {
409 	return qApp->property("stelCommandLine").toStringList();
410 }
411 
init(QSettings * conf)412 void StelApp::init(QSettings* conf)
413 {
414 	gl = QOpenGLContext::currentContext()->functions();
415 	confSettings = conf;
416 
417 	devicePixelsPerPixel = QOpenGLContext::currentContext()->screen()->devicePixelRatio();
418 	if (devicePixelsPerPixel>1)
419 		qDebug() << "Detected a high resolution device! Device pixel ratio:" << devicePixelsPerPixel;
420 
421 	setScreenFontSize(confSettings->value("gui/screen_font_size", 13).toInt());
422 	setGuiFontSize(confSettings->value("gui/gui_font_size", 13).toInt());
423 
424 	core = new StelCore();
425 	if (!fuzzyEquals(saveProjW, -1.) && !fuzzyEquals(saveProjH, -1.))
426 		core->windowHasBeenResized(0, 0, saveProjW, saveProjH);
427 
428 	//Initializing locale at the begining to show all strings translated
429 	localeMgr = new StelLocaleMgr();
430 	localeMgr->init();
431 	//SplashScreen::showMessage(q_("Initializing locales..."));
432 
433 	SplashScreen::showMessage(q_("Initializing textures..."));
434 	// Initialize AFTER creation of openGL context
435 	textureMgr = new StelTextureMgr();
436 
437 	SplashScreen::showMessage(q_("Initializing network access..."));
438 	networkAccessManager = new QNetworkAccessManager(this);
439 	networkAccessManager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
440 	SplashScreen::showMessage(q_("Initializing network disk cache..."));
441 	// Activate http cache if Qt version >= 4.5
442 	QNetworkDiskCache* cache = new QNetworkDiskCache(networkAccessManager);
443 	//make maximum cache size configurable (in MB)
444 	//the default Qt value (50 MB) is quite low, especially for DSS
445 	cache->setMaximumCacheSize(confSettings->value("main/network_cache_size",300).toInt() * 1024 * 1024);
446 	QString cachePath = StelFileMgr::getCacheDir();
447 
448 	qDebug() << "Cache directory is: " << QDir::toNativeSeparators(cachePath);
449 	cache->setCacheDirectory(cachePath);
450 	networkAccessManager->setCache(cache);
451 	connect(networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(reportFileDownloadFinished(QNetworkReply*)));
452 
453 	// Proxy Initialisation
454 	SplashScreen::showMessage(q_("Initializing network proxy..."));
455 	setupNetworkProxy();
456 
457 	//create non-StelModule managers
458 	propMgr = new StelPropertyMgr();
459 	skyCultureMgr = new StelSkyCultureMgr();
460 	propMgr->registerObject(skyCultureMgr);
461 	planetLocationMgr = new StelLocationMgr();
462 	actionMgr = new StelActionMgr();
463 
464 	// register non-modules for StelProperty tracking
465 	propMgr->registerObject(this);
466 	propMgr->registerObject(mainWin);
467 
468 	// Stel Object Data Base manager
469 	SplashScreen::showMessage(q_("Initializing Object Database..."));
470 	stelObjectMgr = new StelObjectMgr();
471 	stelObjectMgr->init();
472 	getModuleMgr().registerModule(stelObjectMgr);
473 
474 	// Hips surveys
475 	SplashScreen::showMessage(q_("Initializing HiPS survey..."));
476 	HipsMgr* hipsMgr = new HipsMgr();
477 	hipsMgr->init();
478 	getModuleMgr().registerModule(hipsMgr);
479 
480 	// Init the solar system first
481 	SplashScreen::showMessage(q_("Initializing Solar System objects..."));
482 	SolarSystem* ssystem = new SolarSystem();
483 	ssystem->init();
484 	getModuleMgr().registerModule(ssystem);
485 
486 	// Init the nomenclature for Solar system bodies
487 	SplashScreen::showMessage(q_("Initializing planetary nomenclature..."));
488 	NomenclatureMgr* nomenclature = new NomenclatureMgr();
489 	nomenclature->init();
490 	getModuleMgr().registerModule(nomenclature);
491 
492 	// Load stars & their names
493 	SplashScreen::showMessage(q_("Initializing stars..."));
494 	StarMgr* hip_stars = new StarMgr();
495 	hip_stars->init();
496 	getModuleMgr().registerModule(hip_stars);
497 
498 	SplashScreen::showMessage(q_("Initializing core..."));
499 	core->init();
500 
501 	// Init nebulas
502 	SplashScreen::showMessage(q_("Initializing deep-sky objects..."));
503 	NebulaMgr* nebulas = new NebulaMgr();
504 	nebulas->init();
505 	getModuleMgr().registerModule(nebulas);
506 
507 	// Init milky way
508 	SplashScreen::showMessage(q_("Initializing Milky Way..."));
509 	MilkyWay* milky_way = new MilkyWay();
510 	milky_way->init();
511 	getModuleMgr().registerModule(milky_way);
512 
513 	// Init zodiacal light
514 	SplashScreen::showMessage(q_("Initializing zodiacal light..."));
515 	ZodiacalLight* zodiacal_light = new ZodiacalLight();
516 	zodiacal_light->init();
517 	getModuleMgr().registerModule(zodiacal_light);
518 
519 	// Init sky image manager
520 	SplashScreen::showMessage(q_("Initializing sky image layer..."));
521 	skyImageMgr = new StelSkyLayerMgr();
522 	skyImageMgr->init();
523 	getModuleMgr().registerModule(skyImageMgr);
524 
525 	// Toast surveys
526 	SplashScreen::showMessage(q_("Initializing TOAST surveys..."));
527 	ToastMgr* toasts = new ToastMgr();
528 	toasts->init();
529 	getModuleMgr().registerModule(toasts);
530 
531 	// Init audio manager
532 	SplashScreen::showMessage(q_("Initializing audio..."));
533 	audioMgr = new StelAudioMgr();
534 
535 	// Init video manager
536 	SplashScreen::showMessage(q_("Initializing video..."));
537 	videoMgr = new StelVideoMgr();
538 	videoMgr->init();
539 	getModuleMgr().registerModule(videoMgr);
540 
541 	// Constellations
542 	SplashScreen::showMessage(q_("Initializing constellations..."));
543 	ConstellationMgr* constellations = new ConstellationMgr(hip_stars);
544 	constellations->init();
545 	getModuleMgr().registerModule(constellations);
546 
547 	// Asterisms
548 	SplashScreen::showMessage(q_("Initializing asterisms..."));
549 	AsterismMgr* asterisms = new AsterismMgr(hip_stars);
550 	asterisms->init();
551 	getModuleMgr().registerModule(asterisms);
552 
553 	// Landscape, atmosphere & cardinal points section
554 	SplashScreen::showMessage(q_("Initializing landscape..."));
555 	LandscapeMgr* landscape = new LandscapeMgr();
556 	landscape->init();
557 	getModuleMgr().registerModule(landscape);
558 
559 	SplashScreen::showMessage(q_("Initializing grid lines..."));
560 	GridLinesMgr* gridLines = new GridLinesMgr();
561 	gridLines->init();
562 	getModuleMgr().registerModule(gridLines);
563 
564 	SplashScreen::showMessage(q_("Initializing special markers..."));
565 	SpecialMarkersMgr* specialMarkers = new SpecialMarkersMgr();
566 	specialMarkers->init();
567 	getModuleMgr().registerModule(specialMarkers);
568 
569 	// Sporadic Meteors
570 	SplashScreen::showMessage(q_("Initializing sporadic meteors..."));
571 	SporadicMeteorMgr* meteors = new SporadicMeteorMgr(10, 72);
572 	meteors->init();
573 	getModuleMgr().registerModule(meteors);
574 
575 	// User labels
576 	SplashScreen::showMessage(q_("Initializing user labels..."));
577 	LabelMgr* skyLabels = new LabelMgr();
578 	skyLabels->init();
579 	getModuleMgr().registerModule(skyLabels);
580 
581 	SplashScreen::showMessage(q_("Initializing sky cultures..."));
582 	skyCultureMgr->init();
583 
584 	// User markers
585 	SplashScreen::showMessage(q_("Initializing user markers..."));
586 	MarkerMgr* skyMarkers = new MarkerMgr();
587 	skyMarkers->init();
588 	getModuleMgr().registerModule(skyMarkers);
589 
590 	// Init custom objects
591 	SplashScreen::showMessage(q_("Initializing custom objects..."));
592 	CustomObjectMgr* custObj = new CustomObjectMgr();
593 	custObj->init();
594 	getModuleMgr().registerModule(custObj);
595 
596 	// Init hightlights
597 	SplashScreen::showMessage(q_("Initializing highlights..."));
598 	HighlightMgr* hlMgr = new HighlightMgr();
599 	hlMgr->init();
600 	getModuleMgr().registerModule(hlMgr);
601 
602 	//Create the script manager here, maybe some modules/plugins may want to connect to it
603 	//It has to be initialized later after all modules have been loaded by calling initScriptMgr
604 #ifdef ENABLE_SCRIPTING
605 	SplashScreen::showMessage(q_("Initializing scripting..."));
606 	scriptAPIProxy = new StelMainScriptAPIProxy(this);
607 	scriptMgr = new StelScriptMgr(this);
608 #endif
609 
610 	SplashScreen::showMessage(q_("Initializing color scheme..."));
611 	// Initialisation of the color scheme
612 	emit colorSchemeChanged("color");
613 	setVisionModeNight(confSettings->value("viewing/flag_night", false).toBool());
614 
615 	// Enable viewport effect at startup if he set
616 	setViewportEffect(confSettings->value("video/viewport_effect", "none").toString());
617 
618 	SplashScreen::clearMessage();
619 	updateI18n();
620 
621 	// Init actions.
622 	actionMgr->addAction("actionShow_Night_Mode", N_("Display Options"), N_("Night mode"), this, "nightMode", "Ctrl+N");
623 
624 	setFlagShowDecimalDegrees(confSettings->value("gui/flag_show_decimal_degrees", false).toBool());
625 	setFlagSouthAzimuthUsage(confSettings->value("gui/flag_use_azimuth_from_south", false).toBool());
626 	setFlagUseFormattingOutput(confSettings->value("gui/flag_use_formatting_output", false).toBool());
627 	setFlagUseCCSDesignation(confSettings->value("gui/flag_use_ccs_designations", false).toBool());
628 	setFlagOverwriteInfoColor(confSettings->value("gui/flag_overwrite_info_color", false).toBool());
629 	setOverwriteInfoColor(Vec3f(confSettings->value("color/info_text_color", "1.0,1.0,1.0").toString()));
630 	setDaylightInfoColor(Vec3f(confSettings->value("color/daylight_text_color", "0.0,0.0,0.0").toString()));
631 
632 	// Animation
633 	animationScale = confSettings->value("gui/pointer_animation_speed", 1.).toDouble();
634 
635 #ifdef ENABLE_SPOUT
636 	//qDebug() << "Property spout is" << qApp->property("spout").toString();
637 	//qDebug() << "Property spoutName is" << qApp->property("spoutName").toString();
638 	if (qApp->property("spout").toString() != "none")
639 	{
640 		//if we are on windows and we have GLES, we are most likely on ANGLE
641 		bool isANGLE=QOpenGLContext::currentContext()->isOpenGLES();
642 
643 		if (isANGLE)
644 		{
645 			qCritical() << "SPOUT: Does not run in ANGLE/OpenGL ES mode!";
646 		}
647 		else
648 		{
649 			SplashScreen::showMessage(q_("Initializing SPOUT sender..."));
650 			// Create the SpoutSender object.
651 			QString spoutName = qApp->property("spoutName").toString();
652 			if(spoutName.isEmpty())
653 				spoutName = "stellarium";
654 
655 			spoutSender = new SpoutSender(spoutName);
656 
657 			if (!spoutSender->isValid())
658 			{
659 				QMessageBox::warning(&StelMainView::getInstance(), "Stellarium SPOUT", q_("Cannot create Spout sender. See log for details."), QMessageBox::Ok);
660 				delete spoutSender;
661 				spoutSender = Q_NULLPTR;
662 				qApp->setProperty("spout", "");
663 			}
664 			SplashScreen::clearMessage();
665 		}
666 	}
667 	else
668 	{
669 		qApp->setProperty("spout", "");
670 	}
671 #endif
672 
673 	initialized = true;
674 }
675 
676 // Load and initialize external modules (plugins)
initPlugIns()677 void StelApp::initPlugIns()
678 {
679 	// Load dynamically all the modules found in the modules/ directories
680 	// which are configured to be loaded at startup
681 	for (const auto& i : moduleMgr->getPluginsList())
682 	{
683 		if (i.loadAtStartup==false)
684 			continue;
685 		SplashScreen::showMessage(QString("%1 \"%2\"...").arg(q_("Loading plugin"), q_(i.info.displayedName)));
686 		StelModule* m = moduleMgr->loadPlugin(i.info.id);
687 		if (m!=Q_NULLPTR)
688 		{
689 			moduleMgr->registerModule(m, true);
690 			//load extensions after the module is registered
691 			moduleMgr->loadExtensions(i.info.id);
692 			m->init();
693 		}
694 	}
695 	SplashScreen::clearMessage();
696 }
697 
deinit()698 void StelApp::deinit()
699 {
700 #ifdef 	ENABLE_SPOUT
701 	delete spoutSender;
702 	spoutSender = Q_NULLPTR;
703 #endif
704 #ifdef ENABLE_SCRIPTING
705 	if (scriptMgr->scriptIsRunning())
706 		scriptMgr->stopScript();
707 #endif
708 	QCoreApplication::processEvents();
709 	getModuleMgr().unloadAllPlugins();
710 	QCoreApplication::processEvents();
711 	StelPainter::deinitGLShaders();
712 }
713 
714 
addProgressBar()715 StelProgressController* StelApp::addProgressBar()
716 {
717 	StelProgressController* p = new StelProgressController(this);
718 	progressControllers.append(p);
719 	emit(progressBarAdded(p));
720 	return p;
721 }
722 
removeProgressBar(StelProgressController * p)723 void StelApp::removeProgressBar(StelProgressController* p)
724 {
725 	progressControllers.removeOne(p);
726 	emit(progressBarRemoved(p));
727 	delete p;
728 }
729 
730 
update(double deltaTime)731 void StelApp::update(double deltaTime)
732 {
733 	if (!initialized)
734 		return;
735 
736 	++frame;
737 	frameTimeAccum+=deltaTime;
738 	if (frameTimeAccum > 1.)
739 	{
740 		// Calc the FPS rate every seconds
741 		fps=static_cast<float>(frame)/static_cast<float>(frameTimeAccum);
742 		frame = 0;
743 		frameTimeAccum=0.;
744 	}
745 
746 	core->update(deltaTime);
747 
748 	moduleMgr->update();
749 
750 	// Send the event to every StelModule
751 	for (auto* i : moduleMgr->getCallOrders(StelModule::ActionUpdate))
752 	{
753 		i->update(deltaTime);
754 	}
755 
756 	stelObjectMgr->update(deltaTime);
757 }
758 
prepareRenderBuffer()759 void StelApp::prepareRenderBuffer()
760 {
761 	if (!viewportEffect) return;
762 	if (!renderBuffer)
763 	{
764 		StelProjector::StelProjectorParams params = core->getCurrentStelProjectorParams();
765 		int w = params.viewportXywh[2];
766 		int h = params.viewportXywh[3];
767 		renderBuffer = new QOpenGLFramebufferObject(w, h, QOpenGLFramebufferObject::Depth); // we only need depth here
768 	}
769 	renderBuffer->bind();
770 }
771 
applyRenderBuffer(GLuint drawFbo)772 void StelApp::applyRenderBuffer(GLuint drawFbo)
773 {
774 	if (!renderBuffer) return;
775 	GL(gl->glBindFramebuffer(GL_FRAMEBUFFER, drawFbo));
776 	viewportEffect->paintViewportBuffer(renderBuffer);
777 }
778 
779 //! Main drawing function called at each frame
draw()780 void StelApp::draw()
781 {
782 	if (!initialized)
783 		return;
784 
785 	//find out which framebuffer is the current one
786 	//this is usually NOT the "zero" FBO, but one provided by QOpenGLWidget
787 	GLint drawFbo;
788 	GL(gl->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &drawFbo));
789 
790 	prepareRenderBuffer();
791 	currentFbo = renderBuffer ? renderBuffer->handle() : static_cast<GLuint>(drawFbo);
792 
793 	core->preDraw();
794 
795 	const QList<StelModule*> modules = moduleMgr->getCallOrders(StelModule::ActionDraw);
796 	for (auto* module : modules)
797 	{
798 		module->draw(core);
799 	}
800 	core->postDraw();
801 #ifdef ENABLE_SPOUT
802 	// At this point, the sky scene has been drawn, but no GUI panels.
803 	if(spoutSender)
804 		spoutSender->captureAndSendFrame(static_cast<GLuint>(drawFbo));
805 #endif
806 	applyRenderBuffer(static_cast<GLuint>(drawFbo));
807 }
808 
809 /*************************************************************************
810  Call this when the size of the GL window has changed
811 *************************************************************************/
glWindowHasBeenResized(const QRectF & rect)812 void StelApp::glWindowHasBeenResized(const QRectF& rect)
813 {
814 	// Remove the effect before resizing the core, or things get messy.
815 	QString effect = getViewportEffect();
816 	setViewportEffect("none");
817 	if (core)
818 	{
819 		core->windowHasBeenResized(rect.x(), rect.y(), rect.width(), rect.height());
820 	}
821 	else
822 	{
823 		saveProjW = rect.width();
824 		saveProjH = rect.height();
825 	}
826 	// Force to recreate the viewport effect if any.
827 	setViewportEffect(effect);
828 #ifdef ENABLE_SPOUT
829 	if (spoutSender)
830 		spoutSender->resize(static_cast<uint>(rect.width()),static_cast<uint>(rect.height()));
831 #endif
832 }
833 
834 // Handle mouse clics
handleClick(QMouseEvent * inputEvent)835 void StelApp::handleClick(QMouseEvent* inputEvent)
836 {
837 	QPointF pos = inputEvent->pos();
838 	qreal x, y;
839 	x = pos.x();
840 	y = pos.y();
841 	if (viewportEffect)
842 		viewportEffect->distortXY(x, y);
843 
844 	QMouseEvent event(inputEvent->type(), QPoint(qRound(x*devicePixelsPerPixel), qRound(y*devicePixelsPerPixel)), inputEvent->button(), inputEvent->buttons(), inputEvent->modifiers());
845 	event.setAccepted(false);
846 
847 	// Send the event to every StelModule
848 	for (auto* i : moduleMgr->getCallOrders(StelModule::ActionHandleMouseClicks))
849 	{
850 		i->handleMouseClicks(&event);
851 		if (event.isAccepted())
852 		{
853 			inputEvent->setAccepted(true);
854 			return;
855 		}
856 	}
857 }
858 
859 // Handle mouse wheel.
handleWheel(QWheelEvent * event)860 void StelApp::handleWheel(QWheelEvent* event)
861 {
862 	event->setAccepted(false);
863 	QWheelEvent deltaEvent(QPoint(qRound(event->pos().x()*devicePixelsPerPixel), qRound(event->pos().y()*devicePixelsPerPixel)),
864 			       QPoint(qRound(event->globalPos().x()*devicePixelsPerPixel), qRound(event->globalPos().y()*devicePixelsPerPixel)),
865 	                       event->delta(), event->buttons(), event->modifiers(), event->orientation());
866 	deltaEvent.setAccepted(false);
867 	// Send the event to every StelModule
868 	for (auto* i : moduleMgr->getCallOrders(StelModule::ActionHandleMouseClicks)) {
869 		i->handleMouseWheel(&deltaEvent);
870 		if (deltaEvent.isAccepted()) {
871 			event->accept();
872 			break;
873 		}
874 	}
875 }
876 
877 // Handle mouse move
handleMove(qreal x,qreal y,Qt::MouseButtons b)878 bool StelApp::handleMove(qreal x, qreal y, Qt::MouseButtons b)
879 {
880 	if (viewportEffect)
881 		viewportEffect->distortXY(x, y);
882 	// Send the event to every StelModule
883 	for (auto* i : moduleMgr->getCallOrders(StelModule::ActionHandleMouseMoves))
884 	{
885 		if (i->handleMouseMoves(qRound(x*devicePixelsPerPixel), qRound(y*devicePixelsPerPixel), b))
886 			return true;
887 	}
888 	return false;
889 }
890 
891 // Handle key press and release
handleKeys(QKeyEvent * event)892 void StelApp::handleKeys(QKeyEvent* event)
893 {
894 	event->setAccepted(false);
895 	// First try to trigger a shortcut.
896 	if (event->type() == QEvent::KeyPress)
897 	{
898 		if (getStelActionManager()->pushKey(event->key() + event->modifiers()))
899 		{
900 			event->setAccepted(true);
901 			return;
902 		}
903 	}
904 	// Send the event to every StelModule
905 	for (auto* i : moduleMgr->getCallOrders(StelModule::ActionHandleKeys))
906 	{
907 		i->handleKeys(event);
908 		if (event->isAccepted())
909 			return;
910 	}
911 }
912 
913 // Handle pinch on multi touch devices
handlePinch(qreal scale,bool started)914 void StelApp::handlePinch(qreal scale, bool started)
915 {
916 	// Send the event to every StelModule
917 	for (auto* i : moduleMgr->getCallOrders(StelModule::ActionHandleMouseMoves))
918 	{
919 		if (i->handlePinch(scale, started))
920 			return;
921 	}
922 }
923 
924 //! Set flag for activating night vision mode
setVisionModeNight(bool b)925 void StelApp::setVisionModeNight(bool b)
926 {
927 	if (flagNightVision!=b)
928 	{
929 		flagNightVision=b;
930 		emit(visionNightModeChanged(b));
931 	}
932 }
933 
setFlagOverwriteInfoColor(bool b)934 void StelApp::setFlagOverwriteInfoColor(bool b)
935 {
936 	if (flagOverwriteInfoColor!=b)
937 	{
938 		flagOverwriteInfoColor=b;
939 		emit(flagOverwriteInfoColorChanged(b));
940 	}
941 }
942 
setFlagShowDecimalDegrees(bool b)943 void StelApp::setFlagShowDecimalDegrees(bool b)
944 {
945 	if (flagShowDecimalDegrees!=b)
946 	{
947 		flagShowDecimalDegrees = b;
948 		emit flagShowDecimalDegreesChanged(b);
949 	}
950 }
951 
setFlagUseFormattingOutput(bool b)952 void StelApp::setFlagUseFormattingOutput(bool b)
953 {
954 	if (flagUseFormattingOutput!=b)
955 	{
956 		flagUseFormattingOutput = b;
957 		emit flagUseFormattingOutputChanged(b);
958 	}
959 }
960 
setFlagUseCCSDesignation(bool b)961 void StelApp::setFlagUseCCSDesignation(bool b)
962 {
963 	if (flagUseCCSDesignation!=b)
964 	{
965 		flagUseCCSDesignation = b;
966 		emit flagUseCCSDesignationChanged(b);
967 	}
968 }
969 
setOverwriteInfoColor(const Vec3f & color)970 void StelApp::setOverwriteInfoColor(const Vec3f& color)
971 {
972 	if (color != overwriteInfoColor)
973 	{
974 		overwriteInfoColor = color;
975 		emit overwriteInfoColorChanged(color);
976 	}
977 }
978 
getOverwriteInfoColor() const979 Vec3f StelApp::getOverwriteInfoColor() const
980 {
981 	return overwriteInfoColor;
982 }
983 
setDaylightInfoColor(const Vec3f & color)984 void StelApp::setDaylightInfoColor(const Vec3f& color)
985 {
986 	if (color != daylightInfoColor)
987 	{
988 		daylightInfoColor = color;
989 		emit daylightInfoColorChanged(color);
990 	}
991 }
992 
getDaylightInfoColor() const993 Vec3f StelApp::getDaylightInfoColor() const
994 {
995 	return daylightInfoColor;
996 }
997 
998 // Update translations and font for sky everywhere in the program
updateI18n()999 void StelApp::updateI18n()
1000 {
1001 #ifdef ENABLE_NLS
1002 	emit(languageChanged());
1003 #endif
1004 }
1005 
ensureGLContextCurrent()1006 void StelApp::ensureGLContextCurrent()
1007 {
1008 	mainWin->glContextMakeCurrent();
1009 }
1010 
1011 // Return the time since when stellarium is running in second.
getTotalRunTime()1012 double StelApp::getTotalRunTime()
1013 {
1014 	return static_cast<double>(QDateTime::currentMSecsSinceEpoch() - StelApp::startMSecs)/1000.;
1015 }
1016 
1017 // Return the scaled time since when stellarium is running in second.
getAnimationTime()1018 double StelApp::getAnimationTime()
1019 {
1020 	return static_cast<double>(QDateTime::currentMSecsSinceEpoch() - StelApp::startMSecs)*StelApp::animationScale/1000.;
1021 }
1022 
reportFileDownloadFinished(QNetworkReply * reply)1023 void StelApp::reportFileDownloadFinished(QNetworkReply* reply)
1024 {
1025 	bool fromCache = reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool();
1026 	if (fromCache)
1027 	{
1028 		++nbUsedCache;
1029 		totalUsedCacheSize+=reply->bytesAvailable();
1030 	}
1031 	else
1032 	{
1033 		++nbDownloadedFiles;
1034 		totalDownloadedSize+=reply->bytesAvailable();
1035 	}
1036 }
1037 
quit()1038 void StelApp::quit()
1039 {
1040 	emit aboutToQuit();
1041 	// Let's allow exit from Stellarium via startup script!
1042 	QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
1043 }
1044 
setDevicePixelsPerPixel(qreal dppp)1045 void StelApp::setDevicePixelsPerPixel(qreal dppp)
1046 {
1047 	// Check that the device-independent pixel size didn't change
1048 	if (!viewportEffect && !fuzzyEquals(devicePixelsPerPixel, dppp))
1049 	{
1050 		devicePixelsPerPixel = dppp;
1051 		StelProjector::StelProjectorParams params = core->getCurrentStelProjectorParams();
1052 		params.devicePixelsPerPixel = devicePixelsPerPixel;
1053 		core->setCurrentStelProjectorParams(params);
1054 	}
1055 }
1056 
setViewportEffect(const QString & name)1057 void StelApp::setViewportEffect(const QString& name)
1058 {
1059 	if (name == getViewportEffect()) return;
1060 	if (renderBuffer)
1061 	{
1062 		ensureGLContextCurrent();
1063 		delete renderBuffer;
1064 		renderBuffer = Q_NULLPTR;
1065 	}
1066 	if (viewportEffect)
1067 	{
1068 		delete viewportEffect;
1069 		viewportEffect = Q_NULLPTR;
1070 	}
1071 	if (name == "none") return;
1072 
1073 	if (!core)
1074 	{
1075 		qDebug() << "No core to set viewport effect";
1076 		return;
1077 	}
1078 	StelProjector::StelProjectorParams params = core->getCurrentStelProjectorParams();
1079 	int w = params.viewportXywh[2];
1080 	int h = params.viewportXywh[3];
1081 	if (name == "sphericMirrorDistorter")
1082 	{
1083 		viewportEffect = new StelViewportDistorterFisheyeToSphericMirror(w, h);
1084 	}
1085 	else
1086 	{
1087 		qDebug() << "unknown viewport effect name:" << name;
1088 		Q_ASSERT(false);
1089 	}
1090 }
1091 
getViewportEffect() const1092 QString StelApp::getViewportEffect() const
1093 {
1094 	if (viewportEffect)
1095 		return viewportEffect->getName();
1096 	return "none";
1097 }
1098 
1099 // Diagnostics
dumpModuleActionPriorities(StelModule::StelModuleActionName actionName) const1100 void StelApp::dumpModuleActionPriorities(StelModule::StelModuleActionName actionName) const
1101 {
1102 	const QList<StelModule*> modules = moduleMgr->getCallOrders(actionName);
1103 	QMetaEnum me = QMetaEnum::fromType<StelModule::StelModuleActionName>();
1104 	qDebug() << "Module Priorities for action named" << me.valueToKey(actionName);
1105 
1106 	for (auto* module : modules)
1107 	{
1108 		module->draw(core);
1109 		qDebug() << " -- " << module->getCallOrder(actionName) << "Module: " << module->objectName();
1110 	}
1111 }
1112 
getModule(const QString & moduleID) const1113 StelModule* StelApp::getModule(const QString& moduleID) const
1114 {
1115 	return getModuleMgr().getModule(moduleID);
1116 }
setScreenFontSize(int s)1117 void StelApp::setScreenFontSize(int s)
1118 {
1119 	if (screenFontSize!=s)
1120 	{
1121 		screenFontSize=s;
1122 		emit screenFontSizeChanged(s);
1123 	}
1124 }
setGuiFontSize(int s)1125 void StelApp::setGuiFontSize(int s)
1126 {
1127 	if (getGuiFontSize()!=s)
1128 	{
1129 		QFont font=QGuiApplication::font();
1130 		font.setPixelSize(s);
1131 		QGuiApplication::setFont(font);
1132 		emit guiFontSizeChanged(s);
1133 	}
1134 }
getGuiFontSize() const1135 int StelApp::getGuiFontSize() const
1136 {
1137 	return QGuiApplication::font().pixelSize();
1138 }
1139 
setAppFont(QFont font)1140 void StelApp::setAppFont(QFont font)
1141 {
1142 	int oldSize=QGuiApplication::font().pixelSize();
1143 	font.setPixelSize(oldSize);
1144 	font.setStyleHint(QFont::AnyStyle, QFont::OpenGLCompatible);
1145 	QGuiApplication::setFont(font);
1146 	emit fontChanged(font);
1147 }
1148 
getVersion() const1149 QString StelApp::getVersion() const
1150 {
1151 	QStringList ver = StelUtils::getApplicationVersion().split(".");
1152 	return QString("%1.%2.%3").arg(ver[0]).arg(ver[1]).arg(ver[2]);
1153 }
1154