diff --git a/third_party/SingleApplication-3.3.0/.github/FUNDING.yml b/third_party/SingleApplication-3.3.0/.github/FUNDING.yml
new file mode 100644
index 00000000..3ca4d97a
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/.github/FUNDING.yml
@@ -0,0 +1 @@
+github: itay-grudev
diff --git a/third_party/SingleApplication-3.3.0/.github/workflows/build-cmake.yml b/third_party/SingleApplication-3.3.0/.github/workflows/build-cmake.yml
new file mode 100644
index 00000000..6344b504
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/.github/workflows/build-cmake.yml
@@ -0,0 +1,56 @@
+name: "CI: Build Test"
+
+on: [push, pull_request]
+
+jobs:
+ build:
+
+ strategy:
+ matrix:
+ qt_version: [5.12.6, 5.13.2, 5.14.0, 5.15.0, 6.0.0]
+ platform: [ubuntu-20.04, windows-latest, macos-latest]
+ include:
+ - qt_version: 6.0.0
+ additional_arguments: -D QT_DEFAULT_MAJOR_VERSION=6
+ - platform: ubuntu-20.04
+ CXXFLAGS: -Wall -Wextra -pedantic -Werror
+ - platform: macos-latest
+ CXXFLAGS: -Wall -Wextra -pedantic -Werror
+ - platform: windows-latest
+ CXXFLAGS: /W4 /WX
+
+ runs-on: ${{ matrix.platform }}
+ env:
+ CXXFLAGS: ${{ matrix.CXXFLAGS }}
+
+ steps:
+ - uses: actions/checkout@v2.3.4
+
+ - name: Install Qt
+ uses: jurplel/install-qt-action@v2.11.1
+ with:
+ version: ${{ matrix.qt_version }}
+
+ - name: cmake
+ run: cmake . ${{ matrix.additional_arguments }}
+
+ - name: cmake build
+ run: cmake --build .
+
+ - name: Build example - basic (cmake)
+ working-directory: examples/basic/
+ run: |
+ cmake . ${{ matrix.additional_arguments }}
+ cmake --build .
+
+ - name: Build example - calculator (cmake)
+ working-directory: examples/calculator/
+ run: |
+ cmake . ${{ matrix.additional_arguments }}
+ cmake --build .
+
+ - name: Build example - sending_arguments (cmake)
+ working-directory: examples/sending_arguments/
+ run: |
+ cmake . ${{ matrix.additional_arguments }}
+ cmake --build .
diff --git a/third_party/SingleApplication-3.2.0-dc8042b/.gitignore b/third_party/SingleApplication-3.3.0/.gitignore
index 35533fe8..35533fe8 100644
--- a/third_party/SingleApplication-3.2.0-dc8042b/.gitignore
+++ b/third_party/SingleApplication-3.3.0/.gitignore
diff --git a/third_party/SingleApplication-3.2.0-dc8042b/CHANGELOG.md b/third_party/SingleApplication-3.3.0/CHANGELOG.md
index e2ba290e..51669b90 100644
--- a/third_party/SingleApplication-3.2.0-dc8042b/CHANGELOG.md
+++ b/third_party/SingleApplication-3.3.0/CHANGELOG.md
@@ -3,6 +3,12 @@ Changelog
If by accident I have forgotten to credit someone in the CHANGELOG, email me and I will fix it.
+
+__3.3.0__
+---------
+
+* Fixed message fragmentation issue causing crashes and incorrectly and inconsistently received messages. - _Nils Jeisecke_
+
__3.2.0__
---------
diff --git a/third_party/SingleApplication-3.2.0-dc8042b/CMakeLists.txt b/third_party/SingleApplication-3.3.0/CMakeLists.txt
index ae1b1439..ae1b1439 100644
--- a/third_party/SingleApplication-3.2.0-dc8042b/CMakeLists.txt
+++ b/third_party/SingleApplication-3.3.0/CMakeLists.txt
diff --git a/third_party/SingleApplication-3.2.0-dc8042b/LICENSE b/third_party/SingleApplication-3.3.0/LICENSE
index a82e5a68..a82e5a68 100644
--- a/third_party/SingleApplication-3.2.0-dc8042b/LICENSE
+++ b/third_party/SingleApplication-3.3.0/LICENSE
diff --git a/third_party/SingleApplication-3.2.0-dc8042b/README.md b/third_party/SingleApplication-3.3.0/README.md
index 457ab339..457ab339 100644
--- a/third_party/SingleApplication-3.2.0-dc8042b/README.md
+++ b/third_party/SingleApplication-3.3.0/README.md
diff --git a/third_party/SingleApplication-3.2.0-dc8042b/SingleApplication b/third_party/SingleApplication-3.3.0/SingleApplication
index 8ead1a42..8ead1a42 100644
--- a/third_party/SingleApplication-3.2.0-dc8042b/SingleApplication
+++ b/third_party/SingleApplication-3.3.0/SingleApplication
diff --git a/third_party/SingleApplication-3.2.0-dc8042b/Windows.md b/third_party/SingleApplication-3.3.0/Windows.md
index 13c52da0..13c52da0 100644
--- a/third_party/SingleApplication-3.2.0-dc8042b/Windows.md
+++ b/third_party/SingleApplication-3.3.0/Windows.md
diff --git a/third_party/SingleApplication-3.3.0/examples/basic/CMakeLists.txt b/third_party/SingleApplication-3.3.0/examples/basic/CMakeLists.txt
new file mode 100644
index 00000000..c1429230
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/examples/basic/CMakeLists.txt
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 3.7.0)
+
+project(basic LANGUAGES CXX)
+
+# SingleApplication base class
+set(QAPPLICATION_CLASS QCoreApplication)
+add_subdirectory(../.. SingleApplication)
+
+add_executable(basic main.cpp)
+
+target_link_libraries(${PROJECT_NAME} SingleApplication::SingleApplication)
+
diff --git a/third_party/SingleApplication-3.3.0/examples/basic/basic.pro b/third_party/SingleApplication-3.3.0/examples/basic/basic.pro
new file mode 100755
index 00000000..b7af16cf
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/examples/basic/basic.pro
@@ -0,0 +1,5 @@
+# Single Application implementation
+include(../../singleapplication.pri)
+DEFINES += QAPPLICATION_CLASS=QCoreApplication
+
+SOURCES += main.cpp
diff --git a/third_party/SingleApplication-3.3.0/examples/basic/main.cpp b/third_party/SingleApplication-3.3.0/examples/basic/main.cpp
new file mode 100755
index 00000000..b2092c6d
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/examples/basic/main.cpp
@@ -0,0 +1,10 @@
+#include <singleapplication.h>
+
+int main(int argc, char *argv[])
+{
+ SingleApplication app( argc, argv );
+
+ qWarning() << "Started a new instance";
+
+ return app.exec();
+}
diff --git a/third_party/SingleApplication-3.3.0/examples/calculator/CMakeLists.txt b/third_party/SingleApplication-3.3.0/examples/calculator/CMakeLists.txt
new file mode 100644
index 00000000..82305f04
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/examples/calculator/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 3.7.0)
+
+project(calculator LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+# SingleApplication base class
+set(QAPPLICATION_CLASS QApplication)
+add_subdirectory(../.. SingleApplication)
+
+find_package(Qt${QT_DEFAULT_MAJOR_VERSION} COMPONENTS Core REQUIRED)
+
+add_executable(${PROJECT_NAME}
+ button.h
+ calculator.h
+ button.cpp
+ calculator.cpp
+ main.cpp
+)
+
+target_link_libraries(${PROJECT_NAME} SingleApplication::SingleApplication)
diff --git a/third_party/SingleApplication-3.3.0/examples/calculator/button.cpp b/third_party/SingleApplication-3.3.0/examples/calculator/button.cpp
new file mode 100644
index 00000000..d6cca0a0
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/examples/calculator/button.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtWidgets>
+
+#include "button.h"
+
+//! [0]
+Button::Button(const QString &text, QWidget *parent)
+ : QToolButton(parent)
+{
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ setText(text);
+}
+//! [0]
+
+//! [1]
+QSize Button::sizeHint() const
+//! [1] //! [2]
+{
+ QSize size = QToolButton::sizeHint();
+ size.rheight() += 20;
+ size.rwidth() = qMax(size.width(), size.height());
+ return size;
+}
+//! [2]
diff --git a/third_party/SingleApplication-3.3.0/examples/calculator/button.h b/third_party/SingleApplication-3.3.0/examples/calculator/button.h
new file mode 100644
index 00000000..2c014c7b
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/examples/calculator/button.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef BUTTON_H
+#define BUTTON_H
+
+#include <QToolButton>
+
+//! [0]
+class Button : public QToolButton
+{
+ Q_OBJECT
+
+public:
+ explicit Button(const QString &text, QWidget *parent = 0);
+
+ QSize sizeHint() const Q_DECL_OVERRIDE;
+};
+//! [0]
+
+#endif
diff --git a/third_party/SingleApplication-3.3.0/examples/calculator/calculator.cpp b/third_party/SingleApplication-3.3.0/examples/calculator/calculator.cpp
new file mode 100644
index 00000000..3d34c2a7
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/examples/calculator/calculator.cpp
@@ -0,0 +1,406 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtWidgets>
+
+#include <cmath>
+
+#include "button.h"
+#include "calculator.h"
+
+//! [0]
+Calculator::Calculator(QWidget *parent)
+ : QWidget(parent)
+{
+ sumInMemory = 0.0;
+ sumSoFar = 0.0;
+ factorSoFar = 0.0;
+ waitingForOperand = true;
+//! [0]
+
+//! [1]
+ display = new QLineEdit("0");
+//! [1] //! [2]
+ display->setReadOnly(true);
+ display->setAlignment(Qt::AlignRight);
+ display->setMaxLength(15);
+
+ QFont font = display->font();
+ font.setPointSize(font.pointSize() + 8);
+ display->setFont(font);
+//! [2]
+
+//! [4]
+ for (int i = 0; i < NumDigitButtons; ++i) {
+ digitButtons[i] = createButton(QString::number(i), SLOT(digitClicked()));
+ }
+
+ Button *pointButton = createButton(".", SLOT(pointClicked()));
+ Button *changeSignButton = createButton("\302\261", SLOT(changeSignClicked()));
+
+ Button *backspaceButton = createButton("Backspace", SLOT(backspaceClicked()));
+ Button *clearButton = createButton("Clear", SLOT(clear()));
+ Button *clearAllButton = createButton("Clear All", SLOT(clearAll()));
+
+ Button *clearMemoryButton = createButton("MC", SLOT(clearMemory()));
+ Button *readMemoryButton = createButton("MR", SLOT(readMemory()));
+ Button *setMemoryButton = createButton("MS", SLOT(setMemory()));
+ Button *addToMemoryButton = createButton("M+", SLOT(addToMemory()));
+
+ Button *divisionButton = createButton("\303\267", SLOT(multiplicativeOperatorClicked()));
+ Button *timesButton = createButton("\303\227", SLOT(multiplicativeOperatorClicked()));
+ Button *minusButton = createButton("-", SLOT(additiveOperatorClicked()));
+ Button *plusButton = createButton("+", SLOT(additiveOperatorClicked()));
+
+ Button *squareRootButton = createButton("Sqrt", SLOT(unaryOperatorClicked()));
+ Button *powerButton = createButton("x\302\262", SLOT(unaryOperatorClicked()));
+ Button *reciprocalButton = createButton("1/x", SLOT(unaryOperatorClicked()));
+ Button *equalButton = createButton("=", SLOT(equalClicked()));
+//! [4]
+
+//! [5]
+ QGridLayout *mainLayout = new QGridLayout;
+//! [5] //! [6]
+ mainLayout->setSizeConstraint(QLayout::SetFixedSize);
+ mainLayout->addWidget(display, 0, 0, 1, 6);
+ mainLayout->addWidget(backspaceButton, 1, 0, 1, 2);
+ mainLayout->addWidget(clearButton, 1, 2, 1, 2);
+ mainLayout->addWidget(clearAllButton, 1, 4, 1, 2);
+
+ mainLayout->addWidget(clearMemoryButton, 2, 0);
+ mainLayout->addWidget(readMemoryButton, 3, 0);
+ mainLayout->addWidget(setMemoryButton, 4, 0);
+ mainLayout->addWidget(addToMemoryButton, 5, 0);
+
+ for (int i = 1; i < NumDigitButtons; ++i) {
+ int row = ((9 - i) / 3) + 2;
+ int column = ((i - 1) % 3) + 1;
+ mainLayout->addWidget(digitButtons[i], row, column);
+ }
+
+ mainLayout->addWidget(digitButtons[0], 5, 1);
+ mainLayout->addWidget(pointButton, 5, 2);
+ mainLayout->addWidget(changeSignButton, 5, 3);
+
+ mainLayout->addWidget(divisionButton, 2, 4);
+ mainLayout->addWidget(timesButton, 3, 4);
+ mainLayout->addWidget(minusButton, 4, 4);
+ mainLayout->addWidget(plusButton, 5, 4);
+
+ mainLayout->addWidget(squareRootButton, 2, 5);
+ mainLayout->addWidget(powerButton, 3, 5);
+ mainLayout->addWidget(reciprocalButton, 4, 5);
+ mainLayout->addWidget(equalButton, 5, 5);
+ setLayout(mainLayout);
+
+ setWindowTitle("Calculator");
+}
+//! [6]
+
+//! [7]
+void Calculator::digitClicked()
+{
+ Button *clickedButton = qobject_cast<Button *>(sender());
+ int digitValue = clickedButton->text().toInt();
+ if (display->text() == "0" && digitValue == 0.0)
+ return;
+
+ if (waitingForOperand) {
+ display->clear();
+ waitingForOperand = false;
+ }
+ display->setText(display->text() + QString::number(digitValue));
+}
+//! [7]
+
+//! [8]
+void Calculator::unaryOperatorClicked()
+//! [8] //! [9]
+{
+ Button *clickedButton = qobject_cast<Button *>(sender());
+ QString clickedOperator = clickedButton->text();
+ double operand = display->text().toDouble();
+ double result = 0.0;
+
+ if (clickedOperator == "Sqrt") {
+ if (operand < 0.0) {
+ abortOperation();
+ return;
+ }
+ result = std::sqrt(operand);
+ } else if (clickedOperator == "x\302\262") {
+ result = std::pow(operand, 2.0);
+ } else if (clickedOperator == "1/x") {
+ if (operand == 0.0) {
+ abortOperation();
+ return;
+ }
+ result = 1.0 / operand;
+ }
+ display->setText(QString::number(result));
+ waitingForOperand = true;
+}
+//! [9]
+
+//! [10]
+void Calculator::additiveOperatorClicked()
+//! [10] //! [11]
+{
+ Button *clickedButton = qobject_cast<Button *>(sender());
+ QString clickedOperator = clickedButton->text();
+ double operand = display->text().toDouble();
+
+//! [11] //! [12]
+ if (!pendingMultiplicativeOperator.isEmpty()) {
+//! [12] //! [13]
+ if (!calculate(operand, pendingMultiplicativeOperator)) {
+ abortOperation();
+ return;
+ }
+ display->setText(QString::number(factorSoFar));
+ operand = factorSoFar;
+ factorSoFar = 0.0;
+ pendingMultiplicativeOperator.clear();
+ }
+
+//! [13] //! [14]
+ if (!pendingAdditiveOperator.isEmpty()) {
+//! [14] //! [15]
+ if (!calculate(operand, pendingAdditiveOperator)) {
+ abortOperation();
+ return;
+ }
+ display->setText(QString::number(sumSoFar));
+ } else {
+ sumSoFar = operand;
+ }
+
+//! [15] //! [16]
+ pendingAdditiveOperator = clickedOperator;
+//! [16] //! [17]
+ waitingForOperand = true;
+}
+//! [17]
+
+//! [18]
+void Calculator::multiplicativeOperatorClicked()
+{
+ Button *clickedButton = qobject_cast<Button *>(sender());
+ QString clickedOperator = clickedButton->text();
+ double operand = display->text().toDouble();
+
+ if (!pendingMultiplicativeOperator.isEmpty()) {
+ if (!calculate(operand, pendingMultiplicativeOperator)) {
+ abortOperation();
+ return;
+ }
+ display->setText(QString::number(factorSoFar));
+ } else {
+ factorSoFar = operand;
+ }
+
+ pendingMultiplicativeOperator = clickedOperator;
+ waitingForOperand = true;
+}
+//! [18]
+
+//! [20]
+void Calculator::equalClicked()
+{
+ double operand = display->text().toDouble();
+
+ if (!pendingMultiplicativeOperator.isEmpty()) {
+ if (!calculate(operand, pendingMultiplicativeOperator)) {
+ abortOperation();
+ return;
+ }
+ operand = factorSoFar;
+ factorSoFar = 0.0;
+ pendingMultiplicativeOperator.clear();
+ }
+ if (!pendingAdditiveOperator.isEmpty()) {
+ if (!calculate(operand, pendingAdditiveOperator)) {
+ abortOperation();
+ return;
+ }
+ pendingAdditiveOperator.clear();
+ } else {
+ sumSoFar = operand;
+ }
+
+ display->setText(QString::number(sumSoFar));
+ sumSoFar = 0.0;
+ waitingForOperand = true;
+}
+//! [20]
+
+//! [22]
+void Calculator::pointClicked()
+{
+ if (waitingForOperand)
+ display->setText("0");
+ if (!display->text().contains('.'))
+ display->setText(display->text() + ".");
+ waitingForOperand = false;
+}
+//! [22]
+
+//! [24]
+void Calculator::changeSignClicked()
+{
+ QString text = display->text();
+ double value = text.toDouble();
+
+ if (value > 0.0) {
+ text.prepend("-");
+ } else if (value < 0.0) {
+ text.remove(0, 1);
+ }
+ display->setText(text);
+}
+//! [24]
+
+//! [26]
+void Calculator::backspaceClicked()
+{
+ if (waitingForOperand)
+ return;
+
+ QString text = display->text();
+ text.chop(1);
+ if (text.isEmpty()) {
+ text = "0";
+ waitingForOperand = true;
+ }
+ display->setText(text);
+}
+//! [26]
+
+//! [28]
+void Calculator::clear()
+{
+ if (waitingForOperand)
+ return;
+
+ display->setText("0");
+ waitingForOperand = true;
+}
+//! [28]
+
+//! [30]
+void Calculator::clearAll()
+{
+ sumSoFar = 0.0;
+ factorSoFar = 0.0;
+ pendingAdditiveOperator.clear();
+ pendingMultiplicativeOperator.clear();
+ display->setText("0");
+ waitingForOperand = true;
+}
+//! [30]
+
+//! [32]
+void Calculator::clearMemory()
+{
+ sumInMemory = 0.0;
+}
+
+void Calculator::readMemory()
+{
+ display->setText(QString::number(sumInMemory));
+ waitingForOperand = true;
+}
+
+void Calculator::setMemory()
+{
+ equalClicked();
+ sumInMemory = display->text().toDouble();
+}
+
+void Calculator::addToMemory()
+{
+ equalClicked();
+ sumInMemory += display->text().toDouble();
+}
+//! [32]
+//! [34]
+Button *Calculator::createButton(const QString &text, const char *member)
+{
+ Button *button = new Button(text);
+ connect(button, SIGNAL(clicked()), this, member);
+ return button;
+}
+//! [34]
+
+//! [36]
+void Calculator::abortOperation()
+{
+ clearAll();
+ display->setText("####");
+}
+//! [36]
+
+//! [38]
+bool Calculator::calculate(double rightOperand, const QString &pendingOperator)
+{
+ if (pendingOperator == "+") {
+ sumSoFar += rightOperand;
+ } else if (pendingOperator == "-") {
+ sumSoFar -= rightOperand;
+ } else if (pendingOperator == "\303\227") {
+ factorSoFar *= rightOperand;
+ } else if (pendingOperator == "\303\267") {
+ if (rightOperand == 0.0)
+ return false;
+ factorSoFar /= rightOperand;
+ }
+ return true;
+}
+//! [38]
diff --git a/third_party/SingleApplication-3.3.0/examples/calculator/calculator.h b/third_party/SingleApplication-3.3.0/examples/calculator/calculator.h
new file mode 100644
index 00000000..250a2f3e
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/examples/calculator/calculator.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CALCULATOR_H
+#define CALCULATOR_H
+
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QLineEdit;
+QT_END_NAMESPACE
+class Button;
+
+//! [0]
+class Calculator : public QWidget
+{
+ Q_OBJECT
+
+public:
+ Calculator(QWidget *parent = 0);
+
+private slots:
+ void digitClicked();
+ void unaryOperatorClicked();
+ void additiveOperatorClicked();
+ void multiplicativeOperatorClicked();
+ void equalClicked();
+ void pointClicked();
+ void changeSignClicked();
+ void backspaceClicked();
+ void clear();
+ void clearAll();
+ void clearMemory();
+ void readMemory();
+ void setMemory();
+ void addToMemory();
+//! [0]
+
+//! [1]
+private:
+//! [1] //! [2]
+ Button *createButton(const QString &text, const char *member);
+ void abortOperation();
+ bool calculate(double rightOperand, const QString &pendingOperator);
+//! [2]
+
+//! [3]
+ double sumInMemory;
+//! [3] //! [4]
+ double sumSoFar;
+//! [4] //! [5]
+ double factorSoFar;
+//! [5] //! [6]
+ QString pendingAdditiveOperator;
+//! [6] //! [7]
+ QString pendingMultiplicativeOperator;
+//! [7] //! [8]
+ bool waitingForOperand;
+//! [8]
+
+//! [9]
+ QLineEdit *display;
+//! [9] //! [10]
+
+ enum { NumDigitButtons = 10 };
+ Button *digitButtons[NumDigitButtons];
+};
+//! [10]
+
+#endif
diff --git a/third_party/SingleApplication-3.3.0/examples/calculator/calculator.pro b/third_party/SingleApplication-3.3.0/examples/calculator/calculator.pro
new file mode 100644
index 00000000..8f132609
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/examples/calculator/calculator.pro
@@ -0,0 +1,11 @@
+QT += widgets
+
+HEADERS = button.h \
+ calculator.h
+SOURCES = button.cpp \
+ calculator.cpp \
+ main.cpp
+
+# Single Application implementation
+include(../../singleapplication.pri)
+DEFINES += QAPPLICATION_CLASS=QApplication
diff --git a/third_party/SingleApplication-3.3.0/examples/calculator/main.cpp b/third_party/SingleApplication-3.3.0/examples/calculator/main.cpp
new file mode 100644
index 00000000..d45438f4
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/examples/calculator/main.cpp
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QApplication>
+
+#include <singleapplication.h>
+
+#include "calculator.h"
+
+int main(int argc, char *argv[])
+{
+ SingleApplication app(argc, argv);
+
+ Calculator calc;
+
+ QObject::connect( &app, &SingleApplication::instanceStarted, [ &calc ]() {
+ calc.raise();
+ calc.activateWindow();
+ });
+
+ calc.show();
+
+ return app.exec();
+}
diff --git a/third_party/SingleApplication-3.3.0/examples/sending_arguments/CMakeLists.txt b/third_party/SingleApplication-3.3.0/examples/sending_arguments/CMakeLists.txt
new file mode 100644
index 00000000..2cc55975
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/examples/sending_arguments/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.7.0)
+
+project(sending_arguments LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+# SingleApplication base class
+set(QAPPLICATION_CLASS QCoreApplication)
+add_subdirectory(../.. SingleApplication)
+
+find_package(Qt${QT_DEFAULT_MAJOR_VERSION} COMPONENTS Core REQUIRED)
+
+add_executable(${PROJECT_NAME}
+ main.cpp
+ messagereceiver.cpp
+ messagereceiver.h
+ main.cpp
+)
+
+target_link_libraries(${PROJECT_NAME} SingleApplication::SingleApplication)
diff --git a/third_party/SingleApplication-3.3.0/examples/sending_arguments/main.cpp b/third_party/SingleApplication-3.3.0/examples/sending_arguments/main.cpp
new file mode 100755
index 00000000..a9d34dd9
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/examples/sending_arguments/main.cpp
@@ -0,0 +1,28 @@
+#include <singleapplication.h>
+#include "messagereceiver.h"
+
+int main(int argc, char *argv[])
+{
+ // Allow secondary instances
+ SingleApplication app( argc, argv, true );
+
+ MessageReceiver msgReceiver;
+
+ // If this is a secondary instance
+ if( app.isSecondary() ) {
+ app.sendMessage( app.arguments().join(' ').toUtf8() );
+ qDebug() << "App already running.";
+ qDebug() << "Primary instance PID: " << app.primaryPid();
+ qDebug() << "Primary instance user: " << app.primaryUser();
+ return 0;
+ } else {
+ QObject::connect(
+ &app,
+ &SingleApplication::receivedMessage,
+ &msgReceiver,
+ &MessageReceiver::receivedMessage
+ );
+ }
+
+ return app.exec();
+}
diff --git a/third_party/SingleApplication-3.3.0/examples/sending_arguments/messagereceiver.cpp b/third_party/SingleApplication-3.3.0/examples/sending_arguments/messagereceiver.cpp
new file mode 100644
index 00000000..0560b072
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/examples/sending_arguments/messagereceiver.cpp
@@ -0,0 +1,12 @@
+#include <QDebug>
+#include "messagereceiver.h"
+
+MessageReceiver::MessageReceiver(QObject *parent) : QObject(parent)
+{
+}
+
+void MessageReceiver::receivedMessage(int instanceId, QByteArray message)
+{
+ qDebug() << "Received message from instance: " << instanceId;
+ qDebug() << "Message Text: " << message;
+}
diff --git a/third_party/SingleApplication-3.3.0/examples/sending_arguments/messagereceiver.h b/third_party/SingleApplication-3.3.0/examples/sending_arguments/messagereceiver.h
new file mode 100644
index 00000000..50a970c8
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/examples/sending_arguments/messagereceiver.h
@@ -0,0 +1,15 @@
+#ifndef MESSAGERECEIVER_H
+#define MESSAGERECEIVER_H
+
+#include <QObject>
+
+class MessageReceiver : public QObject
+{
+ Q_OBJECT
+public:
+ explicit MessageReceiver(QObject *parent = 0);
+public slots:
+ void receivedMessage( int instanceId, QByteArray message );
+};
+
+#endif // MESSAGERECEIVER_H
diff --git a/third_party/SingleApplication-3.3.0/examples/sending_arguments/sending_arguments.pro b/third_party/SingleApplication-3.3.0/examples/sending_arguments/sending_arguments.pro
new file mode 100755
index 00000000..897636a9
--- /dev/null
+++ b/third_party/SingleApplication-3.3.0/examples/sending_arguments/sending_arguments.pro
@@ -0,0 +1,9 @@
+# Single Application implementation
+include(../../singleapplication.pri)
+DEFINES += QAPPLICATION_CLASS=QCoreApplication
+
+SOURCES += main.cpp \
+ messagereceiver.cpp
+
+HEADERS += \
+ messagereceiver.h
diff --git a/third_party/SingleApplication-3.2.0-dc8042b/singleapplication.cpp b/third_party/SingleApplication-3.3.0/singleapplication.cpp
index 276ceee9..09e264ef 100644
--- a/third_party/SingleApplication-3.2.0-dc8042b/singleapplication.cpp
+++ b/third_party/SingleApplication-3.3.0/singleapplication.cpp
@@ -36,7 +36,7 @@
* @param options Optional flags to toggle specific behaviour
* @param timeout Maximum time blocking functions are allowed during app load
*/
-SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout, QString userData )
+SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout, const QString &userData )
: app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
{
Q_D( SingleApplication );
@@ -172,9 +172,9 @@ SingleApplication::~SingleApplication()
* Checks if the current application instance is primary.
* @return Returns true if the instance is primary, false otherwise.
*/
-bool SingleApplication::isPrimary()
+bool SingleApplication::isPrimary() const
{
- Q_D( SingleApplication );
+ Q_D( const SingleApplication );
return d->server != nullptr;
}
@@ -182,9 +182,9 @@ bool SingleApplication::isPrimary()
* Checks if the current application instance is secondary.
* @return Returns true if the instance is secondary, false otherwise.
*/
-bool SingleApplication::isSecondary()
+bool SingleApplication::isSecondary() const
{
- Q_D( SingleApplication );
+ Q_D( const SingleApplication );
return d->server == nullptr;
}
@@ -194,9 +194,9 @@ bool SingleApplication::isSecondary()
* only incremented afterwards.
* @return Returns a unique instance id.
*/
-quint32 SingleApplication::instanceId()
+quint32 SingleApplication::instanceId() const
{
- Q_D( SingleApplication );
+ Q_D( const SingleApplication );
return d->instanceNumber;
}
@@ -206,9 +206,9 @@ quint32 SingleApplication::instanceId()
* specific APIs.
* @return Returns the primary instance PID.
*/
-qint64 SingleApplication::primaryPid()
+qint64 SingleApplication::primaryPid() const
{
- Q_D( SingleApplication );
+ Q_D( const SingleApplication );
return d->primaryPid();
}
@@ -216,9 +216,9 @@ qint64 SingleApplication::primaryPid()
* Returns the username the primary instance is running as.
* @return Returns the username the primary instance is running as.
*/
-QString SingleApplication::primaryUser()
+QString SingleApplication::primaryUser() const
{
- Q_D( SingleApplication );
+ Q_D( const SingleApplication );
return d->primaryUser();
}
@@ -226,7 +226,7 @@ QString SingleApplication::primaryUser()
* Returns the username the current instance is running as.
* @return Returns the username the current instance is running as.
*/
-QString SingleApplication::currentUser()
+QString SingleApplication::currentUser() const
{
return SingleApplicationPrivate::getUsername();
}
@@ -248,10 +248,7 @@ bool SingleApplication::sendMessage( const QByteArray &message, int timeout )
if( ! d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ) )
return false;
- d->socket->write( message );
- bool dataWritten = d->socket->waitForBytesWritten( timeout );
- d->socket->flush();
- return dataWritten;
+ return d->writeConfirmedMessage( timeout, message );
}
/**
@@ -267,8 +264,8 @@ void SingleApplication::abortSafely()
::exit( EXIT_FAILURE );
}
-QStringList SingleApplication::userData()
+QStringList SingleApplication::userData() const
{
- Q_D( SingleApplication );
+ Q_D( const SingleApplication );
return d->appData();
}
diff --git a/third_party/SingleApplication-3.2.0-dc8042b/singleapplication.h b/third_party/SingleApplication-3.3.0/singleapplication.h
index d39a6614..91cabf93 100644
--- a/third_party/SingleApplication-3.2.0-dc8042b/singleapplication.h
+++ b/third_party/SingleApplication-3.3.0/singleapplication.h
@@ -85,44 +85,44 @@ public:
* 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 = 1000, QString userData = QString() );
+ explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000, const QString &userData = {} );
~SingleApplication() override;
/**
* @brief Returns if the instance is the primary instance
* @returns {bool}
*/
- bool isPrimary();
+ bool isPrimary() const;
/**
* @brief Returns if the instance is a secondary instance
* @returns {bool}
*/
- bool isSecondary();
+ bool isSecondary() const;
/**
* @brief Returns a unique identifier for the current instance
* @returns {qint32}
*/
- quint32 instanceId();
+ quint32 instanceId() const;
/**
* @brief Returns the process ID (PID) of the primary instance
* @returns {qint64}
*/
- qint64 primaryPid();
+ qint64 primaryPid() const;
/**
* @brief Returns the username of the user running the primary instance
* @returns {QString}
*/
- QString primaryUser();
+ QString primaryUser() const;
/**
* @brief Returns the username of the current user
* @returns {QString}
*/
- QString currentUser();
+ QString currentUser() const;
/**
* @brief Sends a message to the primary instance. Returns true on success.
@@ -137,7 +137,7 @@ public:
* @brief Get the set user data.
* @returns {QStringList}
*/
- QStringList userData();
+ QStringList userData() const;
Q_SIGNALS:
void instanceStarted();
diff --git a/third_party/SingleApplication-3.2.0-dc8042b/singleapplication.pri b/third_party/SingleApplication-3.3.0/singleapplication.pri
index ae81f599..ae81f599 100644
--- a/third_party/SingleApplication-3.2.0-dc8042b/singleapplication.pri
+++ b/third_party/SingleApplication-3.3.0/singleapplication.pri
diff --git a/third_party/SingleApplication-3.2.0-dc8042b/singleapplication_p.cpp b/third_party/SingleApplication-3.3.0/singleapplication_p.cpp
index 1ab58c23..13397282 100644
--- a/third_party/SingleApplication-3.2.0-dc8042b/singleapplication_p.cpp
+++ b/third_party/SingleApplication-3.3.0/singleapplication_p.cpp
@@ -263,20 +263,46 @@ bool SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType conne
#endif
writeStream << checksum;
- // The header indicates the message length that follows
+ return writeConfirmedMessage( static_cast<int>(msecs - time.elapsed()), initMsg );
+}
+
+void SingleApplicationPrivate::writeAck( QLocalSocket *sock ) {
+ sock->putChar('\n');
+}
+
+bool SingleApplicationPrivate::writeConfirmedMessage (int msecs, const QByteArray &msg)
+{
+ QElapsedTimer time;
+ time.start();
+
+ // Frame 1: The header indicates the message length that follows
QByteArray header;
QDataStream headerStream(&header, QIODevice::WriteOnly);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
headerStream.setVersion(QDataStream::Qt_5_6);
#endif
- headerStream << static_cast <quint64>( initMsg.length() );
+ headerStream << static_cast <quint64>( msg.length() );
- socket->write( header );
- socket->write( initMsg );
- bool result = socket->waitForBytesWritten( static_cast<int>(msecs - time.elapsed()) );
+ if( ! writeConfirmedFrame( static_cast<int>(msecs - time.elapsed()), header ))
+ return false;
+
+ // Frame 2: The message
+ return writeConfirmedFrame( static_cast<int>(msecs - time.elapsed()), msg );
+}
+
+bool SingleApplicationPrivate::writeConfirmedFrame( int msecs, const QByteArray &msg )
+{
+ socket->write( msg );
socket->flush();
- return result;
+
+ bool result = socket->waitForReadyRead( msecs ); // await ack byte
+ if (result) {
+ socket->read( 1 );
+ return true;
+ }
+
+ return false;
}
quint16 SingleApplicationPrivate::blockChecksum() const
@@ -321,32 +347,36 @@ void SingleApplicationPrivate::slotConnectionEstablished()
QLocalSocket *nextConnSocket = server->nextPendingConnection();
connectionMap.insert(nextConnSocket, ConnectionInfo());
- QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
+ QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this,
[nextConnSocket, this](){
auto &info = connectionMap[nextConnSocket];
- Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
+ this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
}
);
- QObject::connect(nextConnSocket, &QLocalSocket::disconnected,
+ QObject::connect(nextConnSocket, &QLocalSocket::disconnected, nextConnSocket, &QLocalSocket::deleteLater);
+
+ QObject::connect(nextConnSocket, &QLocalSocket::destroyed, this,
[nextConnSocket, this](){
connectionMap.remove(nextConnSocket);
- nextConnSocket->deleteLater();
}
);
- QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
+ QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this,
[nextConnSocket, this](){
auto &info = connectionMap[nextConnSocket];
switch(info.stage){
- case StageHeader:
- readInitMessageHeader(nextConnSocket);
+ case StageInitHeader:
+ readMessageHeader( nextConnSocket, StageInitBody );
break;
- case StageBody:
+ case StageInitBody:
readInitMessageBody(nextConnSocket);
break;
- case StageConnected:
- Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId );
+ case StageConnectedHeader:
+ readMessageHeader( nextConnSocket, StageConnectedBody );
+ break;
+ case StageConnectedBody:
+ this->slotDataAvailable( nextConnSocket, info.instanceId );
break;
default:
break;
@@ -355,7 +385,7 @@ void SingleApplicationPrivate::slotConnectionEstablished()
);
}
-void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
+void SingleApplicationPrivate::readMessageHeader( QLocalSocket *sock, SingleApplicationPrivate::ConnectionStage nextStage )
{
if (!connectionMap.contains( sock )){
return;
@@ -375,29 +405,35 @@ void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
quint64 msgLen = 0;
headerStream >> msgLen;
ConnectionInfo &info = connectionMap[sock];
- info.stage = StageBody;
+ info.stage = nextStage;
info.msgLen = msgLen;
- if ( sock->bytesAvailable() >= (qint64) msgLen ){
- readInitMessageBody( sock );
- }
+ writeAck( sock );
}
-void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
+bool SingleApplicationPrivate::isFrameComplete( QLocalSocket *sock )
{
- Q_Q(SingleApplication);
-
if (!connectionMap.contains( sock )){
- return;
+ return false;
}
ConnectionInfo &info = connectionMap[sock];
if( sock->bytesAvailable() < ( qint64 )info.msgLen ){
- return;
+ return false;
}
+ return true;
+}
+
+void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
+{
+ Q_Q(SingleApplication);
+
+ if( !isFrameComplete( sock ) )
+ return;
+
// Read the message body
- QByteArray msgBytes = sock->read(info.msgLen);
+ QByteArray msgBytes = sock->readAll();
QDataStream readStream(msgBytes);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
@@ -437,8 +473,9 @@ void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
return;
}
+ ConnectionInfo &info = connectionMap[sock];
info.instanceId = instanceId;
- info.stage = StageConnected;
+ info.stage = StageConnectedHeader;
if( connectionType == NewInstance ||
( connectionType == SecondaryInstance &&
@@ -447,21 +484,28 @@ void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
Q_EMIT q->instanceStarted();
}
- if (sock->bytesAvailable() > 0){
- Q_EMIT this->slotDataAvailable( sock, instanceId );
- }
+ writeAck( sock );
}
void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
{
Q_Q(SingleApplication);
+
+ if ( !isFrameComplete( dataSocket ) )
+ return;
+
Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
+
+ writeAck( dataSocket );
+
+ ConnectionInfo &info = connectionMap[dataSocket];
+ info.stage = StageConnectedHeader;
}
void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
{
if( closedSocket->bytesAvailable() > 0 )
- Q_EMIT slotDataAvailable( closedSocket, instanceId );
+ slotDataAvailable( closedSocket, instanceId );
}
void SingleApplicationPrivate::randomSleep()
@@ -470,7 +514,7 @@ void SingleApplicationPrivate::randomSleep()
QThread::msleep( QRandomGenerator::global()->bounded( 8u, 18u ));
#else
qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() );
- QThread::msleep( 8 + static_cast <unsigned long>( static_cast <float>( qrand() ) / RAND_MAX * 10 ));
+ QThread::msleep( qrand() % 11 + 8);
#endif
}
diff --git a/third_party/SingleApplication-3.2.0-dc8042b/singleapplication_p.h b/third_party/SingleApplication-3.3.0/singleapplication_p.h
index c49a46dd..58507cf3 100644
--- a/third_party/SingleApplication-3.2.0-dc8042b/singleapplication_p.h
+++ b/third_party/SingleApplication-3.3.0/singleapplication_p.h
@@ -61,9 +61,10 @@ public:
Reconnect = 3
};
enum ConnectionStage : quint8 {
- StageHeader = 0,
- StageBody = 1,
- StageConnected = 2,
+ StageInitHeader = 0,
+ StageInitBody = 1,
+ StageConnectedHeader = 2,
+ StageConnectedBody = 3,
};
Q_DECLARE_PUBLIC(SingleApplication)
@@ -79,8 +80,12 @@ public:
quint16 blockChecksum() const;
qint64 primaryPid() const;
QString primaryUser() const;
- void readInitMessageHeader(QLocalSocket *socket);
+ bool isFrameComplete(QLocalSocket *sock);
+ void readMessageHeader(QLocalSocket *socket, ConnectionStage nextStage);
void readInitMessageBody(QLocalSocket *socket);
+ void writeAck(QLocalSocket *sock);
+ bool writeConfirmedFrame(int msecs, const QByteArray &msg);
+ bool writeConfirmedMessage(int msecs, const QByteArray &msg);
static void randomSleep();
void addAppData(const QString &data);
QStringList appData() const;
|