// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2026 A-La-Karte Contributors #include "desktopimporter.h" #include #include #include #include #include #include DesktopImporter::DesktopImporter(QObject *parent) : PlatformImporter(parent) { } QString DesktopImporter::platformName() const { return QStringLiteral("Desktop"); } QString DesktopImporter::platformId() const { return QStringLiteral("desktop"); } bool DesktopImporter::isAvailable() const { return true; } QStringList DesktopImporter::getDesktopFilePaths() const { QStringList paths; // Standard XDG application directories QStringList appDirs = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation); // Add common system locations appDirs << QStringLiteral("/usr/share/applications"); appDirs << QStringLiteral("/usr/local/share/applications"); appDirs << expandPath(QStringLiteral("~/.local/share/applications")); // Flatpak export directories appDirs << expandPath(QStringLiteral("~/.local/share/flatpak/exports/share/applications")); appDirs << QStringLiteral("/var/lib/flatpak/exports/share/applications"); for (const QString &dir : appDirs) { QDir appDir(dir); if (appDir.exists()) { QStringList files = appDir.entryList({QStringLiteral("*.desktop")}, QDir::Files); for (const QString &file : files) { QString fullPath = appDir.absoluteFilePath(file); if (!paths.contains(fullPath)) { paths << fullPath; } } } } return paths; } bool DesktopImporter::isGameDesktopFile(const QString &filePath) const { KDesktopFile desktopFile(filePath); KConfigGroup desktop = desktopFile.desktopGroup(); const QString type = desktop.readEntry(QStringLiteral("Type")); if (type != QStringLiteral("Application")) { return false; } // Check if hidden or not shown if (desktop.readEntry(QStringLiteral("Hidden"), false)) { return false; } if (desktop.readEntry(QStringLiteral("NoDisplay"), false)) { return false; } // Check categories for game-related entries const QStringList categories = desktop.readEntry(QStringLiteral("Categories")).split(QLatin1Char(';'), Qt::SkipEmptyParts); return hasGameCategory(categories); } Game *DesktopImporter::parseDesktopFile(const QString &filePath) const { KDesktopFile desktopFile(filePath); KConfigGroup desktop = desktopFile.desktopGroup(); 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; } // Clean up the exec command - remove field codes like %f, %u, %F, %U static QRegularExpression fieldCodeRegex(QStringLiteral("%[fFuUdDnNickvm]")); exec = exec.remove(fieldCodeRegex).trimmed(); // Create unique ID from the desktop file name QFileInfo fileInfo(filePath); const QString desktopId = fileInfo.completeBaseName(); QString gameId = QStringLiteral("desktop_") + desktopId; Game *game = new Game(gameId, name); game->setLaunchCommand(exec); game->setPlatform(platformName()); game->setPlatformId(desktopId); if (!comment.isEmpty()) { game->setDescription(comment); } else if (!genericName.isEmpty()) { game->setDescription(genericName); } // Try to find icon if (!icon.isEmpty()) { // If it's an absolute path, use it directly if (QFile::exists(icon)) { game->setIconUrl(QUrl::fromLocalFile(icon)); } else { // Try to find in standard icon locations QStringList iconPaths = { expandPath(QStringLiteral("~/.local/share/icons/hicolor/256x256/apps/") + icon + QStringLiteral(".png")), QStringLiteral("/usr/share/icons/hicolor/256x256/apps/") + icon + QStringLiteral(".png"), QStringLiteral("/usr/share/pixmaps/") + icon + QStringLiteral(".png"), expandPath(QStringLiteral("~/.local/share/icons/hicolor/128x128/apps/") + icon + QStringLiteral(".png")), QStringLiteral("/usr/share/icons/hicolor/128x128/apps/") + icon + QStringLiteral(".png"), }; for (const QString &iconPath : iconPaths) { if (QFile::exists(iconPath)) { const QUrl url = QUrl::fromLocalFile(iconPath); game->setIconUrl(url); if (!game->coverUrl().isValid() || game->coverUrl().isEmpty()) { game->setCoverUrl(url); } break; } } } } return game; } QList DesktopImporter::importGames() { QList games; QStringList desktopFiles = getDesktopFilePaths(); int total = desktopFiles.count(); int current = 0; for (const QString &filePath : desktopFiles) { current++; Q_EMIT importProgress(current, total); if (!isGameDesktopFile(filePath)) { continue; } Game *game = parseDesktopFile(filePath); if (game) { games.append(game); } } return games; }