Browse Source

Implement "Application instances manager"

adaptive-webui-19844
Vladimir Golovnev (Glassez) 6 years ago
parent
commit
c7f3693ad1
No known key found for this signature in database
GPG Key ID: 52A2C7DEE2DFA6F7
  1. 29
      configure
  2. 17
      configure.ac
  3. 12
      src/CMakeLists.txt
  4. 6
      src/app/CMakeLists.txt
  5. 18
      src/app/app.pri
  6. 38
      src/app/application.cpp
  7. 19
      src/app/application.h
  8. 82
      src/app/applicationinstancemanager.cpp
  9. 55
      src/app/applicationinstancemanager.h
  10. 0
      src/app/qtlocalpeer/qtlocalpeer.cpp
  11. 0
      src/app/qtlocalpeer/qtlocalpeer.h
  12. 0
      src/app/qtlocalpeer/qtlockedfile.cpp
  13. 18
      src/app/qtlocalpeer/qtlockedfile.h
  14. 0
      src/app/qtlocalpeer/qtlockedfile_unix.cpp
  15. 0
      src/app/qtlocalpeer/qtlockedfile_win.cpp
  16. 31
      src/app/qtsingleapplication/CMakeLists.txt
  17. 1
      src/app/qtsingleapplication/QtLockedFile
  18. 1
      src/app/qtsingleapplication/QtSingleApplication
  19. 347
      src/app/qtsingleapplication/qtsingleapplication.cpp
  20. 105
      src/app/qtsingleapplication/qtsingleapplication.h
  21. 16
      src/app/qtsingleapplication/qtsingleapplication.pri
  22. 149
      src/app/qtsingleapplication/qtsinglecoreapplication.cpp
  23. 71
      src/app/qtsingleapplication/qtsinglecoreapplication.h
  24. 10
      src/app/qtsingleapplication/qtsinglecoreapplication.pri
  25. 1
      src/gui/CMakeLists.txt

29
configure vendored

@ -719,7 +719,6 @@ ac_user_opts=' @@ -719,7 +719,6 @@ ac_user_opts='
enable_option_checking
enable_dependency_tracking
enable_silent_rules
with_qtsingleapplication
enable_debug
enable_stacktrace
enable_gui
@ -1399,9 +1398,6 @@ Optional Features: @@ -1399,9 +1398,6 @@ Optional Features:
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
--with-qtsingleapplication=[system|shipped]
Use the shipped qtsingleapplication library or the
system one (default=shipped)
--with-boost[=ARG] use Boost library from a standard location
(ARG=yes), from the specified location (ARG=<path>),
or disable it (ARG=no) [ARG=yes]
@ -4187,14 +4183,6 @@ QBT_CXX="$CXX" @@ -4187,14 +4183,6 @@ QBT_CXX="$CXX"
# Define --wth-* and --enable-* arguments
# Check whether --with-qtsingleapplication was given.
if test "${with_qtsingleapplication+set}" = set; then :
withval=$with_qtsingleapplication;
else
with_qtsingleapplication=shipped
fi
# Check whether --enable-debug was given.
if test "${enable_debug+set}" = set; then :
enableval=$enable_debug;
@ -5294,23 +5282,6 @@ fi @@ -5294,23 +5282,6 @@ fi
$as_echo "$as_me: Boost.System LIB: \"$BOOST_SYSTEM_LIB\"" >&6;}
LIBS="$BOOST_SYSTEM_LIB $LIBS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking which qtsingleapplication to use" >&5
$as_echo_n "checking which qtsingleapplication to use... " >&6; }
case "x$with_qtsingleapplication" in #(
"xshipped") :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: shipped" >&5
$as_echo "shipped" >&6; }
QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG usesystemqtsingleapplication" ;; #(
"xsystem") :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: system" >&5
$as_echo "system" >&6; }
QBT_ADD_CONFIG="$QBT_ADD_CONFIG usesystemqtsingleapplication" ;; #(
*) :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_qtsingleapplication" >&5
$as_echo "$with_qtsingleapplication" >&6; }
as_fn_error $? "Unknown option \"$with_qtsingleapplication\". Use either \"system\" or \"shipped\"." "$LINENO" 5 ;;
esac
pkg_failed=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libtorrent" >&5

17
configure.ac

