aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BUGS.md3
-rw-r--r--docs/manual/Contributing.md13
-rw-r--r--src/browser.cpp37
-rw-r--r--src/browser.h6
-rw-r--r--src/main.cpp52
-rw-r--r--src/mainwindow.cpp11
-rw-r--r--src/singleapplication.cpp87
-rw-r--r--src/singleapplication.h22
8 files changed, 177 insertions, 54 deletions
diff --git a/BUGS.md b/BUGS.md
index bcf702f..7392a8a 100644
--- a/BUGS.md
+++ b/BUGS.md
@@ -10,12 +10,13 @@ QUrl always seems to return true when checking if valid url. Workaround is to pr
to the search.
### databases-incognito in home
-Folder is empty.
+https://bugreports.qt.io/browse/QTBUG-62957
## To do list
List of things to do before 1.0 release
### Instance check on startup
+- review SingleApplication::SessionParam
### Auto-destruct cookies
- cookie whitelist and blacklist
diff --git a/docs/manual/Contributing.md b/docs/manual/Contributing.md
index ff8e2b2..c6d1bcd 100644
--- a/docs/manual/Contributing.md
+++ b/docs/manual/Contributing.md
@@ -19,6 +19,19 @@ Requires clang.
To set check levels, set the CLAZY_CHECKS environment variable to 'level0,level1', etc.
+### Setting up in qbs
+1. Create a profile for clazy
+```
+qbs-setup-toolchains --type clang /usr/bin/clazy clazy
+qbs-config profiles.clazy.cpp.cCompilerName clang
+qbs-config profiles.clazy.cpp.cxxCompilerName clazy
+```
+2. Create a profile for qt5-clazy
+```
+qbs-setup-qt /usr/bin/qmake-qt5 qt5-clazy
+qbs-config profiles.qt5-clazy.baseProfile clazy
+```
+
### Setting up in QtCreator
The simplest way to set it up is to use _clazy_ instead of _clang_.
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 &params)
+{
+ 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 &params);
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 &params)
+{
+ 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> &params)
+{
+ 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> &params)
+int SingleApplication::sendMessage(const SessionParam &params)
{
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> &params);
+ int sendMessage(const SessionParam &params);
signals:
- void messageAvailable(const QHash<QString, QVariant> &params);
+ void messageAvailable(const SessionParam &params);
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;
};