aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgignore2
-rw-r--r--3rd-party/SingleApplication/CHANGELOG.md162
-rw-r--r--3rd-party/SingleApplication/CMakeLists.txt26
-rw-r--r--3rd-party/SingleApplication/LICENSE24
-rw-r--r--3rd-party/SingleApplication/README.md287
-rw-r--r--3rd-party/SingleApplication/Windows.md46
-rw-r--r--3rd-party/SingleApplication/singleapplication.cpp468
-rw-r--r--3rd-party/SingleApplication/singleapplication.h135
-rw-r--r--3rd-party/SingleApplication/singleapplication_p.h85
-rw-r--r--CMakeLists.txt2
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/browser.cpp4
-rw-r--r--src/browser.h2
-rw-r--r--src/main.cpp38
-rw-r--r--src/profilemanager.cpp2
-rw-r--r--src/singleapplication.cpp109
-rw-r--r--src/singleapplication.h42
17 files changed, 1268 insertions, 171 deletions
diff --git a/.hgignore b/.hgignore
index 95c3033..ad67aff 100644
--- a/.hgignore
+++ b/.hgignore
@@ -11,3 +11,5 @@ build*
test/plugins.d
+.git
+
diff --git a/3rd-party/SingleApplication/CHANGELOG.md b/3rd-party/SingleApplication/CHANGELOG.md
new file mode 100644
index 0000000..4665222
--- /dev/null
+++ b/3rd-party/SingleApplication/CHANGELOG.md
@@ -0,0 +1,162 @@
+Changelog
+=========
+
+__3.0.10__
+----------
+
+* Removed C style casts and eliminated all clang warnings. Fixed `instanceId`
+ reading from only one byte in the message deserialization. Cleaned up
+ serialization code using `QDataStream`. Changed connection type to use
+ `quint8 enum` rather than `char`.
+* Renamed `SingleAppConnectionType` to `ConnectionType`. Added initialization
+ values to all `ConnectionType` enum cases.
+
+ _Jedidiah Buck McCready_
+
+__3.0.9__
+---------
+
+* Added SingleApplicationPrivate::primaryPid() as a solution to allow
+ bringing the primary window of an application to the foreground on
+ Windows.
+
+ _Eelco van Dam from Peacs BV_
+
+__3.0.8__
+---------
+
+* Bug fix - changed QApplication::instance() to QCoreApplication::instance()
+
+ _Evgeniy Bazhenov_
+
+__3.0.7a__
+----------
+
+* Fixed compilation error with Mingw32 in MXE thanks to Vitaly Tonkacheyev.
+* Removed QMutex used for thread safe behaviour. The implementation now uses
+ QCoreApplication::instance() to get an instance to SingleApplication for
+ memory deallocation.
+
+__3.0.6a__
+----------
+
+* Reverted GetUserName API usage on Windows. Fixed bug with missing library.
+* Fixed bug in the Calculator example, preventing it's window to be raised
+ on Windows.
+
+ Special thanks to Charles Gunawan.
+
+__3.0.5a__
+----------
+
+* Fixed a memory leak in the SingleApplicationPrivate destructor.
+
+ _Sergei Moiseev_
+
+__3.0.4a__
+----------
+
+* Fixed shadow and uninitialised variable warnings.
+
+ _Paul Walmsley_
+
+__3.0.3a__
+----------
+
+* Removed Microsoft Windows specific code for getting username due to
+ multiple problems and compiler differences on Windows platforms. On
+ Windows the shared memory block in User mode now includes the user's
+ home path (which contains the user's username).
+
+* Explicitly getting absolute path of the user's home directory as on Unix
+ a relative path (`~`) may be returned.
+
+__3.0.2a__
+----------
+
+* Fixed bug on Windows when username containing wide characters causes the
+ library to crash.
+
+ _Le Liu_
+
+__3.0.1a__
+----------
+
+* Allows the application path and version to be excluded from the server name
+ hash. The following flags were added for this purpose:
+ * `SingleApplication::Mode::ExcludeAppVersion`
+ * `SingleApplication::Mode::ExcludeAppPath`
+* Allow a non elevated process to connect to a local server created by an
+ elevated process run by the same user on Windows
+* Fixes a problem with upper case letters in paths on Windows
+
+ _Le Liu_
+
+__v3.0a__
+---------
+
+* Depricated secondary instances count.
+* Added a sendMessage() method to send a message to the primary instance.
+* Added a receivedMessage() signal, emitted when a message is received from a
+ secondary instance.
+* The SingleApplication constructor's third parameter is now a bool
+ specifying if the current instance should be allowed to run as a secondary
+ instance if there is already a primary instance.
+* The SingleApplication constructor accept a fourth parameter specifying if
+ the SingleApplication block should be User-wide or System-wide.
+* SingleApplication no longer relies on `applicationName` and
+ `organizationName` to be set. It instead concatenates all of the following
+ data and computes a `SHA256` hash which is used as the key of the
+ `QSharedMemory` block and the `QLocalServer`. Since at least
+ `applicationFilePath` is always present there is no need to explicitly set
+ any of the following prior to initialising `SingleApplication`.
+ * `QCoreApplication::applicationName`
+ * `QCoreApplication::applicationVersion`
+ * `QCoreApplication::applicationFilePath`
+ * `QCoreApplication::organizationName`
+ * `QCoreApplication::organizationDomain`
+ * User name or home directory path if in User mode
+* The primary instance is no longer notified when a secondary instance had
+ been started by default. A `Mode` flag for this feature exists.
+* Added `instanceNumber()` which represents a unique identifier for each
+ secondary instance started. When called from the primary instance will
+ return `0`.
+
+__v2.4__
+--------
+
+* Stability improvements
+* Support for secondary instances.
+* The library now recovers safely after the primary process has crashed
+and the shared memory had not been deleted.
+
+__v2.3__
+--------
+
+* Improved pimpl design and inheritance safety.
+
+ _Vladislav Pyatnichenko_
+
+__v2.2__
+--------
+
+* The `QAPPLICATION_CLASS` macro can now be defined in the file including the
+Single Application header or with a `DEFINES+=` statement in the project file.
+
+__v2.1__
+--------
+
+* A race condition can no longer occur when starting two processes nearly
+ simultaneously.
+
+ Fix issue [#3](https://github.com/itay-grudev/SingleApplication/issues/3)
+
+__v2.0__
+--------
+
+* SingleApplication is now being passed a reference to `argc` instead of a
+ copy.
+
+ Fix issue [#1](https://github.com/itay-grudev/SingleApplication/issues/1)
+
+* Improved documentation.
diff --git a/3rd-party/SingleApplication/CMakeLists.txt b/3rd-party/SingleApplication/CMakeLists.txt
new file mode 100644
index 0000000..5de0253
--- /dev/null
+++ b/3rd-party/SingleApplication/CMakeLists.txt
@@ -0,0 +1,26 @@
+# Find includes in corresponding build directories
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+# Instruct CMake to run moc automatically when needed.
+set(CMAKE_AUTOMOC ON)
+#set(CMAKE_AUTOUIC ON)
+#set(CMAKE_AUTORCC ON)
+
+add_library(SingleApplication
+ singleapplication.cpp
+ singleapplication.h
+ singleapplication_p.h)
+
+target_link_libraries(SingleApplication
+ Qt5::Core Qt5::Network Qt5::Widgets
+)
+
+target_compile_definitions(SingleApplication
+ PRIVATE QAPPLICATION_CLASS=QApplication
+)
+
+if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
+ target_link_libraries(SingleApplication
+ Advapi32
+ )
+endif()
diff --git a/3rd-party/SingleApplication/LICENSE b/3rd-party/SingleApplication/LICENSE
new file mode 100644
index 0000000..85b2a14
--- /dev/null
+++ b/3rd-party/SingleApplication/LICENSE
@@ -0,0 +1,24 @@
+The MIT License (MIT)
+
+Copyright (c) Itay Grudev 2015 - 2016
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+Note: Some of the examples include code not distributed under the terms of the
+MIT License.
diff --git a/3rd-party/SingleApplication/README.md b/3rd-party/SingleApplication/README.md
new file mode 100644
index 0000000..82e7a5b
--- /dev/null
+++ b/3rd-party/SingleApplication/README.md
@@ -0,0 +1,287 @@
+SingleApplication
+=================
+
+This is a replacement of the QtSingleApplication for `Qt5`.
+
+Keeps the Primary Instance of your Application and kills each subsequent
+instances. It can (if enabled) spawn secondary (non-related to the primary)
+instances and can send data to the primary instance from secondary instances.
+
+Usage
+-----
+
+The `SingleApplication` class inherits from whatever `Q[Core|Gui]Application`
+class you specify via the `QAPPLICATION_CLASS` macro (`QCoreApplication` is the
+default). Further usage is similar to the use of the `Q[Core|Gui]Application`
+classes.
+
+The library sets up a `QLocalServer` and a `QSharedMemory` block. The first
+instance of your Application is your Primary Instance. It would check if the
+shared memory block exists and if not it will start a `QLocalServer` and listen
+for connections. Each subsequent instance of your application would check if the
+shared memory block exists and if it does, it will connect to the QLocalServer
+to notify the primary instance that a new instance had been started, after which
+it would terminate with status code `0`. In the Primary Instance
+`SingleApplication` would emit the `instanceStarted()` signal upon detecting
+that a new instance had been started.
+
+The library uses `stdlib` to terminate the program with the `exit()` function.
+
+You can use the library as if you use any other `QCoreApplication` derived
+class:
+
+```cpp
+#include <QApplication>
+#include <SingleApplication.h>
+
+int main( int argc, char* argv[] )
+{
+ SingleApplication app( argc, argv );
+
+ return app.exec();
+}
+```
+
+To include the library files I would recommend that you add it as a git
+submodule to your project and include it's contents with a `.pri` file. Here is
+how:
+
+```bash
+git submodule add git@github.com:itay-grudev/SingleApplication.git singleapplication
+```
+
+Then include the `singleapplication.pri` file in your `.pro` project file. Also
+don't forget to specify which `QCoreApplication` class your app is using if it
+is not `QCoreApplication`.
+
+```qmake
+include(singleapplication/singleapplication.pri)
+DEFINES += QAPPLICATION_CLASS=QApplication
+```
+
+The `Instance Started` signal
+------------------------
+
+The SingleApplication class implements a `instanceStarted()` signal. You can
+bind to that signal to raise your application's window when a new instance had
+been started, for example.
+
+```cpp
+// window is a QWindow instance
+QObject::connect(
+ &app,
+ &SingleApplication::instanceStarted,
+ &window,
+ &QWindow::raise
+);
+```
+
+Using `SingleApplication::instance()` is a neat way to get the
+`SingleApplication` instance for binding to it's signals anywhere in your
+program.
+
+__Note:__ On Windows the ability to bring the application windows to the
+foreground is restricted. See [Windows specific implementations](Windows.md)
+for a workaround and an example implementation.
+
+
+Secondary Instances
+-------------------
+
+If you want to be able to launch additional Secondary Instances (not related to
+your Primary Instance) you have to enable that with the third parameter of the
+`SingleApplication` constructor. The default is `false` meaning no Secondary
+Instances. Here is an example of how you would start a Secondary Instance send
+a message with the command line arguments to the primary instance and then shut
+down.
+
+```cpp
+int main(int argc, char *argv[])
+{
+ SingleApplication app( argc, argv, true );
+
+ if( app.isSecondary() ) {
+ app.sendMessage( app.arguments().join(' ')).toUtf8() );
+ app.exit( 0 );
+ }
+
+ return app.exec();
+}
+```
+
+*__Note:__ A secondary instance won't cause the emission of the
+`instanceStarted()` signal by default. See `SingleApplication::Mode` for more
+details.*
+
+You can check whether your instance is a primary or secondary with the following
+methods:
+
+```cpp
+app.isPrimary();
+// or
+app.isSecondary();
+```
+
+*__Note:__ If your Primary Instance is terminated a newly launched instance
+will replace the Primary one even if the Secondary flag has been set.*
+
+API
+---
+
+### Members
+
+```cpp
+SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 100 )
+```
+
+Depending on whether `allowSecondary` is set, this constructor may terminate
+your app if there is already a primary instance running. Additional `Options`
+can be specified to set whether the SingleApplication block should work
+user-wide or system-wide. Additionally the `Mode::SecondaryNotification` may be
+used to notify the primary instance whenever a secondary instance had been
+started (disabled by default). `timeout` specifies the maximum time in
+milliseconds to wait for blocking operations.
+
+*__Note:__ `argc` and `argv` may be changed as Qt removes arguments that it
+recognizes.*
+
+*__Note:__ `Mode::SecondaryNotification` only works if set on both the primary
+and the secondary instance.*
+
+*__Note:__ Operating system can restrict the shared memory blocks to the same
+user, in which case the User/System modes will have no effect and the block will
+be user wide.*
+
+---
+
+```cpp
+bool SingleApplication::sendMessage( QByteArray message, int timeout = 100 )
+```
+
+Sends `message` to the Primary Instance. Uses `timeout` as a the maximum timeout
+in milliseconds for blocking functions
+
+---
+
+```cpp
+bool SingleApplication::isPrimary()
+```
+
+Returns if the instance is the primary instance.
+
+---
+
+```cpp
+bool SingleApplication::isSecondary()
+```
+Returns if the instance is a secondary instance.
+
+---
+
+```cpp
+quint32 SingleApplication::instanceId()
+```
+
+Returns a unique identifier for the current instance.
+
+---
+
+```cpp
+qint64 SingleApplication::primaryPid()
+```
+
+Returns the process ID (PID) of the primary instance.
+
+### Signals
+
+```cpp
+void SingleApplication::instanceStarted()
+```
+
+Triggered whenever a new instance had been started, except for secondary
+instances if the `Mode::SecondaryNotification` flag is not specified.
+
+---
+
+```cpp
+void SingleApplication::receivedMessage( quint32 instanceId, QByteArray message )
+```
+
+Triggered whenever there is a message received from a secondary instance.
+
+---
+
+### Flags
+
+```cpp
+enum SingleApplication::Mode
+```
+
+* `Mode::User` - The SingleApplication block should apply user wide. This adds
+ user specific data to the key used for the shared memory and server name.
+ This is the default functionality.
+* `Mode::System` – The SingleApplication block applies system-wide.
+* `Mode::SecondaryNotification` – Whether to trigger `instanceStarted()` even
+ whenever secondary instances are started.
+* `Mode::ExcludeAppPath` – Excludes the application path from the server name
+ (and memory block) hash.
+* `Mode::ExcludeAppVersion` – Excludes the application version from the server
+ name (and memory block) hash.
+
+*__Note:__ `Mode::SecondaryNotification` only works if set on both the primary
+and the secondary instance.*
+
+*__Note:__ Operating system can restrict the shared memory blocks to the same
+user, in which case the User/System modes will have no effect and the block will
+be user wide.*
+
+---
+
+Versioning
+----------
+
+Each major version introduces either very significant changes or is not
+backwards compatible with the previous version. Minor versions only add
+additional features, bug fixes or performance improvements and are backwards
+compatible with the previous release. See [`CHANGELOG.md`](CHANGELOG.md) for
+more details.
+
+Implementation
+--------------
+
+The library is implemented with a QSharedMemory block which is thread safe and
+guarantees a race condition will not occur. It also uses a QLocalSocket to
+notify the main process that a new instance had been spawned and thus invoke the
+`instanceStarted()` signal.
+
+To handle an issue on `*nix` systems, where the operating system owns the shared
+memory block and if the program crashes the memory remains untouched, the
+library binds to the following signals and closes the program with
+`error code = 128 + signum` where signum is the number representation of the
+signal listed below. Handling the signal is required in order to safely delete
+the `QSharedMemory` block. Each of these signals are potentially lethal and will
+results in process termination.
+
+* `SIGHUP` - `1`, Hangup.
+* `SIGINT` - `2`, Terminal interrupt signal
+* `SIGQUIT` - `3`, Terminal quit signal.
+* `SIGILL` - `4`, Illegal instruction.
+* `SIGABRT` - `6`, Process abort signal.
+* `SIGBUS` - `7`, Access to an undefined portion of a memory object.
+* `SIGFPE` - `8`, Erroneous arithmetic operation (such as division by zero).
+* `SIGSEGV` - `11`, Invalid memory reference.
+* `SIGSYS` - `12`, Bad system call.
+* `SIGPIPE` - `13`, Write on a pipe with no one to read it.
+* `SIGALRM` - `14`, Alarm clock.
+* `SIGTERM` - `15`, Termination signal.
+* `SIGXCPU` - `24`, CPU time limit exceeded.
+* `SIGXFSZ` - `25`, File size limit exceeded.
+
+Additionally the library can recover from being killed with uncatchable signals
+and will reset the memory block given that there are no other instances running.
+
+License
+-------
+This library and it's supporting documentation are released under
+`The MIT License (MIT)` with the exception of some of the examples distributed
+under the BSD license.
diff --git a/3rd-party/SingleApplication/Windows.md b/3rd-party/SingleApplication/Windows.md
new file mode 100644
index 0000000..48b0748
--- /dev/null
+++ b/3rd-party/SingleApplication/Windows.md
@@ -0,0 +1,46 @@
+Windows Specific Implementations
+================================
+
+Setting the foreground window
+-----------------------------
+
+In the `instanceStarted()` example in the `README` we demonstrated how an
+application can bring it's primary instance window whenever a second copy
+of the application is started.
+
+On Windows the ability to bring the application windows to the foreground is
+restricted, see [`AllowSetForegroundWindow()`][AllowSetForegroundWindow] for more
+details.
+
+The background process (the primary instance) can bring its windows to the
+foreground if it is allowed by the current foreground process (the secondary
+instance). To bypass this `SingleApplication` must be initialized with the
+`allowSecondary` parameter set to `true` and the `options` parameter must
+include `Mode::SecondaryNotification`, See `SingleApplication::Mode` for more
+details.
+
+Here is an example:
+
+```cpp
+if( app.isSecondary() ) {
+ // This API requires LIBS += User32.lib to be added to the project
+ AllowSetForegroundWindow( DWORD( app.getPrimaryPid() ) );
+}
+
+if( app.isPrimary() ) {
+ QObject::connect(
+ &app,
+ &SingleApplication::instanceStarted,
+ this,
+ &App::instanceStarted
+ );
+}
+```
+
+```cpp
+void App::instanceStarted() {
+ QApplication::setActiveWindow( [window/widget to set to the foreground] );
+}
+```
+
+[AllowSetForegroundWindow]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms632668.aspx
diff --git a/3rd-party/SingleApplication/singleapplication.cpp b/3rd-party/SingleApplication/singleapplication.cpp
new file mode 100644
index 0000000..c74d6db
--- /dev/null
+++ b/3rd-party/SingleApplication/singleapplication.cpp
@@ -0,0 +1,468 @@
+// The MIT License (MIT)
+//
+// Copyright (c) Itay Grudev 2015 - 2016
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include <cstdlib>
+
+#include <QtCore/QDir>
+#include <QtCore/QProcess>
+#include <QtCore/QByteArray>
+#include <QtCore/QSemaphore>
+#include <QtCore/QSharedMemory>
+#include <QtCore/QStandardPaths>
+#include <QtCore/QCryptographicHash>
+#include <QtCore/QDataStream>
+#include <QtNetwork/QLocalServer>
+#include <QtNetwork/QLocalSocket>
+
+#ifdef Q_OS_UNIX
+ #include <signal.h>
+ #include <unistd.h>
+#endif
+
+#ifdef Q_OS_WIN
+ #include <windows.h>
+ #include <lmcons.h>
+#endif
+
+#include "singleapplication.h"
+#include "singleapplication_p.h"
+
+
+SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr ) : q_ptr( q_ptr ) {
+ server = nullptr;
+ socket = nullptr;
+}
+
+SingleApplicationPrivate::~SingleApplicationPrivate()
+{
+ if( socket != nullptr ) {
+ socket->close();
+ delete socket;
+ }
+
+ memory->lock();
+ InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
+ if( server != nullptr ) {
+ server->close();
+ delete server;
+ inst->primary = false;
+ inst->primaryPid = -1;
+ }
+ memory->unlock();
+
+ delete memory;
+}
+
+void SingleApplicationPrivate::genBlockServerName( int timeout )
+{
+ QCryptographicHash appData( QCryptographicHash::Sha256 );
+ appData.addData( "SingleApplication", 17 );
+ appData.addData( SingleApplication::app_t::applicationName().toUtf8() );
+ appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
+ appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
+
+ if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ) {
+ appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() );
+ }
+
+ if( ! (options & SingleApplication::Mode::ExcludeAppPath) ) {
+#ifdef Q_OS_WIN
+ appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() );
+#else
+ appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
+#endif
+ }
+
+ // User level block requires a user specific data in the hash
+ if( options & SingleApplication::Mode::User ) {
+#ifdef Q_OS_WIN
+ Q_UNUSED(timeout);
+ wchar_t username [ UNLEN + 1 ];
+ // Specifies size of the buffer on input
+ DWORD usernameLength = UNLEN + 1;
+ if( GetUserNameW( username, &usernameLength ) ) {
+ appData.addData( QString::fromWCharArray(username).toUtf8() );
+ } else {
+ appData.addData( QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).join("").toUtf8() );
+ }
+#endif
+#ifdef Q_OS_UNIX
+ QProcess process;
+ process.start( "whoami" );
+ if( process.waitForFinished( timeout ) &&
+ process.exitCode() == QProcess::NormalExit) {
+ appData.addData( process.readLine() );
+ } else {
+ appData.addData(
+ QDir(
+ QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).first()
+ ).absolutePath().toUtf8()
+ );
+ }
+#endif
+ }
+
+ // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
+ // server naming requirements.
+ blockServerName = appData.result().toBase64().replace("/", "_");
+}
+
+void SingleApplicationPrivate::startPrimary( bool resetMemory )
+{
+ Q_Q(SingleApplication);
+
+#ifdef Q_OS_UNIX
+ // Handle any further termination signals to ensure the
+ // QSharedMemory block is deleted even if the process crashes
+ crashHandler();
+#endif
+ // Successful creation means that no main process exists
+ // So we start a QLocalServer to listen for connections
+ QLocalServer::removeServer( blockServerName );
+ server = new QLocalServer();
+
+ // Restrict access to the socket according to the
+ // SingleApplication::Mode::User flag on User level or no restrictions
+ if( options & SingleApplication::Mode::User ) {
+ server->setSocketOptions( QLocalServer::UserAccessOption );
+ } else {
+ server->setSocketOptions( QLocalServer::WorldAccessOption );
+ }
+
+ server->listen( blockServerName );
+ QObject::connect(
+ server,
+ &QLocalServer::newConnection,
+ this,
+ &SingleApplicationPrivate::slotConnectionEstablished
+ );
+
+ // Reset the number of connections
+ memory->lock();
+ InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
+
+ if( resetMemory ) {
+ inst->secondary = 0;
+ }
+
+ inst->primary = true;
+ inst->primaryPid = q->applicationPid();
+
+ memory->unlock();
+
+ instanceNumber = 0;
+}
+
+void SingleApplicationPrivate::startSecondary()
+{
+#ifdef Q_OS_UNIX
+ // Handle any further termination signals to ensure the
+ // QSharedMemory block is deleted even if the process crashes
+ crashHandler();
+#endif
+}
+
+void SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
+{
+ // Connect to the Local Server of the Primary Instance if not already
+ // connected.
+ if( socket == nullptr ) {
+ socket = new QLocalSocket();
+ }
+
+ // If already connected - we are done;
+ if( socket->state() == QLocalSocket::ConnectedState )
+ return;
+
+ // If not connect
+ if( socket->state() == QLocalSocket::UnconnectedState ||
+ socket->state() == QLocalSocket::ClosingState ) {
+ socket->connectToServer( blockServerName );
+ }
+
+ // Wait for being connected
+ if( socket->state() == QLocalSocket::ConnectingState ) {
+ socket->waitForConnected( msecs );
+ }
+
+ // Initialisation message according to the SingleApplication protocol
+ if( socket->state() == QLocalSocket::ConnectedState ) {
+ // Notify the parent that a new instance had been started;
+ QByteArray initMsg;
+ QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
+ writeStream.setVersion(QDataStream::Qt_5_6);
+ writeStream << blockServerName.toLatin1();
+ writeStream << static_cast<quint8>(connectionType);
+ writeStream << instanceNumber;
+ quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
+ writeStream << checksum;
+
+ socket->write( initMsg );
+ socket->flush();
+ socket->waitForBytesWritten( msecs );
+ }
+}
+
+qint64 SingleApplicationPrivate::primaryPid()
+{
+ qint64 pid;
+
+ memory->lock();
+ InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
+ pid = inst->primaryPid;
+ memory->unlock();
+
+ return pid;
+}
+
+#ifdef Q_OS_UNIX
+ void SingleApplicationPrivate::crashHandler()
+ {
+ // Handle any further termination signals to ensure the
+ // QSharedMemory block is deleted even if the process crashes
+ signal( SIGHUP, SingleApplicationPrivate::terminate ); // 1
+ signal( SIGINT, SingleApplicationPrivate::terminate ); // 2
+ signal( SIGQUIT, SingleApplicationPrivate::terminate ); // 3
+ signal( SIGILL, SingleApplicationPrivate::terminate ); // 4
+ signal( SIGABRT, SingleApplicationPrivate::terminate ); // 6
+ signal( SIGFPE, SingleApplicationPrivate::terminate ); // 8
+ signal( SIGBUS, SingleApplicationPrivate::terminate ); // 10
+ signal( SIGSEGV, SingleApplicationPrivate::terminate ); // 11
+ signal( SIGSYS, SingleApplicationPrivate::terminate ); // 12
+ signal( SIGPIPE, SingleApplicationPrivate::terminate ); // 13
+ signal( SIGALRM, SingleApplicationPrivate::terminate ); // 14
+ signal( SIGTERM, SingleApplicationPrivate::terminate ); // 15
+ signal( SIGXCPU, SingleApplicationPrivate::terminate ); // 24
+ signal( SIGXFSZ, SingleApplicationPrivate::terminate ); // 25
+ }
+
+ void SingleApplicationPrivate::terminate( int signum )
+ {
+ delete ((SingleApplication*)QCoreApplication::instance())->d_ptr;
+ ::exit( 128 + signum );
+ }
+#endif
+
+/**
+ * @brief Executed when a connection has been made to the LocalServer
+ */
+void SingleApplicationPrivate::slotConnectionEstablished()
+{
+ Q_Q(SingleApplication);
+
+ QLocalSocket *nextConnSocket = server->nextPendingConnection();
+
+ quint32 instanceId = 0;
+ ConnectionType connectionType = InvalidConnection;
+ if( nextConnSocket->waitForReadyRead( 100 ) ) {
+ // read all data from message in same order/format as written
+ QByteArray msgBytes = nextConnSocket->read(nextConnSocket->bytesAvailable() - static_cast<qint64>(sizeof(quint16)));
+ QByteArray checksumBytes = nextConnSocket->read(sizeof(quint16));
+ QDataStream readStream(msgBytes);
+ readStream.setVersion(QDataStream::Qt_5_6);
+
+ // server name
+ QByteArray latin1Name;
+ readStream >> latin1Name;
+ // connectioon type
+ quint8 connType = InvalidConnection;
+ readStream >> connType;
+ connectionType = static_cast<ConnectionType>(connType);
+ // instance id
+ readStream >> instanceId;
+ // checksum
+ quint16 msgChecksum = 0;
+ QDataStream checksumStream(checksumBytes);
+ checksumStream.setVersion(QDataStream::Qt_5_6);
+ checksumStream >> msgChecksum;
+
+ const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length()));
+
+ if (readStream.status() != QDataStream::Ok || QLatin1String(latin1Name) != blockServerName || msgChecksum != actualChecksum) {
+ connectionType = InvalidConnection;
+ }
+ }
+
+ if( connectionType == InvalidConnection ) {
+ nextConnSocket->close();
+ delete nextConnSocket;
+ return;
+ }
+
+ QObject::connect(
+ nextConnSocket,
+ &QLocalSocket::aboutToClose,
+ this,
+ [nextConnSocket, instanceId, this]() {
+ Q_EMIT this->slotClientConnectionClosed( nextConnSocket, instanceId );
+ }
+ );
+
+ QObject::connect(
+ nextConnSocket,
+ &QLocalSocket::readyRead,
+ this,
+ [nextConnSocket, instanceId, this]() {
+ Q_EMIT this->slotDataAvailable( nextConnSocket, instanceId );
+ }
+ );
+
+ if( connectionType == NewInstance || (
+ connectionType == SecondaryInstance &&
+ options & SingleApplication::Mode::SecondaryNotification
+ )
+ ) {
+ Q_EMIT q->instanceStarted();
+ }
+
+ if( nextConnSocket->bytesAvailable() > 0 ) {
+ Q_EMIT this->slotDataAvailable( nextConnSocket, instanceId );
+ }
+}
+
+void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
+{
+ Q_Q(SingleApplication);
+ Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
+}
+
+void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
+{
+ if( closedSocket->bytesAvailable() > 0 )
+ Q_EMIT slotDataAvailable( closedSocket, instanceId );
+ closedSocket->deleteLater();
+}
+
+/**
+ * @brief Constructor. Checks and fires up LocalServer or closes the program
+ * if another instance already exists
+ * @param argc
+ * @param argv
+ * @param {bool} allowSecondaryInstances
+ */
+SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout )
+ : app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
+{
+ Q_D(SingleApplication);
+
+ // Store the current mode of the program
+ d->options = options;
+
+ // Generating an application ID used for identifying the shared memory
+ // block and QLocalServer
+ d->genBlockServerName( timeout );
+
+ // Guarantee thread safe behaviour with a shared memory block. Also by
+ // explicitly attaching it and then deleting it we make sure that the
+ // memory is deleted even if the process had crashed on Unix.
+#ifdef Q_OS_UNIX
+ d->memory = new QSharedMemory( d->blockServerName );
+ d->memory->attach();
+ delete d->memory;
+#endif
+ d->memory = new QSharedMemory( d->blockServerName );
+
+ // Create a shared memory block
+ if( d->memory->create( sizeof( InstancesInfo ) ) ) {
+ d->startPrimary( true );
+ return;
+ } else {
+ // Attempt to attach to the memory segment
+ if( d->memory->attach() ) {
+ d->memory->lock();
+ InstancesInfo* inst = static_cast<InstancesInfo*>(d->memory->data());
+
+ if( ! inst->primary ) {
+ d->startPrimary( false );
+ d->memory->unlock();
+ return;
+ }
+
+ // Check if another instance can be started
+ if( allowSecondary ) {
+ inst->secondary += 1;
+ d->instanceNumber = inst->secondary;
+ d->startSecondary();
+ if( d->options & Mode::SecondaryNotification ) {
+ d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance );
+ }
+ d->memory->unlock();
+ return;
+ }
+
+ d->memory->unlock();
+ }
+ }
+
+ d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
+ delete d;
+ ::exit( EXIT_SUCCESS );
+}
+
+/**
+ * @brief Destructor
+ */
+SingleApplication::~SingleApplication()
+{
+ Q_D(SingleApplication);
+ delete d;
+}
+
+bool SingleApplication::isPrimary()
+{
+ Q_D(SingleApplication);
+ return d->server != nullptr;
+}
+
+bool SingleApplication::isSecondary()
+{
+ Q_D(SingleApplication);
+ return d->server == nullptr;
+}
+
+quint32 SingleApplication::instanceId()
+{
+ Q_D(SingleApplication);
+ return d->instanceNumber;
+}
+
+qint64 SingleApplication::primaryPid()
+{
+ Q_D(SingleApplication);
+ return d->primaryPid();
+}
+
+bool SingleApplication::sendMessage( QByteArray message, int timeout )
+{
+ Q_D(SingleApplication);
+
+ // Nobody to connect to
+ if( isPrimary() ) return false;
+
+ // Make sure the socket is connected
+ d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect );
+
+ d->socket->write( message );
+ bool dataWritten = d->socket->flush();
+ d->socket->waitForBytesWritten( timeout );
+ return dataWritten;
+}
diff --git a/3rd-party/SingleApplication/singleapplication.h b/3rd-party/SingleApplication/singleapplication.h
new file mode 100644
index 0000000..33a9898
--- /dev/null
+++ b/3rd-party/SingleApplication/singleapplication.h
@@ -0,0 +1,135 @@
+// The MIT License (MIT)
+//
+// Copyright (c) Itay Grudev 2015 - 2016
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef SINGLE_APPLICATION_H
+#define SINGLE_APPLICATION_H
+
+#include <QtCore/QtGlobal>
+#include <QtNetwork/QLocalSocket>
+
+#ifndef QAPPLICATION_CLASS
+ #define QAPPLICATION_CLASS QCoreApplication
+#endif
+
+#include QT_STRINGIFY(QAPPLICATION_CLASS)
+
+class SingleApplicationPrivate;
+
+/**
+ * @brief The SingleApplication class handles multipe instances of the same
+ * Application
+ * @see QCoreApplication
+ */
+class SingleApplication : public QAPPLICATION_CLASS
+{
+ Q_OBJECT
+
+ typedef QAPPLICATION_CLASS app_t;
+
+public:
+ /**
+ * @brief Mode of operation of SingleApplication.
+ * Whether the block should be user-wide or system-wide and whether the
+ * primary instance should be notified when a secondary instance had been
+ * started.
+ * @note Operating system can restrict the shared memory blocks to the same
+ * user, in which case the User/System modes will have no effect and the
+ * block will be user wide.
+ * @enum
+ */
+ enum Mode {
+ User = 1 << 0,
+ System = 1 << 1,
+ SecondaryNotification = 1 << 2,
+ ExcludeAppVersion = 1 << 3,
+ ExcludeAppPath = 1 << 4
+ };
+ Q_DECLARE_FLAGS(Options, Mode)
+
+ /**
+ * @brief Intitializes a SingleApplication instance with argc command line
+ * arguments in argv
+ * @arg {int &} argc - Number of arguments in argv
+ * @arg {const char *[]} argv - Supplied command line arguments
+ * @arg {bool} allowSecondary - Whether to start the instance as secondary
+ * if there is already a primary instance.
+ * @arg {Mode} mode - Whether for the SingleApplication block to be applied
+ * User wide or System wide.
+ * @arg {int} timeout - Timeout to wait in miliseconds.
+ * @note argc and argv may be changed as Qt removes arguments that it
+ * recognizes
+ * @note Mode::SecondaryNotification only works if set on both the primary
+ * instance and the secondary instance.
+ * @note The timeout is just a hint for the maximum time of blocking
+ * operations. It does not guarantee that the SingleApplication
+ * initialisation will be completed in given time, though is a good hint.
+ * Usually 4*timeout would be the worst case (fail) scenario.
+ * @see See the corresponding QAPPLICATION_CLASS constructor for reference
+ */
+ explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 100 );
+ ~SingleApplication();
+
+ /**
+ * @brief Returns if the instance is the primary instance
+ * @returns {bool}
+ */
+ bool isPrimary();
+
+ /**
+ * @brief Returns if the instance is a secondary instance
+ * @returns {bool}
+ */
+ bool isSecondary();
+
+ /**
+ * @brief Returns a unique identifier for the current instance
+ * @returns {qint32}
+ */
+ quint32 instanceId();
+
+ /**
+ * @brief Returns the process ID (PID) of the primary instance
+ * @returns {qint64}
+ */
+ qint64 primaryPid();
+
+ /**
+ * @brief Sends a message to the primary instance. Returns true on success.
+ * @param {int} timeout - Timeout for connecting
+ * @returns {bool}
+ * @note sendMessage() will return false if invoked from the primary
+ * instance.
+ */
+ bool sendMessage( QByteArray message, int timeout = 100 );
+
+Q_SIGNALS:
+ void instanceStarted();
+ void receivedMessage( quint32 instanceId, QByteArray message );
+
+private:
+ SingleApplicationPrivate *d_ptr;
+ Q_DECLARE_PRIVATE(SingleApplication)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
+
+#endif // SINGLE_APPLICATION_H
diff --git a/3rd-party/SingleApplication/singleapplication_p.h b/3rd-party/SingleApplication/singleapplication_p.h
new file mode 100644
index 0000000..a990a53
--- /dev/null
+++ b/3rd-party/SingleApplication/singleapplication_p.h
@@ -0,0 +1,85 @@
+// The MIT License (MIT)
+//
+// Copyright (c) Itay Grudev 2015 - 2016
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+//
+// W A R N I N G !!!
+// -----------------
+//
+// This file is not part of the SingleApplication API. It is used purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or may even be removed.
+//
+
+#ifndef SINGLEAPPLICATION_P_H
+#define SINGLEAPPLICATION_P_H
+
+#include <QtCore/QSharedMemory>
+#include <QtNetwork/QLocalServer>
+#include <QtNetwork/QLocalSocket>
+#include "singleapplication.h"
+
+struct InstancesInfo {
+ bool primary;
+ quint32 secondary;
+ qint64 primaryPid;
+};
+
+class SingleApplicationPrivate : public QObject {
+Q_OBJECT
+public:
+ enum ConnectionType : quint8 {
+ InvalidConnection = 0,
+ NewInstance = 1,
+ SecondaryInstance = 2,
+ Reconnect = 3
+ };
+ Q_DECLARE_PUBLIC(SingleApplication)
+
+ SingleApplicationPrivate( SingleApplication *q_ptr );
+ ~SingleApplicationPrivate();
+
+ void genBlockServerName( int msecs );
+ void startPrimary( bool resetMemory );
+ void startSecondary();
+ void connectToPrimary(int msecs, ConnectionType connectionType );
+ qint64 primaryPid();
+
+#ifdef Q_OS_UNIX
+ void crashHandler();
+ static void terminate( int signum );
+#endif
+
+ QSharedMemory *memory;
+ SingleApplication *q_ptr;
+ QLocalSocket *socket;
+ QLocalServer *server;
+ quint32 instanceNumber;
+ QString blockServerName;
+ SingleApplication::Options options;
+
+public Q_SLOTS:
+ void slotConnectionEstablished();
+ void slotDataAvailable( QLocalSocket*, quint32 );
+ void slotClientConnectionClosed( QLocalSocket*, quint32 );
+};
+
+#endif // SINGLEAPPLICATION_P_H
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c0a4bda..a76f89e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -61,6 +61,8 @@ endif()
# with static values
configure_file("${PROJECT_SOURCE_DIR}/src/version.h.in" "${PROJECT_BINARY_DIR}/src/version.h")
+add_subdirectory(3rd-party/SingleApplication)
+
add_subdirectory(lib/about)
add_subdirectory(lib/addressbar)
add_subdirectory(lib/bookmarks)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8cd03e6..2f80ad1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -9,8 +9,6 @@ set(CMAKE_AUTORCC ON)
set(poi_SRC
# main
main.cpp
- singleapplication.cpp
- singleapplication.h
browser.cpp
browser.h
session.cpp
@@ -59,6 +57,7 @@ endif()
target_include_directories(poi
PRIVATE ${Boost_INCLUDE_DIRS}
+ PRIVATE ../3rd-party
PRIVATE ../lib ../plugins
PRIVATE ../lib/configuration
PRIVATE ../lib/web)
@@ -66,6 +65,7 @@ target_include_directories(poi
target_link_libraries(poi
Qt5::Core Qt5::Widgets Qt5::Concurrent Qt5::WebEngineWidgets
${Boost_LIBRARIES}
+ SingleApplication
about
addressbar
configuration
@@ -80,6 +80,7 @@ if(Plasma)
endif(Plasma)
target_compile_definitions(poi
+ PRIVATE QAPPLICATION_CLASS=QApplication
PRIVATE QTBUG_65223_WORKAROUND
#PRIVATE QTBUG_68224_WORKAROUND
)
diff --git a/src/browser.cpp b/src/browser.cpp
index 6ffcaaa..f6b3805 100644
--- a/src/browser.cpp
+++ b/src/browser.cpp
@@ -27,11 +27,13 @@
#include <QJsonDocument>
Browser::Browser(int &argc, char *argv[])
- : SingleApplication(argc, argv)
+ : SingleApplication(argc, argv, true, SingleApplication::User | SingleApplication::SecondaryNotification | SingleApplication::ExcludeAppVersion)
{
setApplicationName("smolbote");
setWindowIcon(QIcon(":/icon.svg"));
setApplicationVersion(SMOLBOTE_VERSION);
+
+ //qDebug(QAPPLICATION_CLASS);
}
Browser::~Browser()
diff --git a/src/browser.h b/src/browser.h
index 3405e85..b03243f 100644
--- a/src/browser.h
+++ b/src/browser.h
@@ -9,7 +9,7 @@
#ifndef SMOLBOTE_BROWSER_H
#define SMOLBOTE_BROWSER_H
-#include "singleapplication.h"
+#include "SingleApplication/singleapplication.h"
#include <QJsonObject>
#include <QVector>
#include <functional>
diff --git a/src/main.cpp b/src/main.cpp
index 00a57d5..870120e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -84,6 +84,13 @@ int main(int argc, char **argv)
// set this, otherwise the webview becomes black when using a stylesheet
app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
+ if(app.isPrimary())
+ qDebug("app is primary");
+ else if(app.isSecondary())
+ qDebug("app is secondary");
+ else
+ qDebug("app is something?");
+
// translator
if(config->exists("browser.locale")) {
auto *translator = new QTranslator(&app);
@@ -127,28 +134,29 @@ int main(int argc, char **argv)
}
// set up socket
- bool isSingleInstance = app.bindLocalSocket(socket.value());
- if(isSingleInstance) {
-#ifdef QT_DEBUG
- qDebug("Local socket bound");
-#endif
-
- QObject::connect(&app, &Browser::messageAvailable, &app, &Browser::createSession);
- }
+ QObject::connect(&app, &Browser::receivedMessage, &app, [&app](quint32 instanceId, QByteArray message) {
+ auto doc = QJsonDocument::fromJson(message);
+ app.createSession(doc.object());
+ });
- if(session) {
+ if(app.isPrimary()) {
+ app.createSession(Session::toJsonObject(profile.value(), urls));
+ } else if(session) {
QFile sessionJson(session.value());
if(sessionJson.open(QIODevice::ReadOnly | QIODevice::Text)) {
app.sendMessage(sessionJson.readAll());
sessionJson.close();
+ qDebug("session data sent");
+ return EXIT_SUCCESS;
} else {
qWarning("Could not open session [%s].", qUtf8Printable(sessionJson.fileName()));
}
- } else
- app.sendMessage(Session::toJsonObject(profile.value(), urls));
-
- if(isSingleInstance)
- return app.exec();
- else
+ } else {
+ auto message = Session::toJsonObject(profile.value(), urls);
+ qDebug("sending message: %s", app.sendMessage(QJsonDocument(message).toJson()) ? "okay" : "failed");
+ qDebug("message>>>\n%s", qUtf8Printable(QJsonDocument(message).toJson()));
return EXIT_SUCCESS;
+ }
+
+ return app.exec();
}
diff --git a/src/profilemanager.cpp b/src/profilemanager.cpp
index 1038598..bb967b9 100644
--- a/src/profilemanager.cpp
+++ b/src/profilemanager.cpp
@@ -80,7 +80,7 @@ WebProfile *ProfileManager::profile(const QString &id)
if(profiles.contains(id))
return profiles.value(id);
else
- return nullptr;
+ return WebProfile::defaultProfile();
}
const QMap<QString, WebProfile *> &ProfileManager::profileList()
diff --git a/src/singleapplication.cpp b/src/singleapplication.cpp
deleted file mode 100644
index 3c442b4..0000000
--- a/src/singleapplication.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * This file is part of smolbote. It's copyrighted by the contributors recorded
- * in the version control history of the file, available from its original
- * location: https://neueland.iserlohn-fortress.net/smolbote.hg
- *
- * SPDX-License-Identifier: GPL-3.0
- */
-
-#include "singleapplication.h"
-#include <QDataStream>
-#include <QLocalServer>
-#include <QLocalSocket>
-#include <QJsonObject>
-#include <QJsonDocument>
-
-SingleApplication::SingleApplication(int &argc, char **argv)
- : QApplication(argc, argv)
-{
-}
-
-SingleApplication::~SingleApplication()
-{
- if(m_localServer != nullptr) {
- if(m_localServer->isListening()) {
- m_localServer->close();
- QLocalServer::removeServer(LOCALSERVER_KEY);
- }
- }
-}
-
-/**
- * @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)
-{
- LOCALSERVER_KEY = name;
-
- QLocalSocket socket;
- socket.connectToServer(LOCALSERVER_KEY);
-
- if(socket.waitForConnected(LOCALSERVER_TIMEOUT)) {
- // another server is running
- socket.close();
- 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::receiveMessage);
-
- // no other server
- QLocalServer::removeServer(LOCALSERVER_KEY);
- return m_localServer->listen(LOCALSERVER_KEY);
- }
-}
-
-QString SingleApplication::serverName() const
-{
- Q_CHECK_PTR(m_localServer);
- return m_localServer->fullServerName();
-}
-
-int SingleApplication::sendMessage(const QByteArray &data)
-{
- QLocalSocket socket;
- socket.connectToServer(LOCALSERVER_KEY);
- if(socket.waitForConnected(LOCALSERVER_TIMEOUT)) {
- socket.write(data);
- socket.waitForBytesWritten(LOCALSERVER_TIMEOUT);
- return EXIT_SUCCESS;
- }
- return EXIT_FAILURE;
-}
-
-int SingleApplication::sendMessage(const QJsonObject &message)
-{
- QLocalSocket socket;
- socket.connectToServer(LOCALSERVER_KEY);
- if(socket.waitForConnected(LOCALSERVER_TIMEOUT)) {
- socket.write(QJsonDocument(message).toJson());
- socket.waitForBytesWritten(LOCALSERVER_TIMEOUT);
- return EXIT_SUCCESS;
- }
-
- return EXIT_FAILURE;
-}
-
-void SingleApplication::receiveMessage()
-{
- QLocalSocket *socket = m_localServer->nextPendingConnection();
- if(socket == nullptr) {
- return;
- }
-
- socket->waitForReadyRead();
- auto message = socket->readAll();
-
- if(message.isEmpty())
- return;
-
-#ifdef QT_DEBUG
- qDebug("received message: %s", qUtf8Printable(message));
-#endif
-
- QJsonDocument doc = QJsonDocument::fromJson(message);
- emit messageAvailable(doc.object());
-}
diff --git a/src/singleapplication.h b/src/singleapplication.h
deleted file mode 100644
index 84c0fda..0000000
--- a/src/singleapplication.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * This file is part of smolbote. It's copyrighted by the contributors recorded
- * in the version control history of the file, available from its original
- * location: https://neueland.iserlohn-fortress.net/smolbote.hg
- *
- * SPDX-License-Identifier: GPL-3.0
- */
-
-#ifndef SMOLBOTE_SINGLEAPPLICATION_H
-#define SMOLBOTE_SINGLEAPPLICATION_H
-
-#include <QApplication>
-
-class QLocalServer;
-class SingleApplication : public QApplication
-{
- Q_OBJECT
-
-public:
- explicit SingleApplication(int &argc, char **argv);
- ~SingleApplication() override;
-
- bool bindLocalSocket(const QString &name);
- QString serverName() const;
-
- int sendMessage(const QByteArray &data);
- int sendMessage(const QJsonObject &message);
-
-signals:
- void messageAvailable(const QJsonObject &object);
-
-private slots:
- void receiveMessage();
-
-private:
- const int LOCALSERVER_TIMEOUT = 500;
- QString LOCALSERVER_KEY;
-
- QLocalServer *m_localServer = nullptr;
-};
-
-#endif // SMOLBOTE_SINGLEAPPLICATION_H