diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bff63082..cf88b76eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ set(CMAKE_CXX_STANDARD 11) set(ASSETS_DIR assets) +option(ENABLE_DBUS "Enable single instance handling via DBus." NO) option(ENABLE_UPDATE_CHECK "Enable update check." NO) include(GNUInstallDirs) @@ -77,6 +78,9 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG -DQT_QML_DEBUG -DQT_ # ------------------------------------------------------------------------------ set(QT5_PACKAGES Core Gui Quick Widgets QuickControls2 Svg LinguistTools Concurrent Network) +if(ENABLE_DBUS) + list(APPEND QT5_PACKAGES DBus) +endif() set(QT5_PACKAGES_OPTIONAL TextToSpeech) if (LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) @@ -138,7 +142,6 @@ set(SOURCES src/components/telephone-numbers/TelephoneNumbersModel.cpp src/components/timeline/TimelineModel.cpp src/components/url-handlers/UrlHandlers.cpp - src/externals/single-application/SingleApplication.cpp src/main.cpp src/utils/LinphoneUtils.cpp src/utils/Utils.cpp @@ -193,11 +196,18 @@ set(HEADERS src/components/timeline/TimelineModel.hpp src/components/url-handlers/UrlHandlers.hpp src/externals/single-application/SingleApplication.hpp - src/externals/single-application/SingleApplicationPrivate.hpp src/utils/LinphoneUtils.hpp src/utils/Utils.hpp ) +if(ENABLE_DBUS) + list(APPEND SOURCES src/app/single-application/SingleApplicationDBus.cpp) + list(APPEND HEADERS src/app/single-application/SingleApplicationDBusPrivate.hpp) +else() + list(APPEND SOURCES src/externals/single-application/SingleApplication.cpp) + list(APPEND HEADERS src/externals/single-application/SingleApplicationPrivate.hpp) +endif() + set(QRC_RESOURCES resources.qrc) set(LANGUAGES_DIRECTORY "${ASSETS_DIR}/languages") diff --git a/cmake_builder/CMakeLists.txt b/cmake_builder/CMakeLists.txt index b5a4ca986..8b884e68a 100644 --- a/cmake_builder/CMakeLists.txt +++ b/cmake_builder/CMakeLists.txt @@ -25,3 +25,7 @@ lcb_define_target("linphoneqt" "linphone" "ms2plugins") if(NOT WIN32 AND NOT APPLE) lcb_blacklist_dependencies("turbo-jpeg") # turbo-jpeg is already provided by Qt5 so do not build it endif() + +if(UNIX AND NOT APPLE) + lcb_add_option("DBus" "Enable handling of single application instance via DBus." OFF) +endif() diff --git a/cmake_builder/linphoneqt.cmake b/cmake_builder/linphoneqt.cmake index 813d9b292..e246c7e30 100644 --- a/cmake_builder/linphoneqt.cmake +++ b/cmake_builder/linphoneqt.cmake @@ -26,5 +26,9 @@ lcb_dependencies("linphone" "ms2plugins") lcb_groupable(YES) lcb_cmake_options("-DENABLE_UPDATE_CHECK=${ENABLE_UPDATE_CHECK}") +if(UNIX AND NOT APPLE) + lcb_cmake_options("-DENABLE_DBUS=${ENABLE_DBUS}") +endif() + # Add config step for packaging set(LINPHONE_BUILDER_ADDITIONAL_CONFIG_STEPS "${CMAKE_CURRENT_LIST_DIR}/additional_steps.cmake") diff --git a/src/app/single-application/SingleApplicationDBus.cpp b/src/app/single-application/SingleApplicationDBus.cpp new file mode 100644 index 000000000..96ca63cab --- /dev/null +++ b/src/app/single-application/SingleApplicationDBus.cpp @@ -0,0 +1,129 @@ +/* + * SingleApplicationDBus.cpp + * Copyright (C) 2017 Belledonne Communications, Grenoble, France + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Created on: June 20, 2017 + * Author: Ghislain MARY + */ + +#include +#include + +#include +#include + +#include "../../utils/Utils.hpp" + +#include "../../externals/single-application/SingleApplication.hpp" +#include "SingleApplicationDBusPrivate.hpp" + +// ============================================================================= + +const char *SERVICE_NAME = "org.linphone.SingleApplication"; + +SingleApplicationPrivate::SingleApplicationPrivate (SingleApplication *q_ptr) + : QDBusAbstractAdaptor(q_ptr), q_ptr(q_ptr) { +} + +SingleApplicationPrivate::~SingleApplicationPrivate () { +} + +QDBusConnection SingleApplicationPrivate::getBus () const { + if (options & SingleApplication::Mode::User) { + return QDBusConnection::sessionBus(); + } else { + return QDBusConnection::systemBus(); + } +} + +void SingleApplicationPrivate::startPrimary () { + if (getBus().registerObject("/", this, QDBusConnection::ExportAllSlots) == false) { + qWarning() << "Failed to register single application object on DBus"; + } + instanceNumber = 0; +} + +void SingleApplicationPrivate::startSecondary () { + instanceNumber = 1; +} + +SingleApplication::SingleApplication (int &argc, char *argv[], bool allowSecondary, Options options, int) + : QApplication(argc, argv), d_ptr(new SingleApplicationPrivate(this)) { + Q_D(SingleApplication); + + // Store the current mode of the program + d->options = options; + + if (!d->getBus().isConnected()) { + qWarning() << "Cannot connect to the D-Bus session bus."; + delete d; + ::exit(EXIT_FAILURE); + } + + if (d->getBus().registerService(SERVICE_NAME)) { + d->startPrimary(); + return; + } + + if (allowSecondary) { + d->startSecondary(); + return; + } + + delete d; + ::exit(EXIT_SUCCESS); +} + +SingleApplication::~SingleApplication () { + Q_D(SingleApplication); + delete d; +} + +bool SingleApplication::isPrimary () { + Q_D(SingleApplication); + return d->instanceNumber == 0; +} + +bool SingleApplication::isSecondary () { + Q_D(SingleApplication); + return d->instanceNumber != 0; +} + +quint32 SingleApplication::instanceId () { + Q_D(SingleApplication); + return d->instanceNumber; +} + +bool SingleApplication::sendMessage (QByteArray message, int timeout) { + Q_D(SingleApplication); + + if (isPrimary()) return false; + + QDBusInterface iface(SERVICE_NAME, "/", "", d->getBus()); + if (iface.isValid()) { + iface.setTimeout(timeout); + QDBusMessage msg = iface.call(QDBus::Block, "messageReceived", instanceId(), message); + return true; + } + + return false; +} + +void SingleApplicationPrivate::messageReceived (quint32 instanceId, QByteArray message) { + Q_Q(SingleApplication); + Q_EMIT q->receivedMessage(instanceId, message); +} diff --git a/src/app/single-application/SingleApplicationDBusPrivate.hpp b/src/app/single-application/SingleApplicationDBusPrivate.hpp new file mode 100644 index 000000000..14e2aaecb --- /dev/null +++ b/src/app/single-application/SingleApplicationDBusPrivate.hpp @@ -0,0 +1,58 @@ +/* + * SingleApplicationDBusPrivate.hpp + * Copyright (C) 2017 Belledonne Communications, Grenoble, France + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Created on: June 20, 2017 + * Author: Ghislain MARY + */ + +#ifndef SINGLE_APPLICATION_DBUS_PRIVATE_H +#define SINGLE_APPLICATION_DBUS_PRIVATE_H + +#include "../../externals/single-application/SingleApplication.hpp" + +#include + +// ============================================================================= + +struct InstancesInfo { + bool primary; + quint32 secondary; +}; + +class SingleApplicationPrivate : public QDBusAbstractAdaptor { + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.linphone.DBus.SingleApplication") + +public: + Q_DECLARE_PUBLIC(SingleApplication) SingleApplicationPrivate (SingleApplication *q_ptr); + ~SingleApplicationPrivate (); + + QDBusConnection getBus () const; + + void startPrimary (); + void startSecondary (); + + SingleApplication *q_ptr; + SingleApplication::Options options; + quint32 instanceNumber; + +public Q_SLOTS: + void messageReceived(quint32 instanceId, QByteArray message); +}; + +#endif // SINGLE_APPLICATION_DBUS_PRIVATE_H