]> Nutra Git (v1) - gamesguru/feather.git/commitdiff
remove SingleApplication
authortobtoht <tob@featherwallet.org>
Tue, 1 Oct 2024 02:21:42 +0000 (04:21 +0200)
committertobtoht <tob@featherwallet.org>
Tue, 1 Oct 2024 02:21:42 +0000 (04:21 +0200)
.gitmodules
src/Application.cpp [new file with mode: 0644]
src/Application.h [new file with mode: 0644]
src/CMakeLists.txt
src/WindowManager.cpp
src/WindowManager.h
src/main.cpp
src/third-party/singleapplication [deleted submodule]

index 6187c662c689a733ef75f3d36bf427b9152417d5..a8c3a3d362e50ee281ab57861d25617a5e4940a9 100644 (file)
@@ -1,9 +1,6 @@
 [submodule "monero"]
        path = monero
        url = https://github.com/feather-wallet/monero.git
-[submodule "src/third-party/singleapplication"]
-       path = src/third-party/singleapplication
-       url = https://github.com/itay-grudev/SingleApplication.git
 [submodule "src/third-party/polyseed"]
        path = src/third-party/polyseed
        url = https://github.com/tevador/polyseed.git
diff --git a/src/Application.cpp b/src/Application.cpp
new file mode 100644 (file)
index 0000000..5758662
--- /dev/null
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// SPDX-FileCopyrightText: 2020-2024 The Monero Project
+
+#include "Application.h"
+
+#include "config.h"
+
+#include <QLocalSocket>
+#include <QLockFile>
+#include <QStandardPaths>
+
+namespace
+{
+    constexpr int WaitTimeoutMSec = 150;
+    const char BlockSizeProperty[] = "blockSize";
+} // namespace
+
+Application::Application(int& argc, char** argv)
+        : QApplication(argc, argv)
+        , m_alreadyRunning(false)
+        , m_lockFile(nullptr)
+{
+    QString userName = qgetenv("USER");
+    if (userName.isEmpty()) {
+        userName = qgetenv("USERNAME");
+    }
+    QString identifier = "feather";
+    if (!userName.isEmpty()) {
+        identifier += "-" + userName;
+    }
+
+    QString lockName = identifier + ".lock";
+    m_socketName = identifier + ".socket";
+
+    // According to documentation we should use RuntimeLocation on *nixes, but even Qt doesn't respect
+    // this and creates sockets in TempLocation, so let's be consistent.
+    m_lockFile = new QLockFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/" + lockName);
+    m_lockFile->setStaleLockTime(0);
+    m_lockFile->tryLock();
+
+    m_lockServer.setSocketOptions(QLocalServer::UserAccessOption);
+    connect(&m_lockServer, SIGNAL(newConnection()), this, SIGNAL(anotherInstanceStarted()));
+    connect(&m_lockServer, &QLocalServer::newConnection, this, &Application::processIncomingConnection);
+
+    switch (m_lockFile->error()) {
+        case QLockFile::NoError:
+            // No existing lock was found, start listener
+            m_lockServer.listen(m_socketName);
+            break;
+        case QLockFile::LockFailedError: {
+            // Attempt to connect to the existing instance
+            QLocalSocket client;
+            for (int i = 0; i < 3; ++i) {
+                client.connectToServer(m_socketName);
+                if (client.waitForConnected(WaitTimeoutMSec)) {
+                    // Connection succeeded, this will raise the existing window if minimized
+                    client.abort();
+                    m_alreadyRunning = true;
+                    break;
+                }
+            }
+
+            if (!m_alreadyRunning) {
+                // If we get here then the original instance is likely dead
+                qWarning() << "Existing single-instance lock file is invalid. Launching new instance.";
+
+                // forceably reset the lock file
+                m_lockFile->removeStaleLockFile();
+                m_lockFile->tryLock();
+                // start the listen server
+                m_lockServer.listen(m_socketName);
+            }
+            break;
+        }
+        default:
+            qWarning() << "The lock file could not be created. Single-instance mode disabled.";
+    }
+}
+
+Application::~Application()
+{
+    if (m_lockFile) {
+        m_lockFile->unlock();
+        delete m_lockFile;
+    }
+}
+
+bool Application::isAlreadyRunning() const
+{
+    return m_alreadyRunning;
+}
+
+void Application::processIncomingConnection()
+{
+    qDebug() << "We got an incoming connection";
+    if (m_lockServer.hasPendingConnections()) {
+        QLocalSocket* socket = m_lockServer.nextPendingConnection();
+        socket->setProperty(BlockSizeProperty, 0);
+    }
+}
diff --git a/src/Application.h b/src/Application.h
new file mode 100644 (file)
index 0000000..3600334
--- /dev/null
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// SPDX-FileCopyrightText: 2020-2024 The Monero Project
+
+#ifndef FEATHER_APPLICATION_H
+#define FEATHER_APPLICATION_H
+
+#include <QApplication>
+#include <QtNetwork/qlocalserver.h>
+
+class QLockFile;
+class QSocketNotifier;
+
+class Application : public QApplication {
+    Q_OBJECT
+
+public:
+    Application(int& argc, char** argv);
+    ~Application() override;
+
+    bool isAlreadyRunning() const;
+
+signals:
+    void anotherInstanceStarted();
+
+private slots:
+    void processIncomingConnection();
+
+private:
+    bool m_alreadyRunning;
+    QLockFile* m_lockFile;
+    QLocalServer m_lockServer;
+    QString m_socketName;
+};
+
+
+#endif //FEATHER_APPLICATION_H
index 42253b30ecd15b359d662889a703130d7b0c321b..9100de994d7dad4fda8a62c4c986983bf53d352a 100644 (file)
@@ -20,11 +20,6 @@ endif()
 
 find_package(Qt6 REQUIRED COMPONENTS ${QT_COMPONENTS})
 
