bitcoin.cpp raw
1 // Copyright (c) 2011-present 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 #include <bitcoin-build-config.h> // IWYU pragma: keep
6
7 #include <qt/bitcoin.h>
8
9 #include <chainparams.h>
10 #include <common/args.h>
11 #include <common/init.h>
12 #include <common/system.h>
13 #include <init.h>
14 #include <interfaces/handler.h>
15 #include <interfaces/init.h>
16 #include <interfaces/node.h>
17 #include <node/context.h>
18 #include <node/interface_ui.h>
19 #include <noui.h>
20 #include <qt/bitcoingui.h>
21 #include <qt/clientmodel.h>
22 #include <qt/guiconstants.h>
23 #include <qt/guiutil.h>
24 #include <qt/initexecutor.h>
25 #include <qt/intro.h>
26 #include <qt/networkstyle.h>
27 #include <qt/optionsmodel.h>
28 #include <qt/platformstyle.h>
29 #include <qt/splashscreen.h>
30 #include <qt/utilitydialog.h>
31 #include <qt/winshutdownmonitor.h>
32 #include <uint256.h>
33 #include <util/btcsignals.h>
34 #include <util/exception.h>
35 #include <util/log.h>
36 #include <util/string.h>
37 #include <util/threadnames.h>
38 #include <util/translation.h>
39 #include <validation.h>
40
41 #ifdef ENABLE_WALLET
42 #include <qt/paymentserver.h>
43 #include <qt/walletcontroller.h>
44 #include <qt/walletmodel.h>
45 #include <wallet/types.h>
46 #endif // ENABLE_WALLET
47
48 #include <chrono>
49 #include <memory>
50
51 #include <QApplication>
52 #include <QDebug>
53 #include <QLatin1String>
54 #include <QLibraryInfo>
55 #include <QLocale>
56 #include <QMessageBox>
57 #include <QSettings>
58 #include <QThread>
59 #include <QTimer>
60 #include <QTranslator>
61 #include <QWindow>
62
63 // Declare meta types used for QMetaObject::invokeMethod
64 Q_DECLARE_METATYPE(bool*)
65 Q_DECLARE_METATYPE(CAmount)
66 Q_DECLARE_METATYPE(SynchronizationState)
67 Q_DECLARE_METATYPE(SyncType)
68 Q_DECLARE_METATYPE(uint256)
69 #ifdef ENABLE_WALLET
70 Q_DECLARE_METATYPE(wallet::AddressPurpose)
71 #endif // ENABLE_WALLET
72
73 using util::MakeUnorderedList;
74
75 static void RegisterMetaTypes()
76 {
77 // Register meta types used for QMetaObject::invokeMethod and Qt::QueuedConnection
78 qRegisterMetaType<bool*>();
79 qRegisterMetaType<SynchronizationState>();
80 qRegisterMetaType<SyncType>();
81 #ifdef ENABLE_WALLET
82 qRegisterMetaType<WalletModel*>();
83 qRegisterMetaType<wallet::AddressPurpose>();
84 #endif // ENABLE_WALLET
85 // Register typedefs (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType)
86 // IMPORTANT: if CAmount is no longer a typedef use the normal variant above (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1)
87 qRegisterMetaType<CAmount>("CAmount");
88 qRegisterMetaType<size_t>("size_t");
89
90 qRegisterMetaType<std::function<void()>>("std::function<void()>");
91 qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon");
92 qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo");
93 qRegisterMetaType<BitcoinUnit>("BitcoinUnit");
94 }
95
96 static QString GetLangTerritory()
97 {
98 QSettings settings;
99 // Get desired locale (e.g. "de_DE")
100 // 1) System default language
101 QString lang_territory = QLocale::system().name();
102 // 2) Language from QSettings
103 QString lang_territory_qsettings = settings.value("language", "").toString();
104 if(!lang_territory_qsettings.isEmpty())
105 lang_territory = lang_territory_qsettings;
106 // 3) -lang command line argument
107 lang_territory = QString::fromStdString(gArgs.GetArg("-lang", lang_territory.toStdString()));
108 return lang_territory;
109 }
110
111 /** Set up translations */
112 static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator)
113 {
114 // Remove old translators
115 QApplication::removeTranslator(&qtTranslatorBase);
116 QApplication::removeTranslator(&qtTranslator);
117 QApplication::removeTranslator(&translatorBase);
118 QApplication::removeTranslator(&translator);
119
120 // Get desired locale (e.g. "de_DE")
121 // 1) System default language
122 QString lang_territory = GetLangTerritory();
123
124 // Convert to "de" only by truncating "_DE"
125 QString lang = lang_territory;
126 lang.truncate(lang_territory.lastIndexOf('_'));
127
128 // Load language files for configured locale:
129 // - First load the translator for the base language, without territory
130 // - Then load the more specific locale translator
131
132 const QString translation_path{QLibraryInfo::path(QLibraryInfo::TranslationsPath)};
133 // Load e.g. qt_de.qm
134 if (qtTranslatorBase.load("qt_" + lang, translation_path)) {
135 QApplication::installTranslator(&qtTranslatorBase);
136 }
137
138 // Load e.g. qt_de_DE.qm
139 if (qtTranslator.load("qt_" + lang_territory, translation_path)) {
140 QApplication::installTranslator(&qtTranslator);
141 }
142
143 // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in bitcoin.qrc)
144 if (translatorBase.load(lang, ":/translations/")) {
145 QApplication::installTranslator(&translatorBase);
146 }
147
148 // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in bitcoin.qrc)
149 if (translator.load(lang_territory, ":/translations/")) {
150 QApplication::installTranslator(&translator);
151 }
152 }
153
154 static bool ErrorSettingsRead(const bilingual_str& error, const std::vector<std::string>& details)
155 {
156 QMessageBox messagebox(QMessageBox::Critical, CLIENT_NAME, QString::fromStdString(strprintf("%s.", error.translated)), QMessageBox::Reset | QMessageBox::Abort);
157 /*: Explanatory text shown on startup when the settings file cannot be read.
158 Prompts user to make a choice between resetting or aborting. */
159 messagebox.setInformativeText(QObject::tr("Do you want to reset settings to default values, or to abort without making changes?"));
160 messagebox.setDetailedText(QString::fromStdString(MakeUnorderedList(details)));
161 messagebox.setTextFormat(Qt::PlainText);
162 messagebox.setDefaultButton(QMessageBox::Reset);
163 switch (messagebox.exec()) {
164 case QMessageBox::Reset:
165 return false;
166 case QMessageBox::Abort:
167 return true;
168 default:
169 assert(false);
170 }
171 }
172
173 static void ErrorSettingsWrite(const bilingual_str& error, const std::vector<std::string>& details)
174 {
175 QMessageBox messagebox(QMessageBox::Critical, CLIENT_NAME, QString::fromStdString(strprintf("%s.", error.translated)), QMessageBox::Ok);
176 /*: Explanatory text shown on startup when the settings file could not be written.
177 Prompts user to check that we have the ability to write to the file.
178 Explains that the user has the option of running without a settings file.*/
179 messagebox.setInformativeText(QObject::tr("A fatal error occurred. Check that settings file is writable, or try running with -nosettings."));
180 messagebox.setDetailedText(QString::fromStdString(MakeUnorderedList(details)));
181 messagebox.setTextFormat(Qt::PlainText);
182 messagebox.setDefaultButton(QMessageBox::Ok);
183 messagebox.exec();
184 }
185
186 /* qDebug() message handler --> debug.log */
187 void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg)
188 {
189 Q_UNUSED(context);
190 if (type == QtDebugMsg) {
191 LogDebug(BCLog::QT, "GUI: %s\n", msg.toStdString());
192 } else {
193 LogInfo("GUI: %s", msg.toStdString());
194 }
195 }
196
197 static int qt_argc = 1;
198 static const char* qt_argv = "bitcoin-qt";
199
200 BitcoinApplication::BitcoinApplication()
201 : QApplication(qt_argc, const_cast<char**>(&qt_argv))
202 {
203 // Qt runs setlocale(LC_ALL, "") on initialization.
204 RegisterMetaTypes();
205 setQuitOnLastWindowClosed(false);
206 }
207
208 void BitcoinApplication::setupPlatformStyle()
209 {
210 // UI per-platform customization
211 // This must be done inside the BitcoinApplication constructor, or after it, because
212 // PlatformStyle::instantiate requires a QApplication
213 std::string platformName;
214 platformName = gArgs.GetArg("-uiplatform", BitcoinGUI::DEFAULT_UIPLATFORM);
215 platformStyle = PlatformStyle::instantiate(QString::fromStdString(platformName));
216 if (!platformStyle) // Fall back to "other" if specified name not found
217 platformStyle = PlatformStyle::instantiate("other");
218 assert(platformStyle);
219 }
220
221 BitcoinApplication::~BitcoinApplication()
222 {
223 m_executor.reset();
224
225 delete window;
226 window = nullptr;
227 delete platformStyle;
228 platformStyle = nullptr;
229 }
230
231 #ifdef ENABLE_WALLET
232 void BitcoinApplication::createPaymentServer()
233 {
234 paymentServer = new PaymentServer(this);
235 }
236 #endif
237
238 bool BitcoinApplication::createOptionsModel(bool resetSettings)
239 {
240 optionsModel = new OptionsModel(node(), this);
241 if (resetSettings) {
242 optionsModel->Reset();
243 }
244 bilingual_str error;
245 if (!optionsModel->Init(error)) {
246 fs::path settings_path;
247 if (gArgs.GetSettingsPath(&settings_path)) {
248 error += Untranslated("\n");
249 std::string quoted_path = strprintf("%s", fs::quoted(fs::PathToString(settings_path)));
250 error.original += strprintf("Settings file %s might be corrupt or invalid.", quoted_path);
251 error.translated += tr("Settings file %1 might be corrupt or invalid.").arg(QString::fromStdString(quoted_path)).toStdString();
252 }
253 InitError(error);
254 QMessageBox::critical(nullptr, CLIENT_NAME, QString::fromStdString(error.translated));
255 return false;
256 }
257 return true;
258 }
259
260 void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
261 {
262 window = new BitcoinGUI(node(), platformStyle, networkStyle, nullptr);
263 connect(window, &BitcoinGUI::quitRequested, this, &BitcoinApplication::requestShutdown);
264
265 pollShutdownTimer = new QTimer(window);
266 connect(pollShutdownTimer, &QTimer::timeout, [this]{
267 if (!QApplication::activeModalWidget()) {
268 window->detectShutdown();
269 }
270 });
271 }
272
273 void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle)
274 {
275 assert(!m_splash);
276 m_splash = new SplashScreen(networkStyle);
277 m_splash->show();
278 }
279
280 void BitcoinApplication::createNode(interfaces::Init& init)
281 {
282 assert(!m_node);
283 m_node = init.makeNode();
284 if (m_splash) m_splash->setNode(*m_node);
285 }
286
287 bool BitcoinApplication::baseInitialize()
288 {
289 return node().baseInitialize();
290 }
291
292 void BitcoinApplication::startThread()
293 {
294 assert(!m_executor);
295 m_executor.emplace(node());
296
297 /* communication to and from thread */
298 connect(&m_executor.value(), &InitExecutor::initializeResult, this, &BitcoinApplication::initializeResult);
299 connect(&m_executor.value(), &InitExecutor::shutdownResult, this, [] {
300 QCoreApplication::exit(0);
301 });
302 connect(&m_executor.value(), &InitExecutor::runawayException, this, &BitcoinApplication::handleRunawayException);
303 connect(this, &BitcoinApplication::requestedInitialize, &m_executor.value(), &InitExecutor::initialize);
304 connect(this, &BitcoinApplication::requestedShutdown, &m_executor.value(), &InitExecutor::shutdown);
305 }
306
307 void BitcoinApplication::parameterSetup()
308 {
309 // Default printtoconsole to false for the GUI. GUI programs should not
310 // print to the console unnecessarily.
311 gArgs.SoftSetBoolArg("-printtoconsole", false);
312
313 InitLogging(gArgs);
314 InitParameterInteraction(gArgs);
315 }
316
317 void BitcoinApplication::InitPruneSetting(int64_t prune_MiB)
318 {
319 optionsModel->SetPruneTargetGB(PruneMiBtoGB(prune_MiB));
320 }
321
322 void BitcoinApplication::requestInitialize()
323 {
324 qDebug() << __func__ << ": Requesting initialize";
325 startThread();
326 Q_EMIT requestedInitialize();
327 }
328
329 void BitcoinApplication::requestShutdown()
330 {
331 for (const auto w : QGuiApplication::topLevelWindows()) {
332 w->hide();
333 }
334
335 delete m_splash;
336 m_splash = nullptr;
337
338 // Show a simple window indicating shutdown status
339 // Do this first as some of the steps may take some time below,
340 // for example the RPC console may still be executing a command.
341 shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window));
342
343 qDebug() << __func__ << ": Requesting shutdown";
344
345 // Must disconnect node signals otherwise current thread can deadlock since
346 // no event loop is running.
347 window->unsubscribeFromCoreSignals();
348 // Request node shutdown, which can interrupt long operations, like
349 // rescanning a wallet.
350 node().startShutdown();
351 // Prior to unsetting the client model, stop listening backend signals
352 if (clientModel) {
353 clientModel->stop();
354 }
355
356 // Unsetting the client model can cause the current thread to wait for node
357 // to complete an operation, like wait for a RPC execution to complete.
358 window->setClientModel(nullptr);
359 pollShutdownTimer->stop();
360
361 #ifdef ENABLE_WALLET
362 // Delete wallet controller here manually, instead of relying on Qt object
363 // tracking (https://doc.qt.io/qt-5/objecttrees.html). This makes sure
364 // walletmodel m_handle_* notification handlers are deleted before wallets
365 // are unloaded, which can simplify wallet implementations. It also avoids
366 // these notifications having to be handled while GUI objects are being
367 // destroyed, making GUI code less fragile as well.
368 delete m_wallet_controller;
369 m_wallet_controller = nullptr;
370 #endif // ENABLE_WALLET
371
372 delete clientModel;
373 clientModel = nullptr;
374
375 // Request shutdown from core thread
376 Q_EMIT requestedShutdown();
377 }
378
379 void BitcoinApplication::initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info)
380 {
381 qDebug() << __func__ << ": Initialization result: " << success;
382
383 if (!success || m_node->shutdownRequested()) {
384 requestShutdown();
385 return;
386 }
387
388 delete m_splash;
389 m_splash = nullptr;
390
391 // Log this only after AppInitMain finishes, as then logging setup is guaranteed complete
392 qInfo() << "Platform customization:" << platformStyle->getName();
393 clientModel = new ClientModel(node(), optionsModel);
394 window->setClientModel(clientModel, &tip_info);
395
396 // If '-min' option passed, start window minimized (iconified) or minimized to tray
397 bool start_minimized = gArgs.GetBoolArg("-min", false);
398 #ifdef ENABLE_WALLET
399 if (WalletModel::isWalletEnabled()) {
400 m_wallet_controller = new WalletController(*clientModel, platformStyle, this);
401 window->setWalletController(m_wallet_controller, /*show_loading_minimized=*/start_minimized);
402 if (paymentServer) {
403 paymentServer->setOptionsModel(optionsModel);
404 }
405 }
406 #endif // ENABLE_WALLET
407
408 // Show or minimize window
409 if (!start_minimized) {
410 window->show();
411 } else if (clientModel->getOptionsModel()->getMinimizeToTray() && window->hasTrayIcon()) {
412 // do nothing as the window is managed by the tray icon
413 } else {
414 window->showMinimized();
415 }
416 Q_EMIT windowShown(window);
417
418 #ifdef ENABLE_WALLET
419 // Now that initialization/startup is done, process any command-line
420 // bitcoin: URIs or payment requests:
421 if (paymentServer) {
422 connect(paymentServer, &PaymentServer::receivedPaymentRequest, window, &BitcoinGUI::handlePaymentRequest);
423 connect(window, &BitcoinGUI::receivedURI, paymentServer, &PaymentServer::handleURIOrFile);
424 connect(paymentServer, &PaymentServer::message, [this](const QString& title, const QString& message, unsigned int style) {
425 window->message(title, message, style);
426 });
427 QTimer::singleShot(100ms, paymentServer, &PaymentServer::uiReady);
428 }
429 #endif
430 pollShutdownTimer->start(SHUTDOWN_POLLING_DELAY);
431 }
432
433 void BitcoinApplication::handleRunawayException(const QString &message)
434 {
435 QMessageBox::critical(
436 nullptr, tr("Runaway exception"),
437 tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(CLIENT_NAME) +
438 QLatin1String("<br><br>") + GUIUtil::MakeHtmlLink(message, CLIENT_BUGREPORT));
439 ::exit(EXIT_FAILURE);
440 }
441
442 void BitcoinApplication::handleNonFatalException(const QString& message)
443 {
444 assert(QThread::currentThread() == thread());
445 QMessageBox::warning(
446 nullptr, tr("Internal error"),
447 tr("An internal error occurred. %1 will attempt to continue safely. This is "
448 "an unexpected bug which can be reported as described below.").arg(CLIENT_NAME) +
449 QLatin1String("<br><br>") + GUIUtil::MakeHtmlLink(message, CLIENT_BUGREPORT));
450 }
451
452 WId BitcoinApplication::getMainWinId() const
453 {
454 if (!window)
455 return 0;
456
457 return window->winId();
458 }
459
460 bool BitcoinApplication::event(QEvent* e)
461 {
462 if (e->type() == QEvent::Quit) {
463 requestShutdown();
464 return true;
465 }
466
467 return QApplication::event(e);
468 }
469
470 static void SetupUIArgs(ArgsManager& argsman)
471 {
472 argsman.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
473 argsman.AddArg("-lang=<lang>", "Set language, for example \"de_DE\" (default: system locale)", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
474 argsman.AddArg("-min", "Start minimized", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
475 argsman.AddArg("-resetguisettings", "Reset all settings changed in the GUI", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
476 argsman.AddArg("-splash", strprintf("Show splash screen on startup (default: %u)", DEFAULT_SPLASHSCREEN), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
477 argsman.AddArg("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::GUI);
478 }
479
480 int GuiMain(int argc, char* argv[])
481 {
482 std::unique_ptr<interfaces::Init> init = interfaces::MakeGuiInit(argc, argv);
483
484 SetupEnvironment();
485 util::ThreadSetInternalName("main");
486
487 // Subscribe to global signals from core
488 btcsignals::scoped_connection handler_message_box{::uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox)};
489 btcsignals::scoped_connection handler_question{::uiInterface.ThreadSafeQuestion.connect(noui_ThreadSafeQuestion)};
490 btcsignals::scoped_connection handler_init_message{::uiInterface.InitMessage.connect(noui_InitMessage)};
491
492 // Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory
493
494 /// 1. Basic Qt initialization (not dependent on parameters or configuration)
495 Q_INIT_RESOURCE(bitcoin);
496 Q_INIT_RESOURCE(bitcoin_locale);
497
498 #if defined(QT_QPA_PLATFORM_ANDROID)
499 QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
500 QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
501 QApplication::setAttribute(Qt::AA_DontUseNativeDialogs);
502 #endif
503
504 BitcoinApplication app;
505 GUIUtil::LoadFont(QStringLiteral(":/fonts/monospace"));
506
507 /// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
508 // Command-line options take precedence:
509 SetupServerArgs(gArgs, init->canListenIpc());
510 SetupUIArgs(gArgs);
511 std::string error;
512 if (!gArgs.ParseParameters(argc, argv, error)) {
513 InitError(Untranslated(strprintf("Error parsing command line arguments: %s", error)));
514 // Create a message box, because the gui has neither been created nor has subscribed to core signals
515 QMessageBox::critical(nullptr, CLIENT_NAME,
516 // message cannot be translated because translations have not been initialized
517 QString::fromStdString("Error parsing command line arguments: %1.").arg(QString::fromStdString(error)));
518 return EXIT_FAILURE;
519 }
520
521 // Error out when loose non-argument tokens are encountered on command line
522 // However, allow BIP-21 URIs only if no options follow
523 bool payment_server_token_seen = false;
524 for (int i = 1; i < argc; i++) {
525 QString arg(argv[i]);
526 bool invalid_token = !arg.startsWith("-");
527 #ifdef ENABLE_WALLET
528 if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) {
529 invalid_token &= false;
530 payment_server_token_seen = true;
531 }
532 #endif
533 if (payment_server_token_seen && arg.startsWith("-")) {
534 InitError(Untranslated(strprintf("Options ('%s') cannot follow a BIP-21 payment URI", argv[i])));
535 QMessageBox::critical(nullptr, CLIENT_NAME,
536 // message cannot be translated because translations have not been initialized
537 QString::fromStdString("Options ('%1') cannot follow a BIP-21 payment URI").arg(QString::fromStdString(argv[i])));
538 return EXIT_FAILURE;
539 }
540 if (invalid_token) {
541 InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoin-qt -h for a list of options.", argv[i])));
542 QMessageBox::critical(nullptr, CLIENT_NAME,
543 // message cannot be translated because translations have not been initialized
544 QString::fromStdString("Command line contains unexpected token '%1', see bitcoin-qt -h for a list of options.").arg(QString::fromStdString(argv[i])));
545 return EXIT_FAILURE;
546 }
547 }
548
549 // Now that the QApplication is setup and we have parsed our parameters, we can set the platform style
550 app.setupPlatformStyle();
551
552 /// 3. Application identification
553 // must be set before OptionsModel is initialized or translations are loaded,
554 // as it is used to locate QSettings
555 QApplication::setOrganizationName(QAPP_ORG_NAME);
556 QApplication::setOrganizationDomain(QAPP_ORG_DOMAIN);
557 QApplication::setApplicationName(QAPP_APP_NAME_DEFAULT);
558
559 /// 4. Initialization of translations, so that intro dialog is in user's language
560 // Now that QSettings are accessible, initialize translations
561 QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator;
562 initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator);
563
564 // Show help message immediately after parsing command-line options (for "-lang") and setting locale,
565 // but before showing splash screen.
566 if (HelpRequested(gArgs) || gArgs.GetBoolArg("-version", false)) {
567 HelpMessageDialog help(nullptr, gArgs.GetBoolArg("-version", false));
568 help.showOrPrint();
569 return EXIT_SUCCESS;
570 }
571
572 // Install global event filter that makes sure that long tooltips can be word-wrapped
573 app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
574
575 /// 5. Now that settings and translations are available, ask user for data directory
576 // User language is set up: pick a data directory
577 bool did_show_intro = false;
578 int64_t prune_MiB = 0; // Intro dialog prune configuration
579 // Gracefully exit if the user cancels
580 if (!Intro::showIfNeeded(did_show_intro, prune_MiB)) return EXIT_SUCCESS;
581
582 /// 6-7. Parse bitcoin.conf, determine network, switch to network specific
583 /// options, and create datadir and settings.json.
584 // - Do not call gArgs.GetDataDirNet() before this step finishes
585 // - Do not call Params() before this step
586 // - QSettings() will use the new application name after this, resulting in network-specific settings
587 // - Needs to be done before createOptionsModel
588 if (auto error = common::InitConfig(gArgs, ErrorSettingsRead)) {
589 InitError(error->message, error->details);
590 if (error->status == common::ConfigStatus::FAILED_WRITE) {
591 // Show a custom error message to provide more information in the
592 // case of a datadir write error.
593 ErrorSettingsWrite(error->message, error->details);
594 } else if (error->status != common::ConfigStatus::ABORTED) {
595 // Show a generic message in other cases, and no additional error
596 // message in the case of a read error if the user decided to abort.
597 QMessageBox::critical(nullptr, CLIENT_NAME, QObject::tr("Error: %1").arg(QString::fromStdString(error->message.translated)));
598 }
599 return EXIT_FAILURE;
600 }
601 #ifdef ENABLE_WALLET
602 // Parse URIs on command line
603 PaymentServer::ipcParseCommandLine(argc, argv);
604 #endif
605
606 QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(Params().GetChainType()));
607 assert(!networkStyle.isNull());
608 // Allow for separate UI settings for testnets
609 QApplication::setApplicationName(networkStyle->getAppName());
610 // Re-initialize translations after changing application name (language in network-specific settings can be different)
611 initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator);
612
613 #ifdef ENABLE_WALLET
614 /// 8. URI IPC sending
615 // - Do this early as we don't want to bother initializing if we are just calling IPC
616 // - Do this *after* setting up the data directory, as the data directory hash is used in the name
617 // of the server.
618 // - Do this after creating app and setting up translations, so errors are
619 // translated properly.
620 if (PaymentServer::ipcSendCommandLine())
621 exit(EXIT_SUCCESS);
622
623 // Start up the payment server early, too, so impatient users that click on
624 // bitcoin: links repeatedly have their payment requests routed to this process:
625 if (WalletModel::isWalletEnabled()) {
626 app.createPaymentServer();
627 }
628 #endif // ENABLE_WALLET
629
630 /// 9. Main GUI initialization
631 // Install global event filter that makes sure that out-of-focus labels do not contain text cursor.
632 app.installEventFilter(new GUIUtil::LabelOutOfFocusEventFilter(&app));
633 #if defined(Q_OS_WIN)
634 // Install global event filter for processing Windows session related Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION)
635 // Note: it is safe to call app.node() in the lambda below despite the fact
636 // that app.createNode() hasn't been called yet, because native events will
637 // not be processed until the Qt event loop is executed.
638 qApp->installNativeEventFilter(new WinShutdownMonitor([&app] { app.node().startShutdown(); }));
639 #endif
640 // Install qDebug() message handler to route to debug.log
641 qInstallMessageHandler(DebugMessageHandler);
642 // Allow parameter interaction before we create the options model
643 app.parameterSetup();
644 GUIUtil::LogQtInfo();
645
646 if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
647 app.createSplashScreen(networkStyle.data());
648
649 app.createNode(*init);
650
651 // Load GUI settings from QSettings
652 if (!app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false))) {
653 return EXIT_FAILURE;
654 }
655
656 if (did_show_intro) {
657 // Store intro dialog settings other than datadir (network specific)
658 app.InitPruneSetting(prune_MiB);
659 }
660
661 try
662 {
663 app.createWindow(networkStyle.data());
664 // Perform base initialization before spinning up initialization/shutdown thread
665 // This is acceptable because this function only contains steps that are quick to execute,
666 // so the GUI thread won't be held up.
667 if (app.baseInitialize()) {
668 app.requestInitialize();
669 #if defined(Q_OS_WIN)
670 WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely…").arg(CLIENT_NAME), (HWND)app.getMainWinId());
671 #endif
672 app.exec();
673 } else {
674 // A dialog with detailed error will have been shown by InitError()
675 return EXIT_FAILURE;
676 }
677 } catch (const std::exception& e) {
678 PrintExceptionContinue(&e, "Runaway exception");
679 app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
680 } catch (...) {
681 PrintExceptionContinue(nullptr, "Runaway exception");
682 app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
683 }
684 return app.node().getExitStatus();
685 }
686