diff --git a/linphone-app/CMakeLists.txt b/linphone-app/CMakeLists.txt
index 454a20d28..cf9e805c9 100644
--- a/linphone-app/CMakeLists.txt
+++ b/linphone-app/CMakeLists.txt
@@ -511,10 +511,16 @@ set( UIS)
list(APPEND SOURCES include/LinphoneApp/PluginExample.json)
## Single Application
-list(APPEND HEADERS src/app/single-application/singleapplication.h
- src/app/single-application/singleapplication_p.h)
-list(APPEND SOURCES src/app/single-application/singleapplication.cpp
- src/app/single-application/singleapplication_p.cpp)
+if(APPLE OR WIN32)
+ list(APPEND HEADERS src/app/single-application/singleapplication.h
+ src/app/single-application/singleapplication_p.h)
+ list(APPEND SOURCES src/app/single-application/singleapplication.cpp
+ src/app/single-application/singleapplication_p.cpp)
+else() # Use QDBus for Linux
+ list(APPEND HEADERS src/app/single-application/singleapplication.h
+ src/app/single-application/SingleApplicationDBusPrivate.hpp)
+ list(APPEND SOURCES src/app/single-application/SingleApplicationDBus.cpp)
+endif()
set(MAIN_FILE src/app/main.cpp)
if (APPLE)
diff --git a/linphone-app/src/app/single-application/SingleApplicationDBus.cpp b/linphone-app/src/app/single-application/SingleApplicationDBus.cpp
new file mode 100644
index 000000000..9bc82ff2f
--- /dev/null
+++ b/linphone-app/src/app/single-application/SingleApplicationDBus.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2010-2020 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-desktop
+ * (see https://www.linphone.org).
+ *
+ * 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 3 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, see .
+ */
+
+#include
+
+#include
+
+#include "config.h"
+
+#include "singleapplication.h"
+#include "SingleApplicationDBusPrivate.hpp"
+
+// =============================================================================
+
+namespace {
+ constexpr char ServiceName[] = "org." EXECUTABLE_NAME ".SingleApplication";
+}
+
+SingleApplicationPrivate::SingleApplicationPrivate (SingleApplication *q_ptr)
+ : QDBusAbstractAdaptor(q_ptr), q_ptr(q_ptr) {}
+
+QDBusConnection SingleApplicationPrivate::getBus () const {
+ if (options & SingleApplication::Mode::User)
+ return QDBusConnection::sessionBus();
+
+ return QDBusConnection::systemBus();
+}
+
+void SingleApplicationPrivate::startPrimary () {
+ signal(SIGINT, SingleApplicationPrivate::terminate);
+ if (!getBus().registerObject("/", this, QDBusConnection::ExportAllSlots))
+ qWarning() << QStringLiteral("Failed to register single application object on DBus.");
+ instanceNumber = 0;
+}
+
+void SingleApplicationPrivate::startSecondary () {
+ signal(SIGINT, SingleApplicationPrivate::terminate);
+ instanceNumber = 1;
+}
+
+void SingleApplicationPrivate::terminate (int signum) {
+ SingleApplication::instance()->exit(signum);
+}
+
+SingleApplication::SingleApplication (int &argc, char *argv[], bool allowSecondary, Options options, int, const QString &userData)
+ : 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() << QStringLiteral("Cannot connect to the D-Bus session bus.");
+ delete d;
+ ::exit(EXIT_FAILURE);
+ }
+
+ if (d->getBus().registerService(ServiceName)) {
+ d->startPrimary();
+ return;
+ }
+
+ if (allowSecondary) {
+ d->startSecondary();
+ return;
+ }
+
+ delete d;
+ ::exit(EXIT_SUCCESS);
+}
+
+SingleApplication::~SingleApplication () {
+ Q_D(SingleApplication);
+ delete d;
+}
+
+bool SingleApplication::isPrimary () const {
+ auto d = d_func();
+ return d->instanceNumber == 0;
+}
+
+bool SingleApplication::isSecondary () const {
+ auto d = d_func();
+ return d->instanceNumber != 0;
+}
+
+quint32 SingleApplication::instanceId () const {
+ auto d = d_func();
+ return d->instanceNumber;
+}
+
+bool SingleApplication::sendMessage (const QByteArray &message, int timeout, SendMode sendMode) {
+ auto d = d_func();
+
+ if (isPrimary()) return false;
+
+ QDBusInterface iface(ServiceName, "/", "", d->getBus());
+ if (iface.isValid()) {
+ iface.setTimeout(timeout);
+ iface.call(QDBus::Block, "handleMessageReceived", instanceId(), message);
+ return true;
+ }
+
+ return false;
+}
+
+void SingleApplicationPrivate::handleMessageReceived (quint32 instanceId, QByteArray message) {
+ Q_Q(SingleApplication);
+ emit q->receivedMessage(instanceId, message);
+}
diff --git a/linphone-app/src/app/single-application/SingleApplicationDBusPrivate.hpp b/linphone-app/src/app/single-application/SingleApplicationDBusPrivate.hpp
new file mode 100644
index 000000000..14c199c9a
--- /dev/null
+++ b/linphone-app/src/app/single-application/SingleApplicationDBusPrivate.hpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2010-2020 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-desktop
+ * (see https://www.linphone.org).
+ *
+ * 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 3 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, see .
+ */
+
+#ifndef SINGLE_APPLICATION_DBUS_PRIVATE_H_
+#define SINGLE_APPLICATION_DBUS_PRIVATE_H_
+
+#include
+#include
+
+#include "singleapplication.h"
+
+// =============================================================================
+
+struct InstancesInfo {
+ bool primary;
+ quint32 secondary;
+};
+
+class SingleApplicationPrivate : public QDBusAbstractAdaptor {
+ Q_OBJECT;
+ Q_CLASSINFO("D-Bus Interface", "org.linphone.DBus.SingleApplication");
+
+public:
+ SingleApplicationPrivate (SingleApplication *q_ptr);
+
+ QDBusConnection getBus () const;
+
+ void startPrimary ();
+ void startSecondary ();
+
+ static void terminate (int signum);
+
+ SingleApplication *q_ptr;
+ SingleApplication::Options options;
+ quint32 instanceNumber;
+
+// Explicit public slot. Cannot be private, must be exported as a method via D-Bus.
+public slots:
+ void handleMessageReceived (quint32 instanceId, QByteArray message);
+
+private:
+ Q_DECLARE_PUBLIC(SingleApplication);
+};
+
+#endif // SINGLE_APPLICATION_DBUS_PRIVATE_H_