1 #include "config.h"
2
3 #include "LauncherMainWindow.hxx"
4
5 // Qt headers
6 #include <QMessageBox>
7 #include <QSettings>
8 #include <QDebug>
9 #include <QMenu>
10 #include <QMenuBar>
11
12 #include <QOpenGLContext>
13 #include <QQmlComponent>
14 #include <QQmlContext>
15 #include <QQmlEngine>
16 #include <QQmlError>
17 #include <QQmlFileSelector>
18 #include <QQuickItem>
19
20 #include "version.h"
21
22 // launcher headers
23 #include "AddOnsController.hxx"
24 #include "AircraftModel.hxx"
25 #include "DefaultAircraftLocator.hxx"
26 #include "LaunchConfig.hxx"
27 #include "LauncherController.hxx"
28 #include "LauncherNotificationsController.hxx"
29 #include "LauncherPackageDelegate.hxx"
30 #include "LocalAircraftCache.hxx"
31 #include "LocationController.hxx"
32 #include "QtLauncher.hxx"
33 #include "UpdateChecker.hxx"
34 #include "GettingStartedTip.hxx"
35
36 #include <Main/sentryIntegration.hxx>
37
38 //////////////////////////////////////////////////////////////////////////////
39
LauncherMainWindow(bool inSimMode)40 LauncherMainWindow::LauncherMainWindow(bool inSimMode) : QQuickView()
41 {
42 setTitle("FlightGear " FLIGHTGEAR_VERSION);
43
44 m_controller = new LauncherController(this, this);
45 m_controller->initQML();
46
47 // use a direct connection to be notified synchronously when the render thread
48 // starts OpenGL. We use this to log the OpenGL information from the
49 // context at that time, for tracing purposes.
50 connect(this, &QQuickWindow::sceneGraphInitialized,
51 this, &LauncherMainWindow::renderTheadSceneGraphInitialized,
52 Qt::DirectConnection);
53
54 if (!inSimMode) {
55 #if defined(Q_OS_MAC)
56 QMenuBar* mb = new QMenuBar();
57
58 QMenu* fileMenu = mb->addMenu(tr("File"));
59 QAction* openAction = new QAction(tr("Open saved configuration..."));
60 openAction->setMenuRole(QAction::NoRole);
61 connect(openAction, &QAction::triggered,
62 m_controller, &LauncherController::openConfig);
63
64 QAction* saveAction = new QAction(tr("Save configuration as..."));
65 saveAction->setMenuRole(QAction::NoRole);
66 connect(saveAction, &QAction::triggered,
67 m_controller, &LauncherController::saveConfigAs);
68
69 fileMenu->addAction(openAction);
70 fileMenu->addAction(saveAction);
71
72 QMenu* toolsMenu = mb->addMenu(tr("Tools"));
73 QAction* restoreDefaultsAction = new QAction(tr("Restore defaults..."));
74 restoreDefaultsAction->setMenuRole(QAction::NoRole);
75 connect(restoreDefaultsAction, &QAction::triggered,
76 m_controller, &LauncherController::requestRestoreDefaults);
77
78 QAction* changeDataAction = new QAction(tr("Select data files location..."));
79 changeDataAction->setMenuRole(QAction::NoRole);
80 connect(changeDataAction, &QAction::triggered,
81 m_controller, &LauncherController::requestChangeDataPath);
82
83 QAction* viewCommandLineAction = new QAction(tr("View command-line"));
84 connect(viewCommandLineAction, &QAction::triggered,
85 m_controller, &LauncherController::viewCommandLine);
86
87 toolsMenu->addAction(restoreDefaultsAction);
88 toolsMenu->addAction(changeDataAction);
89 toolsMenu->addAction(viewCommandLineAction);
90 #endif
91
92 QAction* qa = new QAction(this);
93 qa->setMenuRole(QAction::QuitRole); // will be addeed accordingly
94 qa->setShortcut(QKeySequence("Ctrl+Q"));
95 connect(qa, &QAction::triggered, m_controller, &LauncherController::quit);
96 }
97
98 const bool haveQQC2 = checkQQC2Availability();
99
100 connect(this, &QQuickView::statusChanged, this, &LauncherMainWindow::onQuickStatusChanged);
101
102 m_controller->initialRestoreSettings();
103
104 ////////////
105 #if defined(Q_OS_WIN)
106 const QString osName("win");
107 #elif defined(Q_OS_MAC)
108 const QString osName("mac");
109 #else
110 const QString osName("unix");
111 #endif
112
113
114 setResizeMode(QQuickView::SizeRootObjectToView);
115 engine()->addImportPath("qrc:///");
116
117 // allow selecting different QML files based on the Qt version we are
118 // compiled against
119 auto selector = new QQmlFileSelector(engine(), this);
120 #if QT_VERSION >= 0x050600
121 selector->setExtraSelectors({"qt56"});
122 #endif
123
124 #if QT_VERSION >= 0x050700
125 if (haveQQC2) {
126 selector->setExtraSelectors({"qt56", "qt57"});
127 }
128 #endif
129
130
131 QQmlContext* ctx = rootContext();
132 ctx->setContextProperty("_launcher", m_controller);
133 ctx->setContextProperty("_config", m_controller->config());
134 ctx->setContextProperty("_location", m_controller->location());
135 ctx->setContextProperty("_osName", osName);
136
137 auto updater = new UpdateChecker(this);
138 ctx->setContextProperty("_updates", updater);
139
140 auto packageDelegate = new LauncherPackageDelegate(this);
141 ctx->setContextProperty("_packages", packageDelegate);
142
143 auto notifications = new LauncherNotificationsController{this, engine()};
144 ctx->setContextProperty("_notifications", notifications);
145
146 if (!inSimMode) {
147 auto addOnsCtl = new AddOnsController(this, m_controller->config());
148 ctx->setContextProperty("_addOns", addOnsCtl);
149 }
150
151 #if defined(ENABLE_COMPOSITOR)
152 ctx->setContextProperty("_haveCompositor", true);
153 #else
154 ctx->setContextProperty("_haveCompositor", false);
155 #endif
156
157 auto weatherScenariosModel = new flightgear::WeatherScenariosModel(this);
158 ctx->setContextProperty("_weatherScenarios", weatherScenariosModel);
159
160 setSource(QUrl("qrc:///qml/Launcher.qml"));
161 }
162
checkQQC2Availability()163 bool LauncherMainWindow::checkQQC2Availability()
164 {
165 QQmlComponent comp(engine());
166 comp.setData(R"(
167 import QtQuick.Controls 2.0
168 ScrollBar {
169 }
170 )", {});
171 if (comp.isError()) {
172 return false;
173 }
174
175
176 auto item = comp.create();
177 const bool haveQQC2 = (item != nullptr);
178 if (item)
179 item->deleteLater();
180 return haveQQC2;
181 }
182
onQuickStatusChanged(QQuickView::Status status)183 void LauncherMainWindow::onQuickStatusChanged(QQuickView::Status status)
184 {
185 if (status == QQuickView::Error) {
186 QString errorString;
187
188 Q_FOREACH (auto err, errors()) {
189 errorString.append("\n" + err.toString());
190 }
191
192 QMessageBox::critical(nullptr, "UI loading failures.",
193 tr("Problems occurred loading the user interface. This is usually due to missing modules on your system. "
194 "Please report this error to the FlightGear developer list or forum, and take care to mention your system "
195 "distribution, etc. Please also include the information provided below.\n") +
196 errorString);
197 }
198 }
199
~LauncherMainWindow()200 LauncherMainWindow::~LauncherMainWindow()
201 {
202 }
203
event(QEvent * event)204 bool LauncherMainWindow::event(QEvent *event)
205 {
206 if (event->type() == QEvent::Close) {
207 m_controller->saveSettings();
208 }
209 return QQuickView::event(event);
210 }
211
execInApp()212 bool LauncherMainWindow::execInApp()
213 {
214 m_controller->setInAppMode();
215
216 show();
217
218 while (m_controller->keepRunningInAppMode()) {
219 qApp->processEvents();
220 }
221
222 return m_controller->inAppResult();
223 }
224
225 // this slot runs in the context of the render thread. Don't modify
226 // any non-protected state here. We're using it soley to grab the current
227 // QOpenGLContext and thus the driver/vendor info
renderTheadSceneGraphInitialized()228 void LauncherMainWindow::renderTheadSceneGraphInitialized()
229 {
230 auto qContext = QOpenGLContext::currentContext();
231 if (!qContext) {
232 qWarning() << Q_FUNC_INFO << "No current OpenGL context";
233 return;
234 }
235
236 std::string renderer = (char*)glGetString(GL_RENDERER);
237 // capture this to help with debugging this crash:
238 // https://sentry.io/share/issue/f98e38dceb4241dbaeed944d6ce4d746/
239 // https://bugreports.qt.io/browse/QTBUG-69703
240 flightgear::addSentryTag("qt-gl-vendor", (char*)glGetString(GL_VENDOR));
241 flightgear::addSentryTag("qt-gl-renderer", renderer.c_str());
242 flightgear::addSentryTag("qt-gl-version", (char*)glGetString(GL_VERSION));
243 flightgear::addSentryTag("qt-glsl-version", (char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
244
245 const char* gltype[] = {"Desktop", "GLES 2", "GLES 1"};
246 flightgear::addSentryTag("qt-gl-module-type", gltype[QOpenGLContext::openGLModuleType()]);
247
248 // if necessary, borrow more code from:
249 // https://code.qt.io/cgit/qt/qtbase.git/tree/examples/opengl/contextinfo/widget.cpp?h=5.15#n358
250 // to expand what this reports
251 }
252