@ -14,12 +14,6 @@ QBT_CXX="$CXX" @@ -14,12 +14,6 @@ QBT_CXX="$CXX"
# Define --wth-* and --enable-* arguments
AC_ARG_WITH(qtsingleapplication,
[AS_HELP_STRING([--with-qtsingleapplication=@<:@system|shipped@:>@],
[Use the shipped qtsingleapplication library or the system one (default=shipped)])],
[],
[with_qtsingleapplication=shipped])
AC_ARG_ENABLE(debug,
[AS_HELP_STRING([--enable-debug],
[Enable debug build])],
@ -183,17 +177,6 @@ AX_BOOST_SYSTEM() @@ -183,17 +177,6 @@ AX_BOOST_SYSTEM()
AC_MSG_NOTICE([Boost.System LIB: "$BOOST_SYSTEM_LIB"])
LIBS="$BOOST_SYSTEM_LIB $LIBS"
AC_MSG_CHECKING([which qtsingleapplication to use])
AS_CASE(["x$with_qtsingleapplication"],
["xshipped"],
[AC_MSG_RESULT([shipped])
QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG usesystemqtsingleapplication"],
["xsystem"],
[AC_MSG_RESULT([system])
QBT_ADD_CONFIG="$QBT_ADD_CONFIG usesystemqtsingleapplication"],
[AC_MSG_RESULT([$with_qtsingleapplication])
AC_MSG_ERROR([Unknown option "$with_qtsingleapplication". Use either "system" or "shipped".])])
PKG_CHECK_MODULES(libtorrent,
[libtorrent-rasterbar >= 1.1.10],
[CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS"

12
src/CMakeLists.txt

@ -68,18 +68,6 @@ endif() @@ -68,18 +68,6 @@ endif()
configure_file(config.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/config.h)
find_package(QtSingleApplication)
set_package_properties(QtSingleApplication PROPERTIES
URL "https://code.qt.io/cgit/qt-solutions/qt-solutions.git/"
DESCRIPTION "Qt library to start applications only once per user"
TYPE RECOMMENDED
PURPOSE "Use the system qtsingleapplication library or shipped one otherwise"
)
if (NOT QtSingleApplication_FOUND)
add_subdirectory(app/qtsingleapplication)
endif ()
add_subdirectory(app)
add_subdirectory(base)

6
src/app/CMakeLists.txt

@ -1,12 +1,16 @@ @@ -1,12 +1,16 @@
add_executable(qBittorrent
application.h
applicationinstancemanager.h
cmdoptions.h
filelogger.h
qtlocalpeer/qtlocalpeer.h
upgrade.h
application.cpp
applicationinstancemanager.cpp
cmdoptions.cpp
filelogger.cpp
main.cpp
qtlocalpeer/qtlocalpeer.cpp
upgrade.cpp
)
@ -135,8 +139,6 @@ target_sources(qBittorrent PRIVATE ${QBT_QM_FILES} ${QBT_APP_RESOURCE_SOURCE}) @@ -135,8 +139,6 @@ target_sources(qBittorrent PRIVATE ${QBT_QM_FILES} ${QBT_APP_RESOURCE_SOURCE})
get_target_property(QBT_EXECUTABLE_NAME qBittorrent OUTPUT_NAME)
target_link_libraries(qBittorrent PRIVATE ${QBT_TARGET_LIBRARIES} QtSingleApplication::QtSingleApplication)
if (APPLE)
set(qbt_BUNDLE_NAME ${QBT_EXECUTABLE_NAME})

18
src/app/app.pri

@ -1,30 +1,20 @@ @@ -1,30 +1,20 @@
INCLUDEPATH += $$PWD
usesystemqtsingleapplication {
nogui {
CONFIG += qtsinglecoreapplication
} else {
CONFIG += qtsingleapplication
}
} else {
nogui {
include(qtsingleapplication/qtsinglecoreapplication.pri)
} else {
include(qtsingleapplication/qtsingleapplication.pri)
}
}
HEADERS += \
$$PWD/application.h \
$$PWD/applicationinstancemanager.h \
$$PWD/cmdoptions.h \
$$PWD/filelogger.h \
$$PWD/qtlocalpeer/qtlocalpeer.h \
$$PWD/upgrade.h
SOURCES += \
$$PWD/application.cpp \
$$PWD/applicationinstancemanager.cpp \
$$PWD/cmdoptions.cpp \
$$PWD/filelogger.cpp \
$$PWD/main.cpp \
$$PWD/qtlocalpeer/qtlocalpeer.cpp \
$$PWD/upgrade.cpp
stacktrace {

38
src/app/application.cpp

@ -82,6 +82,7 @@ @@ -82,6 +82,7 @@
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
#include "applicationinstancemanager.h"
#include "filelogger.h"
#ifndef DISABLE_WEBUI
@ -116,7 +117,8 @@ namespace @@ -116,7 +117,8 @@ namespace
}
Application::Application(const QString &id, int &argc, char **argv)
: BaseApplication(id, argc, argv)
: BaseApplication(argc, argv)
, m_instanceManager(new ApplicationInstanceManager {id, this})
, m_running(false)
, m_shutdownAct(ShutdownDialogAction::Exit)
, m_commandLineArgs(parseCommandLine(this->arguments()))
@ -156,7 +158,7 @@ Application::Application(const QString &id, int &argc, char **argv) @@ -156,7 +158,7 @@ Application::Application(const QString &id, int &argc, char **argv)
connect(this, &QGuiApplication::commitDataRequest, this, &Application::shutdownCleanup, Qt::DirectConnection);
#endif
connect(this, &Application::messageReceived, this, &Application::processMessage);
connect(m_instanceManager, &ApplicationInstanceManager::messageReceived, this, &Application::processMessage);
connect(this, &QCoreApplication::aboutToQuit, this, &Application::cleanup);
if (isFileLoggerEnabled())
@ -417,7 +419,7 @@ void Application::allTorrentsFinished() @@ -417,7 +419,7 @@ void Application::allTorrentsFinished()
bool Application::sendParams(const QStringList &params)
{
return sendMessage(params.join(PARAMS_SEPARATOR));
return m_instanceManager->sendMessage(params.join(PARAMS_SEPARATOR));
}
// As program parameters, we can get paths or urls.
@ -572,34 +574,12 @@ int Application::exec(const QStringList &params) @@ -572,34 +574,12 @@ int Application::exec(const QStringList &params)
return BaseApplication::exec();
}
#ifndef DISABLE_GUI
#ifdef Q_OS_WIN
bool Application::isRunning()
{
const bool running = BaseApplication::isRunning();
QSharedMemory *sharedMem = new QSharedMemory(id() + QLatin1String("-shared-memory-key"), this);
if (!running) {
// First instance creates shared memory and store PID
if (sharedMem->create(sizeof(DWORD)) && sharedMem->lock()) {
*(static_cast<DWORD*>(sharedMem->data())) = ::GetCurrentProcessId();
sharedMem->unlock();
}
}
else {
// Later instances attach to shared memory and retrieve PID
if (sharedMem->attach() && sharedMem->lock()) {
::AllowSetForegroundWindow(*(static_cast<DWORD*>(sharedMem->data())));
sharedMem->unlock();
}
}
if (!sharedMem->isAttached())
qWarning() << "Failed to initialize shared memory: " << sharedMem->errorString();
return running;
return !m_instanceManager->isFirstInstance();
}
#endif // Q_OS_WIN
#ifndef DISABLE_GUI
#ifdef Q_OS_MAC
bool Application::event(QEvent *ev)
{
@ -740,11 +720,11 @@ void Application::cleanup() @@ -740,11 +720,11 @@ void Application::cleanup()
#ifndef DISABLE_GUI
if (m_window) {
#ifdef Q_OS_WIN
typedef BOOL (WINAPI *PSHUTDOWNBRDESTROY)(HWND);
using PSHUTDOWNBRDESTROY = BOOL (WINAPI *)(HWND);
const auto shutdownBRDestroy = Utils::Misc::loadWinAPI<PSHUTDOWNBRDESTROY>("User32.dll", "ShutdownBlockReasonDestroy");
// Only available on Vista+
if (shutdownBRDestroy)
shutdownBRDestroy((HWND)m_window->effectiveWinId());
shutdownBRDestroy(reinterpret_cast<HWND>(m_window->effectiveWinId()));
#endif // Q_OS_WIN
delete m_window;
}

19
src/app/application.h

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015, 2019 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez
*
* This program is free software; you can redistribute it and/or
@ -27,16 +27,15 @@ @@ -27,16 +27,15 @@
* exception statement from your version.
*/
#ifndef APPLICATION_H
#define APPLICATION_H
#pragma once
#include <QPointer>
#include <QStringList>
#include <QTranslator>
#ifndef DISABLE_GUI
#include "qtsingleapplication.h"
typedef QtSingleApplication BaseApplication;
#include <QApplication>
using BaseApplication = QApplication;
class MainWindow;
#ifdef Q_OS_WIN
@ -44,8 +43,8 @@ class QSessionManager; @@ -44,8 +43,8 @@ class QSessionManager;
#endif // Q_OS_WIN
#else
#include "qtsinglecoreapplication.h"
typedef QtSingleCoreApplication BaseApplication;
#include <QCoreApplication>
using BaseApplication = QCoreApplication;
#endif // DISABLE_GUI
#include "base/types.h"
@ -55,6 +54,7 @@ typedef QtSingleCoreApplication BaseApplication; @@ -55,6 +54,7 @@ typedef QtSingleCoreApplication BaseApplication;
class WebUI;
#endif
class ApplicationInstanceManager;
class FileLogger;
namespace BitTorrent
@ -77,9 +77,7 @@ public: @@ -77,9 +77,7 @@ public:
Application(const QString &id, int &argc, char **argv);
~Application() override;
#if (defined(Q_OS_WIN) && !defined(DISABLE_GUI))
bool isRunning();
#endif
int exec(const QStringList &params);
bool sendParams(const QStringList &params);
@ -122,6 +120,7 @@ private slots: @@ -122,6 +120,7 @@ private slots:
#endif
private:
ApplicationInstanceManager *m_instanceManager;
bool m_running;
ShutdownDialogAction m_shutdownAct;
QBtCommandLineParameters m_commandLineArgs;
@ -147,5 +146,3 @@ private: @@ -147,5 +146,3 @@ private:
void sendNotificationEmail(const BitTorrent::TorrentHandle *torrent);
void validateCommandLineParameters();
};
#endif // APPLICATION_H

82
src/app/applicationinstancemanager.cpp

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2019 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "applicationinstancemanager.h"
#ifdef Q_OS_WIN
#include <windows.h>
#endif
#include <QDebug>
#include <QSharedMemory>
#include "qtlocalpeer/qtlocalpeer.h"
ApplicationInstanceManager::ApplicationInstanceManager(const QString &appId, QObject *parent)
: QObject {parent}
, m_peer {new QtLocalPeer {this, appId}}
, m_isFirstInstance {!m_peer->isClient()}
{
connect(m_peer, &QtLocalPeer::messageReceived, this, &ApplicationInstanceManager::messageReceived);
#ifdef Q_OS_WIN
auto sharedMem = new QSharedMemory {appId + QLatin1String {"-shared-memory-key"}, this};
if (m_isFirstInstance) {
// First instance creates shared memory and store PID
if (sharedMem->create(sizeof(DWORD)) && sharedMem->lock()) {
*(static_cast<DWORD *>(sharedMem->data())) = ::GetCurrentProcessId();
sharedMem->unlock();
}
}
else {
// Later instances attach to shared memory and retrieve PID
if (sharedMem->attach() && sharedMem->lock()) {
::AllowSetForegroundWindow(*(static_cast<DWORD *>(sharedMem->data())));
sharedMem->unlock();
}
}
if (!sharedMem->isAttached())
qCritical() << "Failed to initialize shared memory: " << sharedMem->errorString();
#endif
}
bool ApplicationInstanceManager::isFirstInstance() const
{
return m_isFirstInstance;
}
bool ApplicationInstanceManager::sendMessage(const QString &message, const int timeout)
{
return m_peer->sendMessage(message, timeout);
}
QString ApplicationInstanceManager::appId() const
{
return m_peer->applicationId();
}

55
src/app/applicationinstancemanager.h

@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2019 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
#include <QObject>
class QtLocalPeer;
class ApplicationInstanceManager : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(ApplicationInstanceManager)
public:
explicit ApplicationInstanceManager(const QString &appId, QObject *parent = nullptr);
bool isFirstInstance() const;
QString appId() const;
public slots:
bool sendMessage(const QString &message, int timeout = 5000);
signals:
void messageReceived(const QString &message);
private:
QtLocalPeer *m_peer;
const bool m_isFirstInstance;
};

0
src/app/qtsingleapplication/qtlocalpeer.cpp → src/app/qtlocalpeer/qtlocalpeer.cpp

0
src/app/qtsingleapplication/qtlocalpeer.h → src/app/qtlocalpeer/qtlocalpeer.h

0
src/app/qtsingleapplication/qtlockedfile.cpp → src/app/qtlocalpeer/qtlockedfile.cpp

18
src/app/qtsingleapplication/qtlockedfile.h → src/app/qtlocalpeer/qtlockedfile.h

@ -46,25 +46,9 @@ @@ -46,25 +46,9 @@
#include <QVector>
#endif
#if defined(Q_OS_WIN)
# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT)
# define QT_QTLOCKEDFILE_EXPORT
# elif defined(QT_QTLOCKEDFILE_IMPORT)
# if defined(QT_QTLOCKEDFILE_EXPORT)
# undef QT_QTLOCKEDFILE_EXPORT
# endif
# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport)
# elif defined(QT_QTLOCKEDFILE_EXPORT)
# undef QT_QTLOCKEDFILE_EXPORT
# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport)
# endif
#else
# define QT_QTLOCKEDFILE_EXPORT
#endif
namespace QtLP_Private {
class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile
class QtLockedFile : public QFile
{
public:
enum LockMode { NoLock = 0, ReadLock, WriteLock };

0
src/app/qtsingleapplication/qtlockedfile_unix.cpp → src/app/qtlocalpeer/qtlockedfile_unix.cpp

0
src/app/qtsingleapplication/qtlockedfile_win.cpp → src/app/qtlocalpeer/qtlockedfile_win.cpp

31
src/app/qtsingleapplication/CMakeLists.txt

@ -1,31 +0,0 @@ @@ -1,31 +0,0 @@
project(qtsingleapplication)
set(QBT_QTSINGLEAPPLICATION_HEADERS
qtlocalpeer.h
)
set(QBT_QTSINGLEAPPLICATION_SOURCES
qtlocalpeer.cpp
)
if (Qt5Widgets_FOUND)
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsingleapplication.h)
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsingleapplication.cpp)
else (Qt5Widgets_FOUND)
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsinglecoreapplication.h)
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsinglecoreapplication.cpp)
endif (Qt5Widgets_FOUND)
add_library(qtsingleapplication STATIC ${QBT_QTSINGLEAPPLICATION_HEADERS} ${QBT_QTSINGLEAPPLICATION_SOURCES})
target_include_directories(qtsingleapplication INTERFACE "${qtsingleapplication_SOURCE_DIR}")
target_link_libraries(qtsingleapplication PRIVATE Qt5::Network)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
target_compile_options(qtsingleapplication PRIVATE "-w") # disable warning for 3rdparty code
endif()
if (Qt5Widgets_FOUND)
target_link_libraries(qtsingleapplication PRIVATE Qt5::Widgets)
endif (Qt5Widgets_FOUND)
add_library(QtSingleApplication::QtSingleApplication ALIAS qtsingleapplication)

