use crate::config::{ GameLibsMode, GamescopeFilter, GamescopeScaler, GamescopeSize, GamescopeWindowMode, Settings, VkbasaltLogLevel, }; use crate::error::{AppError, config_error}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SettingKey { Mangohud, Gamemode, SteamHostLibs, GameLibs, Verbose, LogFile, LogPath, Gamescope, GamescopeWidth, GamescopeHeight, GamescopeFps, GamescopeNestedWidth, GamescopeNestedHeight, GamescopeUnfocusedFps, GamescopeScaler, GamescopeFilter, GamescopeSharpness, GamescopeMode, GamescopeAdaptiveSync, GamescopeHdr, GamescopeSteam, GamescopeExposeWayland, GamescopeMangoapp, FpsCap, MangohudLog, MangohudLogPath, Vkbasalt, VkbasaltConfig, VkbasaltLogLevel, Esync, Fsync, LargeAddressAware, PreLaunch, PostLaunch, HookErrors, } impl SettingKey { pub fn parse(value: &str) -> Result { match value { "mangohud" => Ok(Self::Mangohud), "gamemode" => Ok(Self::Gamemode), "steam-host-libs" | "host-libs" => Ok(Self::SteamHostLibs), "game-libs" => Ok(Self::GameLibs), "verbose" => Ok(Self::Verbose), "log-file" => Ok(Self::LogFile), "log-path" => Ok(Self::LogPath), "gamescope" => Ok(Self::Gamescope), "gamescope-width" => Ok(Self::GamescopeWidth), "gamescope-height" => Ok(Self::GamescopeHeight), "gamescope-fps" => Ok(Self::GamescopeFps), "gamescope-nested-width" => Ok(Self::GamescopeNestedWidth), "gamescope-nested-height" => Ok(Self::GamescopeNestedHeight), "gamescope-unfocused-fps" => Ok(Self::GamescopeUnfocusedFps), "gamescope-scaler" => Ok(Self::GamescopeScaler), "gamescope-filter" => Ok(Self::GamescopeFilter), "gamescope-sharpness" => Ok(Self::GamescopeSharpness), "gamescope-mode" => Ok(Self::GamescopeMode), "gamescope-adaptive-sync" => Ok(Self::GamescopeAdaptiveSync), "gamescope-hdr" => Ok(Self::GamescopeHdr), "gamescope-steam" => Ok(Self::GamescopeSteam), "gamescope-expose-wayland" => Ok(Self::GamescopeExposeWayland), "gamescope-mangoapp" => Ok(Self::GamescopeMangoapp), "fps-cap" => Ok(Self::FpsCap), "mangohud-log" => Ok(Self::MangohudLog), "mangohud-log-path" => Ok(Self::MangohudLogPath), "vkbasalt" => Ok(Self::Vkbasalt), "vkbasalt-config" => Ok(Self::VkbasaltConfig), "vkbasalt-log-level" => Ok(Self::VkbasaltLogLevel), "esync" => Ok(Self::Esync), "fsync" => Ok(Self::Fsync), "large-address-aware" | "laa" => Ok(Self::LargeAddressAware), "pre-launch" => Ok(Self::PreLaunch), "post-launch" => Ok(Self::PostLaunch), "hook-errors" => Ok(Self::HookErrors), _ => Err(config_error( format!("`{value}` is not a known setting."), "Run `gamewrap help settings` to see all available settings.", )), } } } pub fn set_value(settings: &mut Settings, key: SettingKey, value: &str) -> Result<(), AppError> { match key { SettingKey::Mangohud => settings.mangohud = Some(parse_toggle(value)?), SettingKey::Gamemode => settings.gamemode = Some(parse_toggle(value)?), SettingKey::SteamHostLibs => settings.steam_host_libs = Some(parse_toggle(value)?), SettingKey::GameLibs => settings.game_libs = Some(parse_game_libs(value)?), SettingKey::Verbose => settings.verbose = Some(parse_toggle(value)?), SettingKey::LogFile => settings.log_file = Some(parse_toggle(value)?), SettingKey::LogPath => settings.log_path = Some(value.to_string()), SettingKey::Gamescope => settings.gamescope = Some(parse_toggle(value)?), SettingKey::GamescopeWidth => { settings.gamescope_width = Some(parse_gamescope_size(value)?); } SettingKey::GamescopeHeight => { settings.gamescope_height = Some(parse_gamescope_size(value)?); } SettingKey::GamescopeFps => { settings.gamescope_fps = Some(parse_fps(value, "gamescope-fps")?); } SettingKey::GamescopeNestedWidth => { settings.gamescope_nested_width = Some(parse_pixel_count(value)?); } SettingKey::GamescopeNestedHeight => { settings.gamescope_nested_height = Some(parse_pixel_count(value)?); } SettingKey::GamescopeUnfocusedFps => { settings.gamescope_unfocused_fps = Some(parse_fps(value, "gamescope-unfocused-fps")?); } SettingKey::GamescopeScaler => { settings.gamescope_scaler = Some(parse_gamescope_scaler(value)?); } SettingKey::GamescopeFilter => { settings.gamescope_filter = Some(parse_gamescope_filter(value)?); } SettingKey::GamescopeSharpness => { settings.gamescope_sharpness = Some(parse_sharpness(value)?); } SettingKey::GamescopeMode => { settings.gamescope_window_mode = Some(parse_gamescope_window_mode(value)?); } SettingKey::GamescopeAdaptiveSync => { settings.gamescope_adaptive_sync = Some(parse_toggle(value)?); } SettingKey::GamescopeHdr => { settings.gamescope_hdr = Some(parse_toggle(value)?); } SettingKey::GamescopeSteam => { settings.gamescope_steam = Some(parse_toggle(value)?); } SettingKey::GamescopeExposeWayland => { settings.gamescope_expose_wayland = Some(parse_toggle(value)?); } SettingKey::GamescopeMangoapp => { settings.gamescope_mangoapp = Some(parse_toggle(value)?); } SettingKey::FpsCap => { let n: u32 = value.parse().map_err(|_| { config_error( format!("`{value}` is not a valid FPS cap. Use a number like `60` or `120`."), "Use `gamewrap config set fps-cap 60` for a 60 FPS cap.", ) })?; settings.fps_cap = Some(n); } SettingKey::MangohudLog => settings.mangohud_log = Some(parse_toggle(value)?), SettingKey::MangohudLogPath => settings.mangohud_log_path = Some(value.to_string()), SettingKey::Vkbasalt => settings.vkbasalt = Some(parse_toggle(value)?), SettingKey::VkbasaltConfig => settings.vkbasalt_config = Some(value.to_string()), SettingKey::VkbasaltLogLevel => { settings.vkbasalt_log_level = Some(parse_vkbasalt_log_level(value)?); } SettingKey::Esync => settings.esync = Some(parse_toggle(value)?), SettingKey::Fsync => settings.fsync = Some(parse_toggle(value)?), SettingKey::LargeAddressAware => settings.large_address_aware = Some(parse_toggle(value)?), SettingKey::PreLaunch => settings.pre_launch = Some(value.to_string()), SettingKey::PostLaunch => settings.post_launch = Some(value.to_string()), SettingKey::HookErrors => settings.hook_errors = Some(parse_hook_errors(value)?), } Ok(()) } pub fn reset_value(settings: &mut Settings, key: SettingKey) { match key { SettingKey::Mangohud => settings.mangohud = None, SettingKey::Gamemode => settings.gamemode = None, SettingKey::SteamHostLibs => settings.steam_host_libs = None, SettingKey::GameLibs => settings.game_libs = None, SettingKey::Verbose => settings.verbose = None, SettingKey::LogFile => settings.log_file = None, SettingKey::LogPath => settings.log_path = None, SettingKey::Gamescope => settings.gamescope = None, SettingKey::GamescopeWidth => settings.gamescope_width = None, SettingKey::GamescopeHeight => settings.gamescope_height = None, SettingKey::GamescopeFps => settings.gamescope_fps = None, SettingKey::GamescopeNestedWidth => settings.gamescope_nested_width = None, SettingKey::GamescopeNestedHeight => settings.gamescope_nested_height = None, SettingKey::GamescopeUnfocusedFps => settings.gamescope_unfocused_fps = None, SettingKey::GamescopeScaler => settings.gamescope_scaler = None, SettingKey::GamescopeFilter => settings.gamescope_filter = None, SettingKey::GamescopeSharpness => settings.gamescope_sharpness = None, SettingKey::GamescopeMode => settings.gamescope_window_mode = None, SettingKey::GamescopeAdaptiveSync => settings.gamescope_adaptive_sync = None, SettingKey::GamescopeHdr => settings.gamescope_hdr = None, SettingKey::GamescopeSteam => settings.gamescope_steam = None, SettingKey::GamescopeExposeWayland => settings.gamescope_expose_wayland = None, SettingKey::GamescopeMangoapp => settings.gamescope_mangoapp = None, SettingKey::FpsCap => settings.fps_cap = None, SettingKey::MangohudLog => settings.mangohud_log = None, SettingKey::MangohudLogPath => settings.mangohud_log_path = None, SettingKey::Vkbasalt => settings.vkbasalt = None, SettingKey::VkbasaltConfig => settings.vkbasalt_config = None, SettingKey::VkbasaltLogLevel => settings.vkbasalt_log_level = None, SettingKey::Esync => settings.esync = None, SettingKey::Fsync => settings.fsync = None, SettingKey::LargeAddressAware => settings.large_address_aware = None, SettingKey::PreLaunch => settings.pre_launch = None, SettingKey::PostLaunch => settings.post_launch = None, SettingKey::HookErrors => settings.hook_errors = None, } } fn parse_toggle(value: &str) -> Result { match value { "on" => Ok(true), "off" => Ok(false), _ => Err(config_error( format!("`{value}` is not a valid on/off value."), "Use `on` or `off`.", )), } } fn parse_game_libs(value: &str) -> Result { match value { "auto" => Ok(GameLibsMode::Auto), "keep" => Ok(GameLibsMode::Keep), "gamemode" => Ok(GameLibsMode::Gamemode), _ => Err(config_error( format!("`{value}` is not a valid value for game-libs."), "Use one of: auto, keep, gamemode.", )), } } fn parse_vkbasalt_log_level(value: &str) -> Result { match value { "debug" => Ok(VkbasaltLogLevel::Debug), "info" => Ok(VkbasaltLogLevel::Info), "warning" => Ok(VkbasaltLogLevel::Warning), "error" => Ok(VkbasaltLogLevel::Error), "none" => Ok(VkbasaltLogLevel::None), _ => Err(config_error( format!("`{value}` is not a valid vkBasalt log level."), "Use one of: debug, info, warning, error, none.", )), } } fn parse_gamescope_scaler(value: &str) -> Result { match value { "auto" => Ok(GamescopeScaler::Auto), "integer" => Ok(GamescopeScaler::Integer), "fit" => Ok(GamescopeScaler::Fit), "fill" => Ok(GamescopeScaler::Fill), "stretch" => Ok(GamescopeScaler::Stretch), _ => Err(config_error( format!("`{value}` is not a valid gamescope scaler."), "Use one of: auto, integer, fit, fill, stretch.", )), } } fn parse_gamescope_filter(value: &str) -> Result { match value { "linear" => Ok(GamescopeFilter::Linear), "nearest" => Ok(GamescopeFilter::Nearest), "fsr" => Ok(GamescopeFilter::Fsr), "nis" => Ok(GamescopeFilter::Nis), "pixel" => Ok(GamescopeFilter::Pixel), _ => Err(config_error( format!("`{value}` is not a valid gamescope filter."), "Use one of: linear, nearest, fsr, nis, pixel.", )), } } fn parse_gamescope_size(value: &str) -> Result { if value == "native" { return Ok(GamescopeSize::Native); } let n: u32 = value.parse().map_err(|_| { config_error( format!( "`{value}` is not a valid resolution. Use a pixel count like `1920` or `native`." ), "Use a whole-number pixel count or `native` to auto-detect the display resolution.", ) })?; Ok(GamescopeSize::Pixels(n)) } fn parse_gamescope_window_mode(value: &str) -> Result { match value { "windowed" => Ok(GamescopeWindowMode::Windowed), "borderless" => Ok(GamescopeWindowMode::Borderless), "fullscreen" => Ok(GamescopeWindowMode::Fullscreen), _ => Err(config_error( format!("`{value}` is not a valid window mode."), "Use one of: windowed, borderless, fullscreen.", )), } } fn parse_sharpness(value: &str) -> Result { let n: u8 = value.parse().map_err(|_| { config_error( format!("`{value}` is not a valid sharpness value. Use a number from 0 to 20."), "0 is maximum sharpness, 20 is minimum sharpness.", ) })?; if n > 20 { return Err(config_error( format!("`{value}` is out of range. Sharpness must be 0–20."), "0 is maximum sharpness, 20 is minimum sharpness.", )); } Ok(n) } fn parse_pixel_count(value: &str) -> Result { value.parse::().map_err(|_| { config_error( format!("`{value}` is not a valid pixel count. Use a number like `1920`."), "Use a whole-number pixel count for gamescope resolution settings.", ) }) } fn parse_fps(value: &str, setting: &str) -> Result { value.parse::().map_err(|_| { config_error( format!("`{value}` is not a valid FPS. Use a number like `60`."), format!("Use `gamewrap config set {setting} 60` for a 60 FPS target."), ) }) } fn parse_hook_errors(value: &str) -> Result { match value { "warn" => Ok(crate::config::HookErrors::Warn), "fail" => Ok(crate::config::HookErrors::Fail), _ => Err(config_error( format!("`{value}` is not a valid hook-errors value."), "Use `warn` (log and continue) or `fail` (abort launch on hook failure).", )), } }