bitcoin.cpp
1 // Copyright (c) 2011-2022 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 <logging.h> 18 #include <node/context.h> 19 #include <node/interface_ui.h> 20 #include <noui.h> 21 #include <qt/bitcoingui.h> 22 #include <qt/clientmodel.h> 23 #include <qt/guiconstants.h> 24 #include <qt/guiutil.h> 25 #include <qt/initexecutor.h> 26 #include <qt/intro.h> 27 #include <qt/networkstyle.h> 28 #include <qt/optionsmodel.h> 29 #include <qt/platformstyle.h> 30 #include <qt/splashscreen.h> 31 #include <qt/utilitydialog.h> 32 #include <qt/winshutdownmonitor.h> 33 #include <uint256.h> 34 #include <util/exception.h> 35 #include <util/string.h> 36 #include <util/threadnames.h> 37 #include <util/translation.h> 38 #include <validation.h> 39 40 #ifdef ENABLE_WALLET 41 #include <qt/paymentserver.h> 42 #include <qt/walletcontroller.h> 43 #include <qt/walletmodel.h> 44 #include <wallet/types.h> 45 #endif // ENABLE_WALLET 46 47 #include <boost/signals2/connection.hpp> 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) { 384 delete m_splash; 385 m_splash = nullptr; 386 387 // Log this only after AppInitMain finishes, as then logging setup is guaranteed complete 388 qInfo() << "Platform customization:" << platformStyle->getName(); 389 clientModel = new ClientModel(node(), optionsModel); 390 window->setClientModel(clientModel, &tip_info); 391 392 // If '-min' option passed, start window minimized (iconified) or minimized to tray 393 bool start_minimized = gArgs.GetBoolArg("-min", false); 394 #ifdef ENABLE_WALLET 395 if (WalletModel::isWalletEnabled()) { 396 m_wallet_controller = new WalletController(*clientModel, platformStyle, this); 397 window->setWalletController(m_wallet_controller, /*show_loading_minimized=*/start_minimized); 398 if (paymentServer) { 399 paymentServer->setOptionsModel(optionsModel); 400 } 401 } 402 #endif // ENABLE_WALLET 403 404 // Show or minimize window 405 if (!start_minimized) { 406 window->show(); 407 } else if (clientModel->getOptionsModel()->getMinimizeToTray() && window->hasTrayIcon()) { 408 // do nothing as the window is managed by the tray icon 409 } else { 410 window->showMinimized(); 411 } 412 Q_EMIT windowShown(window); 413 414 #ifdef ENABLE_WALLET 415 // Now that initialization/startup is done, process any command-line 416 // bitcoin: URIs or payment requests: 417 if (paymentServer) { 418 connect(paymentServer, &PaymentServer::receivedPaymentRequest, window, &BitcoinGUI::handlePaymentRequest); 419 connect(window, &BitcoinGUI::receivedURI, paymentServer, &PaymentServer::handleURIOrFile); 420 connect(paymentServer, &PaymentServer::message, [this](const QString& title, const QString& message, unsigned int style) { 421 window->message(title, message, style); 422 }); 423 QTimer::singleShot(100ms, paymentServer, &PaymentServer::uiReady); 424 } 425 #endif 426 pollShutdownTimer->start(SHUTDOWN_POLLING_DELAY); 427 } else { 428 requestShutdown(); 429 } 430 } 431 432 void BitcoinApplication::handleRunawayException(const QString &message) 433 { 434 QMessageBox::critical( 435 nullptr, tr("Runaway exception"), 436 tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(CLIENT_NAME) + 437 QLatin1String("<br><br>") + GUIUtil::MakeHtmlLink(message, CLIENT_BUGREPORT)); 438 ::exit(EXIT_FAILURE); 439 } 440 441 void BitcoinApplication::handleNonFatalException(const QString& message) 442 { 443 assert(QThread::currentThread() == thread()); 444 QMessageBox::warning( 445 nullptr, tr("Internal error"), 446 tr("An internal error occurred. %1 will attempt to continue safely. This is " 447 "an unexpected bug which can be reported as described below.").arg(CLIENT_NAME) + 448 QLatin1String("<br><br>") + GUIUtil::MakeHtmlLink(message, CLIENT_BUGREPORT)); 449 } 450 451 WId BitcoinApplication::getMainWinId() const 452 { 453 if (!window) 454 return 0; 455 456 return window->winId(); 457 } 458 459 bool BitcoinApplication::event(QEvent* e) 460 { 461 if (e->type() == QEvent::Quit) { 462 requestShutdown(); 463 return true; 464 } 465 466 return QApplication::event(e); 467 } 468 469 static void SetupUIArgs(ArgsManager& argsman) 470 { 471 argsman.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), ArgsManager::ALLOW_ANY, OptionsCategory::GUI); 472 argsman.AddArg("-lang=<lang>", "Set language, for example \"de_DE\" (default: system locale)", ArgsManager::ALLOW_ANY, OptionsCategory::GUI); 473 argsman.AddArg("-min", "Start minimized", ArgsManager::ALLOW_ANY, OptionsCategory::GUI); 474 argsman.AddArg("-resetguisettings", "Reset all settings changed in the GUI", ArgsManager::ALLOW_ANY, OptionsCategory::GUI); 475 argsman.AddArg("-splash", strprintf("Show splash screen on startup (default: %u)", DEFAULT_SPLASHSCREEN), ArgsManager::ALLOW_ANY, OptionsCategory::GUI); 476 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); 477 } 478 479 int GuiMain(int argc, char* argv[]) 480 { 481 std::unique_ptr<interfaces::Init> init = interfaces::MakeGuiInit(argc, argv); 482 483 SetupEnvironment(); 484 util::ThreadSetInternalName("main"); 485 486 // Subscribe to global signals from core 487 boost::signals2::scoped_connection handler_message_box = ::uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox); 488 boost::signals2::scoped_connection handler_question = ::uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestion); 489 boost::signals2::scoped_connection handler_init_message = ::uiInterface.InitMessage_connect(noui_InitMessage); 490 491 // Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory 492 493 /// 1. Basic Qt initialization (not dependent on parameters or configuration) 494 Q_INIT_RESOURCE(bitcoin); 495 Q_INIT_RESOURCE(bitcoin_locale); 496 497 #if defined(QT_QPA_PLATFORM_ANDROID) 498 QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar); 499 QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); 500 QApplication::setAttribute(Qt::AA_DontUseNativeDialogs); 501 #endif 502 503 BitcoinApplication app; 504 GUIUtil::LoadFont(QStringLiteral(":/fonts/monospace")); 505 506 /// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these 507 // Command-line options take precedence: 508 SetupServerArgs(gArgs, init->canListenIpc()); 509 SetupUIArgs(gArgs); 510 std::string error; 511 if (!gArgs.ParseParameters(argc, argv, error)) { 512 InitError(Untranslated(strprintf("Error parsing command line arguments: %s", error))); 513 // Create a message box, because the gui has neither been created nor has subscribed to core signals 514 QMessageBox::critical(nullptr, CLIENT_NAME, 515 // message cannot be translated because translations have not been initialized 516 QString::fromStdString("Error parsing command line arguments: %1.").arg(QString::fromStdString(error))); 517 return EXIT_FAILURE; 518 } 519 520 // Error out when loose non-argument tokens are encountered on command line 521 // However, allow BIP-21 URIs only if no options follow 522 bool payment_server_token_seen = false; 523 for (int i = 1; i < argc; i++) { 524 QString arg(argv[i]); 525 bool invalid_token = !arg.startsWith("-"); 526 #ifdef ENABLE_WALLET 527 if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) { 528 invalid_token &= false; 529 payment_server_token_seen = true; 530 } 531 #endif 532 if (payment_server_token_seen && arg.startsWith("-")) { 533 InitError(Untranslated(strprintf("Options ('%s') cannot follow a BIP-21 payment URI", argv[i]))); 534 QMessageBox::critical(nullptr, CLIENT_NAME, 535 // message cannot be translated because translations have not been initialized 536 QString::fromStdString("Options ('%1') cannot follow a BIP-21 payment URI").arg(QString::fromStdString(argv[i]))); 537 return EXIT_FAILURE; 538 } 539 if (invalid_token) { 540 InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoin-qt -h for a list of options.", argv[i]))); 541 QMessageBox::critical(nullptr, CLIENT_NAME, 542 // message cannot be translated because translations have not been initialized 543 QString::fromStdString("Command line contains unexpected token '%1', see bitcoin-qt -h for a list of options.").arg(QString::fromStdString(argv[i]))); 544 return EXIT_FAILURE; 545 } 546 } 547 548 // Now that the QApplication is setup and we have parsed our parameters, we can set the platform style 549 app.setupPlatformStyle(); 550 551 /// 3. Application identification 552 // must be set before OptionsModel is initialized or translations are loaded, 553 // as it is used to locate QSettings 554 QApplication::setOrganizationName(QAPP_ORG_NAME); 555 QApplication::setOrganizationDomain(QAPP_ORG_DOMAIN); 556 QApplication::setApplicationName(QAPP_APP_NAME_DEFAULT); 557 558 /// 4. Initialization of translations, so that intro dialog is in user's language 559 // Now that QSettings are accessible, initialize translations 560 QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator; 561 initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); 562 563 // Show help message immediately after parsing command-line options (for "-lang") and setting locale, 564 // but before showing splash screen. 565 if (HelpRequested(gArgs) || gArgs.GetBoolArg("-version", false)) { 566 HelpMessageDialog help(nullptr, gArgs.GetBoolArg("-version", false)); 567 help.showOrPrint(); 568 return EXIT_SUCCESS; 569 } 570 571 // Install global event filter that makes sure that long tooltips can be word-wrapped 572 app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app)); 573 574 /// 5. Now that settings and translations are available, ask user for data directory 575 // User language is set up: pick a data directory 576 bool did_show_intro = false; 577 int64_t prune_MiB = 0; // Intro dialog prune configuration 578 // Gracefully exit if the user cancels 579 if (!Intro::showIfNeeded(did_show_intro, prune_MiB)) return EXIT_SUCCESS; 580 581 /// 6-7. Parse bitcoin.conf, determine network, switch to network specific 582 /// options, and create datadir and settings.json. 583 // - Do not call gArgs.GetDataDirNet() before this step finishes 584 // - Do not call Params() before this step 585 // - QSettings() will use the new application name after this, resulting in network-specific settings 586 // - Needs to be done before createOptionsModel 587 if (auto error = common::InitConfig(gArgs, ErrorSettingsRead)) { 588 InitError(error->message, error->details); 589 if (error->status == common::ConfigStatus::FAILED_WRITE) { 590 // Show a custom error message to provide more information in the 591 // case of a datadir write error. 592 ErrorSettingsWrite(error->message, error->details); 593 } else if (error->status != common::ConfigStatus::ABORTED) { 594 // Show a generic message in other cases, and no additional error 595 // message in the case of a read error if the user decided to abort. 596 QMessageBox::critical(nullptr, CLIENT_NAME, QObject::tr("Error: %1").arg(QString::fromStdString(error->message.translated))); 597 } 598 return EXIT_FAILURE; 599 } 600 #ifdef ENABLE_WALLET 601 // Parse URIs on command line 602 PaymentServer::ipcParseCommandLine(argc, argv); 603 #endif 604 605 QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(Params().GetChainType())); 606 assert(!networkStyle.isNull()); 607 // Allow for separate UI settings for testnets 608 QApplication::setApplicationName(networkStyle->getAppName()); 609 // Re-initialize translations after changing application name (language in network-specific settings can be different) 610 initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); 611 612 #ifdef ENABLE_WALLET 613 /// 8. URI IPC sending 614 // - Do this early as we don't want to bother initializing if we are just calling IPC 615 // - Do this *after* setting up the data directory, as the data directory hash is used in the name 616 // of the server. 617 // - Do this after creating app and setting up translations, so errors are 618 // translated properly. 619 if (PaymentServer::ipcSendCommandLine()) 620 exit(EXIT_SUCCESS); 621 622 // Start up the payment server early, too, so impatient users that click on 623 // bitcoin: links repeatedly have their payment requests routed to this process: 624 if (WalletModel::isWalletEnabled()) { 625 app.createPaymentServer(); 626 } 627 #endif // ENABLE_WALLET 628 629 /// 9. Main GUI initialization 630 // Install global event filter that makes sure that out-of-focus labels do not contain text cursor. 631 app.installEventFilter(new GUIUtil::LabelOutOfFocusEventFilter(&app)); 632 #if defined(Q_OS_WIN) 633 // Install global event filter for processing Windows session related Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION) 634 // Note: it is safe to call app.node() in the lambda below despite the fact 635 // that app.createNode() hasn't been called yet, because native events will 636 // not be processed until the Qt event loop is executed. 637 qApp->installNativeEventFilter(new WinShutdownMonitor([&app] { app.node().startShutdown(); })); 638 #endif 639 // Install qDebug() message handler to route to debug.log 640 qInstallMessageHandler(DebugMessageHandler); 641 // Allow parameter interaction before we create the options model 642 app.parameterSetup(); 643 GUIUtil::LogQtInfo(); 644 645 if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false)) 646 app.createSplashScreen(networkStyle.data()); 647 648 app.createNode(*init); 649 650 // Load GUI settings from QSettings 651 if (!app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false))) { 652 return EXIT_FAILURE; 653 } 654 655 if (did_show_intro) { 656 // Store intro dialog settings other than datadir (network specific) 657 app.InitPruneSetting(prune_MiB); 658 } 659 660 try 661 { 662 app.createWindow(networkStyle.data()); 663 // Perform base initialization before spinning up initialization/shutdown thread 664 // This is acceptable because this function only contains steps that are quick to execute, 665 // so the GUI thread won't be held up. 666 if (app.baseInitialize()) { 667 app.requestInitialize(); 668 #if defined(Q_OS_WIN) 669 WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely…").arg(CLIENT_NAME), (HWND)app.getMainWinId()); 670 #endif 671 app.exec(); 672 } else { 673 // A dialog with detailed error will have been shown by InitError() 674 return EXIT_FAILURE; 675 } 676 } catch (const std::exception& e) { 677 PrintExceptionContinue(&e, "Runaway exception"); 678 app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated)); 679 } catch (...) { 680 PrintExceptionContinue(nullptr, "Runaway exception"); 681 app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated)); 682 } 683 return app.node().getExitStatus(); 684 }