1
src/app/qtsingleapplication/QtLockedFile

@ -1 +0,0 @@ @@ -1 +0,0 @@
#include "qtlockedfile.h"

1
src/app/qtsingleapplication/QtSingleApplication

@ -1 +0,0 @@ @@ -1 +0,0 @@
#include "qtsingleapplication.h"

347
src/app/qtsingleapplication/qtsingleapplication.cpp

@ -1,347 +0,0 @@ @@ -1,347 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Solutions component.
**
** $QT_BEGIN_LICENSE:BSD$
** 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 Digia Plc and its Subsidiary(-ies) 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 "qtsingleapplication.h"
#include "qtlocalpeer.h"
#include <QWidget>
/*!
\class QtSingleApplication qtsingleapplication.h
\brief The QtSingleApplication class provides an API to detect and
communicate with running instances of an application.
This class allows you to create applications where only one
instance should be running at a time. I.e., if the user tries to
launch another instance, the already running instance will be
activated instead. Another usecase is a client-server system,
where the first started instance will assume the role of server,
and the later instances will act as clients of that server.
By default, the full path of the executable file is used to
determine whether two processes are instances of the same
application. You can also provide an explicit identifier string
that will be compared instead.
The application should create the QtSingleApplication object early
in the startup phase, and call isRunning() to find out if another
instance of this application is already running. If isRunning()
returns false, it means that no other instance is running, and
this instance has assumed the role as the running instance. In
this case, the application should continue with the initialization
of the application user interface before entering the event loop
with exec(), as normal.
The messageReceived() signal will be emitted when the running
application receives messages from another instance of the same
application. When a message is received it might be helpful to the
user to raise the application so that it becomes visible. To
facilitate this, QtSingleApplication provides the
setActivationWindow() function and the activateWindow() slot.
If isRunning() returns true, another instance is already
running. It may be alerted to the fact that another instance has
started by using the sendMessage() function. Also data such as
startup parameters (e.g. the name of the file the user wanted this
new instance to open) can be passed to the running instance with
this function. Then, the application should terminate (or enter
client mode).
If isRunning() returns true, but sendMessage() fails, that is an
indication that the running instance is frozen.
Here's an example that shows how to convert an existing
application to use QtSingleApplication. It is very simple and does
not make use of all QtSingleApplication's functionality (see the
examples for that).
\code
// Original
int main(int argc, char **argv)
{
QApplication app(argc, argv);
MyMainWidget mmw;
mmw.show();
return app.exec();
}
// Single instance
int main(int argc, char **argv)
{
QtSingleApplication app(argc, argv);
if (app.isRunning())
return !app.sendMessage(someDataString);
MyMainWidget mmw;
app.setActivationWindow(&mmw);
mmw.show();
return app.exec();
}
\endcode
Once this QtSingleApplication instance is destroyed (normally when
the process exits or crashes), when the user next attempts to run the
application this instance will not, of course, be encountered. The
next instance to call isRunning() or sendMessage() will assume the
role as the new running instance.
For console (non-GUI) applications, QtSingleCoreApplication may be
used instead of this class, to avoid the dependency on the QtGui
library.
\sa QtSingleCoreApplication
*/
void QtSingleApplication::sysInit(const QString &appId)
{
actWin = 0;
peer = new QtLocalPeer(this, appId);
connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
}
/*!
Creates a QtSingleApplication object. The application identifier
will be QCoreApplication::applicationFilePath(). \a argc, \a
argv, and \a GUIenabled are passed on to the QAppliation constructor.
If you are creating a console application (i.e. setting \a
GUIenabled to false), you may consider using
QtSingleCoreApplication instead.
*/
QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled)
: QApplication(argc, argv, GUIenabled)
{
sysInit();
}
/*!
Creates a QtSingleApplication object with the application
identifier \a appId. \a argc and \a argv are passed on to the
QAppliation constructor.
*/
QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv)
: QApplication(argc, argv)
{
sysInit(appId);
}
#if QT_VERSION < 0x050000
/*!
Creates a QtSingleApplication object. The application identifier
will be QCoreApplication::applicationFilePath(). \a argc, \a
argv, and \a type are passed on to the QAppliation constructor.
*/
QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type)
: QApplication(argc, argv, type)
{
sysInit();
}
# if defined(Q_WS_X11)
/*!
Special constructor for X11, ref. the documentation of
QApplication's corresponding constructor. The application identifier
will be QCoreApplication::applicationFilePath(). \a dpy, \a visual,
and \a cmap are passed on to the QApplication constructor.
*/
QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap)
: QApplication(dpy, visual, cmap)
{
sysInit();
}
/*!
Special constructor for X11, ref. the documentation of
QApplication's corresponding constructor. The application identifier
will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a
argv, \a visual, and \a cmap are passed on to the QApplication
constructor.
*/
QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap)
: QApplication(dpy, argc, argv, visual, cmap)
{
sysInit();
}
/*!
Special constructor for X11, ref. the documentation of
QApplication's corresponding constructor. The application identifier
will be \a appId. \a dpy, \a argc, \a
argv, \a visual, and \a cmap are passed on to the QApplication
constructor.
*/
QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap)
: QApplication(dpy, argc, argv, visual, cmap)
{
sysInit(appId);
}
# endif // Q_WS_X11
#endif // QT_VERSION < 0x050000
/*!
Returns true if another instance of this application is running;
otherwise false.
This function does not find instances of this application that are
being run by a different user (on Windows: that are running in
another session).
\sa sendMessage()
*/
bool QtSingleApplication::isRunning()
{
return peer->isClient();
}
/*!
Tries to send the text \a message to the currently running
instance. The QtSingleApplication object in the running instance
will emit the messageReceived() signal when it receives the
message.
This function returns true if the message has been sent to, and
processed by, the current instance. If there is no instance
currently running, or if the running instance fails to process the
message within \a timeout milliseconds, this function return false.
\sa isRunning(), messageReceived()
*/
bool QtSingleApplication::sendMessage(const QString &message, int timeout)
{
return peer->sendMessage(message, timeout);
}
/*!
Returns the application identifier. Two processes with the same
identifier will be regarded as instances of the same application.
*/
QString QtSingleApplication::id() const
{
return peer->applicationId();
}
/*!
Sets the activation window of this application to \a aw. The
activation window is the widget that will be activated by
activateWindow(). This is typically the application's main window.
If \a activateOnMessage is true (the default), the window will be
activated automatically every time a message is received, just prior
to the messageReceived() signal being emitted.
\sa activateWindow(), messageReceived()
*/
void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage)
{
actWin = aw;
if (activateOnMessage)
connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow()));
else
disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow()));
}
/*!
Returns the applications activation window if one has been set by
calling setActivationWindow(), otherwise returns 0.
\sa setActivationWindow()
*/
QWidget* QtSingleApplication::activationWindow() const
{
return actWin;
}
/*!
De-minimizes, raises, and activates this application's activation window.
This function does nothing if no activation window has been set.
This is a convenience function to show the user that this
application instance has been activated when he has tried to start
another instance.
This function should typically be called in response to the
messageReceived() signal. By default, that will happen
automatically, if an activation window has been set.
\sa setActivationWindow(), messageReceived(), initialize()
*/
void QtSingleApplication::activateWindow()
{
if (actWin) {
actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized);
actWin->raise();
actWin->activateWindow();
}
}
/*!
\fn void QtSingleApplication::messageReceived(const QString& message)
This signal is emitted when the current instance receives a \a
message from another instance of this application.
\sa sendMessage(), setActivationWindow(), activateWindow()
*/
/*!
\fn void QtSingleApplication::initialize(bool dummy = true)
\obsolete
*/