-if (NOT APPLE)
-    set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication")
-    add_subdirectory(third-party/singleapplication)
-endif()
-
 if (CHECK_UPDATES)
     add_subdirectory(openpgp)
 endif()
@@ -280,10 +275,6 @@ target_link_libraries(feather PRIVATE
         ${BCUR_LIBRARY}
 )
 
-if(NOT APPLE)
-    target_link_libraries(feather PRIVATE SingleApplication::SingleApplication)
-endif()
-
 if(CHECK_UPDATES)
     target_link_libraries(feather PRIVATE openpgp)
 endif()
index 6a37a62cc9ef560c6fc35a0ebc0f8aa7196b7931..2dd5370169ac5efe2ac93eca2890bf950e4b0bdc 100644 (file)
@@ -3,12 +3,12 @@
 
 #include "WindowManager.h"
 
-#include <QApplication>
 #include <QDialogButtonBox>
 #include <QInputDialog>
 #include <QMessageBox>
 #include <QWindow>
 
+#include "Application.h"
 #include "constants.h"
 #include "dialog/PasswordDialog.h"
 #include "dialog/SplashDialog.h"
@@ -33,6 +33,7 @@ WindowManager::WindowManager(QObject *parent)
     connect(m_walletManager, &WalletManager::deviceError,         this, &WindowManager::onDeviceError);
     connect(m_walletManager, &WalletManager::walletPassphraseNeeded, this, &WindowManager::onWalletPassphraseNeeded);
 
+    connect(qApp, SIGNAL(anotherInstanceStarted()), this, SLOT(raise()));
     connect(qApp, &QGuiApplication::lastWindowClosed, this, &WindowManager::quitAfterLastWindow);
 
     m_tray = new QSystemTrayIcon(icons()->icon("appicons/64x64.png"));
