mirror of
https://invent.kde.org/marcoa/a-la-karte.git
synced 2026-03-26 17:03:08 +00:00
Compare commits
13 commits
b0d09be497
...
cca49615d6
| Author | SHA1 | Date | |
|---|---|---|---|
| cca49615d6 | |||
| 4b7655bc6b | |||
| 058fe8c8e0 | |||
| 88db54458e | |||
| 6080962cf9 | |||
| 2285de6b1e | |||
| 144076675c | |||
| a032bec8c4 | |||
| 9160076aa2 | |||
| c6f3e645f4 | |||
| 05ca3de5f5 | |||
| ca19e21661 | |||
| b1dc8afd48 |
11 changed files with 892 additions and 420 deletions
718
src/app.cpp
718
src/app.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <QHash>
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
|
|
@ -36,6 +38,8 @@ public:
|
|||
static App *instance();
|
||||
static App *create(QQmlEngine *engine, QJSEngine *scriptEngine);
|
||||
|
||||
~App() override;
|
||||
|
||||
GameModel *gameModel() const;
|
||||
GameLauncher *launcher() const;
|
||||
RunnerManagerClient *runnerManager() const;
|
||||
|
|
@ -90,6 +94,8 @@ private:
|
|||
QString m_importStatus;
|
||||
QHash<QString, QJsonObject> m_removedGames;
|
||||
|
||||
std::atomic_bool m_shuttingDown{false};
|
||||
|
||||
void setImporting(bool importing);
|
||||
void setImportStatus(const QString &status);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "gamecenterdaemon.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDBusArgument>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusConnectionInterface>
|
||||
|
|
@ -878,6 +879,48 @@ GameCenterDaemon::GameCenterDaemon(QObject *parent)
|
|||
{
|
||||
}
|
||||
|
||||
GameCenterDaemon::~GameCenterDaemon()
|
||||
{
|
||||
prepareForShutdown();
|
||||
}
|
||||
|
||||
void GameCenterDaemon::prepareForShutdown()
|
||||
{
|
||||
for (auto it = m_sessions.begin(); it != m_sessions.end(); ++it) {
|
||||
if (it.value().scanner) {
|
||||
it.value().scanner->cancel();
|
||||
}
|
||||
|
||||
QPointer<QProcess> proc = it.value().process;
|
||||
it.value().process = nullptr;
|
||||
if (proc) {
|
||||
proc->disconnect(this);
|
||||
connect(proc, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), proc, &QObject::deleteLater);
|
||||
if (proc->state() != QProcess::NotRunning) {
|
||||
proc->setParent(nullptr);
|
||||
} else {
|
||||
proc->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
if (it.value().scanner) {
|
||||
it.value().scanner->disconnect(this);
|
||||
it.value().scanner->deleteLater();
|
||||
it.value().scanner = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = m_unitPathWatchers.begin(); it != m_unitPathWatchers.end(); ++it) {
|
||||
if (QObject *watcher = it.value()) {
|
||||
watcher->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
m_unitPathWatchers.clear();
|
||||
m_unitPathToSessionId.clear();
|
||||
m_unitNameToSessionId.clear();
|
||||
}
|
||||
|
||||
bool GameCenterDaemon::init()
|
||||
{
|
||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
|
|
@ -897,6 +940,10 @@ bool GameCenterDaemon::init()
|
|||
|
||||
recoverExistingSessions();
|
||||
|
||||
if (QCoreApplication::instance()) {
|
||||
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &GameCenterDaemon::prepareForShutdown);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1232,7 +1279,7 @@ QString GameCenterDaemon::launchDirect(const QVariantMap &launchSpec)
|
|||
}
|
||||
|
||||
process->start(program, args);
|
||||
if (!process->waitForStarted()) {
|
||||
if (!process->waitForStarted(5000)) {
|
||||
const QVariantMap ctx = {
|
||||
{QStringLiteral("command"), command},
|
||||
{QStringLiteral("program"), program},
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ class GameCenterDaemon : public QObject
|
|||
|
||||
public:
|
||||
explicit GameCenterDaemon(QObject *parent = nullptr);
|
||||
~GameCenterDaemon() override;
|
||||
|
||||
bool init();
|
||||
|
||||
|
|
@ -76,6 +77,8 @@ private:
|
|||
void handleSystemdUnitPropertiesChanged(const QDBusObjectPath &unitPath, const QVariantMap &changedProperties);
|
||||
void removeSessionInternal(const QString &sessionId, const QString &finalState);
|
||||
|
||||
void prepareForShutdown();
|
||||
|
||||
private Q_SLOTS:
|
||||
void handleSystemdUnitNew(const QString &unitName, const QDBusObjectPath &unitPath);
|
||||
void handleSystemdUnitRemoved(const QString &unitName, const QDBusObjectPath &unitPath);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QSaveFile>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include <memory>
|
||||
|
|
@ -87,14 +88,6 @@ static QString gamepadTypeToString(SDL_GamepadType t)
|
|||
return QStringLiteral("unknown");
|
||||
}
|
||||
}
|
||||
|
||||
static QVariantMap unwrapVariantMap(QVariant v)
|
||||
{
|
||||
if (v.canConvert<QVariantMap>()) {
|
||||
return v.toMap();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QVariantMap InputDaemon::Profile::toVariantMap() const
|
||||
|
|
@ -273,7 +266,10 @@ void InputDaemon::loadProfiles()
|
|||
bool InputDaemon::saveProfiles() const
|
||||
{
|
||||
const QString path = profilesPath();
|
||||
QDir().mkpath(QFileInfo(path).absolutePath());
|
||||
const QString dirPath = QFileInfo(path).absolutePath();
|
||||
if (!QDir().mkpath(dirPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonArray profiles;
|
||||
for (const Profile &p : m_profiles) {
|
||||
|
|
@ -289,13 +285,17 @@ bool InputDaemon::saveProfiles() const
|
|||
root.insert(QStringLiteral("profiles"), profiles);
|
||||
root.insert(QStringLiteral("assignments"), assignments);
|
||||
|
||||
QFile f(path);
|
||||
QSaveFile f(path);
|
||||
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
f.write(QJsonDocument(root).toJson());
|
||||
return true;
|
||||
const QByteArray payload = QJsonDocument(root).toJson();
|
||||
if (f.write(payload) != payload.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return f.commit();
|
||||
}
|
||||
|
||||
InputDaemon::Profile InputDaemon::profileById(const QString &profileId) const
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ QList<Game *> LutrisImporter::importGames()
|
|||
QProcess process;
|
||||
process.start(lutrisPath, {QStringLiteral("-lo"), QStringLiteral("--json")});
|
||||
|
||||
if (process.waitForFinished(30000)) {
|
||||
if (process.waitForStarted(5000) && process.waitForFinished(30000)) {
|
||||
QByteArray output = process.readAllStandardOutput();
|
||||
QJsonDocument doc = QJsonDocument::fromJson(output);
|
||||
|
||||
|
|
@ -139,6 +139,14 @@ QList<Game *> LutrisImporter::importGames()
|
|||
Q_EMIT importProgress(current, total);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (process.state() != QProcess::NotRunning) {
|
||||
process.terminate();
|
||||
if (!process.waitForFinished(3000)) {
|
||||
process.kill();
|
||||
process.waitForFinished(3000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -115,7 +115,10 @@ Kirigami.Dialog {
|
|||
|
||||
readonly property bool anyConfirmOpen: !!(deletePrefixConfirmDialog && deletePrefixConfirmDialog.opened)
|
||||
|
||||
readonly property bool anyMenuOpen: !!(runnerCombo && runnerCombo.popup && runnerCombo.popup.visible)
|
||||
readonly property bool anyMenuOpen: {
|
||||
if (!runnerCombo || !runnerCombo.popup) return false
|
||||
return runnerCombo.popup.visible === true
|
||||
}
|
||||
|
||||
function currentConfirmDialog() {
|
||||
if (deletePrefixConfirmDialog && deletePrefixConfirmDialog.opened) return deletePrefixConfirmDialog
|
||||
|
|
@ -133,7 +136,7 @@ Kirigami.Dialog {
|
|||
}
|
||||
|
||||
function closeCurrentMenu() {
|
||||
if (runnerCombo && runnerCombo.popup && runnerCombo.popup.visible) {
|
||||
if (runnerCombo && runnerCombo.popup && runnerCombo.popup.visible === true) {
|
||||
runnerCombo.popup.close()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -328,8 +328,6 @@ Kirigami.ApplicationWindow {
|
|||
|
||||
footer: Item {
|
||||
implicitWidth: root.width
|
||||
anchors.left: parent ? parent.left : undefined
|
||||
anchors.right: parent ? parent.right : undefined
|
||||
|
||||
implicitHeight: footerBar.implicitHeight
|
||||
height: implicitHeight
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QProcess>
|
||||
#include <QSaveFile>
|
||||
#include <QSet>
|
||||
#include <QStandardPaths>
|
||||
#include <QTimer>
|
||||
|
|
@ -201,7 +202,10 @@ void RunnerManagerDaemon::loadRegistry()
|
|||
bool RunnerManagerDaemon::saveRegistry() const
|
||||
{
|
||||
const QString path = registryPath();
|
||||
QDir().mkpath(QFileInfo(path).absolutePath());
|
||||
const QString dirPath = QFileInfo(path).absolutePath();
|
||||
if (!QDir().mkpath(dirPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonArray arr;
|
||||
|
||||
|
|
@ -221,13 +225,17 @@ bool RunnerManagerDaemon::saveRegistry() const
|
|||
QJsonObject root;
|
||||
root.insert(QStringLiteral("runners"), arr);
|
||||
|
||||
QFile f(path);
|
||||
QSaveFile f(path);
|
||||
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
f.write(QJsonDocument(root).toJson(QJsonDocument::Indented));
|
||||
return true;
|
||||
const QByteArray payload = QJsonDocument(root).toJson(QJsonDocument::Indented);
|
||||
if (f.write(payload) != payload.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return f.commit();
|
||||
}
|
||||
|
||||
QString RunnerManagerDaemon::gameProfilesPath() const
|
||||
|
|
@ -267,7 +275,10 @@ void RunnerManagerDaemon::loadGameProfiles()
|
|||
bool RunnerManagerDaemon::saveGameProfiles() const
|
||||
{
|
||||
const QString path = gameProfilesPath();
|
||||
QDir().mkpath(QFileInfo(path).absolutePath());
|
||||
const QString dirPath = QFileInfo(path).absolutePath();
|
||||
if (!QDir().mkpath(dirPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonArray arr;
|
||||
for (auto it = m_gameProfiles.constBegin(); it != m_gameProfiles.constEnd(); ++it) {
|
||||
|
|
@ -281,13 +292,17 @@ bool RunnerManagerDaemon::saveGameProfiles() const
|
|||
QJsonObject root;
|
||||
root.insert(QStringLiteral("profiles"), arr);
|
||||
|
||||
QFile f(path);
|
||||
QSaveFile f(path);
|
||||
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
f.write(QJsonDocument(root).toJson(QJsonDocument::Indented));
|
||||
return true;
|
||||
const QByteArray payload = QJsonDocument(root).toJson(QJsonDocument::Indented);
|
||||
if (f.write(payload) != payload.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return f.commit();
|
||||
}
|
||||
|
||||
QVariantMap RunnerManagerDaemon::gameProfileForGameId(const QString &gameId) const
|
||||
|
|
|
|||
|
|
@ -152,15 +152,23 @@ void RunnerManagerClient::shutdownSpawnedRunnerDaemon()
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_runnerdProcess->state() == QProcess::NotRunning) {
|
||||
QProcess *p = m_runnerdProcess;
|
||||
const auto state = p->state();
|
||||
if (state == QProcess::NotRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_runnerdProcess->terminate();
|
||||
if (!m_runnerdProcess->waitForFinished(1000)) {
|
||||
m_runnerdProcess->kill();
|
||||
m_runnerdProcess->waitForFinished(1000);
|
||||
// Avoid our finished() handler nulling the pointer while we're shutting down.
|
||||
p->disconnect(this);
|
||||
|
||||
p->terminate();
|
||||
if (!p->waitForFinished(3000)) {
|
||||
p->kill();
|
||||
p->waitForFinished(3000);
|
||||
}
|
||||
|
||||
p->deleteLater();
|
||||
m_runnerdProcess = nullptr;
|
||||
}
|
||||
|
||||
void RunnerManagerClient::ensureRunnerDaemon()
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
#include <KConfigGroup>
|
||||
#include <KSharedConfig>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkReply>
|
||||
#include <QSaveFile>
|
||||
#include <QStandardPaths>
|
||||
#include <QUrlQuery>
|
||||
|
||||
|
|
@ -297,16 +297,28 @@ void SteamGridDB::onImageDownloaded()
|
|||
QString coversPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/covers");
|
||||
QDir dir(coversPath);
|
||||
if (!dir.exists()) {
|
||||
dir.mkpath(coversPath);
|
||||
if (!dir.mkpath(coversPath)) {
|
||||
Q_EMIT fetchError(game, QStringLiteral("Failed to create covers directory"));
|
||||
m_processedGames++;
|
||||
Q_EMIT fetchProgress(m_processedGames, m_totalGames);
|
||||
processNextGame();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QString fileName = game->id() + QStringLiteral(".jpg");
|
||||
QString filePath = coversPath + QStringLiteral("/") + fileName;
|
||||
|
||||
QFile file(filePath);
|
||||
if (file.open(QIODevice::WriteOnly)) {
|
||||
file.write(reply->readAll());
|
||||
file.close();
|
||||
const QByteArray payload = reply->readAll();
|
||||
QSaveFile file(filePath);
|
||||
if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
if (file.write(payload) != payload.size() || !file.commit()) {
|
||||
Q_EMIT fetchError(game, QStringLiteral("Failed to save cover image"));
|
||||
m_processedGames++;
|
||||
Q_EMIT fetchProgress(m_processedGames, m_totalGames);
|
||||
processNextGame();
|
||||
return;
|
||||
}
|
||||
|
||||
QUrl localUrl = QUrl::fromLocalFile(filePath);
|
||||
game->setCoverUrl(localUrl);
|
||||
|
|
|
|||
Loading…
Reference in a new issue