105
src/app/qtsingleapplication/qtsingleapplication.h

@ -1,105 +0,0 @@ @@ -1,105 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Solutions component.
**
** $QT_BEGIN_LICENSE:BSD$
** 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 Digia Plc and its Subsidiary(-ies) 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 QTSINGLEAPPLICATION_H
#define QTSINGLEAPPLICATION_H
#include <QApplication>
class QtLocalPeer;
#if defined(Q_OS_WIN)
# if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT)
# define QT_QTSINGLEAPPLICATION_EXPORT
# elif defined(QT_QTSINGLEAPPLICATION_IMPORT)
# if defined(QT_QTSINGLEAPPLICATION_EXPORT)
# undef QT_QTSINGLEAPPLICATION_EXPORT
# endif
# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport)
# elif defined(QT_QTSINGLEAPPLICATION_EXPORT)
# undef QT_QTSINGLEAPPLICATION_EXPORT
# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport)
# endif
#else
# define QT_QTSINGLEAPPLICATION_EXPORT
#endif
class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication
{
Q_OBJECT
public:
QtSingleApplication(int &argc, char **argv, bool GUIenabled = true);
QtSingleApplication(const QString &id, int &argc, char **argv);
#if QT_VERSION < 0x050000
QtSingleApplication(int &argc, char **argv, Type type);
# if defined(Q_WS_X11)
QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0);
QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
# endif // Q_WS_X11
#endif // QT_VERSION < 0x050000
bool isRunning();
QString id() const;
void setActivationWindow(QWidget* aw, bool activateOnMessage = true);
QWidget* activationWindow() const;
// Obsolete:
void initialize(bool dummy = true)
{ isRunning(); Q_UNUSED(dummy) }
public Q_SLOTS:
bool sendMessage(const QString &message, int timeout = 5000);
void activateWindow();
Q_SIGNALS:
void messageReceived(const QString &message);
private:
void sysInit(const QString &appId = QString());
QtLocalPeer *peer;
QWidget *actWin;
};
#endif // QTSINGLEAPPLICATION_H

