Fix desktop and Flatpak game detection

Use KDesktopFile/KConfigGroup to parse .desktop files reliably.
This avoids Categories parsing pitfalls and improves detection of KDE
stock games. Centralize game-category matching and use stable IDs
based on the full desktop-file basename.
This commit is contained in:
Marco Allegretti 2026-01-20 00:12:12 +01:00
parent 747b02035a
commit e468f53c91
4 changed files with 64 additions and 71 deletions

View file

@ -3,10 +3,11 @@
#include "desktopimporter.h"
#include <KConfigGroup>
#include <KDesktopFile>
#include <QDir>
#include <QFile>
#include <QRegularExpression>
#include <QSettings>
#include <QStandardPaths>
DesktopImporter::DesktopImporter(QObject *parent)
@ -63,58 +64,38 @@ QStringList DesktopImporter::getDesktopFilePaths() const
bool DesktopImporter::isGameDesktopFile(const QString &filePath) const
{
QSettings desktop(filePath, QSettings::IniFormat);
desktop.beginGroup(QStringLiteral("Desktop Entry"));
KDesktopFile desktopFile(filePath);
KConfigGroup desktop = desktopFile.desktopGroup();
QString type = desktop.value(QStringLiteral("Type")).toString();
const QString type = desktop.readEntry(QStringLiteral("Type"));
if (type != QStringLiteral("Application")) {
return false;
}
// Check if hidden or not shown
if (desktop.value(QStringLiteral("Hidden"), false).toBool()) {
if (desktop.readEntry(QStringLiteral("Hidden"), false)) {
return false;
}
if (desktop.value(QStringLiteral("NoDisplay"), false).toBool()) {
if (desktop.readEntry(QStringLiteral("NoDisplay"), false)) {
return false;
}
// Check categories for game-related entries
QString categories = desktop.value(QStringLiteral("Categories")).toString();
QStringList gameCategories = {QStringLiteral("Game"),
QStringLiteral("ArcadeGame"),
QStringLiteral("ActionGame"),
QStringLiteral("AdventureGame"),
QStringLiteral("BlocksGame"),
QStringLiteral("BoardGame"),
QStringLiteral("CardGame"),
QStringLiteral("KidsGame"),
QStringLiteral("LogicGame"),
QStringLiteral("RolePlaying"),
QStringLiteral("Shooter"),
QStringLiteral("Simulation"),
QStringLiteral("SportsGame"),
QStringLiteral("StrategyGame")};
const QStringList categories = desktop.readEntry(QStringLiteral("Categories")).split(QLatin1Char(';'), Qt::SkipEmptyParts);
for (const QString &cat : gameCategories) {
if (categories.contains(cat, Qt::CaseInsensitive)) {
return true;
}
}
return false;
return hasGameCategory(categories);
}
Game *DesktopImporter::parseDesktopFile(const QString &filePath) const
{
QSettings desktop(filePath, QSettings::IniFormat);
desktop.beginGroup(QStringLiteral("Desktop Entry"));
KDesktopFile desktopFile(filePath);
KConfigGroup desktop = desktopFile.desktopGroup();
QString name = desktop.value(QStringLiteral("Name")).toString();
QString exec = desktop.value(QStringLiteral("Exec")).toString();
QString icon = desktop.value(QStringLiteral("Icon")).toString();
QString comment = desktop.value(QStringLiteral("Comment")).toString();
QString genericName = desktop.value(QStringLiteral("GenericName")).toString();
const QString name = desktop.readEntry(QStringLiteral("Name"));
QString exec = desktop.readEntry(QStringLiteral("Exec"));
const QString icon = desktop.readEntry(QStringLiteral("Icon"));
const QString comment = desktop.readEntry(QStringLiteral("Comment"));
const QString genericName = desktop.readEntry(QStringLiteral("GenericName"));
if (name.isEmpty() || exec.isEmpty()) {
return nullptr;
@ -126,12 +107,13 @@ Game *DesktopImporter::parseDesktopFile(const QString &filePath) const
// Create unique ID from the desktop file name
QFileInfo fileInfo(filePath);
QString gameId = QStringLiteral("desktop_") + fileInfo.baseName();
const QString desktopId = fileInfo.completeBaseName();
QString gameId = QStringLiteral("desktop_") + desktopId;
Game *game = new Game(gameId, name);
game->setLaunchCommand(exec);
game->setPlatform(platformName());
game->setPlatformId(platformId());
game->setPlatformId(desktopId);
if (!comment.isEmpty()) {
game->setDescription(comment);

View file

@ -3,11 +3,12 @@
#include "flatpakimporter.h"
#include <KConfigGroup>
#include <KDesktopFile>
#include <QDir>
#include <QFile>
#include <QProcess>
#include <QRegularExpression>
#include <QSettings>
FlatpakImporter::FlatpakImporter(QObject *parent)
: PlatformImporter(parent)
@ -43,54 +44,34 @@ bool FlatpakImporter::isAvailable() const
bool FlatpakImporter::isGameApp(const QString &desktopFilePath) const
{
QSettings desktop(desktopFilePath, QSettings::IniFormat);
desktop.beginGroup(QStringLiteral("Desktop Entry"));
KDesktopFile desktopFile(desktopFilePath);
KConfigGroup desktop = desktopFile.desktopGroup();
QString type = desktop.value(QStringLiteral("Type")).toString();
const QString type = desktop.readEntry(QStringLiteral("Type"));
if (type != QStringLiteral("Application")) {
return false;
}
if (desktop.value(QStringLiteral("Hidden"), false).toBool()) {
if (desktop.readEntry(QStringLiteral("Hidden"), false)) {
return false;
}
if (desktop.value(QStringLiteral("NoDisplay"), false).toBool()) {
if (desktop.readEntry(QStringLiteral("NoDisplay"), false)) {
return false;
}
QString categories = desktop.value(QStringLiteral("Categories")).toString();
QStringList gameCategories = {QStringLiteral("Game"),
QStringLiteral("ArcadeGame"),
QStringLiteral("ActionGame"),
QStringLiteral("AdventureGame"),
QStringLiteral("BlocksGame"),
QStringLiteral("BoardGame"),
QStringLiteral("CardGame"),
QStringLiteral("KidsGame"),
QStringLiteral("LogicGame"),
QStringLiteral("RolePlaying"),
QStringLiteral("Shooter"),
QStringLiteral("Simulation"),
QStringLiteral("SportsGame"),
QStringLiteral("StrategyGame")};
const QStringList categories = desktop.readEntry(QStringLiteral("Categories")).split(QLatin1Char(';'), Qt::SkipEmptyParts);
for (const QString &cat : gameCategories) {
if (categories.contains(cat, Qt::CaseInsensitive)) {
return true;
}
}
return false;
return hasGameCategory(categories);
}
Game *FlatpakImporter::parseDesktopFile(const QString &filePath, const QString &appId) const
{
QSettings desktop(filePath, QSettings::IniFormat);
desktop.beginGroup(QStringLiteral("Desktop Entry"));
KDesktopFile desktopFile(filePath);
KConfigGroup desktop = desktopFile.desktopGroup();
QString name = desktop.value(QStringLiteral("Name")).toString();
QString icon = desktop.value(QStringLiteral("Icon")).toString();
QString comment = desktop.value(QStringLiteral("Comment")).toString();
const QString name = desktop.readEntry(QStringLiteral("Name"));
const QString icon = desktop.readEntry(QStringLiteral("Icon"));
const QString comment = desktop.readEntry(QStringLiteral("Comment"));
if (name.isEmpty()) {
return nullptr;
@ -100,7 +81,7 @@ Game *FlatpakImporter::parseDesktopFile(const QString &filePath, const QString &
Game *game = new Game(gameId, name);
game->setPlatform(platformName());
game->setPlatformId(platformId());
game->setPlatformId(appId);
game->setLaunchCommand(QStringLiteral("flatpak run %1").arg(appId));
if (!comment.isEmpty()) {

View file

@ -44,3 +44,31 @@ QString PlatformImporter::expandPath(const QString &path) const
return result;
}
bool PlatformImporter::hasGameCategory(const QStringList &categories)
{
static const QStringList gameCategories = {QStringLiteral("Game"),
QStringLiteral("ArcadeGame"),
QStringLiteral("ActionGame"),
QStringLiteral("AdventureGame"),
QStringLiteral("BlocksGame"),
QStringLiteral("BoardGame"),
QStringLiteral("CardGame"),
QStringLiteral("KidsGame"),
QStringLiteral("LogicGame"),
QStringLiteral("RolePlaying"),
QStringLiteral("Shooter"),
QStringLiteral("Simulation"),
QStringLiteral("SportsGame"),
QStringLiteral("StrategyGame")};
for (const QString &category : categories) {
for (const QString &cat : gameCategories) {
if (category.compare(cat, Qt::CaseInsensitive) == 0) {
return true;
}
}
}
return false;
}

View file

@ -5,6 +5,7 @@
#include <QList>
#include <QObject>
#include <QStringList>
#include "game.h"
@ -29,4 +30,5 @@ protected:
QString findExecutable(const QString &name) const;
bool directoryExists(const QString &path) const;
QString expandPath(const QString &path) const;
static bool hasGameCategory(const QStringList &categories);
};