@@ -66,6 +67,7 @@ WindowManager::~WindowManager() {
     qDebug() << "~WindowManager";
     m_cleanupThread->quit();
     m_cleanupThread->wait();
+    qDebug() << "Cleanup thread done";
 }
 
 // ######################## APPLICATION LIFECYCLE ########################
@@ -100,6 +102,7 @@ void WindowManager::close() {
 
     torManager()->stop();
 
+    qDebug() << "Calling QApplication::quit()";
     QApplication::quit();
 }
 
index 92c9225d677fbbb97feb21d01a07c6093a9d9243..beccd5ed7da187f35bfa6de99985f436d9194f91 100644 (file)
@@ -30,7 +30,6 @@ public:
     void closeWindow(MainWindow *window);
     void showWizard(WalletWizard::Page startPage);
     void restartApplication(const QString &binaryFilename);
-    void raise();
 
     void showSettings(Nodes *nodes, QWidget *parent, bool showProxyTab = false);
 
@@ -59,6 +58,7 @@ public slots:
     void tryOpenWallet(const QString &path, const QString &password);
 
 private slots:
+    void raise();
     void onWalletOpened(Wallet *wallet);
     void onWalletCreated(Wallet *wallet);
     void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path);
index e1512a001797a62b35a13473f0796fe8c81d1b9a..1627805b4802dd6f1b6e7aa4aa40361481da74bf 100644 (file)
@@ -1,17 +1,9 @@
 // SPDX-License-Identifier: BSD-3-Clause
 // SPDX-FileCopyrightText: 2020-2024 The Monero Project
 
-#include <QResource>
-#include <QApplication>
-#include <QtCore>
-#include <QtGui>
-#if !defined(Q_OS_MAC)
-#include <singleapplication.h>
-#endif
-
+#include "Application.h"
 #include "config-feather.h"
 #include "constants.h"
-#include "MainWindow.h"
 #include "utils/EventFilter.h"
 #include "utils/os/Prestium.h"
 #include "WindowManager.h"
@@ -24,8 +16,6 @@
 #include <fstream>
 #endif
 
-#include <QObject>
-
 #if defined(Q_OS_WIN)
 #include <windows.h>
 #include <vfw.h>
@@ -89,12 +79,7 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
     QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
 #endif
 
-#if defined(Q_OS_MAC)
-    // https://github.com/itay-grudev/SingleApplication/issues/136#issuecomment-1925441403
-    QApplication app(argc, argv);
-#else
-    SingleApplication app(argc, argv);
-#endif
+    Application app(argc, argv);
 
     QApplication::setApplicationName("Feather");
     QApplication::setApplicationVersion(FEATHER_VERSION);
@@ -122,6 +107,11 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
         return EXIT_SUCCESS;
     }
 
+    if (app.isAlreadyRunning()) {
+        qWarning() << "Another instance of Feather is already running";
+        return EXIT_SUCCESS;
+    }
+
     bool stagenet = parser.isSet(stagenetOption);
     bool testnet = parser.isSet(testnetOption);
     bool quiet = parser.isSet(quietModeOption);
@@ -193,8 +183,6 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
 
     conf()->set(Config::restartRequired, false);
 
-    parser.process(app); // Parse again for --help and --version
-
     if (!quiet) {
         QMap<QString, QString> info;
         info["Qt"] = QT_VERSION_STR;
@@ -238,13 +226,7 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
     auto wm = windowManager();
     wm->setEventFilter(&filter);
 
-#if !defined(Q_OS_MAC)
-    QObject::connect(&app, &SingleApplication::instanceStarted, [&wm]() {
-        wm->raise();
-    });
-#endif
-
-    int exitCode = QApplication::exec();
-    qDebug() << "QApplication::exec() returned";
+    int exitCode = Application::exec();
+    qDebug() << "Application::exec() returned";
     return exitCode;
 }
diff --git a/src/third-party/singleapplication b/src/third-party/singleapplication
deleted file mode 160000 (submodule)
index 3e8e85d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 3e8e85d1a487e433751711a8a090659684d42e3b