The Problem
Desktop Linux users with multiple game launchers (Steam, Lutris, Heroic, etc.) must open each client to view or launch a game, and there is no unified, offline‑first view that respects the Omarchy visual language. Maintaining separate libraries also fragments achievement tracking and metadata.
What This Does
omakade aggregates locally‑installed games from six launchers into a single Qt‑based UI that matches the Omarchy theme. The discovery logic lives in the src/sources/ folder (e.g., SteamScanner.cpp, LutrisScanner.cpp) and populates unified model objects such as UnifiedGameModel.cpp. UI components are QML files under qml/ (Main.qml, GameCard.qml). The application entry point is src/app/main.cpp, which boots the Qt engine, loads settings from src/app/AppSettings.cpp, and instantiates the QML UI.
Key runtime pieces:
- Scanning – each
*Scannerimplements ascan()method that reads launcher config files or databases and emitsGameInfostructs. - Model layer –
src/library/UnifiedGameModel.cppmerges results from the individual scanners and presents them through Qt’sQAbstractListModel. - Launching –
src/launch/GameLauncher.cppdispatches a request to the appropriate concrete launcher (SteamLauncher.cpp,RetroArchLauncher.cpp, etc.). - Achievements & metadata – optional enrichment via
src/achievements/SteamAchievementApi.cppandsrc/metadata/IgdbApi.cpp.
All assets (icons, demo video) are stored in docs/assets/ and referenced by the QML UI.
How It Is Wired
- Entry point –
src/app/main.cppcreates aQGuiApplication, checks for a single instance viaSingleInstance.cpp, loads persistent settings (AppSettings.cpp), and callsQQuickView::setSource(QUrl(QStringLiteral("qrc:/Main.qml"))). - App initialization –
AppSettingsreads/writes~/.config/omakade/settings.json(file handling is encapsulated inAppSettings.cpp). - Data population –
UnifiedGameModel(insrc/library/UnifiedGameModel.cpp) constructs a list ofGameModelobjects. It invokes each scanner (SteamScanner,LutrisScanner,HeroicScanner,FaugusScanner,RetroArchScanner) which read launcher‑specific files (e.g., Steam’slibraryfolders.vdf, Lutris’lutris.db). The scanners expose astd::vector<GameInfo>thatUnifiedGameModelconverts into Qt roles. - UI binding – QML files under
qml/import the model viacontext->setContextProperty("libraryModel", &unifiedModel). UI components (GameCard.qml,LibraryView.qml) bind to model roles (title,coverPath,source). - User actions – Clicking “Play” triggers a QML signal that calls
GameLauncher::launch(const GameInfo&)(implemented insrc/launch/GameLauncher.cpp). The launcher selects the concrete implementation (SteamLauncher,RetroArchLauncher, …) based on thesourcefield and executes the platform’s launch command (e.g.,steam://run/<appid>orretroarch -L <core> <rom>). - Optional enrichment – If the user enables IGDB or Steam achievement panels,
IgdbApi.cppandSteamAchievementApi.cppperform HTTP requests (Qt Network) and populate additional model roles.
The most “blast‑radius” module is UnifiedGameModel.cpp; changes here affect every UI component and launcher because it is the single source of truth for the game list. The scanner classes are relatively isolated – each reads only its own launcher files, making them safe to modify independently.
No circular dependencies are visible; the call graph is a clear tree from main.cpp → settings → model → scanners/launchers → UI.
How To Use It
# Clone the upstream repository
git clone https://github.com/moses-y/omakade.git
cd omakade
# Configure with the provided dev preset (requires CMake ≥3.24, Ninja, Qt 6.8+, SDL 3, libsecret)
cmake --preset dev
cmake --build --preset dev # builds ./build/dev/omakade
ctest --preset dev # runs the two unit tests in tests/CoreTests.cpp
./build/dev/omakade # launches the application
Configuration files are stored under ~/.config/omakade/. No environment variables are required for a default install. To run the demo library without scanning real launchers, start the binary with --demo (documented in README.md).
Real‑World Use
A user on an Arch‑based desktop installs Omakade alongside their existing Steam, Lutris, and Heroic installations. After the first launch, Omakade scans each launcher’s local database, presents a single, Omarchy‑styled grid of covers, and launches games via the original client when the user selects a title. Achievements from Steam appear inline, and IGDB ratings are fetched on demand, all without contacting the launchers’ cloud services.
Code Health & Issues
- Low – No test coverage for UI layer – only
tests/CoreTests.cppexists; UI components are untested. - Low – Hard‑coded asset paths – QML references assets via relative paths (
docs/assets/…) which may break if the install layout changes. - Low – Limited platform abstraction – launch commands are embedded in each concrete launcher (
SteamLauncher.cpp,RetroArchLauncher.cpp), making addition of a new platform a moderate effort but not a blocker.
Static analysis (CI workflow ci.yml) reports no missing licenses, lockfiles, or structural warnings.
The Bottom Line
omakade delivers a functional, Qt‑based aggregator for Linux game launchers with a clean Omarchy UI and a modular codebase that isolates launcher‑specific logic. It is ready for developers who need a local, offline‑first game library and are comfortable extending the scanner/launcher interfaces to add new sources. Missing UI tests and some hard‑coded paths suggest modest maintenance effort before production deployment.