16
src/app/qtsingleapplication/qtsingleapplication.pri

@ -1,16 +0,0 @@ @@ -1,16 +0,0 @@
INCLUDEPATH += $$PWD
DEPENDPATH += $$PWD
QT *= network
greaterThan(QT_MAJOR_VERSION, 4): QT *= widgets
qtsingleapplication-uselib:!qtsingleapplication-buildlib {
LIBS += -L$$QTSINGLEAPPLICATION_LIBDIR -l$$QTSINGLEAPPLICATION_LIBNAME
} else {
SOURCES += $$PWD/qtsingleapplication.cpp $$PWD/qtlocalpeer.cpp
HEADERS += $$PWD/qtsingleapplication.h $$PWD/qtlocalpeer.h
}
win32 {
contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTSINGLEAPPLICATION_EXPORT
else:qtsingleapplication-uselib:DEFINES += QT_QTSINGLEAPPLICATION_IMPORT
}

149
src/app/qtsingleapplication/qtsinglecoreapplication.cpp

@ -1,149 +0,0 @@ @@ -1,149 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Solutions component.
**
** $QT_BEGIN_LICENSE:BSD$
** 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 Digia Plc and its Subsidiary(-ies) 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 "qtsinglecoreapplication.h"
#include "qtlocalpeer.h"
/*!
\class QtSingleCoreApplication qtsinglecoreapplication.h
\brief A variant of the QtSingleApplication class for non-GUI applications.
This class is a variant of QtSingleApplication suited for use in
console (non-GUI) applications. It is an extension of
QCoreApplication (instead of QApplication). It does not require
the QtGui library.
The API and usage is identical to QtSingleApplication, except that
functions relating to the "activation window" are not present, for
obvious reasons. Please refer to the QtSingleApplication
documentation for explanation of the usage.
A QtSingleCoreApplication instance can communicate to a
QtSingleApplication instance if they share the same application
id. Hence, this class can be used to create a light-weight
command-line tool that sends commands to a GUI application.
\sa QtSingleApplication
*/
/*!
Creates a QtSingleCoreApplication object. The application identifier
will be QCoreApplication::applicationFilePath(). \a argc and \a
argv are passed on to the QCoreAppliation constructor.
*/
QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv)
: QCoreApplication(argc, argv)
{
peer = new QtLocalPeer(this);
connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
}
/*!
Creates a QtSingleCoreApplication object with the application
identifier \a appId. \a argc and \a argv are passed on to the
QCoreAppliation constructor.
*/
QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv)
: QCoreApplication(argc, argv)
{
peer = new QtLocalPeer(this, appId);
connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
}
/*!
Returns true if another instance of this application is running;
otherwise false.
This function does not find instances of this application that are
being run by a different user (on Windows: that are running in
another session).
\sa sendMessage()
*/
bool QtSingleCoreApplication::isRunning()
{
return peer->isClient();
}
/*!
Tries to send the text \a message to the currently running
instance. The QtSingleCoreApplication object in the running instance
will emit the messageReceived() signal when it receives the
message.
This function returns true if the message has been sent to, and
processed by, the current instance. If there is no instance
currently running, or if the running instance fails to process the
message within \a timeout milliseconds, this function return false.
\sa isRunning(), messageReceived()
*/
bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout)
{
return peer->sendMessage(message, timeout);
}
/*!
Returns the application identifier. Two processes with the same
identifier will be regarded as instances of the same application.
*/
QString QtSingleCoreApplication::id() const
{
return peer->applicationId();
}
/*!
\fn void QtSingleCoreApplication::messageReceived(const QString& message)
This signal is emitted when the current instance receives a \a
message from another instance of this application.
\sa sendMessage()
*/

