Move code to ./src

This commit is contained in:
lupoDharkael
2017-05-22 23:24:18 +02:00
parent 74cea599d9
commit 3593332781
59 changed files with 1956 additions and 36 deletions

View File

@@ -27,38 +27,38 @@ DEFINES += QT_DEPRECATED_WARNINGS
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
include(singleapplication/singleapplication.pri)
include(Qt-Color-Widgets//color_widgets.pri)
include(src/singleapplication/singleapplication.pri)
include(src/Qt-Color-Widgets//color_widgets.pri)
DEFINES += QAPPLICATION_CLASS=QApplication
SOURCES += main.cpp\
nativeeventfilter.cpp \
controller.cpp \
capture/button.cpp \
capture/buttonhandler.cpp \
infowindow.cpp \
config/configwindow.cpp \
capture/screenshot.cpp \
capture/capturewidget.cpp \
capture/capturemodification.cpp \
capture/colorpicker.cpp \
config/buttonlistview.cpp \
config/uicoloreditor.cpp
SOURCES += src/main.cpp\
src/nativeeventfilter.cpp \
src/controller.cpp \
src/capture/button.cpp \
src/capture/buttonhandler.cpp \
src/infowindow.cpp \
src/config/configwindow.cpp \
src/capture/screenshot.cpp \
src/capture/capturewidget.cpp \
src/capture/capturemodification.cpp \
src/capture/colorpicker.cpp \
src/config/buttonlistview.cpp \
src/config/uicoloreditor.cpp
HEADERS += \
nativeeventfilter.h \
controller.h \
capture/button.h \
capture/buttonhandler.h \
infowindow.h \
config/configwindow.h \
capture/screenshot.h \
capture/capturewidget.h \
capture/capturemodification.h \
capture/colorpicker.h \
config/buttonlistview.h \
config/uicoloreditor.h
src/nativeeventfilter.h \
src/controller.h \
src/capture/button.h \
src/capture/buttonhandler.h \
src/infowindow.h \
src/config/configwindow.h \
src/capture/screenshot.h \
src/capture/capturewidget.h \
src/capture/capturemodification.h \
src/capture/colorpicker.h \
src/config/buttonlistview.h \
src/config/uicoloreditor.h
RESOURCES += \
graphics.qrc

View File

Before

Width:  |  Height:  |  Size: 208 B

After

Width:  |  Height:  |  Size: 208 B

View File

@@ -16,7 +16,7 @@
// along with Flameshot. If not, see <http://www.gnu.org/licenses/>.
#include "button.h"
#include "capture/capturewidget.h"
#include "src/capture/capturewidget.h"
#include <QIcon>
#include <QPropertyAnimation>
#include <QToolTip>

View File

@@ -20,7 +20,6 @@
#include <QPushButton>
#include <map>
#include "button.h"
class QWidget;
class QPropertyAnimation;

View File

@@ -25,7 +25,7 @@
#include "capturemodification.h"
#include "capturewidget.h"
#include "button.h"
#include "capture/colorpicker.h"
#include "src/capture/colorpicker.h"
#include <QScreen>
#include <QWindow>
#include <QGuiApplication>

View File

@@ -16,7 +16,7 @@
// along with Flameshot. If not, see <http://www.gnu.org/licenses/>.
#include "buttonlistview.h"
#include "capture/button.h"
#include "src/capture/button.h"
#include <QListWidgetItem>
#include <QListWidgetItem>
#include <QSettings>

View File

@@ -16,9 +16,9 @@
// along with Flameshot. If not, see <http://www.gnu.org/licenses/>.
#include "configwindow.h"
#include "capture/button.h"
#include "config/buttonlistview.h"
#include "config/uicoloreditor.h"
#include "src/capture/button.h"
#include "src/config/buttonlistview.h"
#include "src/config/uicoloreditor.h"
#include <QIcon>
#include <QVBoxLayout>
#include <QGroupBox>

View File

@@ -17,7 +17,7 @@
#include "uicoloreditor.h"
#include "color_wheel.hpp"
#include "capture/button.h"
#include "src/capture/button.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QSettings>

View File

@@ -0,0 +1,134 @@
Changelog
=========
__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.

View File

@@ -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.

View File

@@ -0,0 +1,274 @@
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.
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
### 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.

View File

@@ -0,0 +1,5 @@
# Single Application implementation
include(../../singleapplication.pri)
DEFINES += QAPPLICATION_CLASS=QCoreApplication
SOURCES += main.cpp

View File

@@ -0,0 +1,9 @@
#include <SingleApplication.h>
int main(int argc, char *argv[])
{
// Allow secondary instances
SingleApplication app( argc, argv );
return app.exec();
}

View File

@@ -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]

View File

@@ -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

View File

@@ -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(tr("."), SLOT(pointClicked()));
Button *changeSignButton = createButton(tr("\302\261"), SLOT(changeSignClicked()));
Button *backspaceButton = createButton(tr("Backspace"), SLOT(backspaceClicked()));
Button *clearButton = createButton(tr("Clear"), SLOT(clear()));
Button *clearAllButton = createButton(tr("Clear All"), SLOT(clearAll()));
Button *clearMemoryButton = createButton(tr("MC"), SLOT(clearMemory()));
Button *readMemoryButton = createButton(tr("MR"), SLOT(readMemory()));
Button *setMemoryButton = createButton(tr("MS"), SLOT(setMemory()));
Button *addToMemoryButton = createButton(tr("M+"), SLOT(addToMemory()));
Button *divisionButton = createButton(tr("\303\267"), SLOT(multiplicativeOperatorClicked()));
Button *timesButton = createButton(tr("\303\227"), SLOT(multiplicativeOperatorClicked()));
Button *minusButton = createButton(tr("-"), SLOT(additiveOperatorClicked()));
Button *plusButton = createButton(tr("+"), SLOT(additiveOperatorClicked()));
Button *squareRootButton = createButton(tr("Sqrt"), SLOT(unaryOperatorClicked()));
Button *powerButton = createButton(tr("x\302\262"), SLOT(unaryOperatorClicked()));
Button *reciprocalButton = createButton(tr("1/x"), SLOT(unaryOperatorClicked()));
Button *equalButton = createButton(tr("="), 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(tr("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 == tr("Sqrt")) {
if (operand < 0.0) {
abortOperation();
return;
}
result = std::sqrt(operand);
} else if (clickedOperator == tr("x\302\262")) {
result = std::pow(operand, 2.0);
} else if (clickedOperator == tr("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() + tr("."));
waitingForOperand = false;
}
//! [22]
//! [24]
void Calculator::changeSignClicked()
{
QString text = display->text();
double value = text.toDouble();
if (value > 0.0) {
text.prepend(tr("-"));
} 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(tr("####"));
}
//! [36]
//! [38]
bool Calculator::calculate(double rightOperand, const QString &pendingOperator)
{
if (pendingOperator == tr("+")) {
sumSoFar += rightOperand;
} else if (pendingOperator == tr("-")) {
sumSoFar -= rightOperand;
} else if (pendingOperator == tr("\303\227")) {
factorSoFar *= rightOperand;
} else if (pendingOperator == tr("\303\267")) {
if (rightOperand == 0.0)
return false;
factorSoFar /= rightOperand;
}
return true;
}
//! [38]

View File

@@ -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

View File

@@ -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

View File

@@ -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();
}

View File

@@ -0,0 +1,25 @@
#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() );
return 0;
} else {
QObject::connect(
&app,
&SingleApplication::receivedMessage,
&msgReceiver,
&MessageReceiver::receivedMessage
);
}
return app.exec();
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -0,0 +1,9 @@
# Single Application implementation
include(../../singleapplication.pri)
DEFINES += QAPPLICATION_CLASS=QCoreApplication
SOURCES += main.cpp \
messagereceiver.cpp
HEADERS += \
messagereceiver.h

View File

@@ -0,0 +1,450 @@
// 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 <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"
static const char NewInstance = 'N';
static const char SecondaryInstance = 'S';
static const char Reconnect = 'R';
static const char InvalidConnection = '\0';
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 = (InstancesInfo*)memory->data();
if( server != nullptr ) {
server->close();
delete server;
inst->primary = false;
}
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 )
{
#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 = (InstancesInfo*)memory->data();
if( resetMemory ){
inst->primary = true;
inst->secondary = 0;
} else {
inst->primary = true;
}
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, char 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 = blockServerName.toLatin1();
initMsg.append( connectionType );
initMsg.append( (const char *)&instanceNumber, sizeof(quint32) );
initMsg.append( QByteArray::number( qChecksum( initMsg.constData(), initMsg.length() ), 256) );
socket->write( initMsg );
socket->flush();
socket->waitForBytesWritten( msecs );
}
}
#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*)QApplication::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();
// Verify that the new connection follows the SingleApplication protocol
char connectionType = InvalidConnection;
quint32 instanceId;
QByteArray initMsg, tmp;
if( nextConnSocket->waitForReadyRead( 100 ) ) {
tmp = nextConnSocket->read( blockServerName.length() );
// Verify that the socket data start with blockServerName
if( tmp == blockServerName.toLatin1() ) {
initMsg = tmp;
connectionType = nextConnSocket->read( 1 )[0];
switch( connectionType ) {
case NewInstance:
case SecondaryInstance:
case Reconnect:
{
initMsg += connectionType;
tmp = nextConnSocket->read( sizeof(quint32) );
const char * data = tmp.constData();
instanceId = (quint32)*data;
initMsg += tmp;
// Verify the checksum of the initMsg
QByteArray checksum = QByteArray::number(
qChecksum( initMsg.constData(), initMsg.length() ),
256
);
tmp = nextConnSocket->read( checksum.length() );
if( checksum == tmp )
break; // Otherwise set to invalid connection (next line)
}
default:
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 = (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, SecondaryInstance );
}
d->memory->unlock();
return;
}
d->memory->unlock();
}
}
d->connectToPrimary( timeout, 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;
}
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, Reconnect );
d->socket->write( message );
bool dataWritten = d->socket->flush();
d->socket->waitForBytesWritten( timeout );
return dataWritten;
}

View File

@@ -0,0 +1,129 @@
// 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 {int}
*/
quint32 instanceId();
/**
* @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

View File

@@ -0,0 +1,13 @@
QT += core network
CONFIG += c++11
HEADERS += $$PWD/singleapplication.h \
$$PWD/singleapplication_p.h
SOURCES += $$PWD/singleapplication.cpp
INCLUDEPATH += $$PWD
win32 {
msvc:LIBS += Advapi32.lib
gcc:LIBS += -lAdvapi32
}

View File

@@ -0,0 +1,77 @@
// 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;
};
class SingleApplicationPrivate : public QObject {
Q_OBJECT
public:
Q_DECLARE_PUBLIC(SingleApplication)
SingleApplicationPrivate( SingleApplication *q_ptr );
~SingleApplicationPrivate();
void genBlockServerName( int msecs );
void startPrimary( bool resetMemory );
void startSecondary();
void connectToPrimary( int msecs, char connectionType );
#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