Updater stuff.

This commit is contained in:
Martin Rotter 2014-04-10 13:06:35 +02:00
parent a876726177
commit 2643feaaa9
20 changed files with 812 additions and 189 deletions

View File

@ -261,12 +261,13 @@ elseif(WIN32 AND MSVC)
set(APP_SOURCES ${APP_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/resources/executable_properties/rssguard_win.rc)
endif(MINGW AND WIN32)
# Add source files.
# APP source files.
set(APP_SOURCES
${APP_SOURCES}
# QTSINGLEAPPLICATION sources.
src/qtsingleapplication/qtlocalpeer.cpp
src/qtsingleapplication/qtsinglecoreapplication.cpp
src/qtsingleapplication/qtsingleapplication.cpp
# GUI sources.
@ -340,12 +341,13 @@ set(APP_SOURCES
src/main.cpp
)
# Add headers.
# APP headers.
set(APP_HEADERS
${APP_HEADERS}
# QTSINGLEAPPLICATION headers.
src/qtsingleapplication/qtlocalpeer.h
src/qtsingleapplication/qtsinglecoreapplication.h
src/qtsingleapplication/qtsingleapplication.h
# GUI headers.
@ -410,7 +412,7 @@ set(APP_HEADERS
src/application.h
)
# Add form files.
# APP form files.
set(APP_FORMS
src/gui/formupdate.ui
src/gui/formmain.ui
@ -421,7 +423,7 @@ set(APP_FORMS
src/gui/toolbareditor.ui
)
# Add translations.
# APP translations.
set(APP_TRANSLATIONS
localization/rssguard-cs_CZ.ts
localization/rssguard-de_DE.ts
@ -480,10 +482,57 @@ include_directories (
${CMAKE_CURRENT_SOURCE_DIR}/src/gui
${CMAKE_CURRENT_SOURCE_DIR}/src/network-web
${CMAKE_CURRENT_SOURCE_DIR}/src/dynamic-shortcuts
${CMAKE_CURRENT_SOURCE_DIR}/src/updater
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}/src
)
if(WIN32 OR OS2)
project(updater)
set(UPDATER_SOURCES
# QTSINGLEAPPLICATION sources.
src/qtsingleapplication/qtlocalpeer.cpp
src/qtsingleapplication/qtsinglecoreapplication.cpp
src/updater/detector.cpp
src/updater/main.cpp
)
set(UPDATER_HEADERS
# QTSINGLEAPPLICATION headers.
src/qtsingleapplication/qtlocalpeer.h
src/qtsingleapplication/qtsinglecoreapplication.h
src/updater/detector.h
)
if(${USE_QT_5})
add_executable("updater"
${UPDATER_SOURCES}
)
# Use modules from Qt.
qt5_use_modules(updater
Core
Network
)
else(${USE_QT_5})
qt4_wrap_cpp(UPDATER_MOC ${UPDATER_HEADERS})
add_executable("updater"
${UPDATER_SOURCES}
${UPDATER_MOC}
)
# Link modules from Qt.
target_link_libraries("updater"
${QT_QTCORE_LIBRARY}
${QT_QTNETWORK_LIBRARY}
)
endif(${USE_QT_5})
endif(WIN32 OR OS2)
# Setup compilation for Qt 5.
if(${USE_QT_5})
add_executable(${EXE_NAME} WIN32 MACOSX_BUNDLE

View File

@ -145,7 +145,7 @@ QPair<FeedsModelFeed*, QNetworkReply::NetworkError> FeedsModelFeed::guessFeed(co
}
QByteArray feed_contents;
if ((result.second = NetworkFactory::downloadFeedFile(url,
if ((result.second = NetworkFactory::downloadFile(url,
Settings::instance()->value(APP_CFG_FEEDS, "feed_update_timeout", DOWNLOAD_TIMEOUT).toInt(),
feed_contents,
!username.isEmpty(),
@ -349,7 +349,7 @@ QVariant FeedsModelFeed::data(int column, int role) const {
void FeedsModelFeed::update() {
QByteArray feed_contents;
int download_timeout = Settings::instance()->value(APP_CFG_FEEDS, "feed_update_timeout", DOWNLOAD_TIMEOUT).toInt();
QNetworkReply::NetworkError download_result = NetworkFactory::downloadFeedFile(url(),
QNetworkReply::NetworkError download_result = NetworkFactory::downloadFile(url(),
download_timeout,
feed_contents,
passwordProtected(),

View File

@ -103,6 +103,7 @@
#define APP_REVISION "@APP_REVISION@"
#define APP_QUIT_INSTANCE "app_quit"
#define APP_IS_RUNNING "app_is_running"
#define APP_SKIN_DEFAULT "base/vergilius.xml"
#define APP_THEME_DEFAULT "mini-kfaenza"

View File

@ -172,6 +172,9 @@ void FormMain::processExecutionMessage(const QString &message) {
display();
}
else if (message == APP_QUIT_INSTANCE) {
qApp->quit();
}
}
void FormMain::quit() {

View File

@ -27,6 +27,7 @@
#include <QNetworkReply>
#include <QDesktopServices>
#include <QProcess>
FormUpdate::FormUpdate(QWidget *parent)
@ -78,7 +79,7 @@ void FormUpdate::checkForUpdates() {
m_ui->m_lblAvailableRelease->setText(update.first.m_availableVersion);
m_ui->m_txtChanges->setText(update.first.m_changes);
if (update.first.m_availableVersion > APP_VERSION) {
if (update.first.m_availableVersion >= APP_VERSION) {
m_ui->m_lblStatus->setStatus(WidgetWithStatus::Ok,
tr("New release available."),
tr("This is new version which can be\ndownloaded and installed."));
@ -108,6 +109,44 @@ void FormUpdate::startUpdate() {
url_file = APP_URL;
}
#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
// On Windows/OS2 we can update the application right away.
// Download the files.
QByteArray output;
//NetworkFactory::downloadFile(url_file, DOWNLOAD_TIMEOUT, output);
#if QT_VERSION >= 0x050000
QString temp_directory = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
#else
QString temp_directory = QDesktopServices::storageLocation(QDesktopServices::TempLocation);
#endif
if (!temp_directory.isEmpty()) {
QString output_file_name = url_file.mid(url_file.lastIndexOf('/') + 1);
QFile output_file(temp_directory + QDir::separator() + output_file_name);
if (output_file.exists()) {
output_file.remove();
}
if (output_file.open(QIODevice::WriteOnly)) {
output_file.write(output);
output_file.flush();
output_file.close();
// TODO: spustit updater
// pouzit qprocess, nebo neco multiplatformniho
}
else {
// TODO: chyba - nelze zapisovat do souboru
}
}
else {
// TODO: chyba - nelze ulozit soubor.
}
#else
if (!WebFactory::instance()->openUrlInExternalBrowser(url_file)) {
if (SystemTrayIcon::isSystemTrayActivated()) {
SystemTrayIcon::instance()->showMessage(tr("Cannot update application"),
@ -123,4 +162,5 @@ void FormUpdate::startUpdate() {
"manually on project website."));
}
}
#endif
}

View File

@ -66,7 +66,7 @@ int main(int argc, char *argv[]) {
#endif
// Instantiate base application object.
QtSingleApplication application(argc, argv);
QtSingleApplication application(APP_LOW_NAME, argc, argv);
qDebug("Instantiated QtSingleApplication class.");
// Check if another instance is running.

View File

@ -170,7 +170,7 @@ QPair<UpdateInfo, QNetworkReply::NetworkError> SystemFactory::checkForUpdates()
QPair<UpdateInfo, QNetworkReply::NetworkError> result;
QByteArray releases_xml;
result.second = NetworkFactory::downloadFeedFile(RELEASES_LIST,
result.second = NetworkFactory::downloadFile(RELEASES_LIST,
5000,
releases_xml);

View File

@ -105,9 +105,9 @@ QNetworkReply::NetworkError NetworkFactory::downloadIcon(const QString &url,
#endif
QByteArray icon_data;
QNetworkReply::NetworkError network_result = downloadFeedFile(google_s2_with_url,
timeout,
icon_data);
QNetworkReply::NetworkError network_result = downloadFile(google_s2_with_url,
timeout,
icon_data);
if (network_result == QNetworkReply::NoError) {
QPixmap icon_pixmap;
@ -118,12 +118,12 @@ QNetworkReply::NetworkError NetworkFactory::downloadIcon(const QString &url,
return network_result;
}
QNetworkReply::NetworkError NetworkFactory::downloadFeedFile(const QString &url,
int timeout,
QByteArray &output,
bool protected_contents,
const QString &username,
const QString &password) {
QNetworkReply::NetworkError NetworkFactory::downloadFile(const QString &url,
int timeout,
QByteArray &output,
bool protected_contents,
const QString &username,
const QString &password) {
// Original asynchronous behavior of QNetworkAccessManager
// is replaced by synchronous behavior in order to make
// process of downloading of a file easier to understand.

View File

@ -41,7 +41,7 @@ class NetworkFactory {
// Performs SYNCHRONOUS download of file with given URL
// and given timeout.
static QNetworkReply::NetworkError downloadFeedFile(const QString &url,
static QNetworkReply::NetworkError downloadFile(const QString &url,
int timeout,
QByteArray &output,
bool protected_contents = false,

View File

@ -49,7 +49,7 @@
typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*);
static PProcessIdToSessionId pProcessIdToSessionId = 0;
#endif
#if defined(Q_OS_UNIX) || defined(Q_OS_OS2)
#if defined(Q_OS_UNIX)
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
@ -67,129 +67,137 @@ namespace QtLP_Private {
const char* QtLocalPeer::ack = "ack";
QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId)
: QObject(parent), id(appId) {
QString prefix = id;
if (id.isEmpty()) {
id = QCoreApplication::applicationFilePath();
: QObject(parent), id(appId)
{
QString prefix = id;
if (id.isEmpty()) {
id = QCoreApplication::applicationFilePath();
#if defined(Q_OS_WIN)
id = id.toLower();
id = id.toLower();
#endif
prefix = id.section(QLatin1Char('/'), -1);
}
prefix.remove(QRegExp("[^a-zA-Z]"));
prefix.truncate(6);
prefix = id.section(QLatin1Char('/'), -1);
}
prefix.remove(QRegExp("[^a-zA-Z]"));
prefix.truncate(6);
QByteArray idc = id.toUtf8();
quint16 idNum = qChecksum(idc.constData(), idc.size());
socketName = QLatin1String("qtsingleapp-") + prefix
+ QLatin1Char('-') + QString::number(idNum, 16);
QByteArray idc = id.toUtf8();
quint16 idNum = qChecksum(idc.constData(), idc.size());
socketName = QLatin1String("qtsingleapp-") + prefix
+ QLatin1Char('-') + QString::number(idNum, 16);
#if defined(Q_OS_WIN)
if (!pProcessIdToSessionId) {
QLibrary lib("kernel32");
pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId");
}
if (pProcessIdToSessionId) {
DWORD sessionId = 0;
pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
socketName += QLatin1Char('-') + QString::number(sessionId, 16);
}
if (!pProcessIdToSessionId) {
QLibrary lib("kernel32");
pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId");
}
if (pProcessIdToSessionId) {
DWORD sessionId = 0;
pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
socketName += QLatin1Char('-') + QString::number(sessionId, 16);
}
#else
socketName += QLatin1Char('-') + QString::number(::getuid(), 16);
socketName += QLatin1Char('-') + QString::number(::getuid(), 16);
#endif
server = new QLocalServer(this);
QString lockName = QDir(QDir::tempPath()).absolutePath()
+ QLatin1Char('/') + socketName
+ QLatin1String("-lockfile");
lockFile.setFileName(lockName);
lockFile.open(QIODevice::ReadWrite);
server = new QLocalServer(this);
QString lockName = QDir(QDir::tempPath()).absolutePath()
+ QLatin1Char('/') + socketName
+ QLatin1String("-lockfile");
lockFile.setFileName(lockName);
lockFile.open(QIODevice::ReadWrite);
}
bool QtLocalPeer::isClient() {
if (lockFile.isLocked())
return false;
if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false))
return true;
bool res = server->listen(socketName);
bool QtLocalPeer::isClient()
{
if (lockFile.isLocked())
return false;
if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false))
return true;
bool res = server->listen(socketName);
#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0))
// ### Workaround
if (!res && server->serverError() == QAbstractSocket::AddressInUseError) {
QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName);
res = server->listen(socketName);
}
// ### Workaround
if (!res && server->serverError() == QAbstractSocket::AddressInUseError) {
QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName);
res = server->listen(socketName);
}
#endif
if (!res)
qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString()));
QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection()));
return false;
if (!res)
qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString()));
QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection()));
return false;
}
bool QtLocalPeer::sendMessage(const QString &message, int timeout) {
if (!isClient())
return false;
QLocalSocket socket;
bool connOk = false;
for(int i = 0; i < 2; i++) {
// Try twice, in case the other instance is just starting up
socket.connectToServer(socketName);
connOk = socket.waitForConnected(timeout/2);
if (connOk || i)
break;
int ms = 250;
bool QtLocalPeer::sendMessage(const QString &message, int timeout)
{
if (!isClient())
return false;
QLocalSocket socket;
bool connOk = false;
for(int i = 0; i < 2; i++) {
// Try twice, in case the other instance is just starting up
socket.connectToServer(socketName);
connOk = socket.waitForConnected(timeout/2);
if (connOk || i)
break;
int ms = 250;
#if defined(Q_OS_WIN)
Sleep(DWORD(ms));
Sleep(DWORD(ms));
#else
struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
nanosleep(&ts, NULL);
struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
nanosleep(&ts, NULL);
#endif
}
if (!connOk)
return false;
}
if (!connOk)
return false;
QByteArray uMsg(message.toUtf8());
QDataStream ds(&socket);
ds.writeBytes(uMsg.constData(), uMsg.size());
bool res = socket.waitForBytesWritten(timeout);
if (res) {
res &= socket.waitForReadyRead(timeout); // wait for ack
if (res)
res &= (socket.read(qstrlen(ack)) == ack);
}
return res;
QByteArray uMsg(message.toUtf8());
QDataStream ds(&socket);
ds.writeBytes(uMsg.constData(), uMsg.size());
bool res = socket.waitForBytesWritten(timeout);
if (res) {
res &= socket.waitForReadyRead(timeout); // wait for ack
if (res)
res &= (socket.read(qstrlen(ack)) == ack);
}
return res;
}
void QtLocalPeer::receiveConnection() {
QLocalSocket* socket = server->nextPendingConnection();
if (!socket)
return;
while (socket->bytesAvailable() < (int)sizeof(quint32))
socket->waitForReadyRead();
QDataStream ds(socket);
QByteArray uMsg;
quint32 remaining;
ds >> remaining;
uMsg.resize(remaining);
int got = 0;
char* uMsgBuf = uMsg.data();
do {
got = ds.readRawData(uMsgBuf, remaining);
remaining -= got;
uMsgBuf += got;
} while (remaining && got >= 0 && socket->waitForReadyRead(2000));
if (got < 0) {
qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData());
void QtLocalPeer::receiveConnection()
{
QLocalSocket* socket = server->nextPendingConnection();
if (!socket)
return;
while (socket->bytesAvailable() < (int)sizeof(quint32))
socket->waitForReadyRead();
QDataStream ds(socket);
QByteArray uMsg;
quint32 remaining;
ds >> remaining;
uMsg.resize(remaining);
int got = 0;
char* uMsgBuf = uMsg.data();
do {
got = ds.readRawData(uMsgBuf, remaining);
remaining -= got;
uMsgBuf += got;
} while (remaining && got >= 0 && socket->waitForReadyRead(2000));
if (got < 0) {
qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData());
delete socket;
return;
}
QString message(QString::fromUtf8(uMsg));
socket->write(ack, qstrlen(ack));
socket->waitForBytesWritten(1000);
socket->waitForDisconnected(1000); // make sure client reads ack
delete socket;
return;
}
QString message(QString::fromUtf8(uMsg));
socket->write(ack, qstrlen(ack));
socket->waitForBytesWritten(1000);
socket->waitForDisconnected(1000); // make sure client reads ack
delete socket;
emit messageReceived(message); //### (might take a long time to return)
emit messageReceived(message); //### (might take a long time to return)
}

View File

@ -47,23 +47,21 @@
#include "qtlockedfile.h"
class QtLocalPeer : public QObject {
class QtLocalPeer : public QObject
{
Q_OBJECT
public:
explicit QtLocalPeer(QObject *parent = 0, const QString &appId = QString());
QtLocalPeer(QObject *parent = 0, const QString &appId = QString());
bool isClient();
bool sendMessage(const QString &message, int timeout);
QString applicationId() const
{ return id; }
QString applicationId() const {
return id;
}
Q_SIGNALS:
Q_SIGNALS:
void messageReceived(const QString &message);
protected Q_SLOTS:
protected Q_SLOTS:
void receiveConnection();
protected:

View File

@ -158,8 +158,8 @@ QtLockedFile::LockMode QtLockedFile::lockMode() const
can be locked.
If \a block is true, this function will block until the lock is
acquired. If \a block is false, this function returns \e false
immediately if the lock cannot be acquired.
aquired. If \a block is false, this function returns \e false
immediately if the lock cannot be aquired.
If this object already has a lock of type \a mode, this function
returns \e true immediately. If this object has a lock of a

View File

@ -69,8 +69,8 @@ class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile
public:
enum LockMode { NoLock = 0, ReadLock, WriteLock };
explicit QtLockedFile();
explicit QtLockedFile(const QString &name);
QtLockedFile();
QtLockedFile(const QString &name);
~QtLockedFile();
bool open(OpenMode mode);

View File

@ -38,79 +38,310 @@
**
****************************************************************************/
#include "qtsingleapplication.h"
#include "qtlocalpeer.h"
#include <QWidget>
void QtSingleApplication::sysInit(const QString &appId) {
actWin = 0;
peer = new QtLocalPeer(this, appId);
connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
/*!
\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();
: 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);
: 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();
: QApplication(argc, argv, type)
{
sysInit();
}
#if defined(Q_WS_X11)
# 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();
: 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();
: 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);
: QApplication(dpy, argc, argv, visual, cmap)
{
sysInit(appId);
}
# endif // Q_WS_X11
#endif // QT_VERSION < 0x050000
bool QtSingleApplication::isRunning() {
return peer->isClient();
/*!
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();
}
bool QtSingleApplication::sendMessage(const QString &message, int timeout) {
return peer->sendMessage(message, timeout);
/*!
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);
}
QString QtSingleApplication::id() const {
return peer->applicationId();
/*!
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();
}
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()));
/*!
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()));
}
QWidget* QtSingleApplication::activationWindow() const {
return actWin;
/*!
Returns the applications activation window if one has been set by
calling setActivationWindow(), otherwise returns 0.
\sa setActivationWindow()
*/
QWidget* QtSingleApplication::activationWindow() const
{
return actWin;
}
void QtSingleApplication::activateWindow() {
if (actWin) {
actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized);
actWin->raise();
actWin->activateWindow();
}
/*!
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
*/

View File

@ -61,18 +61,19 @@ class QtLocalPeer;
# define QT_QTSINGLEAPPLICATION_EXPORT
#endif
class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication {
class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication
{
Q_OBJECT
public:
explicit QtSingleApplication(int &argc, char **argv, bool GUIenabled = true);
explicit QtSingleApplication(const QString &id, int &argc, char **argv);
public:
QtSingleApplication(int &argc, char **argv, bool GUIenabled = true);
QtSingleApplication(const QString &id, int &argc, char **argv);
#if QT_VERSION < 0x050000
explicit QtSingleApplication(int &argc, char **argv, Type type);
QtSingleApplication(int &argc, char **argv, Type type);
# if defined(Q_WS_X11)
explicit QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
explicit QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0);
explicit QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
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
@ -83,23 +84,19 @@ class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication {
QWidget* activationWindow() const;
// Obsolete:
void initialize(bool dummy = true) {
isRunning();
Q_UNUSED(dummy)
}
void initialize(bool dummy = true)
{ isRunning(); Q_UNUSED(dummy) }
static QtSingleApplication *instance() {
return static_cast<QtSingleApplication*>(qApp);
}
public Q_SLOTS:
public Q_SLOTS:
bool sendMessage(const QString &message, int timeout = 5000);
void activateWindow();
Q_SIGNALS:
Q_SIGNALS:
void messageReceived(const QString &message);
private:
private:
void sysInit(const QString &appId = QString());
QtLocalPeer *peer;
QWidget *actWin;

View File

@ -0,0 +1,149 @@
/****************************************************************************
**
** 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()
*/

View File

@ -0,0 +1,71 @@
/****************************************************************************
**
** 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

11
src/updater/detector.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "updater/detector.h"
Detector::Detector(QObject *parent) : QObject(parent) {
}
void Detector::handleMessage(const QString &message) {
if (message == "app_is_running") {
qDebug("Detected another instance of RSS Guard/Updater was starting...");
}
}

17
src/updater/detector.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef DETECTOR_H
#define DETECTOR_H
#include <QObject>
class Detector : public QObject {
Q_OBJECT
public:
explicit Detector(QObject *parent = 0);
public slots:
void handleMessage(const QString& message);
};
#endif // DETECTOR_H

48
src/updater/main.cpp Normal file
View File

@ -0,0 +1,48 @@
// This file is part of RSS Guard.
//
// Copyright (C) 2011-2014 by Martin Rotter <rotter.martinos@gmail.com>
//
// RSS Guard 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 3 of the License, or
// (at your option) any later version.
//
// RSS Guard 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 RSS Guard. If not, see <http://www.gnu.org/licenses/>.
#include "qtsingleapplication/qtsinglecoreapplication.h"
#include "updater/detector.h"
#include <QTranslator>
#include <QDebug>
#include <QThread>
int main(int argc, char *argv[]) {
// Instantiate base application object.
QtSingleCoreApplication application("rssguard", argc, argv);
qDebug("Instantiated QtSingleApplication class.");
// Check if main RSS Guard instance is running.
if (application.sendMessage("app_quit")) {
qDebug("RSS Guard application is running. Quitting it...");
}
Detector detector;
qDebug().nospace() << "Running updater in thread: \'" <<
QThread::currentThreadId() << "\'.";
// Setup single-instance behavior.
QObject::connect(&application, SIGNAL(messageReceived(QString)),
&detector, SLOT(handleMessage(QString)));
// Enter global event loop.
return QtSingleCoreApplication::exec();
}