71
src/app/qtsingleapplication/qtsinglecoreapplication.h

@ -1,71 +0,0 @@ @@ -1,71 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Solutions component.
**
** $QT_BEGIN_LICENSE:BSD$
** 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 Digia Plc and its Subsidiary(-ies) 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 QTSINGLECOREAPPLICATION_H
#define QTSINGLECOREAPPLICATION_H
#include <QCoreApplication>
class QtLocalPeer;
class QtSingleCoreApplication : public QCoreApplication
{
Q_OBJECT
public:
QtSingleCoreApplication(int &argc, char **argv);
QtSingleCoreApplication(const QString &id, int &argc, char **argv);
bool isRunning();
QString id() const;
public Q_SLOTS:
bool sendMessage(const QString &message, int timeout = 5000);
Q_SIGNALS:
void messageReceived(const QString &message);
private:
QtLocalPeer* peer;
};
#endif // QTSINGLECOREAPPLICATION_H

10
src/app/qtsingleapplication/qtsinglecoreapplication.pri

@ -1,10 +0,0 @@ @@ -1,10 +0,0 @@
INCLUDEPATH += $$PWD
DEPENDPATH += $$PWD
HEADERS += $$PWD/qtsinglecoreapplication.h $$PWD/qtlocalpeer.h
SOURCES += $$PWD/qtsinglecoreapplication.cpp $$PWD/qtlocalpeer.cpp
QT *= network
win32:contains(TEMPLATE, lib):contains(CONFIG, shared) {
DEFINES += QT_QTSINGLECOREAPPLICATION_EXPORT=__declspec(dllexport)
}

1
src/gui/CMakeLists.txt

@ -130,7 +130,6 @@ target_link_libraries(qbt_gui @@ -130,7 +130,6 @@ target_link_libraries(qbt_gui
PRIVATE
qbt_lineedit qbt_powermanagement qbt_rss qbt_properties qbt_searchengine
qbt_base
QtSingleApplication::QtSingleApplication
)
target_include_directories(qbt_gui

Loading…
Cancel
Save