Only allow a single instance of program to be run at a time (#2923)

Signed-off-by: Adam Treat <treat.adam@gmail.com>
Signed-off-by: Jared Van Bortel <jared@nomic.ai>
Co-authored-by: Jared Van Bortel <jared@nomic.ai>
This commit is contained in:
AT 2024-08-30 12:11:32 -04:00 committed by GitHub
parent e1d49d970f
commit 2f02cd407f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 50 additions and 6 deletions

3
.gitmodules vendored
View File

@ -5,3 +5,6 @@
[submodule "gpt4all-chat/usearch"] [submodule "gpt4all-chat/usearch"]
path = gpt4all-chat/deps/usearch path = gpt4all-chat/deps/usearch
url = https://github.com/nomic-ai/usearch.git url = https://github.com/nomic-ai/usearch.git
[submodule "gpt4all-chat/deps/SingleApplication"]
path = gpt4all-chat/deps/SingleApplication
url = https://github.com/nomic-ai/SingleApplication.git

View File

@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
### Changed ### Changed
- Smaller default window size, dynamic minimum size, and scaling tweaks ([#2904](https://github.com/nomic-ai/gpt4all/pull/2904)) - Smaller default window size, dynamic minimum size, and scaling tweaks ([#2904](https://github.com/nomic-ai/gpt4all/pull/2904))
- Only allow a single instance of program to be run at a time ([#2923](https://github.com/nomic-ai/gpt4all/pull/2923]))
### Fixed ### Fixed
- Bring back "Auto" option for Embeddings Device as "Application default," which went missing in v3.1.0 ([#2873](https://github.com/nomic-ai/gpt4all/pull/2873)) - Bring back "Auto" option for Embeddings Device as "Application default," which went missing in v3.1.0 ([#2873](https://github.com/nomic-ai/gpt4all/pull/2873))

View File

@ -105,6 +105,8 @@ if (APPLE)
list(APPEND CHAT_EXE_RESOURCES "${LOCAL_EMBEDDING_MODEL_PATH}") list(APPEND CHAT_EXE_RESOURCES "${LOCAL_EMBEDDING_MODEL_PATH}")
endif() endif()
set(QAPPLICATION_CLASS QGuiApplication)
add_subdirectory(deps/SingleApplication)
add_subdirectory(src) add_subdirectory(src)
target_sources(chat PRIVATE ${APP_ICON_RESOURCE} ${CHAT_EXE_RESOURCES}) target_sources(chat PRIVATE ${APP_ICON_RESOURCE} ${CHAT_EXE_RESOURCES})
@ -238,7 +240,7 @@ else()
PRIVATE Qt6::Quick Qt6::Svg Qt6::HttpServer Qt6::Sql Qt6::Pdf) PRIVATE Qt6::Quick Qt6::Svg Qt6::HttpServer Qt6::Sql Qt6::Pdf)
endif() endif()
target_link_libraries(chat target_link_libraries(chat
PRIVATE llmodel) PRIVATE llmodel SingleApplication)
# -- install -- # -- install --

@ -0,0 +1 @@
Subproject commit 21bdef01eddcbd78044eea1d50b9dee08d218ff2

View File

@ -9,15 +9,14 @@
#include "network.h" #include "network.h"
#include <gpt4all-backend/llmodel.h> #include <gpt4all-backend/llmodel.h>
#include <singleapplication.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QGuiApplication>
#include <QObject> #include <QObject>
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QQmlEngine> #include <QQuickWindow>
#include <QSettings> #include <QSettings>
#include <QString> #include <QString>
#include <QTranslator>
#include <QUrl> #include <QUrl>
#include <Qt> #include <Qt>
@ -25,6 +24,29 @@
# include <QIcon> # include <QIcon>
#endif #endif
#ifdef Q_OS_WINDOWS
# include <windows.h>
#endif
using namespace Qt::Literals::StringLiterals;
static void raiseWindow(QWindow *window)
{
#ifdef Q_OS_WINDOWS
HWND hwnd = HWND(window->winId());
// check if window is minimized to Windows task bar
if (IsIconic(hwnd))
ShowWindow(hwnd, SW_RESTORE);
SetForegroundWindow(hwnd);
#else
window->show();
window->raise();
window->requestActivate();
#endif
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@ -36,7 +58,15 @@ int main(int argc, char *argv[])
Logger::globalInstance(); Logger::globalInstance();
QGuiApplication app(argc, argv); SingleApplication app(argc, argv, true /*allowSecondary*/);
if (app.isSecondary()) {
#ifdef Q_OS_WINDOWS
AllowSetForegroundWindow(DWORD(app.primaryPid()));
#endif
app.sendMessage("RAISE_WINDOW");
return 0;
}
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
app.setWindowIcon(QIcon(":/gpt4all/icons/gpt4all.svg")); app.setWindowIcon(QIcon(":/gpt4all/icons/gpt4all.svg"));
#endif #endif
@ -77,7 +107,7 @@ int main(int argc, char *argv[])
qmlRegisterSingletonInstance("localdocs", 1, 0, "LocalDocs", LocalDocs::globalInstance()); qmlRegisterSingletonInstance("localdocs", 1, 0, "LocalDocs", LocalDocs::globalInstance());
qmlRegisterUncreatableMetaObject(MySettingsEnums::staticMetaObject, "mysettingsenums", 1, 0, "MySettingsEnums", "Error: only enums"); qmlRegisterUncreatableMetaObject(MySettingsEnums::staticMetaObject, "mysettingsenums", 1, 0, "MySettingsEnums", "Error: only enums");
const QUrl url(u"qrc:/gpt4all/main.qml"_qs); const QUrl url(u"qrc:/gpt4all/main.qml"_s);
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) { &app, [url](QObject *obj, const QUrl &objUrl) {
@ -86,6 +116,13 @@ int main(int argc, char *argv[])
}, Qt::QueuedConnection); }, Qt::QueuedConnection);
engine.load(url); engine.load(url);
QObject *rootObject = engine.rootObjects().first();
QQuickWindow *windowObject = qobject_cast<QQuickWindow *>(rootObject);
Q_ASSERT(windowObject);
if (windowObject)
QObject::connect(&app, &SingleApplication::receivedMessage,
windowObject, [windowObject] () { raiseWindow(windowObject); } );
#if 0 #if 0
QDirIterator it("qrc:", QDirIterator::Subdirectories); QDirIterator it("qrc:", QDirIterator::Subdirectories);
while (it.hasNext()) { while (it.hasNext()) {