# Architecture — MangoTune ## Rust Edition & MSRV - Rust edition: **2021** - Minimum Supported Rust Version: **1.75.0** - Build target: `x86_64-unknown-linux-gnu` (primary), `aarch64-unknown-linux-gnu` (secondary) ## Cargo.toml Dependencies ```toml [package] name = "mangotune" version = "0.1.0" edition = "2021" authors = ["MangoTune Contributors"] description = "A modern MangoHud configurator for Linux" license = "GPL-3.0" repository = "https://github.com/your-org/mangotune" [[bin]] name = "mangotune" path = "src/main.rs" [dependencies] # GUI gtk4 = { version = "0.9", features = ["v4_12"] } libadwaita = { version = "0.7", features = ["v1_5"] } glib = "0.20" gio = "0.20" # Async runtime (for subprocess management, file watching) tokio = { version = "1", features = ["rt-multi-thread", "process", "fs", "sync", "time"] } # Serialization (for GSettings schema, internal state) serde = { version = "1", features = ["derive"] } serde_json = "1" # Config file parsing indexmap = "2" # preserve insertion order in parsed configs # Error handling anyhow = "1" thiserror = "1" # Filesystem watching (live reload when config changes externally) notify = "6" # XDG base directory resolution xdg = "2" # Regex (for config line parsing) regex = "1" once_cell = "1" # Process detection (checking if gamemode is running, etc.) sysinfo = "0.31" # Logging/tracing tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } [build-dependencies] # For compiling GSettings schema and other build-time assets glib-build-tools = "0.20" [dev-dependencies] tempfile = "3" assert_fs = "1" ``` ## System Dependencies (must be present at build time) | Package | Ubuntu/Debian | Fedora/RHEL | Arch | |--------------------------|---------------------------|---------------------------------|----------------| | GTK4 dev headers | `libgtk-4-dev` | `gtk4-devel` | `gtk4` | | libadwaita dev headers | `libadwaita-1-dev` | `libadwaita-devel` | `libadwaita` | | GLib dev headers | `libglib2.0-dev` | `glib2-devel` | `glib2` | | pkg-config | `pkg-config` | `pkgconf` | `pkgconf` | Runtime (optional, detected at launch): - `mangohud` — the actual overlay - `vkcube` — from `vulkan-tools` package - `glxgears` — from `mesa-utils` package - `gamemoded` — from `gamemode` package - `gamemodectl` — from `gamemode` package ## Module Dependency Graph ``` main.rs └── app.rs (GtkApplication) └── window.rs (AdwApplicationWindow) ├── ui/pages/*.rs ← all pages │ ├── config/resolver.rs ← discovers config stack │ ├── config/validator.rs ← validates on every change │ ├── config/parser.rs ← reads/writes files │ └── config/schema.rs ← option definitions ├── ui/widgets/*.rs ← reusable widgets ├── system/detect.rs ← run at startup ├── system/paths.rs ← XDG resolution ├── launcher/runner.rs ← test process management └── integrations/*.rs ← GameMode/Steam/Lutris/Heroic ``` ## Data Flow ``` Startup: system::detect::run() → SystemInfo { mangohud_version, gpu_vendor, display_server, available_tools } config::resolver::discover() → Vec { path, source_type, priority, exists } For each ConfigLayer: config::parser::read(path) → RawConfig { lines: Vec } config::schema::annotate(raw) → AnnotatedConfig { options: IndexMap } User edits a field: ui::widgets::* emits change signal → config::validator::check(key, value, &schema) → ValidationResult::Ok | ValidationResult::Error(msg) | ValidationResult::Warning(msg) If Ok: update in-memory AnnotatedConfig check for dependency side-effects update cascade_view to show which layer owns the value enable Save button if no errors anywhere Save: config::validator::check_all(&config) ← full pass before any write → if any Error: abort, show error summary toast → if all Ok: config::parser::write(path, config) preserving all comment lines unchanged ``` ## Threading Model - **Main thread**: GTK4 event loop only. No blocking calls. - **Tokio thread pool**: file I/O, subprocess spawning, filesystem watcher. - Communication: `glib::MainContext::channel()` for sending results back to GTK main thread. - Never call GTK functions from tokio threads. ## GSettings Schema Used for persisting app preferences (window size, last-opened config path, theme preference). NOT used for MangoHud config itself — that is always written directly to .conf files. Schema ID: `com.mangotune.MangoTune` Keys: - `last-config-path` (string) - `window-width` (int, default 1200) - `window-height` (int, default 780) - `active-page` (string, default "performance") - `show-raw-editor` (bool, default false) ## Error Handling Strategy - All I/O operations return `anyhow::Result`. - UI layer converts errors to `AdwToast` notifications (non-blocking). - Critical startup errors (GTK init failure) use `eprintln!` + `process::exit(1)`. - Validation errors are `thiserror` enums, displayed inline, never panicked on. - Never use `.unwrap()` or `.expect()` in production paths. Use `?` or match. ## Config File Format Notes MangoHud .conf files follow these rules (the parser must handle all of them): 1. Lines starting with `#` are comments — preserve verbatim. 2. Empty lines — preserve verbatim. 3. `key=value` — option with value. 4. `key` alone (no `=`) — boolean flag, presence = enabled. 5. `# key` — commented-out option (disabled). 6. `# key=value` — commented-out option with default value shown. 7. Inline comments after values are NOT standard and should be treated as part of value. 8. Duplicate keys: last occurrence wins (MangoHud behavior). 9. Encoding: UTF-8. The parser must distinguish between: - An option that is absent from the file (use MangoHud's compiled default) - An option explicitly set to 0 or empty (user explicitly disabled) - An option present as a bare key (user enabled a flag)