diff options
author | Aqua-sama <aqua@iserlohn-fortress.net> | 2017-12-22 23:00:45 +0100 |
---|---|---|
committer | Aqua-sama <aqua@iserlohn-fortress.net> | 2017-12-22 23:00:45 +0100 |
commit | 3ace62f90d48015fec1bd37ad216466fb666061d (patch) | |
tree | ac64ca02db0e046a781d79dc83758311f4dadf90 /src | |
parent | Edited documentation (diff) | |
download | smolbote-3ace62f90d48015fec1bd37ad216466fb666061d.tar.xz |
Single instance check works again
- In Debug builds, startup time is calculated
Diffstat (limited to 'src')
-rw-r--r-- | src/browser.cpp | 37 | ||||
-rw-r--r-- | src/browser.h | 6 | ||||
-rw-r--r-- | src/main.cpp | 52 | ||||
-rw-r--r-- | src/mainwindow.cpp | 11 | ||||
-rw-r--r-- | src/singleapplication.cpp | 87 | ||||
-rw-r--r-- | src/singleapplication.h | 22 |
6 files changed, 162 insertions, 53 deletions
diff --git a/src/browser.cpp b/src/browser.cpp index 7a169e9..7d39035 100644 --- a/src/browser.cpp +++ b/src/browser.cpp @@ -19,6 +19,8 @@ Browser::Browser(int &argc, char *argv[]) : { setApplicationName("smolbote"); setWindowIcon(QIcon(":/icon.svg")); + + connect(this, &Browser::messageAvailable, this, &Browser::createSession); } Browser::~Browser() @@ -94,6 +96,41 @@ MainWindow *Browser::createWindow() return window; } +MainWindow *Browser::createSession(const SessionParam ¶ms) +{ + MainWindow *window = nullptr; + if(params.newWindow || m_windows.isEmpty()) { + window = createWindow(); + window->setProfile(profile(params.profile)); + } else { + // reverse-iterate through windows to check for window with the same profile + for(auto it = m_windows.rbegin(); it != m_windows.rend(); ++it) { + if((*it)->profile()->storageName() == params.profile) { + window = *it; + break; + } + } + // if none is found, create one + if(window == nullptr) { + window = createWindow(); + window->setProfile(profile(params.profile)); + } + } + + Q_CHECK_PTR(window); + + if(params.urls.isEmpty()) { + // no URLs were given + window->newTab(QUrl::fromUserInput(m_config->value<std::string>("profile.homepage").value().c_str())); + } else { + for(const QUrl &url : params.urls) { + window->newTab(url); + } + } + + return window; +} + std::shared_ptr<WebEngineProfile> Browser::profile(const QString name) { return m_profiles[name]; diff --git a/src/browser.h b/src/browser.h index 3b8975f..52dc97e 100644 --- a/src/browser.h +++ b/src/browser.h @@ -33,14 +33,14 @@ public: std::shared_ptr<WebEngineProfile> profile(const QString name); // QStringList profiles(); - //MainWindow *activeWindow(); - public slots: - MainWindow* createWindow(); + MainWindow *createSession(const SessionParam ¶ms); private: Q_DISABLE_COPY(Browser) + MainWindow *createWindow(); + std::shared_ptr<Configuration> m_config; QVector<MainWindow *> m_windows; diff --git a/src/main.cpp b/src/main.cpp index 7433cc8..e8307c6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,6 +15,11 @@ #include <QStandardPaths> #include "mainwindow.h" +// startup time measuring +#ifdef QT_DEBUG +#include <QElapsedTimer> +#endif + // read config into std::string, supports qrc inline std::string readConfig(QString path) { @@ -60,6 +65,11 @@ int main(int argc, char *argv[]) instance.setApplicationVersion("1.0.0"); #endif +#ifdef QT_DEBUG + QElapsedTimer timer; + timer.start(); +#endif + QCommandLineParser parser; parser.setApplicationDescription("yet another Qt browser"); parser.addHelpOption(); @@ -87,15 +97,13 @@ int main(int argc, char *argv[]) profileOption.setDefaultValue(""); parser.addOption(profileOption); - QCommandLineOption newInstanceOption("new-instance", "Skip instance check at startup"); - parser.addOption(newInstanceOption); + QCommandLineOption socketOption("socket", "Set socket to use for IPC, leave blank for default, 'none' to disable.", "name"); + socketOption.setDefaultValue(""); + parser.addOption(socketOption); QCommandLineOption newWindowOption("in-new-window", "Open URL in new window"); parser.addOption(newWindowOption); - QCommandLineOption newTabOption("in-new-tab", "Open URL in new tab"); - parser.addOption(newTabOption); - parser.addPositionalArgument("URL", "URL(s) to open"); parser.process(instance); @@ -111,7 +119,24 @@ int main(int argc, char *argv[]) return 0; } + Browser::SessionParam sessionParams; + sessionParams.profile = parser.value(profileOption); + sessionParams.newWindow = parser.isSet(newWindowOption); + for(const QString &url : parser.positionalArguments()) { + sessionParams.urls.append(QUrl::fromUserInput(url)); + } + // TODO: check for other instances + // if we socket hasn't been disabled (socket is not none) + if(parser.value(socketOption) != "none") { + bool bindOk = instance.bindLocalSocket(parser.value(socketOption)); + if(bindOk) { + qDebug("Connected to local socket: %s", qUtf8Printable(instance.serverName())); + } else { + // pass arguments to new instance + return instance.sendMessage(sessionParams); + } + } std::shared_ptr<Configuration> config = std::make_shared<Configuration>(); config->readDefaultConfiguration(readConfig(parser.value(defaultConfigOption))); @@ -148,19 +173,10 @@ int main(int argc, char *argv[]) instance.setConfiguration(config); instance.loadProfiles(); - MainWindow* mainWindow = instance.createWindow(); - if(parser.isSet(profileOption)) { - mainWindow->setProfile(instance.profile(parser.value(profileOption))); - } - - if(parser.positionalArguments().isEmpty()) { - // no URLs were given - mainWindow->newTab(QUrl::fromUserInput(config->value<std::string>("profile.homepage").value().c_str())); - } else { - for(const QString &url : parser.positionalArguments()) { - mainWindow->newTab(QUrl::fromUserInput(url)); - } - } + instance.createSession(sessionParams); +#ifdef QT_DEBUG + qDebug("Startup complete in %lldms", timer.elapsed()); +#endif return instance.exec(); } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 5c3048c..66d0092 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -182,12 +182,11 @@ void MainWindow::newTab(const QUrl &url) MainWindow *MainWindow::newWindow(const QUrl &url) { Browser *instance = static_cast<Browser*>(qApp->instance()); - MainWindow *window = instance->createWindow(); - window->setProfile(m_profile); - if(!url.isEmpty()) { - window->newTab(url); - } - return window; + Browser::SessionParam params; + params.profile = m_profile->storageName(); + params.newWindow = true; + params.urls.append(url); + return instance->createSession(params); } void MainWindow::focusAddress() diff --git a/src/singleapplication.cpp b/src/singleapplication.cpp index 1dd96e3..e71d1be 100644 --- a/src/singleapplication.cpp +++ b/src/singleapplication.cpp @@ -10,14 +10,47 @@ #include <QLocalServer> #include <QLocalSocket> #include <QDataStream> +#include <QUrl> +#include <cstdlib> + +QHash<QString, QVariant> paramStructToHash(const SingleApplication::SessionParam ¶ms) +{ + QHash<QString, QVariant> hashedParams; + hashedParams.insert("profile", params.profile); + hashedParams.insert("newWindow", params.newWindow); + QList<QVariant> urls; + for(const QUrl &url : params.urls) { + urls.append(url); + } + hashedParams.insert("urls", urls); + return hashedParams; +} + +SingleApplication::SessionParam paramStructFromHash(const QHash<QString, QVariant> ¶ms) +{ + SingleApplication::SessionParam structParams; + structParams.profile = params.value("profile").toString(); + structParams.newWindow = params.value("newWindow").toBool(); + for(const QVariant &val : params.value("urls").toList()) { + structParams.urls.append(val.toUrl()); + } + return structParams; +} SingleApplication::SingleApplication(int &argc, char **argv) : QApplication(argc, argv) { +#ifdef Q_OS_UNIX + // could be a path such as "/tmp/foo" + LOCALSERVER_KEY = "smolbote_socket"; +#elif Q_OS_WIN32 + // could be a pipe path such as "\\.\pipe\foo" + LOCALSERVER_KEY = "\\.\pipe\smolbote_socket"; +#endif } SingleApplication::~SingleApplication() { - if(m_localServer) { + if(m_localServer != nullptr) { if(m_localServer->isListening()) { m_localServer->close(); QLocalServer::removeServer(LOCALSERVER_KEY); @@ -25,56 +58,70 @@ SingleApplication::~SingleApplication() } } -void SingleApplication::bindLocalSocket() +/** + * @brief SingleApplication::bindLocalSocket check for running local server by connecting to it + * @return true if no other instance, false otherwise + */ +bool SingleApplication::bindLocalSocket(const QString &name) { - m_localServer = new QLocalServer(this); - connect(m_localServer, &QLocalServer::newConnection, this, &SingleApplication::slot_receiveMessage); + // if a name has been set + if(!name.isEmpty()) { + LOCALSERVER_KEY = name; + } - // check for running local server by connecting to it QLocalSocket socket; socket.connectToServer(LOCALSERVER_KEY); + if(socket.waitForConnected(LOCALSERVER_TIMEOUT)) { - // there is another server - qWarning("Another server is running"); + // another server is running socket.close(); - return; - } else { + return false; + } + + // there is either no such socket, or the socket wasn't cleaned up + else { + m_localServer = new QLocalServer(this); + connect(m_localServer, &QLocalServer::newConnection, this, &SingleApplication::parseMessage); + // no other server QLocalServer::removeServer(LOCALSERVER_KEY); if(!m_localServer->listen(LOCALSERVER_KEY)) { - qWarning("Cannot bind local server [%s]", qUtf8Printable(LOCALSERVER_KEY)); + // for some reason, we still can't bind the socket + return false; } + return true; } } -bool SingleApplication::isRunning() +QString SingleApplication::serverName() const { - return !m_localServer->isListening(); + Q_CHECK_PTR(m_localServer); + return m_localServer->fullServerName(); } -bool SingleApplication::sendMessage(const QHash<QString, QVariant> ¶ms) +int SingleApplication::sendMessage(const SessionParam ¶ms) { QLocalSocket socket; socket.connectToServer(LOCALSERVER_KEY); if(socket.waitForConnected(LOCALSERVER_TIMEOUT)) { QByteArray argumentData; QDataStream ds(&argumentData, QIODevice::WriteOnly); - ds << params; + ds << paramStructToHash(params); socket.write(argumentData); socket.waitForBytesWritten(LOCALSERVER_TIMEOUT); - return true; + return EXIT_SUCCESS; } - return false; + return EXIT_FAILURE; } -void SingleApplication::slot_receiveMessage() +void SingleApplication::parseMessage() { QLocalSocket *socket = m_localServer->nextPendingConnection(); - if(!socket) { - // null socket --> return + // null socket --> return + if(socket == nullptr) { return; } @@ -93,5 +140,5 @@ void SingleApplication::slot_receiveMessage() socket->deleteLater(); - emit messageAvailable(params); + emit messageAvailable(paramStructFromHash(params)); } diff --git a/src/singleapplication.h b/src/singleapplication.h index 053ead2..fd9ce39 100644 --- a/src/singleapplication.h +++ b/src/singleapplication.h @@ -10,29 +10,39 @@ #define SINGLEAPPLICATION_H #include <QApplication> +#include <QVector> class QLocalServer; class SingleApplication : public QApplication { Q_OBJECT + public: + + struct SessionParam + { + QString profile; + bool newWindow; + QVector<QUrl> urls; + }; + explicit SingleApplication(int &argc, char **argv); ~SingleApplication(); - void bindLocalSocket(); + bool bindLocalSocket(const QString &name = QString()); + QString serverName() const; - bool isRunning(); - bool sendMessage(const QHash<QString, QVariant> ¶ms); + int sendMessage(const SessionParam ¶ms); signals: - void messageAvailable(const QHash<QString, QVariant> ¶ms); + void messageAvailable(const SessionParam ¶ms); private slots: - void slot_receiveMessage(); + void parseMessage(); private: const int LOCALSERVER_TIMEOUT = 500; - const QString LOCALSERVER_KEY = "smolbote_socket"; + QString LOCALSERVER_KEY; QLocalServer *m_localServer = nullptr; }; |