diff options
Diffstat (limited to 'src/qt')
-rw-r--r-- | src/qt/bitcoin.cpp | 33 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 84 | ||||
-rw-r--r-- | src/qt/bitcoingui.h | 2 | ||||
-rw-r--r-- | src/qt/clientmodel.cpp | 6 | ||||
-rw-r--r-- | src/qt/clientmodel.h | 1 | ||||
-rw-r--r-- | src/qt/guiconstants.h | 2 | ||||
-rw-r--r-- | src/qt/paymentserver.cpp | 159 | ||||
-rw-r--r-- | src/qt/paymentserver.h | 66 | ||||
-rw-r--r-- | src/qt/qtipcserver.cpp | 165 | ||||
-rw-r--r-- | src/qt/qtipcserver.h | 16 |
10 files changed, 285 insertions, 249 deletions
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index afd8d71a0e..75e9b965b1 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -9,12 +9,13 @@ #include "guiconstants.h" #include "init.h" #include "ui_interface.h" -#include "qtipcserver.h" +#include "paymentserver.h" #include <QApplication> #include <QMessageBox> #include <QTextCodec> #include <QLocale> +#include <QTimer> #include <QTranslator> #include <QSplashScreen> #include <QLibraryInfo> @@ -74,15 +75,6 @@ static bool ThreadSafeAskFee(int64 nFeeRequired) return payFee; } -static void ThreadSafeHandleURI(const std::string& strURI) -{ - if(!guiref) - return; - - QMetaObject::invokeMethod(guiref, "handleURI", GUIUtil::blockingGUIThreadConnection(), - Q_ARG(QString, QString::fromStdString(strURI))); -} - static void InitMessage(const std::string &message) { if(splashref) @@ -121,14 +113,6 @@ int main(int argc, char *argv[]) // Command-line options take precedence: ParseParameters(argc, argv); - if(GetBoolArg("-testnet")) // Separate message queue name for testnet - strBitcoinURIQueueName = BITCOINURI_QUEUE_NAME_TESTNET; - else - strBitcoinURIQueueName = BITCOINURI_QUEUE_NAME_MAINNET; - - // Do this early as we don't want to bother initializing if we are just calling IPC - ipcScanRelay(argc, argv); - // Internal string conversion is all UTF-8 QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); QTextCodec::setCodecForCStrings(QTextCodec::codecForTr()); @@ -136,6 +120,12 @@ int main(int argc, char *argv[]) Q_INIT_RESOURCE(bitcoin); QApplication app(argc, argv); + // Do this early as we don't want to bother initializing if we are just calling IPC + // ... but do it after creating app, so QCoreApplication::arguments is initialized: + if (PaymentServer::ipcSendCommandLine()) + exit(0); + PaymentServer* paymentServer = new PaymentServer(&app); + // Install global event filter that makes sure that long tooltips can be word-wrapped app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app)); @@ -192,7 +182,6 @@ int main(int argc, char *argv[]) // Subscribe to global signals from core uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox); uiInterface.ThreadSafeAskFee.connect(ThreadSafeAskFee); - uiInterface.ThreadSafeHandleURI.connect(ThreadSafeHandleURI); uiInterface.InitMessage.connect(InitMessage); uiInterface.QueueShutdown.connect(QueueShutdown); uiInterface.Translate.connect(Translate); @@ -253,8 +242,10 @@ int main(int argc, char *argv[]) window.show(); } - // Place this here as guiref has to be defined if we don't want to lose URIs - ipcInit(argc, argv); + // Now that initialization/startup is done, process any command-line + // bitcoin: URIs + QObject::connect(paymentServer, SIGNAL(receivedURI(QString)), &window, SLOT(handleURI(QString))); + QTimer::singleShot(100, paymentServer, SLOT(uiReady())); app.exec(); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index d884701883..f1bf5f5880 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -67,7 +67,8 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): aboutQtAction(0), trayIcon(0), notificator(0), - rpcConsole(0) + rpcConsole(0), + prevBlocks(0) { resize(850, 550); setWindowTitle(tr("Bitcoin") + " - " + tr("Wallet")); @@ -527,52 +528,17 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) importText = tr("Reindexing blocks on disk..."); } - if(count < nTotalBlocks) - { - int nRemainingBlocks = nTotalBlocks - count; - float nPercentageDone = count / (nTotalBlocks * 0.01f); - - progressBarLabel->setText(importText); - progressBarLabel->setVisible(true); - progressBar->setFormat(tr("~%n block(s) remaining", "", nRemainingBlocks)); - progressBar->setMaximum(nTotalBlocks); - progressBar->setValue(count); - progressBar->setVisible(true); - - tooltip = tr("Processed %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2); - } - else - { - progressBarLabel->setVisible(false); - - progressBar->setVisible(false); - tooltip = tr("Processed %1 blocks of transaction history.").arg(count); - } - QDateTime lastBlockDate = clientModel->getLastBlockDate(); - int secs = lastBlockDate.secsTo(QDateTime::currentDateTime()); - QString text; + QDateTime currentDate = QDateTime::currentDateTime(); + int secs = lastBlockDate.secsTo(currentDate); - // Represent time from last generated block in human readable text - if(secs <= 0) - { - // Fully up to date. Leave text empty. - } - else if(secs < 60) - { - text = tr("%n second(s) ago","",secs); - } - else if(secs < 60*60) - { - text = tr("%n minute(s) ago","",secs/60); - } - else if(secs < 24*60*60) + if(count < nTotalBlocks) { - text = tr("%n hour(s) ago","",secs/(60*60)); + tooltip = tr("Processed %1 of %2 (estimated) blocks of transaction history.").arg(count).arg(nTotalBlocks); } else { - text = tr("%n day(s) ago","",secs/(60*60*24)); + tooltip = tr("Processed %1 blocks of transaction history.").arg(count); } // Set icon state: spinning if catching up, tick otherwise @@ -582,20 +548,46 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); overviewPage->showOutOfSyncWarning(false); + + progressBarLabel->setVisible(false); + progressBar->setVisible(false); } else { + // Represent time from last generated block in human readable text + QString timeBehindText; + if(secs < 48*60*60) + { + timeBehindText = tr("%n hour(s)","",secs/(60*60)); + } + else if(secs < 14*24*60*60) + { + timeBehindText = tr("%n day(s)","",secs/(24*60*60)); + } + else + { + timeBehindText = tr("%n week(s)","",secs/(7*24*60*60)); + } + + progressBarLabel->setText(importText); + progressBarLabel->setVisible(true); + progressBar->setFormat(tr("%1 behind").arg(timeBehindText)); + progressBar->setMaximum(1000000000); + progressBar->setValue(clientModel->getVerificationProgress() * 1000000000.0 + 0.5); + progressBar->setVisible(true); + tooltip = tr("Catching up...") + QString("<br>") + tooltip; labelBlocksIcon->setMovie(syncIconMovie); - syncIconMovie->start(); + if(count != prevBlocks) + syncIconMovie->jumpToNextFrame(); + prevBlocks = count; overviewPage->showOutOfSyncWarning(true); - } - if(!text.isEmpty()) - { tooltip += QString("<br>"); - tooltip += tr("Last received block was generated %1.").arg(text); + tooltip += tr("Last received block was generated %1 ago.").arg(timeBehindText); + tooltip += QString("<br>"); + tooltip += tr("Transactions after this will not yet be visible."); } // Don't word-wrap this (fixed-width) tooltip diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index c684fcf249..8ce0335bcd 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -98,6 +98,8 @@ private: RPCConsole *rpcConsole; QMovie *syncIconMovie; + /** Keep track of previous number of blocks, to detect progress */ + int prevBlocks; /** Create the main UI actions. */ void createActions(); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 084ad12a56..858fbe241f 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -6,6 +6,7 @@ #include "alert.h" #include "main.h" +#include "checkpoints.h" #include "ui_interface.h" #include <QDateTime> @@ -54,6 +55,11 @@ QDateTime ClientModel::getLastBlockDate() const return QDateTime::fromTime_t(1231006505); // Genesis block's time } +double ClientModel::getVerificationProgress() const +{ + return Checkpoints::GuessVerificationProgress(pindexBest); +} + void ClientModel::updateTimer() { // Some quantities (such as number of blocks) change so fast that we don't want to be notified for each change. diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 1afccb7859..a3fe92048c 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -34,6 +34,7 @@ public: int getNumBlocks() const; int getNumBlocksAtStartup(); + double getVerificationProgress() const; QDateTime getLastBlockDate() const; //! Return true if client connected to testnet diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 405ba396b7..92417834ec 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -2,7 +2,7 @@ #define GUICONSTANTS_H /* Milliseconds between model updates */ -static const int MODEL_UPDATE_DELAY = 500; +static const int MODEL_UPDATE_DELAY = 250; /* AskPassphraseDialog -- Maximum passphrase length */ static const int MAX_PASSPHRASE_SIZE = 1024; diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp new file mode 100644 index 0000000000..05f2ac10e4 --- /dev/null +++ b/src/qt/paymentserver.cpp @@ -0,0 +1,159 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "paymentserver.h" +#include "guiconstants.h" +#include "ui_interface.h" +#include "util.h" + +#include <QApplication> +#include <QByteArray> +#include <QCoreApplication> +#include <QDataStream> +#include <QDebug> +#include <QFileOpenEvent> +#include <QHash> +#include <QLocalServer> +#include <QLocalSocket> +#include <QStringList> +#include <QUrl> + +using namespace boost; + +const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds +const QString BITCOIN_IPC_PREFIX("bitcoin:"); + +// +// Create a name that is unique for: +// testnet / non-testnet +// data directory +// +static QString ipcServerName() +{ + QString name("BitcoinQt"); + + // Append a simple hash of the datadir + // Note that GetDataDir(true) returns a different path + // for -testnet versus main net + QString ddir(GetDataDir(true).string().c_str()); + name.append(QString::number(qHash(ddir))); + + return name; +} + +// +// This stores payment requests received before +// the main GUI window is up and ready to ask the user +// to send payment. +// +static QStringList savedPaymentRequests; + +// +// Sending to the server is done synchronously, at startup. +// If the server isn't already running, startup continues, +// and the items in savedPaymentRequest will be handled +// when uiReady() is called. +// +bool PaymentServer::ipcSendCommandLine() +{ + bool fResult = false; + + const QStringList& args = QCoreApplication::arguments(); + for (int i = 1; i < args.size(); i++) + { + if (!args[i].startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) + continue; + savedPaymentRequests.append(args[i]); + } + + foreach (const QString& arg, savedPaymentRequests) + { + QLocalSocket* socket = new QLocalSocket(); + socket->connectToServer(ipcServerName(), QIODevice::WriteOnly); + if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT)) + return false; + + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_0); + out << arg; + out.device()->seek(0); + socket->write(block); + socket->flush(); + + socket->waitForBytesWritten(BITCOIN_IPC_CONNECT_TIMEOUT); + socket->disconnectFromServer(); + delete socket; + fResult = true; + } + return fResult; +} + +PaymentServer::PaymentServer(QApplication* parent) : QObject(parent), saveURIs(true) +{ + // Install global event filter to catch QFileOpenEvents on the mac (sent when you click bitcoin: links) + parent->installEventFilter(this); + + QString name = ipcServerName(); + + // Clean up old socket leftover from a crash: + QLocalServer::removeServer(name); + + uriServer = new QLocalServer(this); + + if (!uriServer->listen(name)) + qDebug() << tr("Cannot start bitcoin: click-to-pay handler"); + else + connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection())); +} + +bool PaymentServer::eventFilter(QObject *object, QEvent *event) +{ + // clicking on bitcoin: URLs creates FileOpen events on the Mac: + if (event->type() == QEvent::FileOpen) + { + QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event); + if (!fileEvent->url().isEmpty()) + { + if (saveURIs) // Before main window is ready: + savedPaymentRequests.append(fileEvent->url().toString()); + else + emit receivedURI(fileEvent->url().toString()); + return true; + } + } + return false; +} + +void PaymentServer::uiReady() +{ + saveURIs = false; + foreach (const QString& s, savedPaymentRequests) + emit receivedURI(s); + savedPaymentRequests.clear(); +} + +void PaymentServer::handleURIConnection() +{ + QLocalSocket *clientConnection = uriServer->nextPendingConnection(); + + while (clientConnection->bytesAvailable() < (int)sizeof(quint32)) + clientConnection->waitForReadyRead(); + + connect(clientConnection, SIGNAL(disconnected()), + clientConnection, SLOT(deleteLater())); + + QDataStream in(clientConnection); + in.setVersion(QDataStream::Qt_4_0); + if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) { + return; + } + QString message; + in >> message; + + if (saveURIs) + savedPaymentRequests.append(message); + else + emit receivedURI(message); +} diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h new file mode 100644 index 0000000000..cfc48afb38 --- /dev/null +++ b/src/qt/paymentserver.h @@ -0,0 +1,66 @@ +#ifndef PAYMENTSERVER_H +#define PAYMENTSERVER_H + +// +// This class handles payment requests from clicking on +// bitcoin: URIs +// +// This is somewhat tricky, because we have to deal with +// the situation where the user clicks on a link during +// startup/initialization, when the splash-screen is up +// but the main window (and the Send Coins tab) is not. +// +// So, the strategy is: +// +// Create the server, and register the event handler, +// when the application is created. Save any URIs +// received at or during startup in a list. +// +// When startup is finished and the main window is +// show, a signal is sent to slot uiReady(), which +// emits a receivedURL() signal for any payment +// requests that happened during startup. +// +// After startup, receivedURL() happens as usual. +// +// This class has one more feature: a static +// method that finds URIs passed in the command line +// and, if a server is running in another process, +// sends them to the server. +// +#include <QObject> +#include <QString> + +class QApplication; +class QLocalServer; + +class PaymentServer : public QObject +{ + Q_OBJECT +private: + bool saveURIs; + QLocalServer* uriServer; + +public: + // Returns true if there were URIs on the command line + // which were successfully sent to an already-running + // process. + static bool ipcSendCommandLine(); + + PaymentServer(QApplication* parent); + + bool eventFilter(QObject *object, QEvent *event); + +signals: + void receivedURI(QString); + +public slots: + // Signal this when the main window's UI is ready + // to display payment requests to the user + void uiReady(); + +private slots: + void handleURIConnection(); +}; + +#endif // PAYMENTSERVER_H diff --git a/src/qt/qtipcserver.cpp b/src/qt/qtipcserver.cpp deleted file mode 100644 index 2777fab852..0000000000 --- a/src/qt/qtipcserver.cpp +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include <boost/version.hpp> -#if defined(WIN32) && BOOST_VERSION == 104900 -#define BOOST_INTERPROCESS_HAS_WINDOWS_KERNEL_BOOTTIME -#define BOOST_INTERPROCESS_HAS_KERNEL_BOOTTIME -#endif - -#include "qtipcserver.h" -#include "guiconstants.h" -#include "ui_interface.h" -#include "util.h" - -#include <boost/algorithm/string/predicate.hpp> -#include <boost/date_time/posix_time/posix_time.hpp> -#include <boost/interprocess/ipc/message_queue.hpp> -#include <boost/version.hpp> - -#if defined(WIN32) && (!defined(BOOST_INTERPROCESS_HAS_WINDOWS_KERNEL_BOOTTIME) || !defined(BOOST_INTERPROCESS_HAS_KERNEL_BOOTTIME) || BOOST_VERSION < 104900) -#warning Compiling without BOOST_INTERPROCESS_HAS_WINDOWS_KERNEL_BOOTTIME and BOOST_INTERPROCESS_HAS_KERNEL_BOOTTIME uncommented in boost/interprocess/detail/tmp_dir_helpers.hpp or using a boost version before 1.49 may have unintended results see svn.boost.org/trac/boost/ticket/5392 -#endif - -using namespace boost; -using namespace boost::interprocess; -using namespace boost::posix_time; - -// holds Bitcoin-Qt message queue name (initialized in bitcoin.cpp) -std::string strBitcoinURIQueueName; - -#if defined MAC_OSX || defined __FreeBSD__ -// URI handling not implemented on OSX yet - -void ipcScanRelay(int argc, char *argv[]) { } -void ipcInit(int argc, char *argv[]) { } - -#else - -static void ipcThread2(void* pArg); - -static bool ipcScanCmd(int argc, char *argv[], bool fRelay) -{ - // Check for URI in argv - bool fSent = false; - for (int i = 1; i < argc; i++) - { - if (boost::algorithm::istarts_with(argv[i], "bitcoin:")) - { - const char *strURI = argv[i]; - try { - boost::interprocess::message_queue mq(boost::interprocess::open_only, strBitcoinURIQueueName.c_str()); - if (mq.try_send(strURI, strlen(strURI), 0)) - fSent = true; - else if (fRelay) - break; - } - catch (boost::interprocess::interprocess_exception &ex) { - // don't log the "file not found" exception, because that's normal for - // the first start of the first instance - if (ex.get_error_code() != boost::interprocess::not_found_error || !fRelay) - { - printf("main() - boost interprocess exception #%d: %s\n", ex.get_error_code(), ex.what()); - break; - } - } - } - } - return fSent; -} - -void ipcScanRelay(int argc, char *argv[]) -{ - if (ipcScanCmd(argc, argv, true)) - exit(0); -} - -static void ipcThread(void* pArg) -{ - // Make this thread recognisable as the GUI-IPC thread - RenameThread("bitcoin-gui-ipc"); - - try - { - ipcThread2(pArg); - } - catch (std::exception& e) { - PrintExceptionContinue(&e, "ipcThread()"); - } catch (...) { - PrintExceptionContinue(NULL, "ipcThread()"); - } - printf("ipcThread exited\n"); -} - -static void ipcThread2(void* pArg) -{ - printf("ipcThread started\n"); - - message_queue* mq = (message_queue*)pArg; - char buffer[MAX_URI_LENGTH + 1] = ""; - size_t nSize = 0; - unsigned int nPriority = 0; - - loop - { - ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(100); - if (mq->timed_receive(&buffer, sizeof(buffer), nSize, nPriority, d)) - { - uiInterface.ThreadSafeHandleURI(std::string(buffer, nSize)); - Sleep(1000); - } - - if (fShutdown) - break; - } - - // Remove message queue - message_queue::remove(strBitcoinURIQueueName.c_str()); - // Cleanup allocated memory - delete mq; -} - -void ipcInit(int argc, char *argv[]) -{ - message_queue* mq = NULL; - char buffer[MAX_URI_LENGTH + 1] = ""; - size_t nSize = 0; - unsigned int nPriority = 0; - - try { - mq = new message_queue(open_or_create, strBitcoinURIQueueName.c_str(), 2, MAX_URI_LENGTH); - - // Make sure we don't lose any bitcoin: URIs - for (int i = 0; i < 2; i++) - { - ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(1); - if (mq->timed_receive(&buffer, sizeof(buffer), nSize, nPriority, d)) - { - uiInterface.ThreadSafeHandleURI(std::string(buffer, nSize)); - } - else - break; - } - - // Make sure only one bitcoin instance is listening - message_queue::remove(strBitcoinURIQueueName.c_str()); - delete mq; - - mq = new message_queue(open_or_create, strBitcoinURIQueueName.c_str(), 2, MAX_URI_LENGTH); - } - catch (interprocess_exception &ex) { - printf("ipcInit() - boost interprocess exception #%d: %s\n", ex.get_error_code(), ex.what()); - return; - } - - if (!NewThread(ipcThread, mq)) - { - delete mq; - return; - } - - ipcScanCmd(argc, argv, false); -} - -#endif diff --git a/src/qt/qtipcserver.h b/src/qt/qtipcserver.h deleted file mode 100644 index f775f272c2..0000000000 --- a/src/qt/qtipcserver.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef QTIPCSERVER_H -#define QTIPCSERVER_H - -#include <string> - -// Define Bitcoin-Qt message queue name for mainnet -#define BITCOINURI_QUEUE_NAME_MAINNET "BitcoinURI" -// Define Bitcoin-Qt message queue name for testnet -#define BITCOINURI_QUEUE_NAME_TESTNET "BitcoinURI-testnet" - -extern std::string strBitcoinURIQueueName; - -void ipcScanRelay(int argc, char *argv[]); -void ipcInit(int argc, char *argv[]); - -#endif // QTIPCSERVER_H |