mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-10 08:14:47 -07:00
[Application] Add single instance guard and mime types.
Took 2 hours 39 minutes Took 18 minutes Took 5 minutes
This commit is contained in:
parent
f97864b72b
commit
a9b4be3014
8 changed files with 190 additions and 1 deletions
|
|
@ -34,5 +34,31 @@
|
|||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>cod</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Cockatrice</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Default</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>Cockatrice</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>cockatrice</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -204,6 +204,20 @@ Section "Application" SecApplication
|
|||
SetShellVarContext all
|
||||
SetOutPath "$INSTDIR"
|
||||
|
||||
${If} $PortableMode = 0
|
||||
|
||||
; --- Register .cod file type ---
|
||||
WriteRegStr HKCR ".cod" "" "Cockatrice"
|
||||
WriteRegStr HKCR "Cockatrice" "" "Cockatrice Deck File"
|
||||
WriteRegStr HKCR "Cockatrice\shell\open\command" "" '"$INSTDIR\cockatrice.exe" "%1"'
|
||||
|
||||
; --- Register custom URI protocol ---
|
||||
WriteRegStr HKCR "cockatrice" "" "URL: Cockatrice Protocol"
|
||||
WriteRegStr HKCR "cockatrice" "URL Protocol" ""
|
||||
WriteRegStr HKCR "cockatrice\shell\open\command" "" '"$INSTDIR\cockatrice.exe" "%1"'
|
||||
|
||||
${EndIf}
|
||||
|
||||
${If} $PortableMode = 1
|
||||
${AndIf} ${FileExists} "$INSTDIR\portable.dat"
|
||||
; upgrade portable mode
|
||||
|
|
@ -306,6 +320,9 @@ Section "un.Application" UnSecApplication
|
|||
RMDir "$SMPROGRAMS\Cockatrice"
|
||||
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice"
|
||||
DeleteRegKey HKCR ".cod"
|
||||
DeleteRegKey HKCR "Cockatrice"
|
||||
DeleteRegKey HKCR "cockatrice"
|
||||
SectionEnd
|
||||
|
||||
; unselected because it is /o
|
||||
|
|
|
|||
|
|
@ -325,6 +325,8 @@ set(cockatrice_SOURCES
|
|||
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.h
|
||||
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp
|
||||
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h
|
||||
src/single_instance_manager.cpp
|
||||
src/single_instance_manager.h
|
||||
)
|
||||
|
||||
add_subdirectory(sounds)
|
||||
|
|
@ -382,6 +384,11 @@ set(DESKTOPDIR
|
|||
CACHE STRING "desktop file destination"
|
||||
)
|
||||
|
||||
set(MIMEDIR
|
||||
share/mime/packages
|
||||
CACHE STRING "mime file destination"
|
||||
)
|
||||
|
||||
set(COCKATRICE_MAC_QM_INSTALL_DIR "cockatrice.app/Contents/Resources/translations")
|
||||
set(COCKATRICE_UNIX_QM_INSTALL_DIR "share/cockatrice/translations")
|
||||
set(COCKATRICE_WIN32_QM_INSTALL_DIR "translations")
|
||||
|
|
@ -466,6 +473,7 @@ if(UNIX)
|
|||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/cockatrice.png DESTINATION ${ICONDIR}/hicolor/48x48/apps)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/cockatrice.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cockatrice.desktop DESTINATION ${DESKTOPDIR})
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cockatrice-cod.xml DESTINATION ${MIMEDIR})
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
install(TARGETS cockatrice RUNTIME DESTINATION ./)
|
||||
|
|
|
|||
7
cockatrice/cockatrice-cod.xml
Normal file
7
cockatrice/cockatrice-cod.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||
<mime-type type="application/x-cockatrice">
|
||||
<comment>Cockatrice Deck File</comment>
|
||||
<glob pattern="*.cod"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
|
|
@ -6,3 +6,5 @@ Name=Cockatrice
|
|||
Exec=cockatrice
|
||||
Icon=cockatrice
|
||||
Categories=Game;CardGame;
|
||||
MimeType=application/x-cockatrice;
|
||||
X-Scheme-Handler/cockatrice=true
|
||||
|
|
@ -28,7 +28,9 @@
|
|||
#include "interface/pixel_map_generator.h"
|
||||
#include "interface/theme_manager.h"
|
||||
#include "interface/widgets/dialogs/dlg_settings.h"
|
||||
#include "interface/widgets/tabs/tab_supervisor.h"
|
||||
#include "interface/window_main.h"
|
||||
#include "single_instance_manager.h"
|
||||
#include "version_string.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
|
@ -172,6 +174,7 @@ int main(int argc, char *argv[])
|
|||
SetUnhandledExceptionFilter(CockatriceUnhandledExceptionFilter);
|
||||
#endif
|
||||
|
||||
// Logging setup
|
||||
#ifdef Q_OS_APPLE
|
||||
// <build>/cockatrice/cockatrice.app/Contents/MacOS/cockatrice
|
||||
const QByteArray configPath = "../../../qtlogging.ini";
|
||||
|
|
@ -189,6 +192,7 @@ int main(int argc, char *argv[])
|
|||
// Set the QT_LOGGING_CONF environment variable
|
||||
qputenv("QT_LOGGING_CONF", configPath);
|
||||
}
|
||||
|
||||
qSetMessagePattern(
|
||||
"\033[0m[%{time yyyy-MM-dd h:mm:ss.zzz} "
|
||||
"%{if-debug}\033[36mD%{endif}%{if-info}\033[32mI%{endif}%{if-warning}\033[33mW%{endif}%{if-critical}\033[31mC%{"
|
||||
|
|
@ -198,6 +202,7 @@ int main(int argc, char *argv[])
|
|||
QObject::connect(&app, &QApplication::lastWindowClosed, &app, &QApplication::quit);
|
||||
|
||||
qInstallMessageHandler(CockatriceLogger);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
app.addLibraryPath(app.applicationDirPath() + "/plugins");
|
||||
#endif
|
||||
|
|
@ -213,6 +218,7 @@ int main(int argc, char *argv[])
|
|||
qApp->setAttribute(Qt::AA_DontShowIconsInMenus, true);
|
||||
#endif
|
||||
|
||||
// Translations
|
||||
#ifdef Q_OS_MAC
|
||||
translationPath = qApp->applicationDirPath() + "/../Resources/translations";
|
||||
#elif defined(Q_OS_WIN)
|
||||
|
|
@ -221,6 +227,7 @@ int main(int argc, char *argv[])
|
|||
translationPath = qApp->applicationDirPath() + "/../share/cockatrice/translations";
|
||||
#endif
|
||||
|
||||
// Command-line parser
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("Cockatrice");
|
||||
parser.addHelpOption();
|
||||
|
|
@ -269,6 +276,21 @@ int main(int argc, char *argv[])
|
|||
// then reload the DB. otherwise just reload the DB
|
||||
SpoilerBackgroundUpdater spoilerBackgroundUpdater;
|
||||
|
||||
// --- Handle files or URLs passed at startup ---
|
||||
SingleInstanceManager instance;
|
||||
QStringList startupFiles;
|
||||
|
||||
// Collect command-line files/URLs
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
QString arg = QString::fromLocal8Bit(argv[i]);
|
||||
startupFiles.append(arg);
|
||||
}
|
||||
|
||||
if (!instance.tryRun(startupFiles)) {
|
||||
// Another instance received our files, exit
|
||||
return 0;
|
||||
}
|
||||
|
||||
ui.show();
|
||||
qCInfo(MainLog) << "ui.show() finished";
|
||||
|
||||
|
|
@ -278,7 +300,47 @@ int main(int argc, char *argv[])
|
|||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
app.setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
#endif
|
||||
app.exec();
|
||||
|
||||
for (const QString &file : startupFiles) {
|
||||
if (file.startsWith("cockatrice://")) {
|
||||
// ui.openUrl(QUrl(file));
|
||||
} else if (QFileInfo(file).exists()) {
|
||||
std::optional<LoadedDeck> deckOpt =
|
||||
DeckLoader::loadFromFile(file, DeckFileFormat::getFormatFromName(file), true);
|
||||
if (deckOpt) {
|
||||
ui.getTabSupervisor()->openDeckInNewTab(deckOpt.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to future file/URL events from other instances
|
||||
QObject::connect(&instance, &SingleInstanceManager::filesReceived, [&ui](const QStringList &files) {
|
||||
for (const QString &file : files) {
|
||||
if (file.startsWith("cockatrice://")) {
|
||||
// ui.openUrl(QUrl(file));
|
||||
} else if (QFileInfo(file).exists()) {
|
||||
std::optional<LoadedDeck> deckOpt =
|
||||
DeckLoader::loadFromFile(file, DeckFileFormat::getFormatFromName(file), true);
|
||||
if (deckOpt) {
|
||||
ui.getTabSupervisor()->openDeckInNewTab(deckOpt.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// macOS: handle files opened via Finder after startup
|
||||
QObject::connect(&app, &QApplication::fileOpen, [&ui](const QString &filePath) {
|
||||
qDebug() << "macOS opened file:" << filePath;
|
||||
std::optional<LoadedDeck> deckOpt =
|
||||
DeckLoader::loadFromFile(filePath, DeckFileFormat::getFormatFromName(filePath), true);
|
||||
if (deckOpt) {
|
||||
ui.getTabSupervisor()->openDeckInNewTab(deckOpt.value());
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
int ret = app.exec();
|
||||
|
||||
qCInfo(MainLog) << "Event loop finished, terminating...";
|
||||
delete rng;
|
||||
|
|
|
|||
1
cockatrice/src/single_instance_manager.cpp
Normal file
1
cockatrice/src/single_instance_manager.cpp
Normal file
|
|
@ -0,0 +1 @@
|
|||
#include "single_instance_manager.h"
|
||||
66
cockatrice/src/single_instance_manager.h
Normal file
66
cockatrice/src/single_instance_manager.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#ifndef COCKATRICE_SINGLE_INSTANCE_MANAGER_H
|
||||
#define COCKATRICE_SINGLE_INSTANCE_MANAGER_H
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
|
||||
class SingleInstanceManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SingleInstanceManager(QObject *parent = nullptr) : QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool tryRun(const QStringList &initialFiles)
|
||||
{
|
||||
serverName = "CockatriceSingleInstance";
|
||||
|
||||
QLocalSocket socket;
|
||||
socket.connectToServer(serverName);
|
||||
if (socket.waitForConnected(100)) {
|
||||
// Another instance is running, send files/URLs to it
|
||||
QDataStream out(&socket);
|
||||
out << initialFiles;
|
||||
socket.flush();
|
||||
socket.waitForBytesWritten(1000);
|
||||
|
||||
return false; // Do not continue in this process
|
||||
}
|
||||
|
||||
// No other instance, create server
|
||||
server = new QLocalServer(this);
|
||||
connect(server, &QLocalServer::newConnection, this, &SingleInstanceManager::receiveFiles);
|
||||
if (!server->listen(serverName)) {
|
||||
QLocalServer::removeServer(serverName);
|
||||
server->listen(serverName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
signals:
|
||||
void filesReceived(const QStringList &files);
|
||||
|
||||
private slots:
|
||||
void receiveFiles()
|
||||
{
|
||||
QLocalSocket *clientConnection = server->nextPendingConnection();
|
||||
connect(clientConnection, &QLocalSocket::disconnected, clientConnection, &QLocalSocket::deleteLater);
|
||||
clientConnection->waitForReadyRead(1000);
|
||||
|
||||
QDataStream in(clientConnection);
|
||||
QStringList files;
|
||||
in >> files;
|
||||
|
||||
emit filesReceived(files);
|
||||
clientConnection->disconnectFromServer();
|
||||
}
|
||||
|
||||
private:
|
||||
QString serverName;
|
||||
QLocalServer *server = nullptr;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_SINGLE_INSTANCE_MANAGER_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue