Prevent multiple instances and center settings dialog
This commit is contained in:
@@ -1,7 +1,12 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QByteArray>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
#include <QIODevice>
|
||||||
|
#include <QLocalServer>
|
||||||
|
#include <QLocalSocket>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
#include "src/config/ConfigManager.h"
|
#include "src/config/ConfigManager.h"
|
||||||
#include "src/tray/TrayController.h"
|
#include "src/tray/TrayController.h"
|
||||||
@@ -9,12 +14,41 @@
|
|||||||
#include "src/util/Logger.h"
|
#include "src/util/Logger.h"
|
||||||
#include "src/util/ResourcePaths.h"
|
#include "src/util/ResourcePaths.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const QString SingleInstanceServerName = QStringLiteral("Make.QtDesktopPet.SingleInstance");
|
||||||
|
const QByteArray ActivateMessage("activate\n");
|
||||||
|
constexpr int SingleInstanceConnectTimeoutMs = 500;
|
||||||
|
constexpr int SingleInstanceWriteTimeoutMs = 500;
|
||||||
|
|
||||||
|
bool notifyRunningInstance()
|
||||||
|
{
|
||||||
|
QLocalSocket socket;
|
||||||
|
socket.connectToServer(SingleInstanceServerName, QIODevice::WriteOnly);
|
||||||
|
if (!socket.waitForConnected(SingleInstanceConnectTimeoutMs))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.write(ActivateMessage);
|
||||||
|
socket.flush();
|
||||||
|
socket.waitForBytesWritten(SingleInstanceWriteTimeoutMs);
|
||||||
|
socket.disconnectFromServer();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
QApplication::setApplicationName("QtDesktopPet");
|
QApplication::setApplicationName("QtDesktopPet");
|
||||||
QApplication::setOrganizationName("QtDesktopPet");
|
QApplication::setOrganizationName("QtDesktopPet");
|
||||||
|
|
||||||
|
if (notifyRunningInstance())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const QIcon appIcon(ResourcePaths::appIconPath());
|
const QIcon appIcon(ResourcePaths::appIconPath());
|
||||||
if (!appIcon.isNull())
|
if (!appIcon.isNull())
|
||||||
{
|
{
|
||||||
@@ -23,6 +57,19 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
Logger::info(QStringLiteral("Application started."));
|
Logger::info(QStringLiteral("Application started."));
|
||||||
|
|
||||||
|
QLocalServer singleInstanceServer;
|
||||||
|
QLocalServer::removeServer(SingleInstanceServerName);
|
||||||
|
if (!singleInstanceServer.listen(SingleInstanceServerName))
|
||||||
|
{
|
||||||
|
if (notifyRunningInstance())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::warning(QStringLiteral("Unable to start single instance server: ")
|
||||||
|
+ singleInstanceServer.errorString());
|
||||||
|
}
|
||||||
|
|
||||||
ConfigManager configManager;
|
ConfigManager configManager;
|
||||||
PetWindow window;
|
PetWindow window;
|
||||||
window.applyAppConfig(configManager.loadAppConfig());
|
window.applyAppConfig(configManager.loadAppConfig());
|
||||||
@@ -31,6 +78,37 @@ int main(int argc, char *argv[])
|
|||||||
window.setSettingsFallbackInContextMenuEnabled(!trayController.isAvailable());
|
window.setSettingsFallbackInContextMenuEnabled(!trayController.isAvailable());
|
||||||
trayController.show();
|
trayController.show();
|
||||||
|
|
||||||
|
QObject::connect(&singleInstanceServer, &QLocalServer::newConnection, [&singleInstanceServer, &window]() {
|
||||||
|
while (singleInstanceServer.hasPendingConnections())
|
||||||
|
{
|
||||||
|
QLocalSocket *socket = singleInstanceServer.nextPendingConnection();
|
||||||
|
if (socket == nullptr)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto handleActivationMessage = [socket, &window]() {
|
||||||
|
const QByteArray message = socket->readAll().trimmed();
|
||||||
|
if (message == ActivateMessage.trimmed())
|
||||||
|
{
|
||||||
|
window.activateFromExternalInstance();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
QObject::connect(socket, &QLocalSocket::readyRead, handleActivationMessage);
|
||||||
|
QObject::connect(socket, &QLocalSocket::disconnected, [socket, handleActivationMessage]() {
|
||||||
|
if (socket->bytesAvailable() > 0)
|
||||||
|
{
|
||||||
|
handleActivationMessage();
|
||||||
|
}
|
||||||
|
socket->deleteLater();
|
||||||
|
});
|
||||||
|
if (socket->bytesAvailable() > 0)
|
||||||
|
{
|
||||||
|
handleActivationMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
QObject::connect(&app, &QCoreApplication::aboutToQuit, [&configManager, &window]() {
|
QObject::connect(&app, &QCoreApplication::aboutToQuit, [&configManager, &window]() {
|
||||||
if (!configManager.saveAppConfig(window.currentAppConfig()))
|
if (!configManager.saveAppConfig(window.currentAppConfig()))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "SettingsDialog.h"
|
#include "SettingsDialog.h"
|
||||||
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
#include <QApplication>
|
||||||
#include <QContextMenuEvent>
|
#include <QContextMenuEvent>
|
||||||
#include <QCursor>
|
#include <QCursor>
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
@@ -89,6 +90,46 @@ AppConfig normalizedAppConfig(AppConfig config)
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QScreen *screenForPopup(const QWidget *reference)
|
||||||
|
{
|
||||||
|
if (reference != nullptr)
|
||||||
|
{
|
||||||
|
if (QScreen *screen = QGuiApplication::screenAt(reference->frameGeometry().center()))
|
||||||
|
{
|
||||||
|
return screen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (QScreen *screen = QGuiApplication::screenAt(QCursor::pos()))
|
||||||
|
{
|
||||||
|
return screen;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QGuiApplication::primaryScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void centerDialogOnScreen(QDialog *dialog, const QWidget *reference)
|
||||||
|
{
|
||||||
|
if (dialog == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScreen *screen = screenForPopup(reference);
|
||||||
|
if (screen == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QRect availableGeometry = screen->availableGeometry();
|
||||||
|
const QSize dialogSize = dialog->size().isValid()
|
||||||
|
? dialog->size()
|
||||||
|
: dialog->sizeHint();
|
||||||
|
const int x = availableGeometry.left() + qMax(0, (availableGeometry.width() - dialogSize.width()) / 2);
|
||||||
|
const int y = availableGeometry.top() + qMax(0, (availableGeometry.height() - dialogSize.height()) / 2);
|
||||||
|
dialog->move(QPoint(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
QString userVisibleErrorMessage(const ChatResponse &response)
|
QString userVisibleErrorMessage(const ChatResponse &response)
|
||||||
{
|
{
|
||||||
QString message = response.errorMessage.trimmed();
|
QString message = response.errorMessage.trimmed();
|
||||||
@@ -284,6 +325,7 @@ void PetWindow::openSettingsDialog()
|
|||||||
}, [this]() {
|
}, [this]() {
|
||||||
clearConversation();
|
clearConversation();
|
||||||
}, this);
|
}, this);
|
||||||
|
centerDialogOnScreen(&dialog, this);
|
||||||
if (dialog.exec() != QDialog::Accepted)
|
if (dialog.exec() != QDialog::Accepted)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -301,6 +343,31 @@ void PetWindow::openSettingsDialog()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PetWindow::activateFromExternalInstance()
|
||||||
|
{
|
||||||
|
if (!isVisible())
|
||||||
|
{
|
||||||
|
show();
|
||||||
|
resumeAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMinimized())
|
||||||
|
{
|
||||||
|
setWindowState(windowState() & ~Qt::WindowMinimized);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (QWidget *modalWidget = QApplication::activeModalWidget())
|
||||||
|
{
|
||||||
|
modalWidget->raise();
|
||||||
|
modalWidget->activateWindow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
raise();
|
||||||
|
activateWindow();
|
||||||
|
updateBubblePosition();
|
||||||
|
}
|
||||||
|
|
||||||
void PetWindow::setSettingsFallbackInContextMenuEnabled(bool enabled)
|
void PetWindow::setSettingsFallbackInContextMenuEnabled(bool enabled)
|
||||||
{
|
{
|
||||||
m_settingsFallbackInContextMenuEnabled = enabled;
|
m_settingsFallbackInContextMenuEnabled = enabled;
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ public:
|
|||||||
void applyAppConfig(const AppConfig &config);
|
void applyAppConfig(const AppConfig &config);
|
||||||
AppConfig currentAppConfig() const;
|
AppConfig currentAppConfig() const;
|
||||||
void openSettingsDialog();
|
void openSettingsDialog();
|
||||||
|
void activateFromExternalInstance();
|
||||||
void setSettingsFallbackInContextMenuEnabled(bool enabled);
|
void setSettingsFallbackInContextMenuEnabled(bool enabled);
|
||||||
void pauseAnimation();
|
void pauseAnimation();
|
||||||
void resumeAnimation();
|
void resumeAnimation();
|
||||||
|
|||||||
Reference in New Issue
Block a user