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