1 // Copyright (c) 2011-2019 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #if defined(HAVE_CONFIG_H)
6 #include <config/bitcoin-config.h>
7 #endif
8 
9 #include <qt/bitcoin.h>
10 #include <qt/bitcoingui.h>
11 
12 #include <chainparams.h>
13 #include <qt/clientmodel.h>
14 #include <fs.h>
15 #include <qt/guiconstants.h>
16 #include <qt/guiutil.h>
17 #include <qt/intro.h>
18 #include <qt/networkstyle.h>
19 #include <qt/optionsmodel.h>
20 #include <qt/platformstyle.h>
21 #include <qt/splashscreen.h>
22 #include <qt/utilitydialog.h>
23 #include <qt/winshutdownmonitor.h>
24 
25 #ifdef ENABLE_WALLET
26 #include <qt/paymentserver.h>
27 #include <qt/walletcontroller.h>
28 #endif
29 
30 #include <interfaces/handler.h>
31 #include <interfaces/node.h>
32 #include <noui.h>
33 #include <rpc/server.h>
34 #include <ui_interface.h>
35 #include <uint256.h>
36 #include <util/system.h>
37 #include <warnings.h>
38 
39 #include <walletinitinterface.h>
40 
41 #include <memory>
42 #include <stdint.h>
43 
44 #include <boost/thread.hpp>
45 
46 #include <QApplication>
47 #include <QDebug>
48 #include <QLibraryInfo>
49 #include <QLocale>
50 #include <QMessageBox>
51 #include <QSettings>
52 #include <QThread>
53 #include <QTimer>
54 #include <QTranslator>
55 
56 #if defined(QT_STATICPLUGIN)
57 #include <QtPlugin>
58 #if defined(QT_QPA_PLATFORM_XCB)
59 Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
60 #elif defined(QT_QPA_PLATFORM_WINDOWS)
61 Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
62 #elif defined(QT_QPA_PLATFORM_COCOA)
63 Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
64 #endif
65 #endif
66 
67 // Declare meta types used for QMetaObject::invokeMethod
68 Q_DECLARE_METATYPE(bool*)
Q_DECLARE_METATYPE(CAmount)69 Q_DECLARE_METATYPE(CAmount)
70 Q_DECLARE_METATYPE(uint256)
71 
72 static QString GetLangTerritory()
73 {
74     QSettings settings;
75     // Get desired locale (e.g. "de_DE")
76     // 1) System default language
77     QString lang_territory = QLocale::system().name();
78     // 2) Language from QSettings
79     QString lang_territory_qsettings = settings.value("language", "").toString();
80     if(!lang_territory_qsettings.isEmpty())
81         lang_territory = lang_territory_qsettings;
82     // 3) -lang command line argument
83     lang_territory = QString::fromStdString(gArgs.GetArg("-lang", lang_territory.toStdString()));
84     return lang_territory;
85 }
86 
87 /** Set up translations */
initTranslations(QTranslator & qtTranslatorBase,QTranslator & qtTranslator,QTranslator & translatorBase,QTranslator & translator)88 static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator)
89 {
90     // Remove old translators
91     QApplication::removeTranslator(&qtTranslatorBase);
92     QApplication::removeTranslator(&qtTranslator);
93     QApplication::removeTranslator(&translatorBase);
94     QApplication::removeTranslator(&translator);
95 
96     // Get desired locale (e.g. "de_DE")
97     // 1) System default language
98     QString lang_territory = GetLangTerritory();
99 
100     // Convert to "de" only by truncating "_DE"
101     QString lang = lang_territory;
102     lang.truncate(lang_territory.lastIndexOf('_'));
103 
104     // Load language files for configured locale:
105     // - First load the translator for the base language, without territory
106     // - Then load the more specific locale translator
107 
108     // Load e.g. qt_de.qm
109     if (qtTranslatorBase.load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
110         QApplication::installTranslator(&qtTranslatorBase);
111 
112     // Load e.g. qt_de_DE.qm
113     if (qtTranslator.load("qt_" + lang_territory, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
114         QApplication::installTranslator(&qtTranslator);
115 
116     // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in bitcoin.qrc)
117     if (translatorBase.load(lang, ":/translations/"))
118         QApplication::installTranslator(&translatorBase);
119 
120     // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in bitcoin.qrc)
121     if (translator.load(lang_territory, ":/translations/"))
122         QApplication::installTranslator(&translator);
123 }
124 
125 /* qDebug() message handler --> debug.log */
DebugMessageHandler(QtMsgType type,const QMessageLogContext & context,const QString & msg)126 void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg)
127 {
128     Q_UNUSED(context);
129     if (type == QtDebugMsg) {
130         LogPrint(BCLog::QT, "GUI: %s\n", msg.toStdString());
131     } else {
132         LogPrintf("GUI: %s\n", msg.toStdString());
133     }
134 }
135 
BitcoinCore(interfaces::Node & node)136 BitcoinCore::BitcoinCore(interfaces::Node& node) :
137     QObject(), m_node(node)
138 {
139 }
140 
handleRunawayException(const std::exception * e)141 void BitcoinCore::handleRunawayException(const std::exception *e)
142 {
143     PrintExceptionContinue(e, "Runaway exception");
144     Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings("gui")));
145 }
146 
initialize()147 void BitcoinCore::initialize()
148 {
149     try
150     {
151         qDebug() << __func__ << ": Running initialization in thread";
152         bool rv = m_node.appInitMain();
153         Q_EMIT initializeResult(rv);
154     } catch (const std::exception& e) {
155         handleRunawayException(&e);
156     } catch (...) {
157         handleRunawayException(nullptr);
158     }
159 }
160 
shutdown()161 void BitcoinCore::shutdown()
162 {
163     try
164     {
165         qDebug() << __func__ << ": Running Shutdown in thread";
166         m_node.appShutdown();
167         qDebug() << __func__ << ": Shutdown finished";
168         Q_EMIT shutdownResult();
169     } catch (const std::exception& e) {
170         handleRunawayException(&e);
171     } catch (...) {
172         handleRunawayException(nullptr);
173     }
174 }
175 
176 static int qt_argc = 1;
177 static const char* qt_argv = "litecoin-qt";
178 
BitcoinApplication(interfaces::Node & node)179 BitcoinApplication::BitcoinApplication(interfaces::Node& node):
180     QApplication(qt_argc, const_cast<char **>(&qt_argv)),
181     coreThread(nullptr),
182     m_node(node),
183     optionsModel(nullptr),
184     clientModel(nullptr),
185     window(nullptr),
186     pollShutdownTimer(nullptr),
187     returnValue(0),
188     platformStyle(nullptr)
189 {
190     setQuitOnLastWindowClosed(false);
191 }
192 
setupPlatformStyle()193 void BitcoinApplication::setupPlatformStyle()
194 {
195     // UI per-platform customization
196     // This must be done inside the BitcoinApplication constructor, or after it, because
197     // PlatformStyle::instantiate requires a QApplication
198     std::string platformName;
199     platformName = gArgs.GetArg("-uiplatform", BitcoinGUI::DEFAULT_UIPLATFORM);
200     platformStyle = PlatformStyle::instantiate(QString::fromStdString(platformName));
201     if (!platformStyle) // Fall back to "other" if specified name not found
202         platformStyle = PlatformStyle::instantiate("other");
203     assert(platformStyle);
204 }
205 
~BitcoinApplication()206 BitcoinApplication::~BitcoinApplication()
207 {
208     if(coreThread)
209     {
210         qDebug() << __func__ << ": Stopping thread";
211         coreThread->quit();
212         coreThread->wait();
213         qDebug() << __func__ << ": Stopped thread";
214     }
215 
216     delete window;
217     window = nullptr;
218 #ifdef ENABLE_WALLET
219     delete paymentServer;
220     paymentServer = nullptr;
221     delete m_wallet_controller;
222     m_wallet_controller = nullptr;
223 #endif
224     delete optionsModel;
225     optionsModel = nullptr;
226     delete platformStyle;
227     platformStyle = nullptr;
228 }
229 
230 #ifdef ENABLE_WALLET
createPaymentServer()231 void BitcoinApplication::createPaymentServer()
232 {
233     paymentServer = new PaymentServer(this);
234 }
235 #endif
236 
createOptionsModel(bool resetSettings)237 void BitcoinApplication::createOptionsModel(bool resetSettings)
238 {
239     optionsModel = new OptionsModel(m_node, nullptr, resetSettings);
240 }
241 
createWindow(const NetworkStyle * networkStyle)242 void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
243 {
244     window = new BitcoinGUI(m_node, platformStyle, networkStyle, nullptr);
245 
246     pollShutdownTimer = new QTimer(window);
247     connect(pollShutdownTimer, &QTimer::timeout, window, &BitcoinGUI::detectShutdown);
248 }
249 
createSplashScreen(const NetworkStyle * networkStyle)250 void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle)
251 {
252     SplashScreen *splash = new SplashScreen(m_node, nullptr, networkStyle);
253     // We don't hold a direct pointer to the splash screen after creation, but the splash
254     // screen will take care of deleting itself when finish() happens.
255     splash->show();
256     connect(this, &BitcoinApplication::splashFinished, splash, &SplashScreen::finish);
257     connect(this, &BitcoinApplication::requestedShutdown, splash, &QWidget::close);
258 }
259 
baseInitialize()260 bool BitcoinApplication::baseInitialize()
261 {
262     return m_node.baseInitialize();
263 }
264 
startThread()265 void BitcoinApplication::startThread()
266 {
267     if(coreThread)
268         return;
269     coreThread = new QThread(this);
270     BitcoinCore *executor = new BitcoinCore(m_node);
271     executor->moveToThread(coreThread);
272 
273     /*  communication to and from thread */
274     connect(executor, &BitcoinCore::initializeResult, this, &BitcoinApplication::initializeResult);
275     connect(executor, &BitcoinCore::shutdownResult, this, &BitcoinApplication::shutdownResult);
276     connect(executor, &BitcoinCore::runawayException, this, &BitcoinApplication::handleRunawayException);
277     connect(this, &BitcoinApplication::requestedInitialize, executor, &BitcoinCore::initialize);
278     connect(this, &BitcoinApplication::requestedShutdown, executor, &BitcoinCore::shutdown);
279     /*  make sure executor object is deleted in its own thread */
280     connect(coreThread, &QThread::finished, executor, &QObject::deleteLater);
281 
282     coreThread->start();
283 }
284 
parameterSetup()285 void BitcoinApplication::parameterSetup()
286 {
287     // Default printtoconsole to false for the GUI. GUI programs should not
288     // print to the console unnecessarily.
289     gArgs.SoftSetBoolArg("-printtoconsole", false);
290 
291     m_node.initLogging();
292     m_node.initParameterInteraction();
293 }
294 
requestInitialize()295 void BitcoinApplication::requestInitialize()
296 {
297     qDebug() << __func__ << ": Requesting initialize";
298     startThread();
299     Q_EMIT requestedInitialize();
300 }
301 
requestShutdown()302 void BitcoinApplication::requestShutdown()
303 {
304     // Show a simple window indicating shutdown status
305     // Do this first as some of the steps may take some time below,
306     // for example the RPC console may still be executing a command.
307     shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window));
308 
309     qDebug() << __func__ << ": Requesting shutdown";
310     startThread();
311     window->hide();
312     // Must disconnect node signals otherwise current thread can deadlock since
313     // no event loop is running.
314     window->unsubscribeFromCoreSignals();
315     // Request node shutdown, which can interrupt long operations, like
316     // rescanning a wallet.
317     m_node.startShutdown();
318     // Unsetting the client model can cause the current thread to wait for node
319     // to complete an operation, like wait for a RPC execution to complate.
320     window->setClientModel(nullptr);
321     pollShutdownTimer->stop();
322 
323     delete clientModel;
324     clientModel = nullptr;
325 
326     // Request shutdown from core thread
327     Q_EMIT requestedShutdown();
328 }
329 
initializeResult(bool success)330 void BitcoinApplication::initializeResult(bool success)
331 {
332     qDebug() << __func__ << ": Initialization result: " << success;
333     // Set exit result.
334     returnValue = success ? EXIT_SUCCESS : EXIT_FAILURE;
335     if(success)
336     {
337         // Log this only after AppInitMain finishes, as then logging setup is guaranteed complete
338         qWarning() << "Platform customization:" << platformStyle->getName();
339 #ifdef ENABLE_WALLET
340         m_wallet_controller = new WalletController(m_node, platformStyle, optionsModel, this);
341 #ifdef ENABLE_BIP70
342         PaymentServer::LoadRootCAs();
343 #endif
344         if (paymentServer) {
345             paymentServer->setOptionsModel(optionsModel);
346 #ifdef ENABLE_BIP70
347             connect(m_wallet_controller, &WalletController::coinsSent, paymentServer, &PaymentServer::fetchPaymentACK);
348 #endif
349         }
350 #endif
351 
352         clientModel = new ClientModel(m_node, optionsModel);
353         window->setClientModel(clientModel);
354 #ifdef ENABLE_WALLET
355         window->setWalletController(m_wallet_controller);
356 #endif
357 
358         // If -min option passed, start window minimized (iconified) or minimized to tray
359         if (!gArgs.GetBoolArg("-min", false)) {
360             window->show();
361         } else if (clientModel->getOptionsModel()->getMinimizeToTray() && window->hasTrayIcon()) {
362             // do nothing as the window is managed by the tray icon
363         } else {
364             window->showMinimized();
365         }
366         Q_EMIT splashFinished();
367         Q_EMIT windowShown(window);
368 
369 #ifdef ENABLE_WALLET
370         // Now that initialization/startup is done, process any command-line
371         // bitcoin: URIs or payment requests:
372         if (paymentServer) {
373             connect(paymentServer, &PaymentServer::receivedPaymentRequest, window, &BitcoinGUI::handlePaymentRequest);
374             connect(window, &BitcoinGUI::receivedURI, paymentServer, &PaymentServer::handleURIOrFile);
375             connect(paymentServer, &PaymentServer::message, [this](const QString& title, const QString& message, unsigned int style) {
376                 window->message(title, message, style);
377             });
378             QTimer::singleShot(100, paymentServer, &PaymentServer::uiReady);
379         }
380 #endif
381         pollShutdownTimer->start(200);
382     } else {
383         Q_EMIT splashFinished(); // Make sure splash screen doesn't stick around during shutdown
384         quit(); // Exit first main loop invocation
385     }
386 }
387 
shutdownResult()388 void BitcoinApplication::shutdownResult()
389 {
390     quit(); // Exit second main loop invocation after shutdown finished
391 }
392 
handleRunawayException(const QString & message)393 void BitcoinApplication::handleRunawayException(const QString &message)
394 {
395     QMessageBox::critical(nullptr, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. Litecoin can no longer continue safely and will quit.") + QString("\n\n") + message);
396     ::exit(EXIT_FAILURE);
397 }
398 
getMainWinId() const399 WId BitcoinApplication::getMainWinId() const
400 {
401     if (!window)
402         return 0;
403 
404     return window->winId();
405 }
406 
SetupUIArgs()407 static void SetupUIArgs()
408 {
409 #if defined(ENABLE_WALLET) && defined(ENABLE_BIP70)
410     gArgs.AddArg("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS), true, OptionsCategory::GUI);
411 #endif
412     gArgs.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), false, OptionsCategory::GUI);
413     gArgs.AddArg("-lang=<lang>", "Set language, for example \"de_DE\" (default: system locale)", false, OptionsCategory::GUI);
414     gArgs.AddArg("-min", "Start minimized", false, OptionsCategory::GUI);
415     gArgs.AddArg("-resetguisettings", "Reset all settings changed in the GUI", false, OptionsCategory::GUI);
416     gArgs.AddArg("-rootcertificates=<file>", "Set SSL root certificates for payment request (default: -system-)", false, OptionsCategory::GUI);
417     gArgs.AddArg("-splash", strprintf("Show splash screen on startup (default: %u)", DEFAULT_SPLASHSCREEN), false, OptionsCategory::GUI);
418     gArgs.AddArg("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM), true, OptionsCategory::GUI);
419 }
420 
421 #ifndef BITCOIN_QT_TEST
GuiMain(int argc,char * argv[])422 int GuiMain(int argc, char* argv[])
423 {
424 #ifdef WIN32
425     util::WinCmdLineArgs winArgs;
426     std::tie(argc, argv) = winArgs.get();
427 #endif
428     SetupEnvironment();
429 
430     std::unique_ptr<interfaces::Node> node = interfaces::MakeNode();
431 
432     // Subscribe to global signals from core
433     std::unique_ptr<interfaces::Handler> handler_message_box = node->handleMessageBox(noui_ThreadSafeMessageBox);
434     std::unique_ptr<interfaces::Handler> handler_question = node->handleQuestion(noui_ThreadSafeQuestion);
435     std::unique_ptr<interfaces::Handler> handler_init_message = node->handleInitMessage(noui_InitMessage);
436 
437     // Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory
438 
439     /// 1. Basic Qt initialization (not dependent on parameters or configuration)
440     Q_INIT_RESOURCE(bitcoin);
441     Q_INIT_RESOURCE(bitcoin_locale);
442 
443     // Generate high-dpi pixmaps
444     QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
445 #if QT_VERSION >= 0x050600
446     QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
447 #endif
448 #ifdef Q_OS_MAC
449     QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
450 #endif
451 
452     BitcoinApplication app(*node);
453 
454     // Register meta types used for QMetaObject::invokeMethod
455     qRegisterMetaType< bool* >();
456 #ifdef ENABLE_WALLET
457     qRegisterMetaType<WalletModel*>();
458 #endif
459     //   Need to pass name here as CAmount is a typedef (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
460     //   IMPORTANT if it is no longer a typedef use the normal variant above
461     qRegisterMetaType< CAmount >("CAmount");
462     qRegisterMetaType< std::function<void()> >("std::function<void()>");
463     qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon");
464     /// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
465     // Command-line options take precedence:
466     node->setupServerArgs();
467     SetupUIArgs();
468     std::string error;
469     if (!node->parseParameters(argc, argv, error)) {
470         QMessageBox::critical(nullptr, PACKAGE_NAME,
471             QObject::tr("Error parsing command line arguments: %1.").arg(QString::fromStdString(error)));
472         return EXIT_FAILURE;
473     }
474 
475     // Now that the QApplication is setup and we have parsed our parameters, we can set the platform style
476     app.setupPlatformStyle();
477 
478     /// 3. Application identification
479     // must be set before OptionsModel is initialized or translations are loaded,
480     // as it is used to locate QSettings
481     QApplication::setOrganizationName(QAPP_ORG_NAME);
482     QApplication::setOrganizationDomain(QAPP_ORG_DOMAIN);
483     QApplication::setApplicationName(QAPP_APP_NAME_DEFAULT);
484 
485     /// 4. Initialization of translations, so that intro dialog is in user's language
486     // Now that QSettings are accessible, initialize translations
487     QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator;
488     initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator);
489 
490     // Show help message immediately after parsing command-line options (for "-lang") and setting locale,
491     // but before showing splash screen.
492     if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
493         HelpMessageDialog help(*node, nullptr, gArgs.IsArgSet("-version"));
494         help.showOrPrint();
495         return EXIT_SUCCESS;
496     }
497 
498     /// 5. Now that settings and translations are available, ask user for data directory
499     // User language is set up: pick a data directory
500     if (!Intro::pickDataDirectory(*node))
501         return EXIT_SUCCESS;
502 
503     /// 6. Determine availability of data and blocks directory and parse bitcoin.conf
504     /// - Do not call GetDataDir(true) before this step finishes
505     if (!fs::is_directory(GetDataDir(false)))
506     {
507         QMessageBox::critical(nullptr, PACKAGE_NAME,
508             QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(gArgs.GetArg("-datadir", ""))));
509         return EXIT_FAILURE;
510     }
511     if (!node->readConfigFiles(error)) {
512         QMessageBox::critical(nullptr, PACKAGE_NAME,
513             QObject::tr("Error: Cannot parse configuration file: %1.").arg(QString::fromStdString(error)));
514         return EXIT_FAILURE;
515     }
516 
517     /// 7. Determine network (and switch to network specific options)
518     // - Do not call Params() before this step
519     // - Do this after parsing the configuration file, as the network can be switched there
520     // - QSettings() will use the new application name after this, resulting in network-specific settings
521     // - Needs to be done before createOptionsModel
522 
523     // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
524     try {
525         node->selectParams(gArgs.GetChainName());
526     } catch(std::exception &e) {
527         QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: %1").arg(e.what()));
528         return EXIT_FAILURE;
529     }
530 #ifdef ENABLE_WALLET
531     // Parse URIs on command line -- this can affect Params()
532     PaymentServer::ipcParseCommandLine(*node, argc, argv);
533 #endif
534 
535     QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(QString::fromStdString(Params().NetworkIDString())));
536     assert(!networkStyle.isNull());
537     // Allow for separate UI settings for testnets
538     QApplication::setApplicationName(networkStyle->getAppName());
539     // Re-initialize translations after changing application name (language in network-specific settings can be different)
540     initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator);
541 
542 #ifdef ENABLE_WALLET
543     /// 8. URI IPC sending
544     // - Do this early as we don't want to bother initializing if we are just calling IPC
545     // - Do this *after* setting up the data directory, as the data directory hash is used in the name
546     // of the server.
547     // - Do this after creating app and setting up translations, so errors are
548     // translated properly.
549     if (PaymentServer::ipcSendCommandLine())
550         exit(EXIT_SUCCESS);
551 
552     // Start up the payment server early, too, so impatient users that click on
553     // bitcoin: links repeatedly have their payment requests routed to this process:
554     app.createPaymentServer();
555 #endif
556 
557     /// 9. Main GUI initialization
558     // Install global event filter that makes sure that long tooltips can be word-wrapped
559     app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
560 #if defined(Q_OS_WIN)
561     // Install global event filter for processing Windows session related Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION)
562     qApp->installNativeEventFilter(new WinShutdownMonitor());
563 #endif
564     // Install qDebug() message handler to route to debug.log
565     qInstallMessageHandler(DebugMessageHandler);
566     // Allow parameter interaction before we create the options model
567     app.parameterSetup();
568     // Load GUI settings from QSettings
569     app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false));
570 
571     if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
572         app.createSplashScreen(networkStyle.data());
573 
574     int rv = EXIT_SUCCESS;
575     try
576     {
577         app.createWindow(networkStyle.data());
578         // Perform base initialization before spinning up initialization/shutdown thread
579         // This is acceptable because this function only contains steps that are quick to execute,
580         // so the GUI thread won't be held up.
581         if (app.baseInitialize()) {
582             app.requestInitialize();
583 #if defined(Q_OS_WIN)
584             WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(PACKAGE_NAME), (HWND)app.getMainWinId());
585 #endif
586             app.exec();
587             app.requestShutdown();
588             app.exec();
589             rv = app.getReturnValue();
590         } else {
591             // A dialog with detailed error will have been shown by InitError()
592             rv = EXIT_FAILURE;
593         }
594     } catch (const std::exception& e) {
595         PrintExceptionContinue(&e, "Runaway exception");
596         app.handleRunawayException(QString::fromStdString(node->getWarnings("gui")));
597     } catch (...) {
598         PrintExceptionContinue(nullptr, "Runaway exception");
599         app.handleRunawayException(QString::fromStdString(node->getWarnings("gui")));
600     }
601     return rv;
602 }
603 #endif // BITCOIN_QT_TEST
604