From 9be66ec3e086fe46a90b95b7e8aec8cbd09bef51 Mon Sep 17 00:00:00 2001 From: "craig.p.drummond" Date: Fri, 1 Jun 2012 16:25:49 +0000 Subject: [PATCH] - Make Qt-only builds single instance apps, as per KDE build. - Add commandline file loading support to Qt-only builds. --- CMakeLists.txt | 14 +- ChangeLog | 2 + application.cpp | 171 ++++++ application.h | 66 +++ main.cpp | 109 +--- qtsingleapplication/CMakeLists.txt | 27 + qtsingleapplication/LICENSE.LGPL | 504 ++++++++++++++++++ qtsingleapplication/QtLockedFile | 1 + qtsingleapplication/QtSingleApplication | 1 + qtsingleapplication/qtlocalpeer.cpp | 211 ++++++++ qtsingleapplication/qtlocalpeer.h | 83 +++ qtsingleapplication/qtlockedfile.cpp | 199 +++++++ qtsingleapplication/qtlockedfile.h | 101 ++++ qtsingleapplication/qtlockedfile_unix.cpp | 121 +++++ qtsingleapplication/qtlockedfile_win.cpp | 213 ++++++++ qtsingleapplication/qtsingleapplication.cpp | 369 +++++++++++++ qtsingleapplication/qtsingleapplication.h | 109 ++++ qtsingleapplication/qtsingleapplication.patch | 231 ++++++++ .../qtsinglecoreapplication.cpp | 169 ++++++ qtsingleapplication/qtsinglecoreapplication.h | 77 +++ 20 files changed, 2675 insertions(+), 103 deletions(-) create mode 100644 application.cpp create mode 100644 application.h create mode 100644 qtsingleapplication/CMakeLists.txt create mode 100644 qtsingleapplication/LICENSE.LGPL create mode 100644 qtsingleapplication/QtLockedFile create mode 100644 qtsingleapplication/QtSingleApplication create mode 100644 qtsingleapplication/qtlocalpeer.cpp create mode 100644 qtsingleapplication/qtlocalpeer.h create mode 100644 qtsingleapplication/qtlockedfile.cpp create mode 100644 qtsingleapplication/qtlockedfile.h create mode 100644 qtsingleapplication/qtlockedfile_unix.cpp create mode 100644 qtsingleapplication/qtlockedfile_win.cpp create mode 100644 qtsingleapplication/qtsingleapplication.cpp create mode 100644 qtsingleapplication/qtsingleapplication.h create mode 100644 qtsingleapplication/qtsingleapplication.patch create mode 100644 qtsingleapplication/qtsinglecoreapplication.cpp create mode 100644 qtsingleapplication/qtsinglecoreapplication.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 70a1706a2..7e6bcc2af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,7 @@ macro(cantata_check_for_sse) endmacro(cantata_check_for_sse) SET( CANTATA_SRCS + application.cpp main.cpp gui/mainwindow.cpp gui/preferencesdialog.cpp @@ -425,8 +426,9 @@ IF( ENABLE_KDE_SUPPORT ) add_subdirectory(po) ELSE( ENABLE_KDE_SUPPORT ) SET( CANTATA_SRCS ${CANTATA_SRCS} widgets/dirrequester.cpp widgets/lineedit.cpp network/networkproxyfactory.cpp network/proxysettings.cpp widgets/kmessagewidget.cpp - widgets/dialog.cpp widgets/messagebox.cpp ) - SET( CANTATA_MOC_HDRS ${CANTATA_MOC_HDRS} widgets/dirrequester.h widgets/lineedit.h network/proxysettings.h widgets/kmessagewidget.h widgets/urllabel.h widgets/dialog.h ) + widgets/dialog.cpp widgets/messagebox.cpp ) + SET( CANTATA_MOC_HDRS ${CANTATA_MOC_HDRS} application.h widgets/dirrequester.h widgets/lineedit.h network/proxysettings.h widgets/kmessagewidget.h widgets/urllabel.h + widgets/dialog.h ) SET( CANTATA_UIS ${CANTATA_UIS} network/proxysettings.ui ) SET( CANTATA_RCS ${CANTATA_RCS} cantata_qt.qrc ) QT4_ADD_RESOURCES( CANTATA_RC_SRCS ${CANTATA_RCS} ) @@ -453,6 +455,10 @@ ELSE( ENABLE_KDE_SUPPORT ) install( TARGETS cantata RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) ADD_DEFINITIONS( -DQT_NO_STL -DQT_NO_CAST_TO_ASCII -Wall -Wextra ) SET( XDG_APPS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/applications" ) + + add_subdirectory(qtsingleapplication) + TARGET_LINK_LIBRARIES(cantata qtsingleapplication) + include_directories(${CMAKE_BINARY_DIR}/qtsingleapplication) ENDIF( ENABLE_KDE_SUPPORT ) ADD_SUBDIRECTORY( icons ) @@ -503,8 +509,6 @@ message("") IF( ENABLE_KDE_SUPPORT ) message("INFO: Building with KDE support") message(" Enabled features:") - message(" - Track organizer") - message(" - Open files via command line") message(" - UMS device sync") if (MTP_FOUND) message(" - MTP device sync") @@ -534,7 +538,7 @@ IF( ENABLE_KDE_SUPPORT ) endif( NOT MTP_FOUND OR NOT FFMPEG_FOUND OR NOT MPG123_FOUND ) ELSE( ENABLE_KDE_SUPPORT ) if (NOT WIN32) - message("INFO: Building WITHOUT KDE support - Device sync, ReplayGain calculation, and CommandLine support DISABLED") + message("INFO: Building WITHOUT KDE support - Device sync and ReplayGain calculation support DISABLED") endif (NOT WIN32) ENDIF( ENABLE_KDE_SUPPORT ) message("") diff --git a/ChangeLog b/ChangeLog index 035c469cc..88eb3db90 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,8 @@ 'locate in library'. Its possible that the configured MPD dir does not exist - but 'locate in library' should still work. 7. Implement a basic spinner widgets for item views in Qt-only builds. +8. Make Qt-only builds single instance apps, as per KDE build. +9. Add commandline file loading support to Qt-only builds. 0.7.1 ----- diff --git a/application.cpp b/application.cpp new file mode 100644 index 000000000..8bed3ba94 --- /dev/null +++ b/application.cpp @@ -0,0 +1,171 @@ +/* + * Cantata + * + * Copyright (c) 2011-2012 Craig Drummond + * + * ---- + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "application.h" +#ifdef ENABLE_KDE_SUPPORT +#include +#include +#include +#else +#include +#include +#include +#endif +#include "utils.h" +#include "config.h" +#include "mainwindow.h" + +#ifdef ENABLE_KDE_SUPPORT +#ifdef Q_WS_X11 +Application::Application(Display *display, Qt::HANDLE visual, Qt::HANDLE colormap) + : KUniqueApplication(display, visual, colormap) + , w(0) +{ +} +#endif + +Application::Application() + : KUniqueApplication() + , w(0) +{ +} + +Application::~Application() { + if (w) { + delete w; + w=0; + } +} + +int Application::newInstance() { + if (w) { + if (!w->isVisible()) { + w->showNormal(); + } + } else { + if (0==Utils::getAudioGroupId() && KMessageBox::Cancel==KMessageBox::warningContinueCancel(0, + i18n("You are not currently a member of the \"audio\" group. " + "Cantata will function better (saving of album covers, lyrics, etc. with the correct permissions) if you " + "(or your administrator) add yourself to this group.\n\n" + "Note, that if you do add yourself you will need to logout and back in for this to take effect.\n\n" + "Select \"Continue\" to start Cantata as is."), + i18n("Audio Group"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "audioGroupWarning")) { + QApplication::exit(0); + } + w=new MainWindow(); + } + + KCmdLineArgs *args(KCmdLineArgs::parsedArgs()); + QList urls; + for (int i = 0; i < args->count(); ++i) { + urls.append(QUrl(args->url(i))); + } + if (!urls.isEmpty()) { + w->load(urls); + } + KStartupInfo::appStarted(startupId()); + return 0; +} +#else // ENABLE_KDE_SUPPORT +Application::Application(int &argc, char **argv) + : QtSingleApplication(argc, argv) +{ + connect(this, SIGNAL(messageReceived(const QString &)), SLOT(message(const QString &))); +} + +Application::~Application() +{ +} + +bool Application::start() +{ + if (isRunning()) { + QStringList args(arguments()); + if (args.count()>1) { + args.takeAt(0); + sendMessage(args.join("\n")); + } + return false; + } + + setupIconTheme(); + return true; +} + +void Application::loadFiles() +{ + QStringList args(arguments()); + if (args.count()>1) { + args.takeAt(0); + load(args); + } +} + +void Application::setupIconTheme() +{ + // Check that we have certain icons in the selected icon theme. If not, and oxygen is installed, then + // set icon theme to oxygen. + QString theme=QIcon::themeName(); + if (QLatin1String("oxygen")!=theme) { + QStringList check=QStringList() << "actions/edit-clear-list" << "actions/view-media-playlist" + << "actions/view-media-lyrics" << "actions/configure" + << "actions/view-choose" << "actions/view-media-artist" + << "places/server-database" << "devices/media-optical-audio"; + + foreach (const QString &icn, check) { + if (!QIcon::hasThemeIcon(icn)) { + QStringList paths=QIcon::themeSearchPaths(); + + foreach (const QString &p, paths) { + if (QDir(p+QLatin1String("/oxygen")).exists()) { + QIcon::setThemeName(QLatin1String("oxygen")); + return; + } + } + return; + } + } + } +} + +void Application::message(const QString &msg) +{ + load(msg.split("\n")); +} + +void Application::load(const QStringList &files) +{ + if (files.isEmpty()) { + return; + } + + QList urls; + foreach (const QString &f, files) { + urls.append(QUrl(f)); + } + if (!urls.isEmpty()) { + qobject_cast(activationWindow())->load(urls); + } +} + +#endif diff --git a/application.h b/application.h new file mode 100644 index 000000000..e5035d064 --- /dev/null +++ b/application.h @@ -0,0 +1,66 @@ +/* + * Cantata + * + * Copyright (c) 2011-2012 Craig Drummond + * + * ---- + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef APPLICATION_H +#define APPLICATION_H + +#ifdef ENABLE_KDE_SUPPORT +#include +class MainWindow; +class Application : public KUniqueApplication +{ +public: + #ifdef Q_WS_X11 + Application(Display *display, Qt::HANDLE visual, Qt::HANDLE colormap); + #endif + Application(); + ~Application(); + + int newInstance(); + +private: + MainWindow *w; +}; +#else +#include "qtsingleapplication/qtsingleapplication.h" +class Application : public QtSingleApplication +{ + Q_OBJECT + +public: + Application(int &argc, char **argv); + virtual ~Application(); + + bool start(); + void loadFiles(); + +private: + void setupIconTheme(); + void load(const QStringList &files); + +private Q_SLOTS: + void message(const QString &m); +}; +#endif + +#endif diff --git a/main.cpp b/main.cpp index 617e27951..64a92edc0 100644 --- a/main.cpp +++ b/main.cpp @@ -21,108 +21,17 @@ * Boston, MA 02110-1301, USA. */ +#include "application.h" #ifdef ENABLE_KDE_SUPPORT #include #include #include #include -#include -#else -#include -#include -#include -#include #endif #include "utils.h" #include "config.h" #include "mainwindow.h" -#ifdef ENABLE_KDE_SUPPORT -class CantataApp : public KUniqueApplication -{ -public: - #ifdef Q_WS_X11 - CantataApp(Display *display, Qt::HANDLE visual, Qt::HANDLE colormap) - : KUniqueApplication(display, visual, colormap) - , w(0) - { - } - #endif - - CantataApp() - : KUniqueApplication() - , w(0) - { - } - - ~CantataApp() { - if (w) { - delete w; - w=0; - } - } - - int newInstance() { - if (w) { - if (!w->isVisible()) { - w->showNormal(); - } - } else { - if (0==Utils::getAudioGroupId() && KMessageBox::Cancel==KMessageBox::warningContinueCancel(0, - i18n("You are not currently a member of the \"audio\" group. " - "Cantata will function better (saving of album covers, lyrics, etc. with the correct permissions) if you " - "(or your administrator) add yourself to this group.\n\n" - "Note, that if you do add yourself you will need to logout and back in for this to take effect.\n\n" - "Select \"Continue\" to start Cantata as is."), - i18n("Audio Group"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "audioGroupWarning")) { - QApplication::exit(0); - } - w=new MainWindow(); - } - - KCmdLineArgs *args(KCmdLineArgs::parsedArgs()); - QList urls; - for (int i = 0; i < args->count(); ++i) { - urls.append(QUrl(args->url(i))); - } - if (!urls.isEmpty()) { - w->load(urls); - } - KStartupInfo::appStarted(startupId()); - return 0; - } - - MainWindow *w; -}; -#else -void setupIconTheme() -{ - // Check that we have certain icons in the selected icon theme. If not, and oxygen is installed, then - // set icon theme to oxygen. - QString theme=QIcon::themeName(); - if (QLatin1String("oxygen")!=theme) { - QStringList check=QStringList() << "actions/edit-clear-list" << "actions/view-media-playlist" - << "actions/view-media-lyrics" << "actions/configure" - << "actions/view-choose" << "actions/view-media-artist" - << "places/server-database" << "devices/media-optical-audio"; - - foreach (const QString &icn, check) { - if (!QIcon::hasThemeIcon(icn)) { - QStringList paths=QIcon::themeSearchPaths(); - - foreach (const QString &p, paths) { - if (QDir(p+QLatin1String("/oxygen")).exists()) { - QIcon::setThemeName(QLatin1String("oxygen")); - return; - } - } - return; - } - } - } -} -#endif - int main(int argc, char *argv[]) { #ifdef ENABLE_KDE_SUPPORT @@ -149,18 +58,22 @@ int main(int argc, char *argv[]) if (!KUniqueApplication::start()) exit(0); - CantataApp app; + Application app; #else - QApplication app(argc, argv); - QApplication::setApplicationName(PACKAGE_NAME); + QCoreApplication::setApplicationName(PACKAGE_NAME); #ifdef Q_WS_WIN - QApplication::setOrganizationName("mpd"); + QCoreApplication::setOrganizationName("mpd"); #else - QApplication::setOrganizationName(PACKAGE_NAME); + QCoreApplication::setOrganizationName(PACKAGE_NAME); #endif - setupIconTheme(); + Application app(argc, argv); + if (!app.start()) { + return 0; + } MainWindow mw; + app.setActivationWindow(&mw); + app.loadFiles(); #endif return app.exec(); diff --git a/qtsingleapplication/CMakeLists.txt b/qtsingleapplication/CMakeLists.txt new file mode 100644 index 000000000..1d78baba4 --- /dev/null +++ b/qtsingleapplication/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 2.6) + +set(SINGLEAPP-SOURCES + qtlocalpeer.cpp + qtlockedfile.cpp + qtsingleapplication.cpp + qtsinglecoreapplication.cpp +) + +set(SINGLEAPP-MOC-HEADERS + qtlocalpeer.h + qtsingleapplication.h + qtsinglecoreapplication.h +) + +if(WIN32) + set(SINGLEAPP-SOURCES ${SINGLEAPP-SOURCES} qtlockedfile_win.cpp) +elseif(WIN32) + set(SINGLEAPP-SOURCES ${SINGLEAPP-SOURCES} qtlockedfile_unix.cpp) +endif(WIN32) + +QT4_WRAP_CPP(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS}) + +ADD_LIBRARY(qtsingleapplication STATIC + ${SINGLEAPP-SOURCES} + ${SINGLEAPP-SOURCES-MOC} +) diff --git a/qtsingleapplication/LICENSE.LGPL b/qtsingleapplication/LICENSE.LGPL new file mode 100644 index 000000000..5ab7695ab --- /dev/null +++ b/qtsingleapplication/LICENSE.LGPL @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/qtsingleapplication/QtLockedFile b/qtsingleapplication/QtLockedFile new file mode 100644 index 000000000..16b48ba9d --- /dev/null +++ b/qtsingleapplication/QtLockedFile @@ -0,0 +1 @@ +#include "qtlockedfile.h" diff --git a/qtsingleapplication/QtSingleApplication b/qtsingleapplication/QtSingleApplication new file mode 100644 index 000000000..d111bf72d --- /dev/null +++ b/qtsingleapplication/QtSingleApplication @@ -0,0 +1 @@ +#include "qtsingleapplication.h" diff --git a/qtsingleapplication/qtlocalpeer.cpp b/qtsingleapplication/qtlocalpeer.cpp new file mode 100644 index 000000000..ee335d98e --- /dev/null +++ b/qtsingleapplication/qtlocalpeer.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + + +#include "qtlocalpeer.h" +#include +#include +#include + +#if defined(Q_OS_WIN) +#include +#include +typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); +static PProcessIdToSessionId pProcessIdToSessionId = 0; +#endif +#if defined(Q_OS_UNIX) +#include +#endif + +#include "qtlockedfile.cpp" +#if defined(Q_OS_WIN) +#include "qtlockedfile_win.cpp" +#else +#include "qtlockedfile_unix.cpp" +#endif + +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(); +#if defined(Q_OS_WIN) + id = id.toLower(); +#endif + 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); + +#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); + } +#else + 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); +} + + + +bool QtLocalPeer::isClient() +{ + if (lockFile.isLocked()) + return false; + + if (!lockFile.lock(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); + } +#endif + 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) +{ + return sendMessage(message.toUtf8(), timeout); +} + +bool QtLocalPeer::sendMessage(const char* message, int timeout) +{ + return sendMessage(QByteArray(message), timeout); +} + +bool QtLocalPeer::sendMessage(const QByteArray &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)); +#else + struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; + nanosleep(&ts, NULL); +#endif + } + if (!connOk) + return false; + + QDataStream ds(&socket); + ds.writeBytes(message.constData(), message.size()); + bool res = socket.waitForBytesWritten(timeout); + res &= socket.waitForReadyRead(timeout); // wait for ack + 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" << socket->errorString(); + delete socket; + return; + } + socket->write(ack, qstrlen(ack)); + socket->waitForBytesWritten(1000); + delete socket; + emit messageReceived(uMsg); //### (might take a long time to return) + emit messageReceived(QString::fromUtf8(uMsg)); +} diff --git a/qtsingleapplication/qtlocalpeer.h b/qtsingleapplication/qtlocalpeer.h new file mode 100644 index 000000000..dc3ca2253 --- /dev/null +++ b/qtsingleapplication/qtlocalpeer.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + + +#include +#include +#include + +#include "qtlockedfile.h" + +class QtLocalPeer : public QObject +{ + Q_OBJECT + +public: + QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); + bool isClient(); + bool sendMessage(const QString &message, int timeout); + bool sendMessage(const QByteArray &message, int timeout); + bool sendMessage(const char* message, int timeout); + QString applicationId() const + { return id; } + +Q_SIGNALS: + void messageReceived(const QString &message); + void messageReceived(const QByteArray &message); + void messageReceived(const char* message); + +protected Q_SLOTS: + void receiveConnection(); + +protected: + QString id; + QString socketName; + QLocalServer* server; + QtLockedFile lockFile; + +private: + static const char* ack; +}; diff --git a/qtsingleapplication/qtlockedfile.cpp b/qtsingleapplication/qtlockedfile.cpp new file mode 100644 index 000000000..2cf080584 --- /dev/null +++ b/qtsingleapplication/qtlockedfile.cpp @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + +#include "qtlockedfile.h" + +/*! + \class QtLockedFile + + \brief The QtLockedFile class extends QFile with advisory locking + functions. + + A file may be locked in read or write mode. Multiple instances of + \e QtLockedFile, created in multiple processes running on the same + machine, may have a file locked in read mode. Exactly one instance + may have it locked in write mode. A read and a write lock cannot + exist simultaneously on the same file. + + The file locks are advisory. This means that nothing prevents + another process from manipulating a locked file using QFile or + file system functions offered by the OS. Serialization is only + guaranteed if all processes that access the file use + QLockedFile. Also, while holding a lock on a file, a process + must not open the same file again (through any API), or locks + can be unexpectedly lost. + + The lock provided by an instance of \e QtLockedFile is released + whenever the program terminates. This is true even when the + program crashes and no destructors are called. +*/ + +/*! \enum QtLockedFile::LockMode + + This enum describes the available lock modes. + + \value ReadLock A read lock. + \value WriteLock A write lock. + \value NoLock Neither a read lock nor a write lock. +*/ + +/*! + Constructs an unlocked \e QtLockedFile object. This constructor + behaves in the same way as \e QFile::QFile(). + + \sa QFile::QFile() +*/ +QtLockedFile::QtLockedFile() + : QFile() +{ +#ifdef Q_OS_WIN + wmutex = 0; + rmutex = 0; +#endif + m_lock_mode = NoLock; +} + +/*! + Constructs an unlocked QtLockedFile object with file \a name. This + constructor behaves in the same way as \e QFile::QFile(const + QString&). + + \sa QFile::QFile() +*/ +QtLockedFile::QtLockedFile(const QString &name) + : QFile(name) +{ +#ifdef Q_OS_WIN + wmutex = 0; + rmutex = 0; +#endif + m_lock_mode = NoLock; +} + +/*! + Opens the file in OpenMode \a mode. + + This is identical to QFile::open(), with the one exception that the + Truncate mode flag is disallowed. Truncation would conflict with the + advisory file locking, since the file would be modified before the + write lock is obtained. If truncation is required, use resize(0) + after obtaining the write lock. + + Returns true if successful; otherwise false. + + \sa QFile::open(), QFile::resize() +*/ +bool QtLockedFile::open(OpenMode mode) +{ + if (mode & QIODevice::Truncate) { + qWarning("QtLockedFile::open(): Truncate mode not allowed."); + return false; + } + return QFile::open(mode); +} + +/*! + Returns \e true if this object has a in read or write lock; + otherwise returns \e false. + + \sa lockMode() +*/ +bool QtLockedFile::isLocked() const +{ + return m_lock_mode != NoLock; +} + +/*! + Returns the type of lock currently held by this object, or \e + QtLockedFile::NoLock. + + \sa isLocked() +*/ +QtLockedFile::LockMode QtLockedFile::lockMode() const +{ + return m_lock_mode; +} + +/*! + \fn bool QtLockedFile::lock(LockMode mode, bool block = true) + + Obtains a lock of type \a mode. The file must be opened before it + can be locked. + + If \a block is true, this function will block until the lock is + 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 + different type than \a mode, the lock is first released and then a + new lock is obtained. + + This function returns \e true if, after it executes, the file is + locked by this object, and \e false otherwise. + + \sa unlock(), isLocked(), lockMode() +*/ + +/*! + \fn bool QtLockedFile::unlock() + + Releases a lock. + + If the object has no lock, this function returns immediately. + + This function returns \e true if, after it executes, the file is + not locked by this object, and \e false otherwise. + + \sa lock(), isLocked(), lockMode() +*/ + +/*! + \fn QtLockedFile::~QtLockedFile() + + Destroys the \e QtLockedFile object. If any locks were held, they + are released. +*/ diff --git a/qtsingleapplication/qtlockedfile.h b/qtsingleapplication/qtlockedfile.h new file mode 100644 index 000000000..1d3b918ec --- /dev/null +++ b/qtsingleapplication/qtlockedfile.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + +#ifndef QTLOCKEDFILE_H +#define QTLOCKEDFILE_H + +#include +#ifdef Q_OS_WIN +#include +#endif + +#if defined(Q_WS_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 + +class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile +{ +public: + enum LockMode { NoLock = 0, ReadLock, WriteLock }; + + QtLockedFile(); + QtLockedFile(const QString &name); + ~QtLockedFile(); + + bool open(OpenMode mode); + + bool lock(LockMode mode, bool block = true); + bool unlock(); + bool isLocked() const; + LockMode lockMode() const; + +private: +#ifdef Q_OS_WIN + Qt::HANDLE wmutex; + Qt::HANDLE rmutex; + QVector rmutexes; + QString mutexname; + + Qt::HANDLE getMutexHandle(int idx, bool doCreate); + bool waitMutex(Qt::HANDLE mutex, bool doBlock); + +#endif + LockMode m_lock_mode; +}; + +#endif diff --git a/qtsingleapplication/qtlockedfile_unix.cpp b/qtsingleapplication/qtlockedfile_unix.cpp new file mode 100644 index 000000000..2881bdd2c --- /dev/null +++ b/qtsingleapplication/qtlockedfile_unix.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + +#include +#include +#include +#include + +#include "qtlockedfile.h" + +bool QtLockedFile::lock(LockMode mode, bool block) +{ + if (!isOpen()) { + qWarning("QtLockedFile::lock(): file is not opened"); + return false; + } + + if (mode == NoLock) + return unlock(); + + if (mode == m_lock_mode) + return true; + + if (m_lock_mode != NoLock) + unlock(); + + struct flock fl; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; + int cmd = block ? F_SETLKW : F_SETLK; + int ret = fcntl(handle(), cmd, &fl); + + if (ret == -1) { + if (errno != EINTR && errno != EAGAIN) + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); + return false; + } + + + m_lock_mode = mode; + return true; +} + + +bool QtLockedFile::unlock() +{ + if (!isOpen()) { + qWarning("QtLockedFile::unlock(): file is not opened"); + return false; + } + + if (!isLocked()) + return true; + + struct flock fl; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fl.l_type = F_UNLCK; + int ret = fcntl(handle(), F_SETLKW, &fl); + + if (ret == -1) { + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); + return false; + } + + m_lock_mode = NoLock; + return true; +} + +QtLockedFile::~QtLockedFile() +{ + if (isOpen()) + unlock(); +} + diff --git a/qtsingleapplication/qtlockedfile_win.cpp b/qtsingleapplication/qtlockedfile_win.cpp new file mode 100644 index 000000000..f41bb67d8 --- /dev/null +++ b/qtsingleapplication/qtlockedfile_win.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + +#include "qtlockedfile.h" +#include +#include + +#define MUTEX_PREFIX "QtLockedFile mutex " +// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS +#define MAX_READERS MAXIMUM_WAIT_OBJECTS + +Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate) +{ + if (mutexname.isEmpty()) { + QFileInfo fi(*this); + mutexname = QString::fromLatin1(MUTEX_PREFIX) + + fi.absoluteFilePath().toLower(); + } + QString mname(mutexname); + if (idx >= 0) + mname += QString::number(idx); + + Qt::HANDLE mutex; + if (doCreate) { + QT_WA( { mutex = CreateMutexW(NULL, FALSE, (WCHAR*)mname.utf16()); }, + { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } ); + if (!mutex) { + qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); + return 0; + } + } + else { + QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (WCHAR*)mname.utf16()); }, + { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } ); + if (!mutex) { + if (GetLastError() != ERROR_FILE_NOT_FOUND) + qErrnoWarning("QtLockedFile::lock(): OpenMutex failed"); + return 0; + } + } + return mutex; +} + +bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) +{ + Q_ASSERT(mutex); + DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); + switch (res) { + case WAIT_OBJECT_0: + case WAIT_ABANDONED: + return true; + break; + case WAIT_TIMEOUT: + break; + default: + qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); + } + return false; +} + + + +bool QtLockedFile::lock(LockMode mode, bool block) +{ + if (!isOpen()) { + qWarning("QtLockedFile::lock(): file is not opened"); + return false; + } + + if (mode == NoLock) + return unlock(); + + if (mode == m_lock_mode) + return true; + + if (m_lock_mode != NoLock) + unlock(); + + if (!wmutex && !(wmutex = getMutexHandle(-1, true))) + return false; + + if (!waitMutex(wmutex, block)) + return false; + + if (mode == ReadLock) { + int idx = 0; + for (; idx < MAX_READERS; idx++) { + rmutex = getMutexHandle(idx, false); + if (!rmutex || waitMutex(rmutex, false)) + break; + CloseHandle(rmutex); + } + bool ok = true; + if (idx >= MAX_READERS) { + qWarning("QtLockedFile::lock(): too many readers"); + rmutex = 0; + ok = false; + } + else if (!rmutex) { + rmutex = getMutexHandle(idx, true); + if (!rmutex || !waitMutex(rmutex, false)) + ok = false; + } + if (!ok && rmutex) { + CloseHandle(rmutex); + rmutex = 0; + } + ReleaseMutex(wmutex); + if (!ok) + return false; + } + else { + Q_ASSERT(rmutexes.isEmpty()); + for (int i = 0; i < MAX_READERS; i++) { + Qt::HANDLE mutex = getMutexHandle(i, false); + if (mutex) + rmutexes.append(mutex); + } + if (rmutexes.size()) { + DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), + TRUE, block ? INFINITE : 0); + if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { + if (res != WAIT_TIMEOUT) + qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); + m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky + unlock(); + return false; + } + } + } + + m_lock_mode = mode; + return true; +} + +bool QtLockedFile::unlock() +{ + if (!isOpen()) { + qWarning("QtLockedFile::unlock(): file is not opened"); + return false; + } + + if (!isLocked()) + return true; + + if (m_lock_mode == ReadLock) { + ReleaseMutex(rmutex); + CloseHandle(rmutex); + rmutex = 0; + } + else { + foreach(Qt::HANDLE mutex, rmutexes) { + ReleaseMutex(mutex); + CloseHandle(mutex); + } + rmutexes.clear(); + ReleaseMutex(wmutex); + } + + m_lock_mode = QtLockedFile::NoLock; + return true; +} + +QtLockedFile::~QtLockedFile() +{ + if (isOpen()) + unlock(); + if (wmutex) + CloseHandle(wmutex); +} diff --git a/qtsingleapplication/qtsingleapplication.cpp b/qtsingleapplication/qtsingleapplication.cpp new file mode 100644 index 000000000..a7e19eed1 --- /dev/null +++ b/qtsingleapplication/qtsingleapplication.cpp @@ -0,0 +1,369 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + + +#include "qtsingleapplication.h" +#include "qtlocalpeer.h" +#include + + +/*! + \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() or sendMessage() to + find out if another instance of this application is already + running. Startup parameters (e.g. the name of the file the user + wanted this new instance to open) can be passed to the running + instance in the sendMessage() function. + + If isRunning() or sendMessage() returns false, it means that no + other instance is running, and this instance has assumed the role + as the running instance. 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 application receives messages from + another instance of the same application. + + If isRunning() or sendMessage() returns true, another instance is + already running, and the application should terminate or enter + client mode. + + If 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. + + 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 0; + + MyMainWidget mmw; + + app.setActivationWindow(&mmw); + + mmw.show(); + return app.exec(); + } + \endcode + + Once this QtSingleApplication instance is destroyed(for example, + when the user quits), 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&))); + connect(peer, SIGNAL(messageReceived(const QByteArray&)), SIGNAL(messageReceived(const QByteArray&))); + connect(peer, SIGNAL(messageReceived(const char*)), SIGNAL(messageReceived(const char*))); +} + + +/*! + 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); +} + + +/*! + 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 + + +/*! + 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); +} + +bool QtSingleApplication::sendMessage(const QByteArray &message, int timeout) +{ + return peer->sendMessage(message, timeout); +} + +bool QtSingleApplication::sendMessage(const char* 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())); + connect(peer, SIGNAL(messageReceived(const QByteArray&)), this, SLOT(activateWindow())); + connect(peer, SIGNAL(messageReceived(const char*)), this, SLOT(activateWindow())); + } + else { + disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); + disconnect(peer, SIGNAL(messageReceived(const QByteArray&)), this, SLOT(activateWindow())); + disconnect(peer, SIGNAL(messageReceived(const char*)), 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 +*/ diff --git a/qtsingleapplication/qtsingleapplication.h b/qtsingleapplication/qtsingleapplication.h new file mode 100644 index 000000000..ba0507cb8 --- /dev/null +++ b/qtsingleapplication/qtsingleapplication.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + + +#include + +class QtLocalPeer; + +#if defined(Q_WS_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); + 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 + + 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); + bool sendMessage(const QByteArray &message, int timeout = 5000); + bool sendMessage(const char* message, int timeout = 5000); + void activateWindow(); + + +Q_SIGNALS: + void messageReceived(const QString &message); + void messageReceived(const QByteArray &message); + void messageReceived(const char* message); + + +private: + void sysInit(const QString &appId = QString()); + QtLocalPeer *peer; + QWidget *actWin; +}; diff --git a/qtsingleapplication/qtsingleapplication.patch b/qtsingleapplication/qtsingleapplication.patch new file mode 100644 index 000000000..a65e64ac7 --- /dev/null +++ b/qtsingleapplication/qtsingleapplication.patch @@ -0,0 +1,231 @@ +diff -ur /home/david/qtsingleapplication-2.6_1-opensource/src/qtlocalpeer.cpp qtsingleapplication/qtlocalpeer.cpp +--- /home/david/qtsingleapplication-2.6_1-opensource/src/qtlocalpeer.cpp 2009-12-16 10:43:33.000000000 +0000 ++++ qtsingleapplication/qtlocalpeer.cpp 2010-07-10 16:26:50.000000000 +0100 +@@ -48,6 +48,7 @@ + #include "qtlocalpeer.h" + #include + #include ++#include + + #if defined(Q_OS_WIN) + #include +@@ -59,14 +60,12 @@ + #include + #endif + +-namespace QtLP_Private { + #include "qtlockedfile.cpp" + #if defined(Q_OS_WIN) + #include "qtlockedfile_win.cpp" + #else + #include "qtlockedfile_unix.cpp" + #endif +-} + + const char* QtLocalPeer::ack = "ack"; + +@@ -118,7 +117,7 @@ + if (lockFile.isLocked()) + return false; + +- if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) ++ if (!lockFile.lock(QtLockedFile::WriteLock, false)) + return true; + + bool res = server->listen(socketName); +@@ -138,6 +137,11 @@ + + bool QtLocalPeer::sendMessage(const QString &message, int timeout) + { ++ return sendMessage(message.toUtf8(), timeout); ++} ++ ++bool QtLocalPeer::sendMessage(const QByteArray &message, int timeout) ++{ + if (!isClient()) + return false; + +@@ -160,9 +164,8 @@ + if (!connOk) + return false; + +- QByteArray uMsg(message.toUtf8()); + QDataStream ds(&socket); +- ds.writeBytes(uMsg.constData(), uMsg.size()); ++ ds.writeBytes(message.constData(), message.size()); + bool res = socket.waitForBytesWritten(timeout); + res &= socket.waitForReadyRead(timeout); // wait for ack + res &= (socket.read(qstrlen(ack)) == ack); +@@ -195,9 +198,9 @@ + delete socket; + return; + } +- QString message(QString::fromUtf8(uMsg)); + socket->write(ack, qstrlen(ack)); + socket->waitForBytesWritten(1000); + delete socket; +- emit messageReceived(message); //### (might take a long time to return) ++ emit messageReceived(uMsg); //### (might take a long time to return) ++ emit messageReceived(QString::fromUtf8(uMsg)); + } +diff -ur /home/david/qtsingleapplication-2.6_1-opensource/src/qtlocalpeer.h qtsingleapplication/qtlocalpeer.h +--- /home/david/qtsingleapplication-2.6_1-opensource/src/qtlocalpeer.h 2009-12-16 10:43:33.000000000 +0000 ++++ qtsingleapplication/qtlocalpeer.h 2010-07-10 16:26:16.000000000 +0100 +@@ -49,9 +49,7 @@ + #include + #include + +-namespace QtLP_Private { + #include "qtlockedfile.h" +-} + + class QtLocalPeer : public QObject + { +@@ -61,11 +59,13 @@ + QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); + bool isClient(); + bool sendMessage(const QString &message, int timeout); ++ bool sendMessage(const QByteArray &message, int timeout); + QString applicationId() const + { return id; } + + Q_SIGNALS: + void messageReceived(const QString &message); ++ void messageReceived(const QByteArray &message); + + protected Q_SLOTS: + void receiveConnection(); +@@ -74,7 +74,7 @@ + QString id; + QString socketName; + QLocalServer* server; +- QtLP_Private::QtLockedFile lockFile; ++ QtLockedFile lockFile; + + private: + static const char* ack; +diff -ur /home/david/qtsingleapplication-2.6_1-opensource/src/qtlockedfile_win.cpp qtsingleapplication/qtlockedfile_win.cpp +--- /home/david/qtsingleapplication-2.6_1-opensource/src/qtlockedfile_win.cpp 2009-12-16 10:43:33.000000000 +0000 ++++ qtsingleapplication/qtlockedfile_win.cpp 2010-07-10 16:26:33.000000000 +0100 +@@ -65,7 +65,7 @@ + + Qt::HANDLE mutex; + if (doCreate) { +- QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); }, ++ QT_WA( { mutex = CreateMutexW(NULL, FALSE, (WCHAR*)mname.utf16()); }, + { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } ); + if (!mutex) { + qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); +@@ -73,7 +73,7 @@ + } + } + else { +- QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); }, ++ QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (WCHAR*)mname.utf16()); }, + { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } ); + if (!mutex) { + if (GetLastError() != ERROR_FILE_NOT_FOUND) +diff -ur /home/david/qtsingleapplication-2.6_1-opensource/src/qtsingleapplication.cpp qtsingleapplication/qtsingleapplication.cpp +--- /home/david/qtsingleapplication-2.6_1-opensource/src/qtsingleapplication.cpp 2009-12-16 10:43:33.000000000 +0000 ++++ qtsingleapplication/qtsingleapplication.cpp 2010-07-10 16:23:53.000000000 +0100 +@@ -144,6 +144,7 @@ + actWin = 0; + peer = new QtLocalPeer(this, appId); + connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); ++ connect(peer, SIGNAL(messageReceived(const QByteArray&)), SIGNAL(messageReceived(const QByteArray&))); + } + + +@@ -265,6 +266,11 @@ + return peer->sendMessage(message, timeout); + } + ++bool QtSingleApplication::sendMessage(const QByteArray &message, int timeout) ++{ ++ return peer->sendMessage(message, timeout); ++} ++ + + /*! + Returns the application identifier. Two processes with the same +@@ -291,10 +297,14 @@ + void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) + { + actWin = aw; +- if (activateOnMessage) ++ if (activateOnMessage) { + connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); +- else ++ connect(peer, SIGNAL(messageReceived(const QByteArray&)), this, SLOT(activateWindow())); ++ } ++ else { + disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); ++ disconnect(peer, SIGNAL(messageReceived(const QByteArray&)), this, SLOT(activateWindow())); ++ } + } + + +diff -ur /home/david/qtsingleapplication-2.6_1-opensource/src/qtsingleapplication.h qtsingleapplication/qtsingleapplication.h +--- /home/david/qtsingleapplication-2.6_1-opensource/src/qtsingleapplication.h 2009-12-16 10:43:33.000000000 +0000 ++++ qtsingleapplication/qtsingleapplication.h 2010-07-10 16:23:53.000000000 +0100 +@@ -91,11 +91,13 @@ + + public Q_SLOTS: + bool sendMessage(const QString &message, int timeout = 5000); ++ bool sendMessage(const QByteArray &message, int timeout = 5000); + void activateWindow(); + + + Q_SIGNALS: + void messageReceived(const QString &message); ++ void messageReceived(const QByteArray &message); + + + private: +diff -ur /home/david/qtsingleapplication-2.6_1-opensource/src/qtsinglecoreapplication.cpp qtsingleapplication/qtsinglecoreapplication.cpp +--- /home/david/qtsingleapplication-2.6_1-opensource/src/qtsinglecoreapplication.cpp 2009-12-16 10:43:33.000000000 +0000 ++++ qtsingleapplication/qtsinglecoreapplication.cpp 2010-07-10 16:32:33.000000000 +0100 +@@ -81,6 +81,7 @@ + { + peer = new QtLocalPeer(this); + connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); ++ connect(peer, SIGNAL(messageReceived(const QByteArray&)), SIGNAL(messageReceived(const QByteArray&))); + } + + +@@ -94,6 +95,7 @@ + { + peer = new QtLocalPeer(this, appId); + connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); ++ connect(peer, SIGNAL(messageReceived(const QByteArray&)), SIGNAL(messageReceived(const QByteArray&))); + } + + +@@ -133,6 +135,11 @@ + return peer->sendMessage(message, timeout); + } + ++bool QtSingleCoreApplication::sendMessage(const QByteArray &message, int timeout) ++{ ++ return peer->sendMessage(message, timeout); ++} ++ + + /*! + Returns the application identifier. Two processes with the same +diff -ur /home/david/qtsingleapplication-2.6_1-opensource/src/qtsinglecoreapplication.h qtsingleapplication/qtsinglecoreapplication.h +--- /home/david/qtsingleapplication-2.6_1-opensource/src/qtsinglecoreapplication.h 2009-12-16 10:43:33.000000000 +0000 ++++ qtsingleapplication/qtsinglecoreapplication.h 2010-07-10 16:32:33.000000000 +0100 +@@ -62,10 +62,12 @@ + + public Q_SLOTS: + bool sendMessage(const QString &message, int timeout = 5000); ++ bool sendMessage(const QByteArray &message, int timeout = 5000); + + + Q_SIGNALS: + void messageReceived(const QString &message); ++ void messageReceived(const QByteArray &message); + + + private: diff --git a/qtsingleapplication/qtsinglecoreapplication.cpp b/qtsingleapplication/qtsinglecoreapplication.cpp new file mode 100644 index 000000000..38e16584a --- /dev/null +++ b/qtsingleapplication/qtsinglecoreapplication.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + + +#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&))); + connect(peer, SIGNAL(messageReceived(const QByteArray&)), SIGNAL(messageReceived(const QByteArray&))); + connect(peer, SIGNAL(messageReceived(const char*)), SIGNAL(messageReceived(const char*))); +} + + +/*! + 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&))); + connect(peer, SIGNAL(messageReceived(const QByteArray&)), SIGNAL(messageReceived(const QByteArray&))); + connect(peer, SIGNAL(messageReceived(const char*)), SIGNAL(messageReceived(const char*))); +} + + +/*! + 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); +} + +bool QtSingleCoreApplication::sendMessage(const QByteArray &message, int timeout) +{ + return peer->sendMessage(message, timeout); +} + +bool QtSingleCoreApplication::sendMessage(const char* 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() +*/ diff --git a/qtsingleapplication/qtsinglecoreapplication.h b/qtsingleapplication/qtsinglecoreapplication.h new file mode 100644 index 000000000..f80946da5 --- /dev/null +++ b/qtsingleapplication/qtsinglecoreapplication.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + + +#include + +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); + bool sendMessage(const QByteArray &message, int timeout = 5000); + bool sendMessage(const char* message, int timeout = 5000); + + +Q_SIGNALS: + void messageReceived(const QString &message); + void messageReceived(const QByteArray &message); + void messageReceived(const char* message); + + +private: + QtLocalPeer* peer; +};