Skip to main content

modde_games/tools/
mangohud.rs

1//! `MangoHud` — performance overlay (FPS, CPU/GPU stats, frame timing).
2//!
3//! Enabled via `MANGOHUD=1` env var. Per-game config written to
4//! `~/.local/share/modde/tools/{game_id}/MangoHud.conf` and pointed to
5//! via `MANGOHUD_CONFIG`.
6
7use smallvec::{SmallVec, smallvec};
8
9use super::{
10    GameTool, GeneratedConfig, ToolAvailability, ToolCategory, ToolConfig, tool_config_dir, which,
11};
12
13pub static MANGOHUD: MangoHud = MangoHud;
14
15pub struct MangoHud;
16
17impl GameTool for MangoHud {
18    fn tool_id(&self) -> &'static str {
19        "mangohud"
20    }
21
22    fn display_name(&self) -> &'static str {
23        "MangoHud"
24    }
25
26    fn category(&self) -> ToolCategory {
27        ToolCategory::Overlay
28    }
29
30    fn description(&self) -> &'static str {
31        "Performance HUD overlay for FPS, frame timing, CPU, and GPU telemetry."
32    }
33
34    fn settings_schema(&self) -> Vec<super::ToolSettingSpec> {
35        let mut specs = vec![
36            super::ToolSettingSpec::select(
37                "position",
38                "Position",
39                "Screen corner used for the MangoHud overlay.",
40                &[
41                    "top-left",
42                    "top-center",
43                    "top-right",
44                    "middle-left",
45                    "middle-right",
46                    "bottom-left",
47                    "bottom-center",
48                    "bottom-right",
49                ],
50            ),
51            super::ToolSettingSpec::bool("horizontal", "Horizontal", "Use horizontal HUD layout."),
52            super::ToolSettingSpec::bool(
53                "hud_compact",
54                "Compact HUD",
55                "Use MangoHud compact HUD layout.",
56            ),
57            super::ToolSettingSpec::bool(
58                "no_display",
59                "No display",
60                "Collect/log without showing the overlay.",
61            ),
62            super::ToolSettingSpec::text(
63                "custom_text_center",
64                "Center text",
65                "Custom text shown in the HUD center column.",
66            ),
67            super::ToolSettingSpec::number(
68                "background_alpha",
69                "Background alpha",
70                "HUD background opacity.",
71                0.0,
72                1.0,
73                0.05,
74            ),
75            super::ToolSettingSpec::number(
76                "round_corners",
77                "Round corners",
78                "HUD background corner radius.",
79                0.0,
80                32.0,
81                1.0,
82            ),
83            super::ToolSettingSpec::text(
84                "background_color",
85                "Background color",
86                "HUD background color, such as 020202.",
87            ),
88            super::ToolSettingSpec::number(
89                "font_size",
90                "Font size",
91                "HUD font size.",
92                6.0,
93                48.0,
94                1.0,
95            ),
96            super::ToolSettingSpec::text("text_color", "Text color", "Default HUD text color."),
97            super::ToolSettingSpec::path("font_file", "Font file", "Optional font file path."),
98            super::ToolSettingSpec::number(
99                "offset_x",
100                "Offset X",
101                "Horizontal overlay offset.",
102                -400.0,
103                400.0,
104                1.0,
105            ),
106            super::ToolSettingSpec::number(
107                "offset_y",
108                "Offset Y",
109                "Vertical overlay offset.",
110                -400.0,
111                400.0,
112                1.0,
113            ),
114            super::ToolSettingSpec::text("toggle_hud", "Toggle HUD", "Key used to toggle the HUD."),
115            super::ToolSettingSpec::number(
116                "table_columns",
117                "Table columns",
118                "MangoHud table column count.",
119                1.0,
120                8.0,
121                1.0,
122            ),
123            super::ToolSettingSpec::bool("fps", "FPS", "Show the current frame rate."),
124            super::ToolSettingSpec::bool("frame_timing", "Frame timing", "Show frame timing data."),
125            super::ToolSettingSpec::bool(
126                "frametime",
127                "Frame time graph",
128                "Show frame-time graph data.",
129            ),
130            super::ToolSettingSpec::bool(
131                "show_fps_limit",
132                "Show FPS limit",
133                "Show active FPS limit.",
134            ),
135            super::ToolSettingSpec::bool(
136                "frame_count",
137                "Frame count",
138                "Show rendered frame count.",
139            ),
140            super::ToolSettingSpec::bool("histogram", "Histogram", "Show frame-time histogram."),
141            super::ToolSettingSpec::text("fps_limit", "FPS limit", "FPS limit list or value."),
142            super::ToolSettingSpec::select(
143                "fps_limit_method",
144                "FPS limit method",
145                "MangoHud FPS limiter mode.",
146                &["late", "early"],
147            ),
148            super::ToolSettingSpec::text(
149                "toggle_fps_limit",
150                "Toggle FPS limit",
151                "Key used to toggle FPS limiting.",
152            ),
153            super::ToolSettingSpec::select(
154                "gl_vsync",
155                "OpenGL VSync",
156                "OpenGL VSync setting.",
157                &["-1", "0", "1", "n"],
158            ),
159            super::ToolSettingSpec::text("vsync", "VSync", "VSync setting."),
160            super::ToolSettingSpec::text(
161                "fps_metrics",
162                "FPS metrics",
163                "Low-percentile FPS metrics, such as 0.01 or 0.001.",
164            ),
165            super::ToolSettingSpec::text("fps_color", "FPS color", "FPS text color."),
166            super::ToolSettingSpec::bool(
167                "fps_color_change",
168                "FPS color change",
169                "Change FPS color by threshold.",
170            ),
171            super::ToolSettingSpec::bool("cpu_stats", "CPU stats", "Show CPU load and clocks."),
172            super::ToolSettingSpec::bool(
173                "cpu_load_change",
174                "CPU load change",
175                "Show CPU load changes.",
176            ),
177            super::ToolSettingSpec::bool("core_load", "Core load", "Show per-core load."),
178            super::ToolSettingSpec::bool("core_bars", "Core bars", "Show per-core bars."),
179            super::ToolSettingSpec::bool("cpu_mhz", "CPU MHz", "Show CPU clock speed."),
180            super::ToolSettingSpec::bool("cpu_temp", "CPU temperature", "Show CPU temperature."),
181            super::ToolSettingSpec::bool("cpu_power", "CPU power", "Show CPU power."),
182            super::ToolSettingSpec::bool(
183                "cpu_efficiency",
184                "CPU efficiency",
185                "Show CPU efficiency.",
186            ),
187            super::ToolSettingSpec::bool("core_type", "Core type", "Show CPU core types."),
188            super::ToolSettingSpec::text("cpu_text", "CPU label", "Custom CPU label."),
189            super::ToolSettingSpec::text("cpu_color", "CPU color", "CPU text color."),
190            super::ToolSettingSpec::bool("gpu_stats", "GPU stats", "Show GPU load and clocks."),
191            super::ToolSettingSpec::bool(
192                "gpu_load_change",
193                "GPU load change",
194                "Show GPU load changes.",
195            ),
196            super::ToolSettingSpec::bool("vram", "VRAM", "Show VRAM usage."),
197            super::ToolSettingSpec::bool(
198                "gpu_core_clock",
199                "GPU core clock",
200                "Show GPU core clock.",
201            ),
202            super::ToolSettingSpec::bool(
203                "gpu_mem_clock",
204                "GPU memory clock",
205                "Show GPU memory clock.",
206            ),
207            super::ToolSettingSpec::bool("gpu_temp", "GPU temperature", "Show GPU temperature."),
208            super::ToolSettingSpec::bool(
209                "gpu_mem_temp",
210                "GPU memory temperature",
211                "Show GPU memory temperature.",
212            ),
213            super::ToolSettingSpec::bool(
214                "gpu_junction_temp",
215                "GPU junction temperature",
216                "Show GPU junction temperature.",
217            ),
218            super::ToolSettingSpec::bool("gpu_fan", "GPU fan", "Show GPU fan speed."),
219            super::ToolSettingSpec::bool("gpu_power", "GPU power", "Show GPU power."),
220            super::ToolSettingSpec::bool(
221                "gpu_power_limit",
222                "GPU power limit",
223                "Show GPU power limit.",
224            ),
225            super::ToolSettingSpec::bool(
226                "gpu_efficiency",
227                "GPU efficiency",
228                "Show GPU efficiency.",
229            ),
230            super::ToolSettingSpec::bool(
231                "flip_efficiency",
232                "Flip efficiency",
233                "Show flip efficiency.",
234            ),
235            super::ToolSettingSpec::bool("gpu_voltage", "GPU voltage", "Show GPU voltage."),
236            super::ToolSettingSpec::bool(
237                "throttling_status",
238                "Throttling status",
239                "Show throttling status.",
240            ),
241            super::ToolSettingSpec::bool(
242                "throttling_status_graph",
243                "Throttling graph",
244                "Show throttling graph.",
245            ),
246            super::ToolSettingSpec::bool("gpu_name", "GPU name", "Show GPU name."),
247            super::ToolSettingSpec::bool("vulkan_driver", "Vulkan driver", "Show Vulkan driver."),
248            super::ToolSettingSpec::text("gpu_text", "GPU label", "Custom GPU label."),
249            super::ToolSettingSpec::text("gpu_color", "GPU color", "GPU text color."),
250            super::ToolSettingSpec::text("gpu_list", "GPU list", "GPU list selector."),
251            super::ToolSettingSpec::text("pci_dev", "PCI device", "PCI device selector."),
252        ];
253        specs.extend([
254            super::ToolSettingSpec::bool("ram", "RAM", "Show RAM usage."),
255            super::ToolSettingSpec::bool("io_read", "IO read", "Show disk read throughput."),
256            super::ToolSettingSpec::bool("io_write", "IO write", "Show disk write throughput."),
257            super::ToolSettingSpec::bool("procmem", "Process memory", "Show process memory."),
258            super::ToolSettingSpec::bool("proc_vram", "Process VRAM", "Show process VRAM."),
259            super::ToolSettingSpec::bool("swap", "Swap", "Show swap usage."),
260            super::ToolSettingSpec::bool("ram_temp", "RAM temperature", "Show RAM temperature."),
261            super::ToolSettingSpec::text("vram_color", "VRAM color", "VRAM text color."),
262            super::ToolSettingSpec::text("ram_color", "RAM color", "RAM text color."),
263            super::ToolSettingSpec::text("io_color", "IO color", "IO text color."),
264            super::ToolSettingSpec::text(
265                "frametime_color",
266                "Frame-time color",
267                "Frame-time text color.",
268            ),
269            super::ToolSettingSpec::bool("wine", "Wine", "Show Wine/Proton version."),
270            super::ToolSettingSpec::bool("winesync", "Wine sync", "Show Wine sync method."),
271            super::ToolSettingSpec::bool(
272                "engine_version",
273                "Engine version",
274                "Show game engine version.",
275            ),
276            super::ToolSettingSpec::bool(
277                "engine_short_names",
278                "Engine short names",
279                "Shorten engine names.",
280            ),
281            super::ToolSettingSpec::bool("gamemode", "GameMode status", "Show GameMode status."),
282            super::ToolSettingSpec::bool("vkbasalt", "vkBasalt status", "Show vkBasalt status."),
283            super::ToolSettingSpec::bool("fcat", "FCAT", "Show FCAT overlay."),
284            super::ToolSettingSpec::bool("fex_stats", "FEX stats", "Show FEX stats."),
285            super::ToolSettingSpec::bool("fsr", "FSR", "Show FSR state."),
286            super::ToolSettingSpec::bool("hdr", "HDR", "Show HDR state."),
287            super::ToolSettingSpec::bool("present_mode", "Present mode", "Show present mode."),
288            super::ToolSettingSpec::bool(
289                "display_server",
290                "Display server",
291                "Show display server.",
292            ),
293            super::ToolSettingSpec::bool("arch", "Architecture", "Show process architecture."),
294            super::ToolSettingSpec::bool("resolution", "Resolution", "Show resolution."),
295            super::ToolSettingSpec::bool("refresh_rate", "Refresh rate", "Show refresh rate."),
296            super::ToolSettingSpec::bool("time", "Time", "Show clock."),
297            super::ToolSettingSpec::bool("version", "MangoHud version", "Show MangoHud version."),
298            super::ToolSettingSpec::bool("battery", "Battery", "Show battery state."),
299            super::ToolSettingSpec::bool(
300                "battery_watt",
301                "Battery watts",
302                "Show battery power draw.",
303            ),
304            super::ToolSettingSpec::bool(
305                "battery_time",
306                "Battery time",
307                "Show battery time remaining.",
308            ),
309            super::ToolSettingSpec::bool(
310                "device_battery",
311                "Device battery",
312                "Show controller/device battery.",
313            ),
314            super::ToolSettingSpec::bool(
315                "media_player",
316                "Media player",
317                "Show media player information.",
318            ),
319            super::ToolSettingSpec::bool("network", "Network", "Show network throughput."),
320            super::ToolSettingSpec::text("wine_color", "Wine color", "Wine text color."),
321            super::ToolSettingSpec::text("engine_color", "Engine color", "Engine text color."),
322            super::ToolSettingSpec::text("battery_color", "Battery color", "Battery text color."),
323            super::ToolSettingSpec::text(
324                "media_player_color",
325                "Media color",
326                "Media player text color.",
327            ),
328            super::ToolSettingSpec::path(
329                "output_folder",
330                "Output folder",
331                "MangoHud log output folder.",
332            ),
333            super::ToolSettingSpec::number(
334                "log_duration",
335                "Log duration",
336                "Log duration in seconds.",
337                0.0,
338                86400.0,
339                1.0,
340            ),
341            super::ToolSettingSpec::bool(
342                "autostart_log",
343                "Autostart logging",
344                "Start logging automatically.",
345            ),
346            super::ToolSettingSpec::number(
347                "log_interval",
348                "Log interval",
349                "Logging interval.",
350                0.0,
351                10000.0,
352                1.0,
353            ),
354            super::ToolSettingSpec::text(
355                "toggle_logging",
356                "Toggle logging",
357                "Key used to toggle logging.",
358            ),
359            super::ToolSettingSpec::bool(
360                "log_versioning",
361                "Log versioning",
362                "Version MangoHud log files.",
363            ),
364            super::ToolSettingSpec::bool(
365                "upload_logs",
366                "Upload logs",
367                "Enable MangoHud log upload integration.",
368            ),
369            super::ToolSettingSpec::text("exec", "Exec", "Command executed by MangoHud."),
370            super::ToolSettingSpec::bool(
371                "temp_fahrenheit",
372                "Fahrenheit",
373                "Show temperatures in Fahrenheit.",
374            ),
375            super::ToolSettingSpec::number(
376                "af",
377                "Anisotropic filtering",
378                "Anisotropic filtering override.",
379                0.0,
380                16.0,
381                1.0,
382            ),
383            super::ToolSettingSpec::number(
384                "picmip",
385                "Picmip",
386                "Texture picmip override.",
387                -10.0,
388                10.0,
389                1.0,
390            ),
391            super::ToolSettingSpec::bool("bicubic", "Bicubic", "Enable bicubic scaling flag."),
392            super::ToolSettingSpec::bool(
393                "trilinear",
394                "Trilinear",
395                "Enable trilinear filtering flag.",
396            ),
397            super::ToolSettingSpec::bool("retro", "Retro", "Enable retro scaling flag."),
398        ]);
399        for spec in &mut specs {
400            spec.section = mangohud_setting_section(spec.key);
401        }
402        specs
403    }
404
405    fn detect_available(&self) -> ToolAvailability {
406        #[cfg(not(target_os = "linux"))]
407        {
408            return ToolAvailability::NotInstalled {
409                install_hint: "MangoHud is only available on Linux".into(),
410            };
411        }
412
413        #[cfg(target_os = "linux")]
414        if which("mangohud").is_some() {
415            ToolAvailability::Available { version: None }
416        } else {
417            ToolAvailability::NotInstalled {
418                install_hint: "Install mangohud from your package manager".into(),
419            }
420        }
421    }
422
423    fn env_vars(&self, config: &ToolConfig) -> SmallVec<[(String, String); 4]> {
424        let mut vars = smallvec![("MANGOHUD".into(), "1".into())];
425
426        // Point to per-game config if we generated one
427        if config.settings.as_object().is_some_and(|m| !m.is_empty())
428            && let Some(game_id) = config.get_str("_game_id")
429        {
430            let conf_path = tool_config_dir(game_id).join("MangoHud.conf");
431            vars.push(("MANGOHUD_CONFIG".into(), conf_path.to_string_lossy().into()));
432        }
433
434        vars
435    }
436
437    fn generate_config(&self, config: &ToolConfig) -> Option<GeneratedConfig> {
438        let game_id = config.get_str("_game_id")?;
439        let settings = config.settings.as_object()?;
440
441        let mut lines = Vec::new();
442        lines.push("# Generated by modde — do not edit manually".into());
443
444        for (key, value) in settings {
445            if key.starts_with('_') {
446                continue; // skip internal keys
447            }
448            match value {
449                serde_json::Value::Bool(b) => {
450                    if *b {
451                        lines.push(key.clone());
452                    } else {
453                        lines.push(format!("{key}=0"));
454                    }
455                }
456                serde_json::Value::Number(n) => lines.push(format!("{key}={n}")),
457                serde_json::Value::String(s) => lines.push(format!("{key}={s}")),
458                _ => {}
459            }
460        }
461
462        let dir = tool_config_dir(game_id);
463        Some(GeneratedConfig {
464            path: dir.join("MangoHud.conf"),
465            content: lines.join("\n") + "\n",
466        })
467    }
468
469    fn default_config(&self) -> ToolConfig {
470        let mut config = ToolConfig::new("mangohud");
471        config.set("position", serde_json::json!("top-left"));
472        config.set("fps", serde_json::json!(true));
473        config.set("frametime", serde_json::json!(true));
474        config.set("cpu_stats", serde_json::json!(true));
475        config.set("gpu_stats", serde_json::json!(true));
476        config
477    }
478}
479
480fn mangohud_setting_section(key: &str) -> &'static str {
481    match key {
482        "position" | "horizontal" | "hud_compact" | "no_display" | "custom_text_center"
483        | "background_alpha" | "round_corners" | "background_color" | "font_size"
484        | "text_color" | "font_file" | "offset_x" | "offset_y" | "toggle_hud" | "table_columns" => {
485            "Layout"
486        }
487        "fps" | "frame_timing" | "frametime" | "show_fps_limit" | "frame_count" | "histogram"
488        | "fps_limit" | "fps_limit_method" | "toggle_fps_limit" | "gl_vsync" | "vsync"
489        | "fps_metrics" | "fps_color" | "fps_color_change" => "FPS and Frame Timing",
490        "cpu_stats" | "cpu_load_change" | "core_load" | "core_bars" | "cpu_mhz" | "cpu_temp"
491        | "cpu_power" | "cpu_efficiency" | "core_type" | "cpu_text" | "cpu_color" => "CPU",
492        "gpu_stats"
493        | "gpu_load_change"
494        | "gpu_core_clock"
495        | "gpu_mem_clock"
496        | "gpu_temp"
497        | "gpu_mem_temp"
498        | "gpu_junction_temp"
499        | "gpu_fan"
500        | "gpu_power"
501        | "gpu_power_limit"
502        | "gpu_efficiency"
503        | "flip_efficiency"
504        | "gpu_voltage"
505        | "throttling_status"
506        | "throttling_status_graph"
507        | "gpu_name"
508        | "vulkan_driver"
509        | "gpu_text"
510        | "gpu_color"
511        | "gpu_list"
512        | "pci_dev" => "GPU",
513        "vram" | "ram" | "io_read" | "io_write" | "procmem" | "proc_vram" | "swap" | "ram_temp"
514        | "vram_color" | "ram_color" | "io_color" | "frametime_color" => "Memory and IO",
515        "wine" | "winesync" | "engine_version" | "engine_short_names" | "gamemode" | "vkbasalt"
516        | "fcat" | "fex_stats" | "fsr" | "hdr" | "present_mode" | "display_server" | "arch"
517        | "resolution" | "refresh_rate" | "time" | "version" | "battery" | "battery_watt"
518        | "battery_time" | "device_battery" | "media_player" | "network" | "wine_color"
519        | "engine_color" | "battery_color" | "media_player_color" => "Compatibility",
520        "output_folder" | "log_duration" | "autostart_log" | "log_interval" | "toggle_logging"
521        | "log_versioning" | "upload_logs" | "exec" => "Logging",
522        "temp_fahrenheit" | "af" | "picmip" | "bicubic" | "trilinear" | "retro" => "Filtering",
523        _ => "General",
524    }
525}