From 8211b6aff08191ad6c83115754c1612dbc123bdd Mon Sep 17 00:00:00 2001 From: Jeremy Borgman Date: Sat, 31 Oct 2020 19:46:18 -0500 Subject: [PATCH] updated qtsingleapplication to v3.1.5 --- CMakeLists.txt | 53 +- external/singleapplication/CHANGELOG.md | 125 +++- external/singleapplication/CMakeLists.txt | 34 ++ external/singleapplication/LICENSE | 2 +- external/singleapplication/README.md | 109 ++-- external/singleapplication/SingleApplication | 1 + external/singleapplication/Windows.md | 2 +- .../examples/basic/CMakeLists.txt | 13 + .../examples/basic/basic.pro | 5 + .../singleapplication/examples/basic/main.cpp | 11 + .../examples/calculator/CMakeLists.txt | 20 + .../examples/calculator/button.cpp | 73 +++ .../examples/calculator/button.h | 68 +++ .../examples/calculator/calculator.cpp | 414 +++++++++++++ .../examples/calculator/calculator.h | 120 ++++ .../examples/calculator/calculator.pro | 11 + .../examples/calculator/main.cpp | 71 +++ .../examples/sending_arguments/CMakeLists.txt | 19 + .../examples/sending_arguments/main.cpp | 26 + .../sending_arguments/messagereceiver.cpp | 12 + .../sending_arguments/messagereceiver.h | 15 + .../sending_arguments/sending_arguments.pro | 9 + .../singleapplication/singleapplication.cpp | 572 ++++++------------ .../singleapplication/singleapplication.h | 27 +- .../singleapplication/singleapplication.pri | 8 +- .../singleapplication/singleapplication_p.cpp | 475 +++++++++++++++ .../singleapplication/singleapplication_p.h | 40 +- src/CMakeLists.txt | 44 +- src/core/controller.cpp | 9 - src/utils/confighandler.cpp | 16 - src/utils/confighandler.h | 3 - 31 files changed, 1863 insertions(+), 544 deletions(-) create mode 100644 external/singleapplication/CMakeLists.txt create mode 100644 external/singleapplication/SingleApplication create mode 100644 external/singleapplication/examples/basic/CMakeLists.txt create mode 100755 external/singleapplication/examples/basic/basic.pro create mode 100644 external/singleapplication/examples/basic/main.cpp create mode 100644 external/singleapplication/examples/calculator/CMakeLists.txt create mode 100644 external/singleapplication/examples/calculator/button.cpp create mode 100644 external/singleapplication/examples/calculator/button.h create mode 100644 external/singleapplication/examples/calculator/calculator.cpp create mode 100644 external/singleapplication/examples/calculator/calculator.h create mode 100644 external/singleapplication/examples/calculator/calculator.pro create mode 100644 external/singleapplication/examples/calculator/main.cpp create mode 100644 external/singleapplication/examples/sending_arguments/CMakeLists.txt create mode 100644 external/singleapplication/examples/sending_arguments/main.cpp create mode 100644 external/singleapplication/examples/sending_arguments/messagereceiver.cpp create mode 100644 external/singleapplication/examples/sending_arguments/messagereceiver.h create mode 100755 external/singleapplication/examples/sending_arguments/sending_arguments.pro create mode 100644 external/singleapplication/singleapplication_p.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d9810aa..9b85719d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,17 +11,17 @@ set(PROJECT_NAME_CAPITALIZED "Flameshot") # Configuration options set(DEFAULT_RUN_IN_PLACE FALSE) if(WIN32) - set(DEFAULT_RUN_IN_PLACE TRUE) - # For Windows RC file. - add_definitions(-DFLAMESHOT_VERSION_MAJOR=${CMAKE_PROJECT_VERSION_MAJOR}) - add_definitions(-DFLAMESHOT_VERSION_MINOR=${CMAKE_PROJECT_VERSION_MINOR}) - add_definitions(-DFLAMESHOT_VERSION_BUGFIX=${CMAKE_PROJECT_VERSION_PATCH}) - add_definitions(-DFLAMESHOT_VERSION_BUILD=1) - add_definitions(-DFLAMESHOT_VERSION_STRING="${PROJECT_VERSION}") + set(DEFAULT_RUN_IN_PLACE TRUE) + # For Windows RC file. + add_definitions(-DFLAMESHOT_VERSION_MAJOR=${CMAKE_PROJECT_VERSION_MAJOR}) + add_definitions(-DFLAMESHOT_VERSION_MINOR=${CMAKE_PROJECT_VERSION_MINOR}) + add_definitions(-DFLAMESHOT_VERSION_BUGFIX=${CMAKE_PROJECT_VERSION_PATCH}) + add_definitions(-DFLAMESHOT_VERSION_BUILD=1) + add_definitions(-DFLAMESHOT_VERSION_STRING="${PROJECT_VERSION}") endif() -set(RUN_IN_PLACE ${DEFAULT_RUN_IN_PLACE} CACHE BOOL - "Run directly in source directory structure") - +set(RUN_IN_PLACE + ${DEFAULT_RUN_IN_PLACE} + CACHE BOOL "Run directly in source directory structure") option(GENERATE_TS "Regenerate translation source files" OFF) @@ -45,6 +45,10 @@ enable_sanitizers(project_options) # allow for static analysis options include(cmake/StaticAnalyzers.cmake) +set(QAPPLICATION_CLASS + QApplication + CACHE STRING "Inheritance class for SingleApplication") +add_subdirectory(external/singleapplication) add_subdirectory(src) # CPack @@ -53,14 +57,16 @@ set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Powerful yet simple to use screenshot sof set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) set(CPACK_PACKAGE_HOMEPAGE_URL "https://flameshot.org") set(CPACK_PACKAGE_CONTACT "flameshot-org developers ") -set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/data/img/app/org.flameshot.Flameshot.svg") # TODO: Can any generator make use of this? -set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README.md") # TODO: Where is this used? Do we need a better source? +set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/data/img/app/org.flameshot.Flameshot.svg") # TODO: Can any generator make + # use of this? +set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README.md") # TODO: Where is this used? Do we need a better + # source? if(WIN32) - # Include all dynamically linked runtime libaries such as MSVCRxxx.dll + # Include all dynamically linked runtime libaries such as MSVCRxxx.dll include(InstallRequiredSystemLibraries) - if(RUN_IN_PLACE) + if(RUN_IN_PLACE) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-win64") else() @@ -76,26 +82,23 @@ if(WIN32) set(CPACK_PACKAGE_EXECUTABLES ${PROJECT_NAME} "${PROJECT_NAME_CAPITALIZED}") set(CPACK_CREATE_DESKTOP_LINKS ${PROJECT_NAME}) - # WIX (Windows .msi installer) + # WIX (Windows .msi installer) # 48x48 pixels set(CPACK_WIX_PRODUCT_ICON "${CMAKE_SOURCE_DIR}/data/img/app/flameshot.ico") - # Supported languages can be found at - # http://wixtoolset.org/documentation/manual/v3/wixui/wixui_localization.html - #set(CPACK_WIX_CULTURES "ar-SA,bg-BG,ca-ES,hr-HR,cs-CZ,da-DK,nl-NL,en-US,et-EE,fi-FI,fr-FR,de-DE") + # Supported languages can be found at http://wixtoolset.org/documentation/manual/v3/wixui/wixui_localization.html + # set(CPACK_WIX_CULTURES "ar-SA,bg-BG,ca-ES,hr-HR,cs-CZ,da-DK,nl-NL,en-US,et-EE,fi-FI,fr-FR,de-DE") set(CPACK_WIX_UI_BANNER "${CMAKE_SOURCE_DIR}/packaging/win-installer/Bitmaps/CPACK_WIX_UI_BANNER.BMP") set(CPACK_WIX_UI_DIALOG "${CMAKE_SOURCE_DIR}/packaging/win-installer/Bitmaps/CPACK_WIX_UI_DIALOG.BMP") set(CPACK_WIX_PROPERTY_ARPHELPLINK "${CPACK_PACKAGE_HOMEPAGE_URL}") - set(CPACK_WIX_PROPERTY_ARPURLINFOABOUT "${CPACK_PACKAGE_HOMEPAGE_URL}") - set(CPACK_WIX_ROOT_FEATURE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}") - set(CPACK_WIX_LIGHT_EXTRA_FLAGS "-dcl:high") # set high compression + set(CPACK_WIX_PROPERTY_ARPURLINFOABOUT "${CPACK_PACKAGE_HOMEPAGE_URL}") + set(CPACK_WIX_ROOT_FEATURE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}") + set(CPACK_WIX_LIGHT_EXTRA_FLAGS "-dcl:high") # set high compression - set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/packaging/win-installer/LICENSE/GPL-3.0.txt") set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md") - # The correct way would be to include both x32 and x64 into one installer - # and install the appropriate one. - # CMake does not support that, so there are two separate GUID's + # The correct way would be to include both x32 and x64 into one installer and install the appropriate one. CMake + # does not support that, so there are two separate GUID's if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(CPACK_WIX_UPGRADE_GUID "26D8062A-66D9-48D9-8924-42090FB9B3F9") else() diff --git a/external/singleapplication/CHANGELOG.md b/external/singleapplication/CHANGELOG.md index 4665222c..48e9caf7 100644 --- a/external/singleapplication/CHANGELOG.md +++ b/external/singleapplication/CHANGELOG.md @@ -1,6 +1,129 @@ Changelog ========= +If by accident I have forgotten to credit someone in the CHANGELOG, email me and I will fix it. + +__3.1.5__ + +* Improved library stability in edge cases and very rapid process initialisation +* Fixed Bug where the shared memory block may have been modified without a lock +* Fixed Bug causing instanceStarted() to not get emmited when a second instance + has been started before the primary has initiated it's QLocalServer. + +__3.1.4__ +--------- +* Officially supporting and build-testing against Qt 5.15 +* Fixed an MSVC C4996 warning that suggests using `strncpy_s`. + + _Hennadii Chernyshchyk_ + +__3.1.3.1__ +--------- +* CMake build system improvements +* Fixed Clang Tidy warnings + + _Hennadii Chernyshchyk_ + +__3.1.3__ +--------- +* Improved `CMakeLists.txt` + + _Hennadii Chernyshchyk_ + +__3.1.2__ +--------- + +* Fix a crash when exiting an application on Android and iOS + + _Emeric Grange_ + +__3.1.1a__ +---------- + +* Added currentUser() method that returns the user the current instance is running as. + + _Leander Schulten_ + +__3.1.0a__ +---------- + +* Added primaryUser() method that returns the user the primary instance is running as. + +__3.0.19__ +---------- + +* Fixed code warning for depricated functions in Qt 5.10 related to `QTime` and `qrand()`. + + _Hennadii Chernyshchyk_ + _Anton Filimonov_ + _Jonas Kvinge_ + +__3.0.18__ +---------- + +* Fallback to standard QApplication class on iOS and Android systems where + the library is not supported. + +* Added Build CI tests to verify the library builds successfully on Linux, Windows and MacOS across multiple Qt versions. + + _Anton Filimonov_ + +__3.0.17__ +---------- + +* Fixed compilation warning/error caused by `geteuid()` on unix based systems. + + _Iakov Kirilenko_ + +* Added CMake support + + _Hennadii Chernyshchyk_ + +__3.0.16__ +---------- + +* Use geteuid and getpwuid to get username on Unix, fallback to environment variable. + + _Jonas Kvinge_ + +__3.0.15__ +---------- + +* Bug Fix: sendMessage() might return false even though data was actually written. + + _Jonas Kvinge_ + +__3.0.14__ +---------- + +* Fixed uninitialised variables in the `SingleApplicationPrivate` constructor. + +__3.0.13a__ +---------- + +* Process socket events asynchronously +* Fix undefined variable error on Windows + + _Francis Giraldeau_ + +__3.0.12a__ +---------- + +* Removed signal handling. + +__3.0.11a__ +---------- + +* Fixed bug where the message sent by the second process was not received + correctly when the message is sent immediately following a connection. + + _Francis Giraldeau_ + +* Refactored code and implemented shared memory block consistency checks + via `qChecksum()` (CRC-16). +* Explicit `qWarning` and `qCritical` when the library is unable to initialise + correctly. + __3.0.10__ ---------- @@ -95,7 +218,7 @@ __3.0.1a__ __v3.0a__ --------- -* Depricated secondary instances count. +* Deprecated 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. diff --git a/external/singleapplication/CMakeLists.txt b/external/singleapplication/CMakeLists.txt new file mode 100644 index 00000000..85dba84c --- /dev/null +++ b/external/singleapplication/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.7.0) + +project(SingleApplication LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +add_library(${PROJECT_NAME} STATIC + singleapplication.cpp + singleapplication_p.cpp +) +add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) + +# Find dependencies +find_package(Qt5 COMPONENTS Network REQUIRED) +target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Network) + +if(QAPPLICATION_CLASS STREQUAL QApplication) + find_package(Qt5 COMPONENTS Widgets REQUIRED) + target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Widgets) +elseif(QAPPLICATION_CLASS STREQUAL QGuiApplication) + find_package(Qt5 COMPONENTS Gui REQUIRED) + target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Gui) +else() + set(QAPPLICATION_CLASS QCoreApplication) + find_package(Qt5 COMPONENTS Core REQUIRED) + target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Core) +endif() + +if(WIN32) + target_link_libraries(${PROJECT_NAME} PRIVATE advapi32) +endif() + +target_compile_definitions(${PROJECT_NAME} PUBLIC QAPPLICATION_CLASS=${QAPPLICATION_CLASS}) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/external/singleapplication/LICENSE b/external/singleapplication/LICENSE index 85b2a149..a82e5a68 100644 --- a/external/singleapplication/LICENSE +++ b/external/singleapplication/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) Itay Grudev 2015 - 2016 +Copyright (c) Itay Grudev 2015 - 2020 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/external/singleapplication/README.md b/external/singleapplication/README.md index 82e7a5b6..c3d17a0c 100644 --- a/external/singleapplication/README.md +++ b/external/singleapplication/README.md @@ -1,5 +1,6 @@ SingleApplication ================= +[![CI](https://github.com/itay-grudev/SingleApplication/workflows/CI:%20Build%20Test/badge.svg)](https://github.com/itay-grudev/SingleApplication/actions) This is a replacement of the QtSingleApplication for `Qt5`. @@ -15,18 +16,6 @@ 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: @@ -43,24 +32,49 @@ int main( int argc, char* argv[] ) ``` 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: +submodule to your project. 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:** + +Then include the `singleapplication.pri` file in your `.pro` project file. ```qmake include(singleapplication/singleapplication.pri) DEFINES += QAPPLICATION_CLASS=QApplication ``` +**CMake:** + +Then include the subdirectory in your `CMakeLists.txt` project file. + +```cmake +set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication") +add_subdirectory(src/third-party/singleapplication) +target_link_libraries(${PROJECT_NAME} SingleApplication::SingleApplication) +``` + + +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. + +Also don't forget to specify which `QCoreApplication` class your app is using if it +is not `QCoreApplication` as in examples above. + 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 @@ -125,6 +139,15 @@ 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.* +Examples +-------- + +There are three examples provided in this repository: + +* Basic example that prevents a secondary instance from starting [`examples/basic`](https://github.com/itay-grudev/SingleApplication/tree/master/examples/basic) +* An example of a graphical application raising it's parent window [`examples/calculator`](https://github.com/itay-grudev/SingleApplication/tree/master/examples/calculator) +* A console application sending the primary instance it's command line parameters [`examples/sending_arguments`](https://github.com/itay-grudev/SingleApplication/tree/master/examples/sending_arguments) + API --- @@ -192,6 +215,22 @@ qint64 SingleApplication::primaryPid() Returns the process ID (PID) of the primary instance. +--- + +```cpp +QString SingleApplication::primaryUser() +``` + +Returns the username the primary instance is running as. + +--- + +```cpp +QString SingleApplication::currentUser() +``` + +Returns the username the current instance is running as. + ### Signals ```cpp @@ -252,36 +291,14 @@ 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. +`instanceStarted()` signal and for messaging the primary instance. -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. +Additionally the library can recover from being forcefully killed on *nix +systems 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. +`The MIT License (MIT)` with the exception of the Qt calculator examples which +is distributed under the BSD license. diff --git a/external/singleapplication/SingleApplication b/external/singleapplication/SingleApplication new file mode 100644 index 00000000..8ead1a42 --- /dev/null +++ b/external/singleapplication/SingleApplication @@ -0,0 +1 @@ +#include "singleapplication.h" diff --git a/external/singleapplication/Windows.md b/external/singleapplication/Windows.md index 48b0748c..13c52da0 100644 --- a/external/singleapplication/Windows.md +++ b/external/singleapplication/Windows.md @@ -24,7 +24,7 @@ Here is an example: ```cpp if( app.isSecondary() ) { // This API requires LIBS += User32.lib to be added to the project - AllowSetForegroundWindow( DWORD( app.getPrimaryPid() ) ); + AllowSetForegroundWindow( DWORD( app.primaryPid() ) ); } if( app.isPrimary() ) { diff --git a/external/singleapplication/examples/basic/CMakeLists.txt b/external/singleapplication/examples/basic/CMakeLists.txt new file mode 100644 index 00000000..49be29c3 --- /dev/null +++ b/external/singleapplication/examples/basic/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.7.0) + +project(basic LANGUAGES CXX) + +# SingleApplication base class +set(QAPPLICATION_CLASS QCoreApplication) + +add_executable(basic main.cpp) + +find_package(Qt5 COMPONENTS Core REQUIRED) +add_subdirectory(../.. SingleApplication) +target_link_libraries(${PROJECT_NAME} SingleApplication::SingleApplication) + diff --git a/external/singleapplication/examples/basic/basic.pro b/external/singleapplication/examples/basic/basic.pro new file mode 100755 index 00000000..b7af16cf --- /dev/null +++ b/external/singleapplication/examples/basic/basic.pro @@ -0,0 +1,5 @@ +# Single Application implementation +include(../../singleapplication.pri) +DEFINES += QAPPLICATION_CLASS=QCoreApplication + +SOURCES += main.cpp diff --git a/external/singleapplication/examples/basic/main.cpp b/external/singleapplication/examples/basic/main.cpp new file mode 100644 index 00000000..df1c7383 --- /dev/null +++ b/external/singleapplication/examples/basic/main.cpp @@ -0,0 +1,11 @@ +#include + +int main(int argc, char* argv[]) +{ + // Allow secondary instances + SingleApplication app(argc, argv); + + qWarning() << "Started a new instance"; + + return app.exec(); +} diff --git a/external/singleapplication/examples/calculator/CMakeLists.txt b/external/singleapplication/examples/calculator/CMakeLists.txt new file mode 100644 index 00000000..990bff5c --- /dev/null +++ b/external/singleapplication/examples/calculator/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.7.0) + +project(calculator LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +# SingleApplication base class +set(QAPPLICATION_CLASS QApplication) + +add_executable(${PROJECT_NAME} + button.h + calculator.h + button.cpp + calculator.cpp + main.cpp +) + +find_package(Qt5 COMPONENTS Widgets REQUIRED) +add_subdirectory(../.. SingleApplication) +target_link_libraries(${PROJECT_NAME} SingleApplication::SingleApplication) diff --git a/external/singleapplication/examples/calculator/button.cpp b/external/singleapplication/examples/calculator/button.cpp new file mode 100644 index 00000000..711b2cd7 --- /dev/null +++ b/external/singleapplication/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 + +#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/external/singleapplication/examples/calculator/button.h b/external/singleapplication/examples/calculator/button.h new file mode 100644 index 00000000..7307a3fb --- /dev/null +++ b/external/singleapplication/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 + +//! [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/external/singleapplication/examples/calculator/calculator.cpp b/external/singleapplication/examples/calculator/calculator.cpp new file mode 100644 index 00000000..7070ddfa --- /dev/null +++ b/external/singleapplication/examples/calculator/calculator.cpp @@ -0,0 +1,414 @@ +/**************************************************************************** +** +** 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 + +#include + +#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(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(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(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(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/external/singleapplication/examples/calculator/calculator.h b/external/singleapplication/examples/calculator/calculator.h new file mode 100644 index 00000000..3332e129 --- /dev/null +++ b/external/singleapplication/examples/calculator/calculator.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** 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 + +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/external/singleapplication/examples/calculator/calculator.pro b/external/singleapplication/examples/calculator/calculator.pro new file mode 100644 index 00000000..8f132609 --- /dev/null +++ b/external/singleapplication/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/external/singleapplication/examples/calculator/main.cpp b/external/singleapplication/examples/calculator/main.cpp new file mode 100644 index 00000000..dd7d17f1 --- /dev/null +++ b/external/singleapplication/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 + +#include + +#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/external/singleapplication/examples/sending_arguments/CMakeLists.txt b/external/singleapplication/examples/sending_arguments/CMakeLists.txt new file mode 100644 index 00000000..06c740f2 --- /dev/null +++ b/external/singleapplication/examples/sending_arguments/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.7.0) + +project(sending_arguments LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +# SingleApplication base class +set(QAPPLICATION_CLASS QCoreApplication) + +add_executable(${PROJECT_NAME} + main.cpp + messagereceiver.cpp + messagereceiver.h + main.cpp +) + +find_package(Qt5 COMPONENTS Core REQUIRED) +add_subdirectory(../.. SingleApplication) +target_link_libraries(${PROJECT_NAME} SingleApplication::SingleApplication) diff --git a/external/singleapplication/examples/sending_arguments/main.cpp b/external/singleapplication/examples/sending_arguments/main.cpp new file mode 100644 index 00000000..d48bdc84 --- /dev/null +++ b/external/singleapplication/examples/sending_arguments/main.cpp @@ -0,0 +1,26 @@ +#include "messagereceiver.h" +#include + +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/external/singleapplication/examples/sending_arguments/messagereceiver.cpp b/external/singleapplication/examples/sending_arguments/messagereceiver.cpp new file mode 100644 index 00000000..a89bae86 --- /dev/null +++ b/external/singleapplication/examples/sending_arguments/messagereceiver.cpp @@ -0,0 +1,12 @@ +#include "messagereceiver.h" +#include + +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/external/singleapplication/examples/sending_arguments/messagereceiver.h b/external/singleapplication/examples/sending_arguments/messagereceiver.h new file mode 100644 index 00000000..13f80dc3 --- /dev/null +++ b/external/singleapplication/examples/sending_arguments/messagereceiver.h @@ -0,0 +1,15 @@ +#ifndef MESSAGERECEIVER_H +#define MESSAGERECEIVER_H + +#include + +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/external/singleapplication/examples/sending_arguments/sending_arguments.pro b/external/singleapplication/examples/sending_arguments/sending_arguments.pro new file mode 100755 index 00000000..897636a9 --- /dev/null +++ b/external/singleapplication/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/external/singleapplication/singleapplication.cpp b/external/singleapplication/singleapplication.cpp index a83bd807..61632795 100644 --- a/external/singleapplication/singleapplication.cpp +++ b/external/singleapplication/singleapplication.cpp @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) Itay Grudev 2015 - 2016 +// Copyright (c) Itay Grudev 2015 - 2020 // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -20,358 +20,21 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include - -#include -#include #include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include - -#ifdef Q_OS_UNIX -#include -#include -#endif - -#ifdef Q_OS_WIN -#include -#include -#endif #include "singleapplication.h" #include "singleapplication_p.h" -SingleApplicationPrivate::SingleApplicationPrivate(SingleApplication* q_ptr) - : q_ptr(q_ptr) -{ - server = nullptr; - socket = nullptr; -} - -SingleApplicationPrivate::~SingleApplicationPrivate() -{ - if (socket != nullptr) { - socket->close(); - delete socket; - } - - memory->lock(); - InstancesInfo* inst = static_cast(memory->data()); - if (server != nullptr) { - server->close(); - delete server; - inst->primary = false; - inst->primaryPid = -1; - } - memory->unlock(); - - delete memory; -} - -void SingleApplicationPrivate::genBlockServerName(int timeout) -{ - QCryptographicHash appData(QCryptographicHash::Sha256); - appData.addData("SingleApplication", 17); - appData.addData(SingleApplication::app_t::applicationName().toUtf8()); - appData.addData(SingleApplication::app_t::organizationName().toUtf8()); - appData.addData(SingleApplication::app_t::organizationDomain().toUtf8()); - - if (!(options & SingleApplication::Mode::ExcludeAppVersion)) { - appData.addData( - SingleApplication::app_t::applicationVersion().toUtf8()); - } - - if (!(options & SingleApplication::Mode::ExcludeAppPath)) { -#ifdef Q_OS_WIN - appData.addData( - SingleApplication::app_t::applicationFilePath().toLower().toUtf8()); -#else - appData.addData( - SingleApplication::app_t::applicationFilePath().toUtf8()); -#endif - } - - // User level block requires a user specific data in the hash - if (options & SingleApplication::Mode::User) { -#ifdef Q_OS_WIN - Q_UNUSED(timeout); - wchar_t username[UNLEN + 1]; - // Specifies size of the buffer on input - DWORD usernameLength = UNLEN + 1; - if (GetUserNameW(username, &usernameLength)) { - appData.addData(QString::fromWCharArray(username).toUtf8()); - } else { - appData.addData( - QStandardPaths::standardLocations(QStandardPaths::HomeLocation) - .join("") - .toUtf8()); - } -#endif -#ifdef Q_OS_UNIX - QProcess process; - process.start(QStringLiteral("whoami"), QStringList{}); - - if (process.waitForFinished(timeout) && - process.exitCode() == QProcess::NormalExit) { - appData.addData(process.readLine()); - } else { - appData.addData(QDir(QStandardPaths::standardLocations( - QStandardPaths::HomeLocation) - .first()) - .absolutePath() - .toUtf8()); - } -#endif - } - - // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with - // server naming requirements. - blockServerName = appData.result().toBase64().replace("/", "_"); -} - -void SingleApplicationPrivate::startPrimary(bool resetMemory) -{ - Q_Q(SingleApplication); - -#ifdef Q_OS_UNIX - // Handle any further termination signals to ensure the - // QSharedMemory block is deleted even if the process crashes - crashHandler(); -#endif - // Successful creation means that no main process exists - // So we start a QLocalServer to listen for connections - QLocalServer::removeServer(blockServerName); - server = new QLocalServer(); - - // Restrict access to the socket according to the - // SingleApplication::Mode::User flag on User level or no restrictions - if (options & SingleApplication::Mode::User) { - server->setSocketOptions(QLocalServer::UserAccessOption); - } else { - server->setSocketOptions(QLocalServer::WorldAccessOption); - } - - server->listen(blockServerName); - QObject::connect(server, - &QLocalServer::newConnection, - this, - &SingleApplicationPrivate::slotConnectionEstablished); - - // Reset the number of connections - memory->lock(); - InstancesInfo* inst = static_cast(memory->data()); - - if (resetMemory) { - inst->secondary = 0; - } - - inst->primary = true; - inst->primaryPid = q->applicationPid(); - - memory->unlock(); - - instanceNumber = 0; -} - -void SingleApplicationPrivate::startSecondary() -{ -#ifdef Q_OS_UNIX - // Handle any further termination signals to ensure the - // QSharedMemory block is deleted even if the process crashes - crashHandler(); -#endif -} - -void SingleApplicationPrivate::connectToPrimary(int msecs, - ConnectionType connectionType) -{ - // Connect to the Local Server of the Primary Instance if not already - // connected. - if (socket == nullptr) { - socket = new QLocalSocket(); - } - - // If already connected - we are done; - if (socket->state() == QLocalSocket::ConnectedState) - return; - - // If not connect - if (socket->state() == QLocalSocket::UnconnectedState || - socket->state() == QLocalSocket::ClosingState) { - socket->connectToServer(blockServerName); - } - - // Wait for being connected - if (socket->state() == QLocalSocket::ConnectingState) { - socket->waitForConnected(msecs); - } - - // Initialisation message according to the SingleApplication protocol - if (socket->state() == QLocalSocket::ConnectedState) { - // Notify the parent that a new instance had been started; - QByteArray initMsg; - QDataStream writeStream(&initMsg, QIODevice::WriteOnly); - writeStream.setVersion(QDataStream::Qt_5_2); - writeStream << blockServerName.toLatin1(); - writeStream << static_cast(connectionType); - writeStream << instanceNumber; - quint16 checksum = qChecksum(initMsg.constData(), - static_cast(initMsg.length())); - writeStream << checksum; - - socket->write(initMsg); - socket->flush(); - socket->waitForBytesWritten(msecs); - } -} - -qint64 SingleApplicationPrivate::primaryPid() -{ - qint64 pid; - - memory->lock(); - InstancesInfo* inst = static_cast(memory->data()); - pid = inst->primaryPid; - memory->unlock(); - - return pid; -} - -#ifdef Q_OS_UNIX -void SingleApplicationPrivate::crashHandler() -{ - // Handle any further termination signals to ensure the - // QSharedMemory block is deleted even if the process crashes - signal(SIGHUP, SingleApplicationPrivate::terminate); // 1 - signal(SIGINT, SingleApplicationPrivate::terminate); // 2 - signal(SIGQUIT, SingleApplicationPrivate::terminate); // 3 - signal(SIGILL, SingleApplicationPrivate::terminate); // 4 - signal(SIGABRT, SingleApplicationPrivate::terminate); // 6 - signal(SIGFPE, SingleApplicationPrivate::terminate); // 8 - signal(SIGBUS, SingleApplicationPrivate::terminate); // 10 - signal(SIGSEGV, SingleApplicationPrivate::terminate); // 11 - signal(SIGSYS, SingleApplicationPrivate::terminate); // 12 - signal(SIGPIPE, SingleApplicationPrivate::terminate); // 13 - signal(SIGALRM, SingleApplicationPrivate::terminate); // 14 - signal(SIGTERM, SingleApplicationPrivate::terminate); // 15 - signal(SIGXCPU, SingleApplicationPrivate::terminate); // 24 - signal(SIGXFSZ, SingleApplicationPrivate::terminate); // 25 -} - -void SingleApplicationPrivate::terminate(int signum) -{ - delete ((SingleApplication*)QCoreApplication::instance())->d_ptr; - ::exit(128 + signum); -} -#endif - -/** - * @brief Executed when a connection has been made to the LocalServer - */ -void SingleApplicationPrivate::slotConnectionEstablished() -{ - Q_Q(SingleApplication); - - QLocalSocket* nextConnSocket = server->nextPendingConnection(); - - quint32 instanceId = 0; - ConnectionType connectionType = InvalidConnection; - if (nextConnSocket->waitForReadyRead(100)) { - // read all data from message in same order/format as written - QByteArray msgBytes = - nextConnSocket->read(nextConnSocket->bytesAvailable() - - static_cast(sizeof(quint16))); - QByteArray checksumBytes = nextConnSocket->read(sizeof(quint16)); - QDataStream readStream(msgBytes); - readStream.setVersion(QDataStream::Qt_5_2); - - // server name - QByteArray latin1Name; - readStream >> latin1Name; - // connectioon type - quint8 connType = InvalidConnection; - readStream >> connType; - connectionType = static_cast(connType); - // instance id - readStream >> instanceId; - // checksum - quint16 msgChecksum = 0; - QDataStream checksumStream(checksumBytes); - checksumStream.setVersion(QDataStream::Qt_5_2); - checksumStream >> msgChecksum; - - const quint16 actualChecksum = qChecksum( - msgBytes.constData(), static_cast(msgBytes.length())); - - if (readStream.status() != QDataStream::Ok || - QLatin1String(latin1Name) != blockServerName || - msgChecksum != actualChecksum) { - connectionType = InvalidConnection; - } - } - - if (connectionType == InvalidConnection) { - nextConnSocket->close(); - delete nextConnSocket; - return; - } - - QObject::connect(nextConnSocket, - &QLocalSocket::aboutToClose, - this, - [nextConnSocket, instanceId, this]() { - emit this->slotClientConnectionClosed(nextConnSocket, - instanceId); - }); - - QObject::connect(nextConnSocket, - &QLocalSocket::readyRead, - this, - [nextConnSocket, instanceId, this]() { - emit this->slotDataAvailable(nextConnSocket, - instanceId); - }); - - if (connectionType == NewInstance || - (connectionType == SecondaryInstance && - options & SingleApplication::Mode::SecondaryNotification)) { - emit q->instanceStarted(); - } - - if (nextConnSocket->bytesAvailable() > 0) { - emit this->slotDataAvailable(nextConnSocket, instanceId); - } -} - -void SingleApplicationPrivate::slotDataAvailable(QLocalSocket* dataSocket, - quint32 instanceId) -{ - Q_Q(SingleApplication); - emit q->receivedMessage(instanceId, dataSocket->readAll()); -} - -void SingleApplicationPrivate::slotClientConnectionClosed( - QLocalSocket* closedSocket, - quint32 instanceId) -{ - if (closedSocket->bytesAvailable() > 0) - 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 + * @param allowSecondary Whether to enable secondary instance support + * @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[], @@ -383,115 +46,213 @@ SingleApplication::SingleApplication(int& argc, { Q_D(SingleApplication); +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + // On Android and iOS since the library is not supported fallback to + // standard QApplication behaviour by simply returning at this point. + qWarning() + << "SingleApplication is not supported on Android and iOS systems."; + return; +#endif + // 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); + d->genBlockServerName(); + + // To mitigate QSharedMemory issues with large amount of processes + // attempting to attach at the same time + d->randomSleep(); - // 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 + // By explicitly attaching it and then deleting it we make sure that the + // memory is deleted even after the process has crashed on Unix. d->memory = new QSharedMemory(d->blockServerName); d->memory->attach(); delete d->memory; #endif + // Guarantee thread safe behaviour with a shared memory block. d->memory = new QSharedMemory(d->blockServerName); // Create a shared memory block if (d->memory->create(sizeof(InstancesInfo))) { - d->startPrimary(true); - return; + // Initialize the shared memory block + if (!d->memory->lock()) { + qCritical() + << "SingleApplication: Unable to lock memory block after create."; + abortSafely(); + } + d->initializeMemoryBlock(); } else { - // Attempt to attach to the memory segment - if (d->memory->attach()) { - d->memory->lock(); - InstancesInfo* inst = - static_cast(d->memory->data()); - - if (!inst->primary) { - d->startPrimary(false); - d->memory->unlock(); - return; + if (d->memory->error() == QSharedMemory::AlreadyExists) { + // Attempt to attach to the memory segment + if (!d->memory->attach()) { + qCritical() << "SingleApplication: Unable to attach to shared " + "memory block."; + abortSafely(); } - - // Check if another instance can be started - if (allowSecondary) { - inst->secondary += 1; - d->instanceNumber = inst->secondary; - d->startSecondary(); - if (d->options & Mode::SecondaryNotification) { - d->connectToPrimary( - timeout, SingleApplicationPrivate::SecondaryInstance); - } - d->memory->unlock(); - return; + if (!d->memory->lock()) { + qCritical() << "SingleApplication: Unable to lock memory block " + "after attach."; + abortSafely(); } - - d->memory->unlock(); + } else { + qCritical() << "SingleApplication: Unable to create block."; + abortSafely(); } } - d->connectToPrimary(timeout, SingleApplicationPrivate::NewInstance); - delete d; + auto* inst = static_cast(d->memory->data()); + QElapsedTimer time; + time.start(); - // show message box with inforation that Flameshot is already launched - QMessageBox msgBox; - msgBox.setText(QObject::tr( - "Hi, I'm already running!\nYou can find me in the system tray.")); - int cnt = 3; - QTimer cntDown; - QObject::connect( - &cntDown, &QTimer::timeout, [&msgBox, &cnt, &cntDown]() -> void { - if (--cnt < 0) { - cntDown.stop(); - msgBox.close(); - } else { - msgBox.setWindowTitle(QString("Flameshot (%1)").arg(cnt + 1)); - } - }); - cntDown.start(1000); - msgBox.exec(); + // Make sure the shared memory block is initialised and in consistent state + while (true) { + // If the shared memory block's checksum is valid continue + if (d->blockChecksum() == inst->checksum) + break; + + // If more than 5s have elapsed, assume the primary instance crashed and + // assume it's position + if (time.elapsed() > 5000) { + qWarning() << "SingleApplication: Shared memory block has been in " + "an inconsistent state from more than 5s. Assuming " + "primary instance failure."; + d->initializeMemoryBlock(); + } + + // Otherwise wait for a random period and try again. The random sleep + // here limits the probability of a collision between two racing apps + // and allows the app to initialise faster + if (!d->memory->unlock()) { + qDebug() + << "SingleApplication: Unable to unlock memory for random wait."; + qDebug() << d->memory->errorString(); + } + d->randomSleep(); + if (!d->memory->lock()) { + qCritical() + << "SingleApplication: Unable to lock memory after random wait."; + abortSafely(); + } + } + + if (inst->primary == false) { + d->startPrimary(); + if (!d->memory->unlock()) { + qDebug() << "SingleApplication: Unable to unlock memory after " + "primary start."; + qDebug() << d->memory->errorString(); + } + return; + } + + // Check if another instance can be started + if (allowSecondary) { + d->startSecondary(); + if (d->options & Mode::SecondaryNotification) { + d->connectToPrimary(timeout, + SingleApplicationPrivate::SecondaryInstance); + } + if (!d->memory->unlock()) { + qDebug() << "SingleApplication: Unable to unlock memory after " + "secondary start."; + qDebug() << d->memory->errorString(); + } + return; + } + + if (!d->memory->unlock()) { + qDebug() + << "SingleApplication: Unable to unlock memory at end of execution."; + qDebug() << d->memory->errorString(); + } + + d->connectToPrimary(timeout, SingleApplicationPrivate::NewInstance); + + delete d; ::exit(EXIT_SUCCESS); } -/** - * @brief Destructor - */ SingleApplication::~SingleApplication() { Q_D(SingleApplication); delete d; } +/** + * Checks if the current application instance is primary. + * @return Returns true if the instance is primary, false otherwise. + */ bool SingleApplication::isPrimary() { Q_D(SingleApplication); return d->server != nullptr; } +/** + * Checks if the current application instance is secondary. + * @return Returns true if the instance is secondary, false otherwise. + */ bool SingleApplication::isSecondary() { Q_D(SingleApplication); return d->server == nullptr; } +/** + * Allows you to identify an instance by returning unique consecutive instance + * ids. It is reset when the first (primary) instance of your app starts and + * only incremented afterwards. + * @return Returns a unique instance id. + */ quint32 SingleApplication::instanceId() { Q_D(SingleApplication); return d->instanceNumber; } +/** + * Returns the OS PID (Process Identifier) of the process running the primary + * instance. Especially useful when SingleApplication is coupled with OS. + * specific APIs. + * @return Returns the primary instance PID. + */ qint64 SingleApplication::primaryPid() { Q_D(SingleApplication); return d->primaryPid(); } -bool SingleApplication::sendMessage(QByteArray message, int timeout) +/** + * Returns the username the primary instance is running as. + * @return Returns the username the primary instance is running as. + */ +QString SingleApplication::primaryUser() +{ + Q_D(SingleApplication); + return d->primaryUser(); +} + +/** + * Returns the username the current instance is running as. + * @return Returns the username the current instance is running as. + */ +QString SingleApplication::currentUser() +{ + Q_D(SingleApplication); + return d->getUsername(); +} + +/** + * Sends message to the Primary Instance. + * @param message The message to send. + * @param timeout the maximum timeout in milliseconds for blocking functions. + * @return true if the message was sent successfuly, false otherwise. + */ +bool SingleApplication::sendMessage(const QByteArray& message, int timeout) { Q_D(SingleApplication); @@ -500,10 +261,25 @@ bool SingleApplication::sendMessage(QByteArray message, int timeout) return false; // Make sure the socket is connected - d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect); + if (!d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect)) + return false; d->socket->write(message); - bool dataWritten = d->socket->flush(); - d->socket->waitForBytesWritten(timeout); + bool dataWritten = d->socket->waitForBytesWritten(timeout); + d->socket->flush(); return dataWritten; } + +/** + * Cleans up the shared memory block and exits with a failure. + * This function halts program execution. + */ +void SingleApplication::abortSafely() +{ + Q_D(SingleApplication); + + qCritical() << "SingleApplication: " << d->memory->error() + << d->memory->errorString(); + delete d; + ::exit(EXIT_FAILURE); +} diff --git a/external/singleapplication/singleapplication.h b/external/singleapplication/singleapplication.h index 18115af4..6800464c 100644 --- a/external/singleapplication/singleapplication.h +++ b/external/singleapplication/singleapplication.h @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) Itay Grudev 2015 - 2016 +// Copyright (c) Itay Grudev 2015 - 2018 // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -35,7 +35,7 @@ class SingleApplicationPrivate; /** - * @brief The SingleApplication class handles multipe instances of the same + * @brief The SingleApplication class handles multiple instances of the same * Application * @see QCoreApplication */ @@ -43,7 +43,7 @@ class SingleApplication : public QAPPLICATION_CLASS { Q_OBJECT - typedef QAPPLICATION_CLASS app_t; + using app_t = QAPPLICATION_CLASS; public: /** @@ -75,7 +75,7 @@ public: * 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. + * @arg {int} timeout - Timeout to wait in milliseconds. * @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 @@ -90,8 +90,8 @@ public: char* argv[], bool allowSecondary = false, Options options = Mode::User, - int timeout = 100); - ~SingleApplication(); + int timeout = 1000); + ~SingleApplication() override; /** * @brief Returns if the instance is the primary instance @@ -117,6 +117,18 @@ public: */ qint64 primaryPid(); + /** + * @brief Returns the username of the user running the primary instance + * @returns {QString} + */ + QString primaryUser(); + + /** + * @brief Returns the username of the current user + * @returns {QString} + */ + QString currentUser(); + /** * @brief Sends a message to the primary instance. Returns true on success. * @param {int} timeout - Timeout for connecting @@ -124,7 +136,7 @@ public: * @note sendMessage() will return false if invoked from the primary * instance. */ - bool sendMessage(QByteArray message, int timeout = 100); + bool sendMessage(const QByteArray& message, int timeout = 100); Q_SIGNALS: void instanceStarted(); @@ -133,6 +145,7 @@ Q_SIGNALS: private: SingleApplicationPrivate* d_ptr; Q_DECLARE_PRIVATE(SingleApplication) + void abortSafely(); }; Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options) diff --git a/external/singleapplication/singleapplication.pri b/external/singleapplication/singleapplication.pri index a82ff286..ae81f599 100644 --- a/external/singleapplication/singleapplication.pri +++ b/external/singleapplication/singleapplication.pri @@ -1,15 +1,17 @@ QT += core network CONFIG += c++11 -HEADERS += $$PWD/singleapplication.h \ +HEADERS += $$PWD/SingleApplication \ + $$PWD/singleapplication.h \ $$PWD/singleapplication_p.h -SOURCES += $$PWD/singleapplication.cpp +SOURCES += $$PWD/singleapplication.cpp \ + $$PWD/singleapplication_p.cpp INCLUDEPATH += $$PWD win32 { msvc:LIBS += Advapi32.lib - gcc:LIBS += -lAdvapi32 + gcc:LIBS += -ladvapi32 } DISTFILES += \ diff --git a/external/singleapplication/singleapplication_p.cpp b/external/singleapplication/singleapplication_p.cpp new file mode 100644 index 00000000..42e58145 --- /dev/null +++ b/external/singleapplication/singleapplication_p.cpp @@ -0,0 +1,475 @@ +// The MIT License (MIT) +// +// Copyright (c) Itay Grudev 2015 - 2020 +// +// 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. +// + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) +#include +#else +#include +#endif + +#include "singleapplication.h" +#include "singleapplication_p.h" + +#ifdef Q_OS_UNIX +#include +#include +#include +#endif + +#ifdef Q_OS_WIN +#include +#include +#endif + +SingleApplicationPrivate::SingleApplicationPrivate(SingleApplication* q_ptr) + : q_ptr(q_ptr) +{ + server = nullptr; + socket = nullptr; + memory = nullptr; + instanceNumber = -1; +} + +SingleApplicationPrivate::~SingleApplicationPrivate() +{ + if (socket != nullptr) { + socket->close(); + delete socket; + } + + if (memory != nullptr) { + memory->lock(); + auto* inst = static_cast(memory->data()); + if (server != nullptr) { + server->close(); + delete server; + inst->primary = false; + inst->primaryPid = -1; + inst->primaryUser[0] = '\0'; + inst->checksum = blockChecksum(); + } + memory->unlock(); + + delete memory; + } +} + +QString SingleApplicationPrivate::getUsername() +{ +#ifdef Q_OS_WIN + wchar_t username[UNLEN + 1]; + // Specifies size of the buffer on input + DWORD usernameLength = UNLEN + 1; + if (GetUserNameW(username, &usernameLength)) + return QString::fromWCharArray(username); +#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) + return QString::fromLocal8Bit(qgetenv("USERNAME")); +#else + return qEnvironmentVariable("USERNAME"); +#endif +#endif +#ifdef Q_OS_UNIX + QString username; + uid_t uid = geteuid(); + struct passwd* pw = getpwuid(uid); + if (pw) + username = QString::fromLocal8Bit(pw->pw_name); + if (username.isEmpty()) { +#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) + username = QString::fromLocal8Bit(qgetenv("USER")); +#else + username = qEnvironmentVariable("USER"); +#endif + } + return username; +#endif +} + +void SingleApplicationPrivate::genBlockServerName() +{ + 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) { + appData.addData(getUsername().toUtf8()); + } + + // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with + // server naming requirements. + blockServerName = appData.result().toBase64().replace("/", "_"); +} + +void SingleApplicationPrivate::initializeMemoryBlock() +{ + auto* inst = static_cast(memory->data()); + inst->primary = false; + inst->secondary = 0; + inst->primaryPid = -1; + inst->primaryUser[0] = '\0'; + inst->checksum = blockChecksum(); +} + +void SingleApplicationPrivate::startPrimary() +{ + Q_Q(SingleApplication); + + // Reset the number of connections + auto* inst = static_cast(memory->data()); + + inst->primary = true; + inst->primaryPid = q->applicationPid(); + qstrncpy(inst->primaryUser, + getUsername().toUtf8().data(), + sizeof(inst->primaryUser)); + inst->checksum = blockChecksum(); + instanceNumber = 0; + // 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); +} + +void SingleApplicationPrivate::startSecondary() +{ + auto* inst = static_cast(memory->data()); + + inst->secondary += 1; + inst->checksum = blockChecksum(); + instanceNumber = inst->secondary; +} + +bool SingleApplicationPrivate::connectToPrimary(int timeout, + ConnectionType connectionType) +{ + QElapsedTimer time; + time.start(); + + // Connect to the Local Server of the Primary Instance if not already + // connected. + if (socket == nullptr) { + socket = new QLocalSocket(); + } + + if (socket->state() == QLocalSocket::ConnectedState) + return true; + + if (socket->state() != QLocalSocket::ConnectedState) { + + while (true) { + randomSleep(); + + if (socket->state() != QLocalSocket::ConnectingState) + socket->connectToServer(blockServerName); + + if (socket->state() == QLocalSocket::ConnectingState) { + socket->waitForConnected(timeout - time.elapsed()); + } + + // If connected break out of the loop + if (socket->state() == QLocalSocket::ConnectedState) + break; + + // If elapsed time since start is longer than the method timeout + // return + if (time.elapsed() >= timeout) + return false; + } + } + + // Initialisation message according to the SingleApplication protocol + QByteArray initMsg; + QDataStream writeStream(&initMsg, QIODevice::WriteOnly); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + writeStream.setVersion(QDataStream::Qt_5_6); +#endif + + writeStream << blockServerName.toLatin1(); + writeStream << static_cast(connectionType); + writeStream << instanceNumber; + quint16 checksum = + qChecksum(initMsg.constData(), static_cast(initMsg.length())); + writeStream << checksum; + + // 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(initMsg.length()); + + socket->write(header); + socket->write(initMsg); + socket->flush(); + if (socket->waitForBytesWritten(timeout - time.elapsed())) + return true; + + return false; +} + +quint16 SingleApplicationPrivate::blockChecksum() +{ + return qChecksum(static_cast(memory->data()), + offsetof(InstancesInfo, checksum)); +} + +qint64 SingleApplicationPrivate::primaryPid() +{ + qint64 pid; + + memory->lock(); + auto* inst = static_cast(memory->data()); + pid = inst->primaryPid; + memory->unlock(); + + return pid; +} + +QString SingleApplicationPrivate::primaryUser() +{ + QByteArray username; + + memory->lock(); + auto* inst = static_cast(memory->data()); + username = inst->primaryUser; + memory->unlock(); + + return QString::fromUtf8(username); +} + +/** + * @brief Executed when a connection has been made to the LocalServer + */ +void SingleApplicationPrivate::slotConnectionEstablished() +{ + QLocalSocket* nextConnSocket = server->nextPendingConnection(); + connectionMap.insert(nextConnSocket, ConnectionInfo()); + + QObject::connect( + nextConnSocket, &QLocalSocket::aboutToClose, [nextConnSocket, this]() { + auto& info = connectionMap[nextConnSocket]; + Q_EMIT this->slotClientConnectionClosed(nextConnSocket, + info.instanceId); + }); + + QObject::connect( + nextConnSocket, &QLocalSocket::disconnected, [nextConnSocket, this]() { + connectionMap.remove(nextConnSocket); + nextConnSocket->deleteLater(); + }); + + QObject::connect( + nextConnSocket, &QLocalSocket::readyRead, [nextConnSocket, this]() { + auto& info = connectionMap[nextConnSocket]; + switch (info.stage) { + case StageHeader: + readInitMessageHeader(nextConnSocket); + break; + case StageBody: + readInitMessageBody(nextConnSocket); + break; + case StageConnected: + Q_EMIT this->slotDataAvailable(nextConnSocket, + info.instanceId); + break; + default: + break; + }; + }); +} + +void SingleApplicationPrivate::readInitMessageHeader(QLocalSocket* sock) +{ + if (!connectionMap.contains(sock)) { + return; + } + + if (sock->bytesAvailable() < (qint64)sizeof(quint64)) { + return; + } + + QDataStream headerStream(sock); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + headerStream.setVersion(QDataStream::Qt_5_6); +#endif + + // Read the header to know the message length + quint64 msgLen = 0; + headerStream >> msgLen; + ConnectionInfo& info = connectionMap[sock]; + info.stage = StageBody; + info.msgLen = msgLen; + + if (sock->bytesAvailable() >= (qint64)msgLen) { + readInitMessageBody(sock); + } +} + +void SingleApplicationPrivate::readInitMessageBody(QLocalSocket* sock) +{ + Q_Q(SingleApplication); + + if (!connectionMap.contains(sock)) { + return; + } + + ConnectionInfo& info = connectionMap[sock]; + if (sock->bytesAvailable() < (qint64)info.msgLen) { + return; + } + + // Read the message body + QByteArray msgBytes = sock->read(info.msgLen); + QDataStream readStream(msgBytes); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + readStream.setVersion(QDataStream::Qt_5_6); +#endif + + // server name + QByteArray latin1Name; + readStream >> latin1Name; + + // connection type + ConnectionType connectionType = InvalidConnection; + quint8 connTypeVal = InvalidConnection; + readStream >> connTypeVal; + connectionType = static_cast(connTypeVal); + + // instance id + quint32 instanceId = 0; + readStream >> instanceId; + + // checksum + quint16 msgChecksum = 0; + readStream >> msgChecksum; + + const quint16 actualChecksum = + qChecksum(msgBytes.constData(), + static_cast(msgBytes.length() - sizeof(quint16))); + + bool isValid = readStream.status() == QDataStream::Ok && + QLatin1String(latin1Name) == blockServerName && + msgChecksum == actualChecksum; + + if (!isValid) { + sock->close(); + return; + } + + info.instanceId = instanceId; + info.stage = StageConnected; + + if (connectionType == NewInstance || + (connectionType == SecondaryInstance && + options & SingleApplication::Mode::SecondaryNotification)) { + Q_EMIT q->instanceStarted(); + } + + if (sock->bytesAvailable() > 0) { + Q_EMIT this->slotDataAvailable(sock, 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); +} + +void SingleApplicationPrivate::randomSleep() +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + QThread::msleep(QRandomGenerator::global()->bounded(8u, 18u)); +#else + qsrand(QDateTime::currentMSecsSinceEpoch() % + std::numeric_limits::max()); + QThread::msleep(8 + static_cast(static_cast(qrand()) / + RAND_MAX * 10)); +#endif +} diff --git a/external/singleapplication/singleapplication_p.h b/external/singleapplication/singleapplication_p.h index 491f246d..0190428b 100644 --- a/external/singleapplication/singleapplication_p.h +++ b/external/singleapplication/singleapplication_p.h @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) Itay Grudev 2015 - 2016 +// Copyright (c) Itay Grudev 2015 - 2020 // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -42,6 +42,15 @@ struct InstancesInfo bool primary; quint32 secondary; qint64 primaryPid; + char primaryUser[128]; + quint16 checksum; // Must be the last field +}; + +struct ConnectionInfo +{ + qint64 msgLen = 0; + quint32 instanceId = 0; + quint8 stage = 0; }; class SingleApplicationPrivate : public QObject @@ -55,29 +64,38 @@ public: SecondaryInstance = 2, Reconnect = 3 }; + enum ConnectionStage : quint8 + { + StageHeader = 0, + StageBody = 1, + StageConnected = 2, + }; Q_DECLARE_PUBLIC(SingleApplication) SingleApplicationPrivate(SingleApplication* q_ptr); - ~SingleApplicationPrivate(); + ~SingleApplicationPrivate() override; - void genBlockServerName(int msecs); - void startPrimary(bool resetMemory); + QString getUsername(); + void genBlockServerName(); + void initializeMemoryBlock(); + void startPrimary(); void startSecondary(); - void connectToPrimary(int msecs, ConnectionType connectionType); + bool connectToPrimary(int msecs, ConnectionType connectionType); + quint16 blockChecksum(); qint64 primaryPid(); + QString primaryUser(); + void readInitMessageHeader(QLocalSocket* socket); + void readInitMessageBody(QLocalSocket* socket); + void randomSleep(); -#ifdef Q_OS_UNIX - void crashHandler(); - static void terminate(int signum); -#endif - - QSharedMemory* memory; SingleApplication* q_ptr; + QSharedMemory* memory; QLocalSocket* socket; QLocalServer* server; quint32 instanceNumber; QString blockServerName; SingleApplication::Options options; + QMap connectionMap; public Q_SLOTS: void slotConnectionEstablished(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eb37bcaa..d24eab8b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -63,7 +63,7 @@ endif() target_sources( flameshot - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../external/singleapplication/singleapplication.cpp + PRIVATE # ${CMAKE_CURRENT_SOURCE_DIR}/../external/singleapplication/singleapplication.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../external/Qt-Color-Widgets/src/color_utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../external/Qt-Color-Widgets/src/color_wheel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../external/Qt-Color-Widgets/include/color_wheel.hpp @@ -116,12 +116,13 @@ target_link_libraries( Qt5::Svg Qt5::DBus Qt5::Network - Qt5::Widgets) + Qt5::Widgets + SingleApplication::SingleApplication) set(USE_OPENSSL FALSE) if(ENABLE_OPENSSL) find_package(OpenSSL) - if (OPENSSL_FOUND) + if(OPENSSL_FOUND) message(STATUS "OpenSSL support enabled.") set(USE_OPENSSL TRUE) endif() @@ -136,7 +137,6 @@ endif() target_compile_definitions(flameshot PRIVATE APP_PREFIX="${CMAKE_INSTALL_PREFIX}") target_compile_definitions(flameshot PRIVATE APP_VERSION="v${PROJECT_VERSION}") target_compile_definitions(flameshot PRIVATE IMGUR_CLIENT_ID="313baf0c7b4d3ff") -target_compile_definitions(flameshot PRIVATE QAPPLICATION_CLASS=QApplication) foreach(FILE ${QM_FILES}) get_filename_component(F_NAME ${FILE} NAME) @@ -188,7 +188,7 @@ configure_file(${CMAKE_SOURCE_DIR}/data/img/hicolor/128x128/apps/org.flameshot.F configure_file(${CMAKE_SOURCE_DIR}/data/img/hicolor/scalable/apps/org.flameshot.Flameshot.svg ${CMAKE_CURRENT_BINARY_DIR}/share/icons/hicolor/scalable/apps/org.flameshot.Flameshot.svg COPYONLY) -## Install icon with both names +# Install icon with both names configure_file(${CMAKE_SOURCE_DIR}/data/img/hicolor/48x48/apps/flameshot.png ${CMAKE_CURRENT_BINARY_DIR}/share/icons/hicolor/48x48/apps/flameshot.png COPYONLY) @@ -198,14 +198,12 @@ configure_file(${CMAKE_SOURCE_DIR}/data/img/hicolor/128x128/apps/flameshot.png configure_file(${CMAKE_SOURCE_DIR}/data/img/hicolor/scalable/apps/flameshot.svg ${CMAKE_CURRENT_BINARY_DIR}/share/icons/hicolor/scalable/apps/flameshot.svg COPYONLY) - # Install assets install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/share/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}) # Install Translations install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/flameshot/translations) - # windeployqt if(WIN32) if(EXISTS $ENV{QTDIR}/bin/windeployqt.exe) @@ -216,26 +214,26 @@ if(WIN32) endif() add_custom_command( - TARGET flameshot POST_BUILD + TARGET flameshot + POST_BUILD COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/windeployqt_stuff - COMMAND $ENV{QTDIR}/bin/windeployqt.exe ${BINARIES_TYPE} --no-translations --compiler-runtime --no-system-d3d-compiler --no-angle --no-webkit2 --no-quick-import --dir ${CMAKE_BINARY_DIR}/windeployqt_stuff $ - # copy translations manually - # QM_FILES + COMMAND + $ENV{QTDIR}/bin/windeployqt.exe ${BINARIES_TYPE} --no-translations --compiler-runtime --no-system-d3d-compiler + --no-angle --no-webkit2 --no-quick-import --dir ${CMAKE_BINARY_DIR}/windeployqt_stuff $ + # copy translations manually QM_FILES COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/windeployqt_stuff/translations - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/src/translations ${CMAKE_BINARY_DIR}/windeployqt_stuff/translations - ) + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/src/translations + ${CMAKE_BINARY_DIR}/windeployqt_stuff/translations) - install( - DIRECTORY ${CMAKE_BINARY_DIR}/windeployqt_stuff/ - DESTINATION bin - ) + install(DIRECTORY ${CMAKE_BINARY_DIR}/windeployqt_stuff/ DESTINATION bin) - if (ENABLE_OPENSSL) - if (EXISTS $ENV{OPENSSL_ROOT_DIR}/bin) - install(DIRECTORY $ENV{OPENSSL_ROOT_DIR}/bin/ - DESTINATION bin - FILES_MATCHING PATTERN "*.dll" - ) + if(ENABLE_OPENSSL) + if(EXISTS $ENV{OPENSSL_ROOT_DIR}/bin) + install( + DIRECTORY $ENV{OPENSSL_ROOT_DIR}/bin/ + DESTINATION bin + FILES_MATCHING + PATTERN "*.dll") else() message(WARNING "Unable to find OpenSSL dlls.") endif() diff --git a/src/core/controller.cpp b/src/core/controller.cpp index c04cd403..7003c84c 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -265,15 +265,6 @@ void Controller::enableTrayIcon() #endif m_trayIcon->show(); - if (ConfigHandler().showStartupLaunchMessage()) { - m_trayIcon->showMessage( - "Flameshot", - QObject::tr( - "Hello, I'm here! Click icon in the tray to take a screenshot or " - "click with a right button to see more options."), - QSystemTrayIcon::Information, - 3000); - } } void Controller::disableTrayIcon() diff --git a/src/utils/confighandler.cpp b/src/utils/confighandler.cpp index 2a258458..e37ee35d 100644 --- a/src/utils/confighandler.cpp +++ b/src/utils/confighandler.cpp @@ -379,22 +379,6 @@ void ConfigHandler::setStartupLaunch(const bool start) m_settings.setValue(QStringLiteral("startupLaunch"), start); } -bool ConfigHandler::showStartupLaunchMessage() -{ - if (!m_settings.contains(QStringLiteral("showStartupLaunchMessage"))) { - m_settings.setValue(QStringLiteral("showStartupLaunchMessage"), true); - } - return m_settings.value(QStringLiteral("showStartupLaunchMessage")) - .toBool(); -} - -void ConfigHandler::setShowStartupLaunchMessage( - const bool showStartupLaunchMessage) -{ - m_settings.setValue(QStringLiteral("showStartupLaunchMessage"), - showStartupLaunchMessage); -} - int ConfigHandler::contrastOpacityValue() { int opacity = 190; diff --git a/src/utils/confighandler.h b/src/utils/confighandler.h index 3b507e28..1c03435e 100644 --- a/src/utils/confighandler.h +++ b/src/utils/confighandler.h @@ -73,9 +73,6 @@ public: bool startupLaunchValue(); void setStartupLaunch(const bool); - bool showStartupLaunchMessage(); - void setShowStartupLaunchMessage(const bool); - int contrastOpacityValue(); void setContrastOpacity(const int);