// Copyright (c) 2011-2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# if defined(HAVE_CONFIG_H)
# include "config/bitcoin-config.h"
# endif
# include "bitcoingui.h"
# include "clientmodel.h"
# include "guiconstants.h"
# include "guiutil.h"
# include "intro.h"
# include "optionsmodel.h"
# include "splashscreen.h"
# include "utilitydialog.h"
# include "winshutdownmonitor.h"
# ifdef ENABLE_WALLET
# include "paymentserver.h"
# include "walletmodel.h"
# endif
# include "init.h"
# include "main.h"
# include "rpcserver.h"
# include "ui_interface.h"
# include "util.h"
# ifdef ENABLE_WALLET
# include "wallet.h"
# endif
# include <stdint.h>
# include <boost/filesystem/operations.hpp>
# include <QApplication>
# include <QDebug>
# include <QLibraryInfo>
# include <QLocale>
# include <QMessageBox>
# include <QSettings>
# include <QTimer>
# include <QTranslator>
# include <QThread>
# if defined(QT_STATICPLUGIN)
# include <QtPlugin>
# if QT_VERSION < 0x050000
Q_IMPORT_PLUGIN ( qcncodecs )
Q_IMPORT_PLUGIN ( qjpcodecs )
Q_IMPORT_PLUGIN ( qtwcodecs )
Q_IMPORT_PLUGIN ( qkrcodecs )
Q_IMPORT_PLUGIN ( qtaccessiblewidgets )
# else
Q_IMPORT_PLUGIN ( AccessibleFactory )
# if defined(QT_QPA_PLATFORM_XCB)
Q_IMPORT_PLUGIN ( QXcbIntegrationPlugin ) ;
# elif defined(QT_QPA_PLATFORM_WINDOWS)
Q_IMPORT_PLUGIN ( QWindowsIntegrationPlugin ) ;
# elif defined(QT_QPA_PLATFORM_COCOA)
Q_IMPORT_PLUGIN ( QCocoaIntegrationPlugin ) ;
# endif
# endif
# endif
# if QT_VERSION < 0x050000
# include <QTextCodec>
# endif
// Declare meta types used for QMetaObject::invokeMethod
Q_DECLARE_METATYPE ( bool * )
static void InitMessage ( const std : : string & message )
{
LogPrintf ( " init message: %s \n " , message ) ;
}
/*
Translate string to current locale using Qt .
*/
static std : : string Translate ( const char * psz )
{
return QCoreApplication : : translate ( " bitcoin-core " , psz ) . toStdString ( ) ;
}
/** Set up translations */
static void initTranslations ( QTranslator & qtTranslatorBase , QTranslator & qtTranslator , QTranslator & translatorBase , QTranslator & translator )
{
QSettings settings ;
// Remove old translators
QApplication : : removeTranslator ( & qtTranslatorBase ) ;
QApplication : : removeTranslator ( & qtTranslator ) ;
QApplication : : removeTranslator ( & translatorBase ) ;
QApplication : : removeTranslator ( & translator ) ;
// Get desired locale (e.g. "de_DE")
// 1) System default language
QString lang_territory = QLocale : : system ( ) . name ( ) ;
// 2) Language from QSettings
QString lang_territory_qsettings = settings . value ( " language " , " " ) . toString ( ) ;
if ( ! lang_territory_qsettings . isEmpty ( ) )
lang_territory = lang_territory_qsettings ;
// 3) -lang command line argument
lang_territory = QString : : fromStdString ( GetArg ( " -lang " , lang_territory . toStdString ( ) ) ) ;
// Convert to "de" only by truncating "_DE"
QString lang = lang_territory ;
lang . truncate ( lang_territory . lastIndexOf ( ' _ ' ) ) ;
// Load language files for configured locale:
// - First load the translator for the base language, without territory
// - Then load the more specific locale translator
// Load e.g. qt_de.qm
if ( qtTranslatorBase . load ( " qt_ " + lang , QLibraryInfo : : location ( QLibraryInfo : : TranslationsPath ) ) )
QApplication : : installTranslator ( & qtTranslatorBase ) ;
// Load e.g. qt_de_DE.qm
if ( qtTranslator . load ( " qt_ " + lang_territory , QLibraryInfo : : location ( QLibraryInfo : : TranslationsPath ) ) )
QApplication : : installTranslator ( & qtTranslator ) ;
// Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in bitcoin.qrc)
if ( translatorBase . load ( lang , " :/translations/ " ) )
QApplication : : installTranslator ( & translatorBase ) ;
// Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in bitcoin.qrc)
if ( translator . load ( lang_territory , " :/translations/ " ) )
QApplication : : installTranslator ( & translator ) ;
}
/* qDebug() message handler --> debug.log */
# if QT_VERSION < 0x050000
void DebugMessageHandler ( QtMsgType type , const char * msg )
{
const char * category = ( type = = QtDebugMsg ) ? " qt " : NULL ;
LogPrint ( category , " GUI: %s \n " , msg ) ;
}
# else
void DebugMessageHandler ( QtMsgType type , const QMessageLogContext & context , const QString & msg )
{
Q_UNUSED ( context ) ;
const char * category = ( type = = QtDebugMsg ) ? " qt " : NULL ;
LogPrint ( category , " GUI: %s \n " , msg . toStdString ( ) ) ;
}
# endif
/** Class encapsulating Bitcoin Core startup and shutdown.
* Allows running startup and shutdown in a different thread from the UI thread .
*/
class BitcoinCore : public QObject
{
Q_OBJECT
public :
explicit BitcoinCore ( ) ;
public slots :
void initialize ( ) ;
void shutdown ( ) ;
signals :
void initializeResult ( int retval ) ;
void shutdownResult ( int retval ) ;
void runawayException ( const QString & message ) ;
private :
boost : : thread_group threadGroup ;
/// Pass fatal exception message to UI thread
void handleRunawayException ( std : : exception * e ) ;
} ;
/** Main Bitcoin application object */
class BitcoinApplication : public QApplication
{
Q_OBJECT
public :
explicit BitcoinApplication ( int & argc , char * * argv ) ;
~ BitcoinApplication ( ) ;
# ifdef ENABLE_WALLET
/// Create payment server
void createPaymentServer ( ) ;
# endif
/// Create options model
void createOptionsModel ( ) ;
/// Create main window
void createWindow ( bool isaTestNet ) ;
/// Create splash screen
void createSplashScreen ( bool isaTestNet ) ;
/// Request core initialization
void requestInitialize ( ) ;
/// Request core shutdown
void requestShutdown ( ) ;
/// Get process return value
int getReturnValue ( ) { return returnValue ; }
/// Get window identifier of QMainWindow (BitcoinGUI)
WId getMainWinId ( ) const ;
public slots :
void initializeResult ( int retval ) ;
void shutdownResult ( int retval ) ;
/// Handle runaway exceptions. Shows a message box with the problem and quits the program.
void handleRunawayException ( const QString & message ) ;
signals :
void requestedInitialize ( ) ;
void requestedShutdown ( ) ;
void stopThread ( ) ;
void splashFinished ( QWidget * window ) ;
private :
QThread * coreThread ;
OptionsModel * optionsModel ;
ClientModel * clientModel ;
BitcoinGUI * window ;
QTimer * pollShutdownTimer ;
# ifdef ENABLE_WALLET
PaymentServer * paymentServer ;
WalletModel * walletModel ;
# endif
int returnValue ;
void startThread ( ) ;
} ;
# include "bitcoin.moc"
BitcoinCore : : BitcoinCore ( ) :
QObject ( )
{
}
void BitcoinCore : : handleRunawayException ( std : : exception * e )
{
PrintExceptionContinue ( e , " Runaway exception " ) ;
emit runawayException ( QString : : fromStdString ( strMiscWarning ) ) ;
}
void BitcoinCore : : initialize ( )
{
try
{
qDebug ( ) < < __func__ < < " : Running AppInit2 in thread " ;
int rv = AppInit2 ( threadGroup ) ;
if ( rv )
{
/* Start a dummy RPC thread if no RPC thread is active yet
* to handle timeouts .
*/
StartDummyRPCThread ( ) ;
}
emit initializeResult ( rv ) ;
} catch ( std : : exception & e ) {
handleRunawayException ( & e ) ;
} catch ( . . . ) {
handleRunawayException ( NULL ) ;
}
}
void BitcoinCore : : shutdown ( )
{
try
{
qDebug ( ) < < __func__ < < " : Running Shutdown in thread " ;
threadGroup . interrupt_all ( ) ;
threadGroup . join_all ( ) ;
Shutdown ( ) ;
qDebug ( ) < < __func__ < < " : Shutdown finished " ;
emit shutdownResult ( 1 ) ;
} catch ( std : : exception & e ) {
handleRunawayException ( & e ) ;
} catch ( . . . ) {
handleRunawayException ( NULL ) ;
}
}
BitcoinApplication : : BitcoinApplication ( int & argc , char * * argv ) :
QApplication ( argc , argv ) ,
coreThread ( 0 ) ,
optionsModel ( 0 ) ,
clientModel ( 0 ) ,
window ( 0 ) ,
pollShutdownTimer ( 0 ) ,
# ifdef ENABLE_WALLET
paymentServer ( 0 ) ,
walletModel ( 0 ) ,
# endif
returnValue ( 0 )
{
setQuitOnLastWindowClosed ( false ) ;
}
BitcoinApplication : : ~ BitcoinApplication ( )
{
if ( coreThread )
{
qDebug ( ) < < __func__ < < " : Stopping thread " ;
emit stopThread ( ) ;
coreThread - > wait ( ) ;
qDebug ( ) < < __func__ < < " : Stopped thread " ;
}
delete window ;
window = 0 ;
# ifdef ENABLE_WALLET
delete paymentServer ;
paymentServer = 0 ;
# endif
delete optionsModel ;
optionsModel = 0 ;
}
# ifdef ENABLE_WALLET
void BitcoinApplication : : createPaymentServer ( )
{
paymentServer = new PaymentServer ( this ) ;
}
# endif
void BitcoinApplication : : createOptionsModel ( )
{
optionsModel = new OptionsModel ( ) ;
}
void BitcoinApplication : : createWindow ( bool isaTestNet )
{
window = new BitcoinGUI ( isaTestNet , 0 ) ;
pollShutdownTimer = new QTimer ( window ) ;
connect ( pollShutdownTimer , SIGNAL ( timeout ( ) ) , window , SLOT ( detectShutdown ( ) ) ) ;
pollShutdownTimer - > start ( 200 ) ;
}
void BitcoinApplication : : createSplashScreen ( bool isaTestNet )
{
SplashScreen * splash = new SplashScreen ( QPixmap ( ) , 0 , isaTestNet ) ;
splash - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
splash - > show ( ) ;
connect ( this , SIGNAL ( splashFinished ( QWidget * ) ) , splash , SLOT ( slotFinish ( QWidget * ) ) ) ;
}
void BitcoinApplication : : startThread ( )
{
if ( coreThread )
return ;
coreThread = new QThread ( this ) ;
BitcoinCore * executor = new BitcoinCore ( ) ;
executor - > moveToThread ( coreThread ) ;
/* communication to and from thread */
connect ( executor , SIGNAL ( initializeResult ( int ) ) , this , SLOT ( initializeResult ( int ) ) ) ;
connect ( executor , SIGNAL ( shutdownResult ( int ) ) , this , SLOT ( shutdownResult ( int ) ) ) ;
connect ( executor , SIGNAL ( runawayException ( QString ) ) , this , SLOT ( handleRunawayException ( QString ) ) ) ;
connect ( this , SIGNAL ( requestedInitialize ( ) ) , executor , SLOT ( initialize ( ) ) ) ;
connect ( this , SIGNAL ( requestedShutdown ( ) ) , executor , SLOT ( shutdown ( ) ) ) ;
/* make sure executor object is deleted in its own thread */
connect ( this , SIGNAL ( stopThread ( ) ) , executor , SLOT ( deleteLater ( ) ) ) ;
connect ( this , SIGNAL ( stopThread ( ) ) , coreThread , SLOT ( quit ( ) ) ) ;
coreThread - > start ( ) ;
}
void BitcoinApplication : : requestInitialize ( )
{
qDebug ( ) < < __func__ < < " : Requesting initialize " ;
startThread ( ) ;
emit requestedInitialize ( ) ;
}
void BitcoinApplication : : requestShutdown ( )
{
qDebug ( ) < < __func__ < < " : Requesting shutdown " ;
startThread ( ) ;
window - > hide ( ) ;
window - > setClientModel ( 0 ) ;
pollShutdownTimer - > stop ( ) ;
# ifdef ENABLE_WALLET
window - > removeAllWallets ( ) ;
delete walletModel ;
walletModel = 0 ;
# endif
delete clientModel ;
clientModel = 0 ;
// Show a simple window indicating shutdown status
ShutdownWindow : : showShutdownWindow ( window ) ;
// Request shutdown from core thread
emit requestedShutdown ( ) ;
}
void BitcoinApplication : : initializeResult ( int retval )
{
qDebug ( ) < < __func__ < < " : Initialization result: " < < retval ;
// Set exit result: 0 if successful, 1 if failure
returnValue = retval ? 0 : 1 ;
if ( retval )
{
# ifdef ENABLE_WALLET
PaymentServer : : LoadRootCAs ( ) ;
paymentServer - > setOptionsModel ( optionsModel ) ;
# endif
clientModel = new ClientModel ( optionsModel ) ;
window - > setClientModel ( clientModel ) ;
# ifdef ENABLE_WALLET
if ( pwalletMain )
{
walletModel = new WalletModel ( pwalletMain , optionsModel ) ;
window - > addWallet ( " ~Default " , walletModel ) ;
window - > setCurrentWallet ( " ~Default " ) ;
connect ( walletModel , SIGNAL ( coinsSent ( CWallet * , SendCoinsRecipient , QByteArray ) ) ,
paymentServer , SLOT ( fetchPaymentACK ( CWallet * , const SendCoinsRecipient & , QByteArray ) ) ) ;
}
# endif
emit splashFinished ( window ) ;
// If -min option passed, start window minimized.
if ( GetBoolArg ( " -min " , false ) )
{
window - > showMinimized ( ) ;
}
else
{
window - > show ( ) ;
}
# ifdef ENABLE_WALLET
// Now that initialization/startup is done, process any command-line
// bitcoin: URIs or payment requests:
connect ( paymentServer , SIGNAL ( receivedPaymentRequest ( SendCoinsRecipient ) ) ,
window , SLOT ( handlePaymentRequest ( SendCoinsRecipient ) ) ) ;
connect ( window , SIGNAL ( receivedURI ( QString ) ) ,
paymentServer , SLOT ( handleURIOrFile ( QString ) ) ) ;
connect ( paymentServer , SIGNAL ( message ( QString , QString , unsigned int ) ) ,
window , SLOT ( message ( QString , QString , unsigned int ) ) ) ;
QTimer : : singleShot ( 100 , paymentServer , SLOT ( uiReady ( ) ) ) ;
# endif
} else {
quit ( ) ; // Exit main loop
}
}
void BitcoinApplication : : shutdownResult ( int retval )
{
qDebug ( ) < < __func__ < < " : Shutdown result: " < < retval ;
quit ( ) ; // Exit main loop after shutdown finished
}
void BitcoinApplication : : handleRunawayException ( const QString & message )
{
QMessageBox : : critical ( 0 , " Runaway exception " , BitcoinGUI : : tr ( " A fatal error occurred. Bitcoin can no longer continue safely and will quit. " ) + QString ( " \n \n " ) + message ) ;
: : exit ( 1 ) ;
}
WId BitcoinApplication : : getMainWinId ( ) const
{
if ( ! window )
return 0 ;
return window - > winId ( ) ;
}
# ifndef BITCOIN_QT_TEST
int main ( int argc , char * argv [ ] )
{
SetupEnvironment ( ) ;
/// 1. Parse command-line options. These take precedence over anything else.
// Command-line options take precedence:
ParseParameters ( argc , argv ) ;
// Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory
/// 2. Basic Qt initialization (not dependent on parameters or configuration)
# if QT_VERSION < 0x050000
// Internal string conversion is all UTF-8
QTextCodec : : setCodecForTr ( QTextCodec : : codecForName ( " UTF-8 " ) ) ;
QTextCodec : : setCodecForCStrings ( QTextCodec : : codecForTr ( ) ) ;
# endif
Q_INIT_RESOURCE ( bitcoin ) ;
Q_INIT_RESOURCE ( bitcoin_locale ) ;
BitcoinApplication app ( argc , argv ) ;
# if QT_VERSION > 0x050100
// Generate high-dpi pixmaps
QApplication : : setAttribute ( Qt : : AA_UseHighDpiPixmaps ) ;
# endif
# ifdef Q_OS_MAC
QApplication : : setAttribute ( Qt : : AA_DontShowIconsInMenus ) ;
# endif
// Register meta types used for QMetaObject::invokeMethod
qRegisterMetaType < bool * > ( ) ;
/// 3. Application identification
// must be set before OptionsModel is initialized or translations are loaded,
// as it is used to locate QSettings
QApplication : : setOrganizationName ( QAPP_ORG_NAME ) ;
QApplication : : setOrganizationDomain ( QAPP_ORG_DOMAIN ) ;
QApplication : : setApplicationName ( QAPP_APP_NAME_DEFAULT ) ;
/// 4. Initialization of translations, so that intro dialog is in user's language
// Now that QSettings are accessible, initialize translations
QTranslator qtTranslatorBase , qtTranslator , translatorBase , translator ;
initTranslations ( qtTranslatorBase , qtTranslator , translatorBase , translator ) ;
uiInterface . Translate . connect ( Translate ) ;
// Show help message immediately after parsing command-line options (for "-lang") and setting locale,
// but before showing splash screen.
if ( mapArgs . count ( " -? " ) | | mapArgs . count ( " -help " ) | | mapArgs . count ( " -version " ) )
{
HelpMessageDialog help ( NULL , mapArgs . count ( " -version " ) ) ;
help . showOrPrint ( ) ;
return 1 ;
}
/// 5. Now that settings and translations are available, ask user for data directory
// User language is set up: pick a data directory
Intro : : pickDataDirectory ( ) ;
/// 6. Determine availability of data directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes
if ( ! boost : : filesystem : : is_directory ( GetDataDir ( false ) ) )
{
QMessageBox : : critical ( 0 , QObject : : tr ( " Bitcoin " ) ,
QObject : : tr ( " Error: Specified data directory \" %1 \" does not exist. " ) . arg ( QString : : fromStdString ( mapArgs [ " -datadir " ] ) ) ) ;
return 1 ;
}
try {
ReadConfigFile ( mapArgs , mapMultiArgs ) ;
} catch ( std : : exception & e ) {
QMessageBox : : critical ( 0 , QObject : : tr ( " Bitcoin " ) ,
QObject : : tr ( " Error: Cannot parse configuration file: %1. Only use key=value syntax. " ) . arg ( e . what ( ) ) ) ;
return false ;
}
/// 7. Determine network (and switch to network specific options)
// - Do not call Params() before this step
// - Do this after parsing the configuration file, as the network can be switched there
// - QSettings() will use the new application name after this, resulting in network-specific settings
// - Needs to be done before createOptionsModel
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
if ( ! SelectParamsFromCommandLine ( ) ) {
QMessageBox : : critical ( 0 , QObject : : tr ( " Bitcoin " ) , QObject : : tr ( " Error: Invalid combination of -regtest and -testnet. " ) ) ;
return 1 ;
}
# ifdef ENABLE_WALLET
// Parse URIs on command line -- this can affect Params()
if ( ! PaymentServer : : ipcParseCommandLine ( argc , argv ) )
exit ( 0 ) ;
# endif
bool isaTestNet = Params ( ) . NetworkID ( ) ! = CBaseChainParams : : MAIN ;
// Allow for separate UI settings for testnets
if ( isaTestNet )
QApplication : : setApplicationName ( QAPP_APP_NAME_TESTNET ) ;
else
QApplication : : setApplicationName ( QAPP_APP_NAME_DEFAULT ) ;
// Re-initialize translations after changing application name (language in network-specific settings can be different)
initTranslations ( qtTranslatorBase , qtTranslator , translatorBase , translator ) ;
# ifdef ENABLE_WALLET
/// 8. URI IPC sending
// - Do this early as we don't want to bother initializing if we are just calling IPC
// - Do this *after* setting up the data directory, as the data directory hash is used in the name
// of the server.
// - Do this after creating app and setting up translations, so errors are
// translated properly.
if ( PaymentServer : : ipcSendCommandLine ( ) )
exit ( 0 ) ;
// Start up the payment server early, too, so impatient users that click on
// bitcoin: links repeatedly have their payment requests routed to this process:
app . createPaymentServer ( ) ;
# endif
/// 9. Main GUI initialization
// Install global event filter that makes sure that long tooltips can be word-wrapped
app . installEventFilter ( new GUIUtil : : ToolTipToRichTextFilter ( TOOLTIP_WRAP_THRESHOLD , & app ) ) ;
# if QT_VERSION < 0x050000
// Install qDebug() message handler to route to debug.log
qInstallMsgHandler ( DebugMessageHandler ) ;
# else
# if defined(Q_OS_WIN)
// Install global event filter for processing Windows session related Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION)
qApp - > installNativeEventFilter ( new WinShutdownMonitor ( ) ) ;
# endif
// Install qDebug() message handler to route to debug.log
qInstallMessageHandler ( DebugMessageHandler ) ;
# endif
// Load GUI settings from QSettings
app . createOptionsModel ( ) ;
// Subscribe to global signals from core
uiInterface . InitMessage . connect ( InitMessage ) ;
if ( GetBoolArg ( " -splash " , true ) & & ! GetBoolArg ( " -min " , false ) )
app . createSplashScreen ( isaTestNet ) ;
try
{
app . createWindow ( isaTestNet ) ;
app . requestInitialize ( ) ;
# if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
WinShutdownMonitor : : registerShutdownBlockReason ( QObject : : tr ( " Bitcoin Core didn't yet exit safely... " ) , ( HWND ) app . getMainWinId ( ) ) ;
# endif
app . exec ( ) ;
app . requestShutdown ( ) ;
app . exec ( ) ;
} catch ( std : : exception & e ) {
PrintExceptionContinue ( & e , " Runaway exception " ) ;
app . handleRunawayException ( QString : : fromStdString ( strMiscWarning ) ) ;
} catch ( . . . ) {
PrintExceptionContinue ( NULL , " Runaway exception " ) ;
app . handleRunawayException ( QString : : fromStdString ( strMiscWarning ) ) ;
}
return app . getReturnValue ( ) ;
}
# endif // BITCOIN_QT_TEST