diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5d57d3f6..f3ce2964 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,6 +29,7 @@ find_package(Qt6Core ${QT_MINIMUM_VERSION} REQUIRED)
find_package(Qt6Gui ${QT_MINIMUM_VERSION} REQUIRED)
find_package(Qt6LinguistTools ${QT_MINIMUM_VERSION} REQUIRED)
find_package(Qt6Widgets ${QT_MINIMUM_VERSION} REQUIRED)
+find_package(Qt6Network ${QT_MINIMUM_VERSION} REQUIRED)
if(UNIX)
find_package(Qt6DBus ${QT_MINIMUM_VERSION} REQUIRED)
find_package(Qt6 COMPONENTS Core Core5Compat REQUIRED)
@@ -74,6 +75,7 @@ set(EXE_NAME qterminal)
set(QTERM_SRC
src/main.cpp
src/mainwindow.cpp
+ src/instance-locker.cpp
src/tabbar.cpp
src/tabwidget.cpp
src/termwidget.cpp
@@ -91,6 +93,7 @@ set(QTERM_SRC
set(QTERM_MOC_SRC
src/qterminalapp.h
src/mainwindow.h
+ src/instance-locker.h
src/tabbar.h
src/tabwidget.h
src/termwidget.h
@@ -210,6 +213,7 @@ target_link_libraries(${EXE_NAME}
Qt6::Core
Qt6::Gui
Qt6::Widgets
+ Qt6::Network
qtermwidget6
util
)
diff --git a/src/instance-locker.cpp b/src/instance-locker.cpp
new file mode 100644
index 00000000..dd500acb
--- /dev/null
+++ b/src/instance-locker.cpp
@@ -0,0 +1,168 @@
+/***************************************************************************
+* Copyright (C) 2024 by Marcus Britanicus *
+* marcusbritanicus@gmail.com *
+* *
+* 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, see . *
+***************************************************************************/
+
+#include
+
+#include "instance-locker.h"
+
+static inline QString getSocketName(QString appId)
+{
+ /** Get the env-var XDG_RUNTIME_DIR */
+ QString sockName = QString::fromUtf8(qgetenv("XDG_RUNTIME_DIR"));
+
+ /** The env-var was not set. We will use /tmp/ */
+ if (not sockName.length())
+ {
+ sockName = QString::fromUtf8("/tmp/");
+ }
+
+ if (not sockName.endsWith(QString::fromUtf8("/")))
+ {
+ sockName += QString::fromUtf8("/");
+ }
+
+ /** Append a random number */
+ sockName += QString::fromUtf8("%1-Scoket-%2").arg(appId).arg(getuid());
+
+ return sockName;
+}
+
+
+InstanceLocker::InstanceLocker(QString appId, QObject *parent) : QObject(parent)
+{
+ mServer = nullptr;
+
+ /* App ID */
+ mAppId = appId;
+
+ /** Get the env-var XDG_RUNTIME_DIR */
+ mSocketName = getSocketName(mAppId);
+
+ /* Lock File */
+ lockFile = new QLockFile(mSocketName + QString::fromUtf8(".lock"));
+
+ /* Try to lock the @lockFile, if it fails, then we're not the first instance */
+ if (lockFile->tryLock())
+ {
+ /* Local Server for communication */
+ mServer = new QLocalServer(this);
+
+ /* Start the server */
+ bool res = mServer->listen(mSocketName);
+
+ /* @res can't be false at the moment, because we're the first instance. */
+ /* The only reason why @res is false, the socket file exists from a previous */
+ /* crash. So delete it and try again. */
+ if (not res && (mServer->serverError() == QAbstractSocket::AddressInUseError))
+ {
+ QLocalServer::removeServer(mSocketName);
+ res = mServer->listen(mSocketName);
+
+ if (!res)
+ {
+ qWarning("InstanceLocker: listen on local socket failed, %s", qPrintable(mServer->errorString()));
+ }
+ }
+ }
+}
+
+
+InstanceLocker::~InstanceLocker()
+{
+ disconnect();
+
+ delete lockFile;
+}
+
+
+bool InstanceLocker::isRunning()
+{
+ /* If we have the lock, we're the server */
+ /* In other words, if we're not there, there is no server */
+ if (lockFile->isLocked())
+ {
+ return false;
+ }
+
+ /* If we cannot get the lock then the server is running elsewhere */
+ if (not lockFile->tryLock())
+ {
+ return true;
+ }
+
+ /* Be default, we'll assume that the server is running elsewhere */
+ return true;
+}
+
+
+bool InstanceLocker::sendMessage(const QString& message, int timeout)
+{
+ if (not isRunning())
+ {
+ return false;
+ }
+
+ /* Preparing socket */
+ QLocalSocket socket(this);
+
+ /* Connecting to server */
+ socket.connectToServer(mSocketName);
+
+ /* Wait for ACK (500 ms) */
+ if (not socket.waitForConnected(timeout))
+ {
+ return false;
+ }
+
+ /* Send the message to the server */
+ socket.write(message.toUtf8());
+
+ /** Should finish writing in 500 ms */
+ return socket.waitForBytesWritten(timeout);
+}
+
+
+void InstanceLocker::disconnect()
+{
+ if (mServer)
+ {
+ mServer->close();
+ }
+
+ lockFile->unlock();
+}
+
+
+QString InstanceLocker::handleConnection()
+{
+ /* Preparing socket */
+ QLocalSocket *socket = mServer->nextPendingConnection();
+
+ if (socket == nullptr)
+ {
+ return QString();
+ }
+
+ socket->waitForReadyRead(2000);
+ QByteArray msg = socket->readAll();
+
+ /** Close the connection */
+ socket->close();
+
+ return QString::fromUtf8(msg);
+}
diff --git a/src/instance-locker.h b/src/instance-locker.h
new file mode 100644
index 00000000..951d50a5
--- /dev/null
+++ b/src/instance-locker.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+* Copyright (C) 2024 by Marcus Britanicus *
+* marcusbritanicus@gmail.com *
+* *
+* 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, see . *
+***************************************************************************/
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+class InstanceLocker : public QObject {
+Q_OBJECT
+
+public:
+InstanceLocker(QString appId, QObject *parent = nullptr);
+~InstanceLocker();
+
+/** Check if another isntance is running */
+bool isRunning();
+
+/** Send a message to the original instance */
+bool sendMessage(const QString& message, int timeout = 500);
+
+/** Disconnect from the server */
+void disconnect();
+
+QString handleConnection();
+
+QLockFile *lockFile = nullptr;
+bool mLocked = false;
+
+QString mSocketName;
+QString mAppId;
+
+QLocalServer *mServer;
+};
diff --git a/src/main.cpp b/src/main.cpp
index 8b456298..6360ce48 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -19,6 +19,10 @@
#include
#include
+#include
+#include
+#include
+
#include
#include
#include
@@ -29,11 +33,11 @@
#include "processadaptor.h"
#endif
-
#include "mainwindow.h"
#include "qterminalapp.h"
#include "qterminalutils.h"
#include "terminalconfig.h"
+#include "instance-locker.h"
#define out
@@ -197,7 +201,38 @@ int main(int argc, char *argv[])
app->installTranslator(&translator);
TerminalConfig initConfig = TerminalConfig(workdir, shell_command);
- app->newWindow(dropMode, initConfig);
+ MainWindow *window = app->newWindow(dropMode, initConfig);
+
+ /** Instance locker */
+ if ( dropMode ) {
+ InstanceLocker *locker = new InstanceLocker( QString::fromUtf8( "%1-%2" ).arg( qApp->organizationName() ).arg( qApp->applicationName() ), nullptr );
+
+ /** Another isntance is running */
+ if ( locker->isRunning() ) {
+ /** Ask the other instance to toggle the instance. */
+ locker->sendMessage( QString::fromUtf8( "toggle" ) );
+
+ delete locker;
+
+ return 0;
+ }
+
+ QObject::connect(
+ locker->mServer, &QLocalServer::newConnection, [ = ] () {
+ QString msg = locker->handleConnection();
+ if (msg == QString::fromUtf8( "toggle" ))
+ {
+ if ( window->isVisible() ) {
+ window->hide();
+ }
+
+ else {
+ window->show();
+ }
+ }
+ }
+ );
+ }
int ret = app->exec();
delete Properties::Instance();
@@ -342,4 +377,3 @@ bool QTerminalApp::toggleDropdown() {
#endif
-