diff --git a/CHANGELOG.md b/CHANGELOG.md index d362e5b..ac97adc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- Added game sandboxing feature +- Added debugger to wine tools + +### Changed + +- Removed fractions displaying in components downloading progress bar +- Moved to upgraded launcher SDK + ## [3.4.1] - 12.04.2023 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 6b4d561..4c48bd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,12 +40,13 @@ dependencies = [ [[package]] name = "anime-game-core" -version = "1.6.2" -source = "git+https://github.com/an-anime-team/anime-game-core?tag=1.6.2#f41c0edbf6d830662b20a220987531bb517cf8cb" +version = "1.7.0" +source = "git+https://github.com/an-anime-team/anime-game-core?tag=1.7.0#290cc5ff9399f68799e8e00fd3f78fa26d076296" dependencies = [ "anyhow", "bzip2", "cached", + "dns-lookup", "flate2", "fs_extra", "kinda-virtual-fs", @@ -86,8 +87,8 @@ dependencies = [ [[package]] name = "anime-launcher-sdk" -version = "0.5.17" -source = "git+https://github.com/an-anime-team/anime-launcher-sdk?tag=0.5.17#5b4403956feaca7204f057a26f06e69b7fc1b285" +version = "1.0.1" +source = "git+https://github.com/an-anime-team/anime-launcher-sdk?tag=1.0.1#1d56514b71da92b827ec4162975a12e4bf9b13fc" dependencies = [ "anime-game-core", "anyhow", @@ -329,9 +330,9 @@ dependencies = [ [[package]] name = "cached" -version = "0.42.0" +version = "0.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5877db5d1af7fae60d06b5db9430b68056a69b3582a0be8e3691e87654aeb6" +checksum = "bc2fafddf188d13788e7099295a59b99e99b2148ab2195cae454e754cc099925" dependencies = [ "async-trait", "async_once", @@ -631,6 +632,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "dns-lookup" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53ecafc952c4528d9b51a458d1a8904b81783feff9fde08ab6ed2545ff396872" +dependencies = [ + "cfg-if", + "libc", + "socket2", + "winapi", +] + [[package]] name = "doc-comment" version = "0.3.3" diff --git a/Cargo.toml b/Cargo.toml index cdceb54..1b01968 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,8 @@ glib-build-tools = "0.17" [dependencies.anime-launcher-sdk] git = "https://github.com/an-anime-team/anime-launcher-sdk" -tag = "0.5.17" +tag = "1.0.1" +features = ["all", "genshin"] # path = "../anime-launcher-sdk" # ! for dev purposes only @@ -29,14 +30,14 @@ adw = { package = "libadwaita", version = "0.3", features = ["v1_2"] } rfd = { version = "0.11", features = ["xdg-portal"], default-features = false } open = "4.0" +serde_json = "1" +anyhow = "1.0" +lazy_static = "1.4.0" +cached = { version = "0.43", features = ["proc_macro"] } +md-5 = { version = "0.10", features = ["asm"] } + tracing = "0.1" tracing-subscriber = "0.3" fluent-templates = "0.8" unic-langid = "0.9" - -lazy_static = "1.4.0" -anyhow = "1.0" -cached = { version = "0.42", features = ["proc_macro"] } -serde_json = "1" -md-5 = { version = "0.10", features = ["asm"] } diff --git a/assets/locales/de/general.ftl b/assets/locales/de/general.ftl index 3fa9c9c..f329d3b 100644 --- a/assets/locales/de/general.ftl +++ b/assets/locales/de/general.ftl @@ -69,6 +69,7 @@ registry-editor = Registry editor explorer = Explorer task-manager = Task manager configuration = Configuration +debugger = Debugger dxvk-version = DXVK version dxvk-selection-disabled = DXVK auswahl ist durch ihre Wine auswahl deaktiviert diff --git a/assets/locales/de/sandbox.ftl b/assets/locales/de/sandbox.ftl new file mode 100644 index 0000000..4d09ae6 --- /dev/null +++ b/assets/locales/de/sandbox.ftl @@ -0,0 +1,24 @@ +sandbox = Sandbox +sandbox-description = Run the game in isolated environment, preventing it from accessing your personal data + +enable-sandboxing = Enable sandboxing +enable-sandboxing-description = Run the game in read-only copy of your root filesystem + +hide-home-directory = Hide home directory +hide-home-directory-description = Isolate your /home, /var/home/$USER, and $HOME folders from the game + +hostname = Hostname + +private-directories = Private directories +private-directories-description = These folders will be replaced by an empty virtual filesystem (tmpfs), and their original content will not be available to sandboxed game + +path = Path + +shared-directories = Shared directories +shared-directories-description = These directories will be symlinked to directories in your host system + +original-path = Original path +new-path = New path + +read-only = Read-only +read-only-description = Forbid game to write any data to this directory diff --git a/assets/locales/en/general.ftl b/assets/locales/en/general.ftl index a85cd76..53630d0 100644 --- a/assets/locales/en/general.ftl +++ b/assets/locales/en/general.ftl @@ -69,6 +69,7 @@ registry-editor = Registry editor explorer = Explorer task-manager = Task manager configuration = Configuration +debugger = Debugger dxvk-version = DXVK version dxvk-selection-disabled = DXVK selection is disabled by your wine group preferences diff --git a/assets/locales/en/sandbox.ftl b/assets/locales/en/sandbox.ftl new file mode 100644 index 0000000..4d09ae6 --- /dev/null +++ b/assets/locales/en/sandbox.ftl @@ -0,0 +1,24 @@ +sandbox = Sandbox +sandbox-description = Run the game in isolated environment, preventing it from accessing your personal data + +enable-sandboxing = Enable sandboxing +enable-sandboxing-description = Run the game in read-only copy of your root filesystem + +hide-home-directory = Hide home directory +hide-home-directory-description = Isolate your /home, /var/home/$USER, and $HOME folders from the game + +hostname = Hostname + +private-directories = Private directories +private-directories-description = These folders will be replaced by an empty virtual filesystem (tmpfs), and their original content will not be available to sandboxed game + +path = Path + +shared-directories = Shared directories +shared-directories-description = These directories will be symlinked to directories in your host system + +original-path = Original path +new-path = New path + +read-only = Read-only +read-only-description = Forbid game to write any data to this directory diff --git a/assets/locales/es/general.ftl b/assets/locales/es/general.ftl index 74ca0ff..7379d51 100755 --- a/assets/locales/es/general.ftl +++ b/assets/locales/es/general.ftl @@ -69,6 +69,7 @@ registry-editor = Editor del Registro explorer = Explorador task-manager = Administrador de Tareas configuration = Configuración +debugger = Debugger dxvk-version = Versión de DXVK dxvk-selection-disabled = La selección de DXVK está deshabilitada por las preferencias de su grupo de vinos diff --git a/assets/locales/es/sandbox.ftl b/assets/locales/es/sandbox.ftl new file mode 100644 index 0000000..4d09ae6 --- /dev/null +++ b/assets/locales/es/sandbox.ftl @@ -0,0 +1,24 @@ +sandbox = Sandbox +sandbox-description = Run the game in isolated environment, preventing it from accessing your personal data + +enable-sandboxing = Enable sandboxing +enable-sandboxing-description = Run the game in read-only copy of your root filesystem + +hide-home-directory = Hide home directory +hide-home-directory-description = Isolate your /home, /var/home/$USER, and $HOME folders from the game + +hostname = Hostname + +private-directories = Private directories +private-directories-description = These folders will be replaced by an empty virtual filesystem (tmpfs), and their original content will not be available to sandboxed game + +path = Path + +shared-directories = Shared directories +shared-directories-description = These directories will be symlinked to directories in your host system + +original-path = Original path +new-path = New path + +read-only = Read-only +read-only-description = Forbid game to write any data to this directory diff --git a/assets/locales/fr/general.ftl b/assets/locales/fr/general.ftl index 3bed0d9..5f041aa 100644 --- a/assets/locales/fr/general.ftl +++ b/assets/locales/fr/general.ftl @@ -69,6 +69,7 @@ registry-editor = Registry editor explorer = Explorer task-manager = Task manager configuration = Configuration +debugger = Debugger dxvk-version = Version de DXVK dxvk-selection-disabled = La sélection de versions DXVK est désactivé par vos préférences de groupe wine diff --git a/assets/locales/fr/sandbox.ftl b/assets/locales/fr/sandbox.ftl new file mode 100644 index 0000000..4d09ae6 --- /dev/null +++ b/assets/locales/fr/sandbox.ftl @@ -0,0 +1,24 @@ +sandbox = Sandbox +sandbox-description = Run the game in isolated environment, preventing it from accessing your personal data + +enable-sandboxing = Enable sandboxing +enable-sandboxing-description = Run the game in read-only copy of your root filesystem + +hide-home-directory = Hide home directory +hide-home-directory-description = Isolate your /home, /var/home/$USER, and $HOME folders from the game + +hostname = Hostname + +private-directories = Private directories +private-directories-description = These folders will be replaced by an empty virtual filesystem (tmpfs), and their original content will not be available to sandboxed game + +path = Path + +shared-directories = Shared directories +shared-directories-description = These directories will be symlinked to directories in your host system + +original-path = Original path +new-path = New path + +read-only = Read-only +read-only-description = Forbid game to write any data to this directory diff --git a/assets/locales/ru/general.ftl b/assets/locales/ru/general.ftl index 75b33f8..dfa744a 100644 --- a/assets/locales/ru/general.ftl +++ b/assets/locales/ru/general.ftl @@ -69,6 +69,7 @@ registry-editor = Редактор реестра explorer = Проводник task-manager = Диспетчер задач configuration = Настройки +debugger = Отладчик dxvk-version = Версия DXVK dxvk-selection-disabled = Выбор версии DXVK отключен настройками выбранного вами Wine diff --git a/assets/locales/ru/sandbox.ftl b/assets/locales/ru/sandbox.ftl new file mode 100644 index 0000000..a0ab2be --- /dev/null +++ b/assets/locales/ru/sandbox.ftl @@ -0,0 +1,24 @@ +sandbox = Песочница +sandbox-description = Запускать игру в изолированном окружении предотвращая доступ к вашим персональным данным + +enable-sandboxing = Использовать песочницу +enable-sandboxing-description = Запускать игру в копии корневой файловой системы компьютера без прав на изменение файлов + +hide-home-directory = Скрыть домашнюю директорию +hide-home-directory-description = Изолировать ваши директории /home, /var/home/$USER, и $HOME от игры + +hostname = Имя хоста + +private-directories = Приватные директории +private-directories-description = Эти папки будут заменены пустой виртуальной файловой системой (tmpfs) и их изначальное содержимое не будет доступно игре + +path = Путь + +shared-directories = Общие директории +shared-directories-description = Эти директории будут доступны для игры + +original-path = Изначальный путь +new-path = Путь в песочнице + +read-only = Только для чтения +read-only-description = Запретить игре изменять содержимое этой директории diff --git a/assets/locales/tr/general.ftl b/assets/locales/tr/general.ftl index 02b3dd1..3703943 100644 --- a/assets/locales/tr/general.ftl +++ b/assets/locales/tr/general.ftl @@ -69,6 +69,7 @@ registry-editor = Registry editor explorer = Explorer task-manager = Task manager configuration = Configuration +debugger = Debugger dxvk-version = DXVK versiyonu dxvk-selection-disabled = DXVK özelliği Wine grup tercihleriniz yüzünden devre dışı diff --git a/assets/locales/tr/sandbox.ftl b/assets/locales/tr/sandbox.ftl new file mode 100644 index 0000000..4d09ae6 --- /dev/null +++ b/assets/locales/tr/sandbox.ftl @@ -0,0 +1,24 @@ +sandbox = Sandbox +sandbox-description = Run the game in isolated environment, preventing it from accessing your personal data + +enable-sandboxing = Enable sandboxing +enable-sandboxing-description = Run the game in read-only copy of your root filesystem + +hide-home-directory = Hide home directory +hide-home-directory-description = Isolate your /home, /var/home/$USER, and $HOME folders from the game + +hostname = Hostname + +private-directories = Private directories +private-directories-description = These folders will be replaced by an empty virtual filesystem (tmpfs), and their original content will not be available to sandboxed game + +path = Path + +shared-directories = Shared directories +shared-directories-description = These directories will be symlinked to directories in your host system + +original-path = Original path +new-path = New path + +read-only = Read-only +read-only-description = Forbid game to write any data to this directory diff --git a/assets/locales/zh-cn/errors.ftl b/assets/locales/zh-cn/errors.ftl index 540f495..448f22b 100644 --- a/assets/locales/zh-cn/errors.ftl +++ b/assets/locales/zh-cn/errors.ftl @@ -6,7 +6,7 @@ debug-file-opening-error = 打开调试文件失败 wish-url-search-failed = 找不到祈愿 URL wish-url-opening-error = 无法转到祈愿 URL -wine-run-error = Failed to run {$executable} executable using wine +wine-run-error = 使用 wine 运行 {$executable} 失败 game-launching-failed = 启动游戏失败 failed-get-selected-wine = 选择 Wine 版本失败 diff --git a/assets/locales/zh-cn/first_run.ftl b/assets/locales/zh-cn/first_run.ftl index 8ec7824..234ffe4 100644 --- a/assets/locales/zh-cn/first_run.ftl +++ b/assets/locales/zh-cn/first_run.ftl @@ -36,14 +36,14 @@ show-all-folders-subtitle = 显示额外的路径选项。按我说的做... runners-folder = 运行程序文件夹 dxvks-folder = DXVK 文件夹 wine-prefix-folder = Wine prefix 文件夹 -global-game-installation-folder = Global game version installation folder -chinese-game-installation-folder = Chinese game version installation folder +global-game-installation-folder = 国际服安装目录 +chinese-game-installation-folder = 国服安装目录 fps-unlocker-folder = FPS Unlocker 文件夹 components-index = 成分指数 patch-folder = 补丁文件夹 temp-folder = 临时文件夹 -migrate = Migrate +migrate = 迁移 select-voice-packages = 选择语音包 diff --git a/assets/locales/zh-cn/general.ftl b/assets/locales/zh-cn/general.ftl index 03665f7..c2202ee 100644 --- a/assets/locales/zh-cn/general.ftl +++ b/assets/locales/zh-cn/general.ftl @@ -7,22 +7,22 @@ update-background-description = 下载官方启动器背景图片。你可以关 launcher-language = 启动器语言 launcher-language-description = 重启后生效 -game-edition = Game edition -global = Global -china = China +game-edition = 游戏版本 +global = 国际服 +china = 国服 -game-environment = Game environment -game-environment-description = Get specific features like additional payment methods +game-environment = 游戏环境 +game-environment-description = 获取特定功能,如其他付款方式 game-voiceovers = 游戏语音 -game-voiceovers-description = List of downloaded game voiceovers. You can select them in the game settings +game-voiceovers-description = 已下载的游戏语音,可以在游戏设置中更换 english = 英语 japanese = 日语 korean = 韩语 chinese = 汉语 -migrate-installation = Migrate installation -migrate-installation-description = Open special window where you can change your game installation folder +migrate-installation = 迁移安装 +migrate-installation-description = 打开此窗口以改变游戏安装文件夹 repair-game = 修复游戏 status = 状态 @@ -63,12 +63,13 @@ recommended-only = 仅显示推荐版本 wine-version = Wine 版本 wine-recommended-description = 仅显示推荐的 Wine 版本 -wine-tools = Wine tools -command-line = Command line -registry-editor = Registry editor -explorer = Explorer -task-manager = Task manager -configuration = Configuration +wine-tools = Wine 工具 +command-line = 命令行 +registry-editor = 注册表编辑器 +explorer = 资源管理器 +task-manager = 任务管理器 +configuration = wine 设置 +debugger = 调试器 dxvk-version = DXVK 版本 dxvk-selection-disabled = 您的葡萄酒组首选项禁用 DXVK 选择 diff --git a/assets/locales/zh-cn/main.ftl b/assets/locales/zh-cn/main.ftl index 38ea420..93e83f1 100644 --- a/assets/locales/zh-cn/main.ftl +++ b/assets/locales/zh-cn/main.ftl @@ -40,17 +40,17 @@ downloading = 正在下载 unpacking = 正在解压缩 verifying-files = 正在检验文件 repairing-files = 正在修复文件 -migrating-folders = Migrating folders -applying-hdiff = Applying hdiff patches -removing-outdated = Removing outdated files +migrating-folders = 正在迁移目录 +applying-hdiff = 正在应用 hdiff 补丁 +removing-outdated = 删除过期的文件 components-index-updated = 组件索引已更新 launch = 启动 -migrate-folders = Migrate folders -migrate-folders-tooltip = Update game folders structure +migrate-folders = 迁移目录 +migrate-folders-tooltip = 更新游戏目录结构 apply-patch = 安装补丁 download-wine = 下载 Wine create-prefix = 创建 Wine prefix diff --git a/assets/locales/zh-cn/sandbox.ftl b/assets/locales/zh-cn/sandbox.ftl new file mode 100644 index 0000000..57f20e5 --- /dev/null +++ b/assets/locales/zh-cn/sandbox.ftl @@ -0,0 +1,24 @@ +sandbox = 沙盒 +sandbox-description = 在隔离环境中运行游戏,阻止其对个人数据的访问 + +enable-sandboxing = 启用沙盒 +enable-sandboxing-description = 在根文件系统的只读副本中运行游戏 + +hide-home-directory = 隐藏家目录 +hide-home-directory-description = 将 /home、 /var/home/$USER 和 $HOME 目录与游戏隔离 + +hostname = 主机名 + +private-directories = 隐私目录 +private-directories-description = 这些目录将会被空的虚拟文件系统(tmpfs)替代,其中的原始内容不可被沙盒中的游戏访问 + +path = 路径 + +shared-directories = 共享目录 +shared-directories-description = 这些目录将会被软链接到主机系统上的目录 + +original-path = 原路径 +new-path = 新路径 + +read-only = 只读 +read-only-description = 禁止游戏向此目录写入任何数据 diff --git a/src/main.rs b/src/main.rs index f17433f..e945496 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,10 @@ use relm4::prelude::*; -use anime_launcher_sdk::config; -use anime_launcher_sdk::states::LauncherState; -use anime_launcher_sdk::consts::launcher_dir; +use anime_launcher_sdk::config::ConfigExt; +use anime_launcher_sdk::genshin::config::{Config, Schema}; + +use anime_launcher_sdk::genshin::states::LauncherState; +use anime_launcher_sdk::genshin::consts::launcher_dir; use anime_launcher_sdk::anime_game_core::prelude::*; use anime_launcher_sdk::anime_game_core::genshin::prelude::*; @@ -34,9 +36,9 @@ pub fn is_ready() -> bool { } lazy_static::lazy_static! { - /// Config loaded on the app's start. Use `config::get()` to get up to date config instead. + /// Config loaded on the app's start. Use `Config::get()` to get up to date config instead. /// This one is used to prepare some launcher UI components on start - pub static ref CONFIG: config::Config = config::get().expect("Failed to load config"); + pub static ref CONFIG: Schema = Config::get().expect("Failed to load config"); pub static ref GAME: Game = Game::new(CONFIG.game.path.for_edition(CONFIG.launcher.edition)); @@ -69,11 +71,11 @@ fn main() { std::fs::write(FIRST_RUN_FILE.as_path(), "").expect("Failed to create .first-run file"); // Set initial launcher language based on system language - let mut config = config::get().expect("Failed to get config"); + let mut config = Config::get().expect("Failed to get config"); config.launcher.language = i18n::format_lang(&i18n::get_default_lang()); - config::update_raw(config).expect("Failed to update config"); + Config::update_raw(config).expect("Failed to update config"); } // Force debug output @@ -161,7 +163,7 @@ fn main() { ", BACKGROUND_FILE.to_string_lossy())); // Set game edition - GameEdition::from(CONFIG.launcher.edition).select(); + CONFIG.launcher.edition.select(); // Set UI language let lang = CONFIG.launcher.language.parse().expect("Wrong language format used in config"); @@ -187,7 +189,7 @@ fn main() { match state { LauncherState::Launch => { - anime_launcher_sdk::game::run().expect("Failed to run the game"); + anime_launcher_sdk::genshin::game::run().expect("Failed to run the game"); return; } @@ -196,7 +198,7 @@ fn main() { LauncherState::UnityPlayerPatchAvailable(UnityPlayerPatch { status: PatchStatus::NotAvailable, .. }) | LauncherState::XluaPatchAvailable(XluaPatch { status: PatchStatus::NotAvailable, .. }) => { if just_run_game { - anime_launcher_sdk::game::run().expect("Failed to run the game"); + anime_launcher_sdk::genshin::game::run().expect("Failed to run the game"); return; } diff --git a/src/ui/about.rs b/src/ui/about.rs index a85f0b9..33bae89 100644 --- a/src/ui/about.rs +++ b/src/ui/about.rs @@ -46,6 +46,10 @@ impl SimpleComponent for AboutDialog { "Nikita Podvirnyy https://github.com/krypt0nn" ], + add_credit_section: (Some("Patch developer"), &[ + "@Krock https://notabug.org/Krock/dawn" + ]), + add_credit_section: (Some("An Anime Team"), &[ "@Marie https://github.com/Mar0xy", "@lane https://github.com/laurinneff", @@ -78,17 +82,18 @@ impl SimpleComponent for AboutDialog { set_release_notes_version: &APP_VERSION, set_release_notes: &[ - "

Fixed

", + "

Added

", "", "

Changed

", "", ].join("\n"), diff --git a/src/ui/components/version.rs b/src/ui/components/version.rs index 5f10ca3..9870ae9 100644 --- a/src/ui/components/version.rs +++ b/src/ui/components/version.rs @@ -6,13 +6,15 @@ use adw::prelude::*; use gtk::glib::clone; -use super::progress_bar::ProgressBarMsg; - -use anime_launcher_sdk::config; use anime_launcher_sdk::anime_game_core::prelude::*; +use anime_launcher_sdk::config::ConfigExt; +use anime_launcher_sdk::genshin::config::Config; + use std::path::PathBuf; +use super::progress_bar::ProgressBarMsg; + #[derive(Debug, Clone, PartialEq, Eq)] pub enum VersionState { Downloaded, @@ -142,7 +144,7 @@ impl SimpleAsyncComponent for ComponentVersion { } VersionState::NotDownloaded => { - if let Ok(config) = config::get() { + if let Ok(config) = Config::get() { // todo let mut installer = Installer::new(&self.download_uri) .expect("Failed to create installer instance for this version"); diff --git a/src/ui/first_run/default_paths.rs b/src/ui/first_run/default_paths.rs index ca78482..dc5731f 100644 --- a/src/ui/first_run/default_paths.rs +++ b/src/ui/first_run/default_paths.rs @@ -3,8 +3,6 @@ use relm4::component::*; use adw::prelude::*; -use anime_launcher_sdk::config; - use std::path::PathBuf; use crate::*; @@ -360,7 +358,7 @@ impl SimpleAsyncComponent for DefaultPathsApp { #[allow(unused_must_use)] DefaultPathsAppMsg::Continue => { - let old_config = config::get().unwrap_or_else(|_| CONFIG.clone()); + let old_config = Config::get().unwrap_or_else(|_| CONFIG.clone()); match self.update_config() { Ok(_) => { @@ -431,7 +429,7 @@ impl SimpleAsyncComponent for DefaultPathsApp { impl DefaultPathsApp { pub fn update_config(&self) -> anyhow::Result<()> { - let mut config = config::get()?; + let mut config = Config::get()?; config.game.wine.builds = self.runners.clone(); config.game.dxvk.builds = self.dxvks.clone(); @@ -444,6 +442,6 @@ impl DefaultPathsApp { config.game.enhancements.fps_unlocker.path = self.fps_unlocker.clone(); - config::update_raw(config) + Config::update_raw(config) } } diff --git a/src/ui/first_run/download_components.rs b/src/ui/first_run/download_components.rs index 987e6e6..7db9a1a 100644 --- a/src/ui/first_run/download_components.rs +++ b/src/ui/first_run/download_components.rs @@ -4,10 +4,13 @@ use relm4::component::*; use adw::prelude::*; use anime_launcher_sdk::anime_game_core::prelude::*; +use anime_launcher_sdk::wincompatlib::prelude::*; + use anime_launcher_sdk::components::*; use anime_launcher_sdk::components::wine::WincompatlibWine; -use anime_launcher_sdk::config; -use anime_launcher_sdk::wincompatlib::prelude::*; + +use anime_launcher_sdk::config::ConfigExt; +use anime_launcher_sdk::genshin::config::Config; use std::path::PathBuf; @@ -312,7 +315,7 @@ impl SimpleAsyncComponent for DownloadComponentsApp { async fn update(&mut self, msg: Self::Input, sender: AsyncComponentSender) { match msg { DownloadComponentsAppMsg::UpdateVersionsLists => { - let config = config::get().unwrap_or_else(|_| CONFIG.clone()); + let config = Config::get().unwrap_or_else(|_| CONFIG.clone()); // 4 latest versions of 4 first available wine group self.wine_versions = wine::get_groups(&config.components.path).unwrap() @@ -331,7 +334,7 @@ impl SimpleAsyncComponent for DownloadComponentsApp { #[allow(unused_must_use)] DownloadComponentsAppMsg::DownloadWine => { - let config = config::get().unwrap_or_else(|_| CONFIG.clone()); + let config = Config::get().unwrap_or_else(|_| CONFIG.clone()); self.selected_wine = Some(self.wine_versions[self.wine_combo.selected() as usize].clone()); self.selected_dxvk = Some(self.dxvk_versions[self.dxvk_combo.selected() as usize].clone()); @@ -350,11 +353,11 @@ impl SimpleAsyncComponent for DownloadComponentsApp { if wine.is_downloaded_in(&config.game.wine.builds) { tracing::info!("Wine already installed: {}", wine.name); - let mut config = config::get().unwrap_or_else(|_| CONFIG.clone()); + let mut config = Config::get().unwrap_or_else(|_| CONFIG.clone()); config.game.wine.selected = Some(wine.name); - if let Err(err) = config::update_raw(config) { + if let Err(err) = Config::update_raw(config) { tracing::error!("Failed to update config: {err}"); sender.output(Self::Output::Toast { @@ -402,11 +405,11 @@ impl SimpleAsyncComponent for DownloadComponentsApp { // Create prefix InstallerUpdate::UnpackingFinished => { - let mut config = config::get().unwrap_or_else(|_| CONFIG.clone()); + let mut config = Config::get().unwrap_or_else(|_| CONFIG.clone()); config.game.wine.selected = Some(wine.name.clone()); - if let Err(err) = config::update_raw(config) { + if let Err(err) = Config::update_raw(config) { tracing::error!("Failed to update config: {err}"); sender.output(Self::Output::Toast { @@ -443,7 +446,7 @@ impl SimpleAsyncComponent for DownloadComponentsApp { self.downloading_wine = Some(true); self.creating_prefix = Some(false); - let config = config::get().unwrap_or_else(|_| CONFIG.clone()); + let config = Config::get().unwrap_or_else(|_| CONFIG.clone()); tracing::info!("Creating wine prefix"); @@ -477,7 +480,7 @@ impl SimpleAsyncComponent for DownloadComponentsApp { self.creating_prefix = Some(true); self.downloading_dxvk = Some(false); - let config = config::get().unwrap_or_else(|_| CONFIG.clone()); + let config = Config::get().unwrap_or_else(|_| CONFIG.clone()); let dxvk = self.selected_dxvk.clone().unwrap(); let progress_bar_input = self.progress_bar.sender().clone(); @@ -554,7 +557,7 @@ impl SimpleAsyncComponent for DownloadComponentsApp { self.downloading_dxvk = Some(true); self.applying_dxvk = Some(false); - let config = config::get().unwrap_or_else(|_| CONFIG.clone()); + let config = Config::get().unwrap_or_else(|_| CONFIG.clone()); tracing::info!("Applying DXVK"); diff --git a/src/ui/first_run/main.rs b/src/ui/first_run/main.rs index 43b8b9f..2e8abec 100644 --- a/src/ui/first_run/main.rs +++ b/src/ui/first_run/main.rs @@ -219,7 +219,7 @@ impl SimpleComponent for FirstRunApp { // Update components index sender.input(FirstRunAppMsg::SetLoadingStatus(Some(Some(tr("updating-components-index"))))); - let config = config::get().unwrap_or_else(|_| CONFIG.clone()); + let config = Config::get().unwrap_or_else(|_| CONFIG.clone()); let components_sender = self.download_components.sender().clone(); let components = ComponentsLoader::new(config.components.path); diff --git a/src/ui/first_run/select_voiceovers.rs b/src/ui/first_run/select_voiceovers.rs index eb43715..397e90a 100644 --- a/src/ui/first_run/select_voiceovers.rs +++ b/src/ui/first_run/select_voiceovers.rs @@ -3,7 +3,8 @@ use relm4::component::*; use adw::prelude::*; -use anime_launcher_sdk::config; +use anime_launcher_sdk::config::ConfigExt; +use anime_launcher_sdk::genshin::config::Config; use crate::i18n::*; use super::main::*; @@ -153,7 +154,7 @@ impl SimpleAsyncComponent for SelectVoiceoversApp { impl SelectVoiceoversApp { pub fn update_config(&self) -> anyhow::Result<()> { - let mut config = config::get()?; + let mut config = Config::get()?; config.game.voices = Vec::new(); @@ -173,6 +174,6 @@ impl SelectVoiceoversApp { config.game.voices.push(String::from("zh-cn")); } - config::update_raw(config) + Config::update_raw(config) } } diff --git a/src/ui/main/apply_patch.rs b/src/ui/main/apply_patch.rs index 794a3be..0a5a9da 100644 --- a/src/ui/main/apply_patch.rs +++ b/src/ui/main/apply_patch.rs @@ -1,7 +1,5 @@ use relm4::prelude::*; -use anime_launcher_sdk::config; - use crate::*; use crate::i18n::*; use super::{App, AppMsg}; @@ -16,7 +14,7 @@ pub fn apply_patch(sender: ComponentSender< PatchStatus::Available { .. } => { sender.input(AppMsg::DisableButtons(true)); - let config = config::get().unwrap(); + let config = Config::get().unwrap(); std::thread::spawn(move || { let mut apply_patch_if_needed = true; diff --git a/src/ui/main/create_prefix.rs b/src/ui/main/create_prefix.rs index 30ea025..62d6cb1 100644 --- a/src/ui/main/create_prefix.rs +++ b/src/ui/main/create_prefix.rs @@ -1,13 +1,15 @@ use relm4::prelude::*; -use anime_launcher_sdk::config; use anime_launcher_sdk::wincompatlib::prelude::*; +use anime_launcher_sdk::config::ConfigExt; +use anime_launcher_sdk::genshin::config::Config; + use crate::i18n::*; use super::{App, AppMsg}; pub fn create_prefix(sender: ComponentSender) { - let config = config::get().unwrap(); + let config = Config::get().unwrap(); match config.get_selected_wine() { Ok(Some(wine)) => { diff --git a/src/ui/main/download_diff.rs b/src/ui/main/download_diff.rs index c35d4d3..60c0147 100644 --- a/src/ui/main/download_diff.rs +++ b/src/ui/main/download_diff.rs @@ -5,7 +5,6 @@ use relm4::{ use gtk::glib::clone; -use anime_launcher_sdk::config; use anime_launcher_sdk::anime_game_core::installer::diff::VersionDiff; use crate::*; @@ -17,7 +16,7 @@ pub fn download_diff(sender: ComponentSender, progress_bar_input: Sender move |state| { diff --git a/src/ui/main/download_wine.rs b/src/ui/main/download_wine.rs index d33ea3c..c70308f 100644 --- a/src/ui/main/download_wine.rs +++ b/src/ui/main/download_wine.rs @@ -5,7 +5,6 @@ use relm4::{ use gtk::glib::clone; -use anime_launcher_sdk::config; use anime_launcher_sdk::components::wine; use crate::*; @@ -14,7 +13,7 @@ use crate::ui::components::*; use super::{App, AppMsg}; pub fn download_wine(sender: ComponentSender, progress_bar_input: Sender) { - let mut config = config::get().unwrap(); + let mut config = Config::get().unwrap(); match wine::get_downloaded(&CONFIG.components.path, &config.game.wine.builds) { Ok(downloaded) => { @@ -22,7 +21,7 @@ pub fn download_wine(sender: ComponentSender, progress_bar_input: Sender, progress_bar_input: Sender) { sender.input(AppMsg::HideWindow); std::thread::spawn(move || { - if let Err(err) = anime_launcher_sdk::game::run() { + if let Err(err) = anime_launcher_sdk::genshin::game::run() { tracing::error!("Failed to launch game: {err}"); sender.input(AppMsg::Toast { diff --git a/src/ui/main/mod.rs b/src/ui/main/mod.rs index 0ed377d..c4039ce 100644 --- a/src/ui/main/mod.rs +++ b/src/ui/main/mod.rs @@ -18,10 +18,15 @@ mod download_diff; mod migrate_folder; mod launch; -use anime_launcher_sdk::config::launcher::LauncherStyle; -use anime_launcher_sdk::states::LauncherState; use anime_launcher_sdk::components::loader::ComponentsLoader; -use anime_launcher_sdk::anime_game_core::genshin::consts::GameEdition; + +use anime_launcher_sdk::config::ConfigExt; +use anime_launcher_sdk::genshin::config::Config; + +use anime_launcher_sdk::genshin::config::schema::launcher::LauncherStyle; + +use anime_launcher_sdk::genshin::states::*; +use anime_launcher_sdk::genshin::consts::*; use crate::*; use crate::i18n::*; @@ -328,7 +333,7 @@ impl SimpleComponent for App { #[watch] set_sensitive: match model.state.as_ref() { Some(LauncherState::PredownloadAvailable { game, voices }) => { - let config = config::get().unwrap(); + let config = Config::get().unwrap(); let temp = config.launcher.temp.unwrap_or_else(std::env::temp_dir); let downloaded = temp.join(game.file_name().unwrap()).exists() && @@ -343,7 +348,7 @@ impl SimpleComponent for App { #[watch] set_css_classes: match model.state.as_ref() { Some(LauncherState::PredownloadAvailable { game, voices }) => { - let config = config::get().unwrap(); + let config = Config::get().unwrap(); let temp = config.launcher.temp.unwrap_or_else(std::env::temp_dir); let downloaded = temp.join(game.file_name().unwrap()).exists() && @@ -481,7 +486,7 @@ impl SimpleComponent for App { }, connect_close_request[sender] => move |_| { - if let Err(err) = config::flush() { + if let Err(err) = Config::flush() { sender.input(AppMsg::Toast { title: tr("config-update-error"), description: Some(err.to_string()) @@ -558,7 +563,7 @@ impl SimpleComponent for App { }))); group.add_action::(&RelmAction::new_stateless(clone!(@strong sender => move |_| { - let path = match config::get() { + let path = match Config::get() { Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(), Err(_) => CONFIG.game.path.for_edition(CONFIG.launcher.edition).to_path_buf(), }; @@ -574,7 +579,7 @@ impl SimpleComponent for App { }))); group.add_action::(&RelmAction::new_stateless(clone!(@strong sender => move |_| { - if let Ok(file) = anime_launcher_sdk::consts::config_file() { + if let Ok(file) = config_file() { if let Err(err) = open::that(file) { sender.input(AppMsg::Toast { title: tr("config-file-opening-error"), @@ -599,10 +604,10 @@ impl SimpleComponent for App { group.add_action::(&RelmAction::new_stateless(clone!(@strong sender => move |_| { std::thread::spawn(clone!(@strong sender => move || { - let config = config::get().unwrap_or_else(|_| CONFIG.clone()); + let config = Config::get().unwrap_or_else(|_| CONFIG.clone()); let web_cache = config.game.path.for_edition(config.launcher.edition) - .join(GameEdition::from(config.launcher.edition).data_folder()) + .join(config.launcher.edition.data_folder()) .join("webCaches/Cache/Cache_Data/data_2"); if !web_cache.exists() { @@ -860,8 +865,6 @@ impl SimpleComponent for App { } let updater = clone!(@strong sender => move |state| { - use anime_launcher_sdk::states::StateUpdating; - if show_status_page { match state { StateUpdating::Game => { @@ -963,7 +966,7 @@ impl SimpleComponent for App { #[allow(unused_must_use)] AppMsg::PredownloadUpdate => { if let Some(LauncherState::PredownloadAvailable { game, mut voices }) = self.state.clone() { - let tmp = config::get().unwrap().launcher.temp.unwrap_or_else(std::env::temp_dir); + let tmp = Config::get().unwrap().launcher.temp.unwrap_or_else(std::env::temp_dir); self.downloading = true; diff --git a/src/ui/main/repair_game.rs b/src/ui/main/repair_game.rs index ecbcdda..4b679ab 100644 --- a/src/ui/main/repair_game.rs +++ b/src/ui/main/repair_game.rs @@ -7,8 +7,6 @@ use gtk::glib::clone; use std::path::Path; -use anime_launcher_sdk::config; - use crate::*; use crate::i18n::*; use crate::ui::components::*; @@ -16,7 +14,7 @@ use super::{App, AppMsg}; #[allow(unused_must_use)] pub fn repair_game(sender: ComponentSender, progress_bar_input: Sender) { - let config = config::get().unwrap(); + let config = Config::get().unwrap(); progress_bar_input.send(ProgressBarMsg::UpdateCaption(Some(tr("verifying-files")))); sender.input(AppMsg::SetDownloading(true)); diff --git a/src/ui/preferences/enhancements.rs b/src/ui/preferences/enhancements.rs index 4c00617..5371312 100644 --- a/src/ui/preferences/enhancements.rs +++ b/src/ui/preferences/enhancements.rs @@ -3,8 +3,11 @@ use relm4::component::*; use adw::prelude::*; -use anime_launcher_sdk::config; -use anime_launcher_sdk::config::prelude::*; +use anime_launcher_sdk::config::ConfigExt; +use anime_launcher_sdk::genshin::config::Config; + +use anime_launcher_sdk::config::schema_blanks::prelude::*; + use anime_launcher_sdk::is_available; use crate::i18n::tr; @@ -52,10 +55,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_selected_notify => |row| unsafe { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.wine.sync = WineSync::from_ordinal_unsafe(row.selected() as i8); - config::update(config); + Config::update(config); } } } @@ -84,10 +87,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_selected_notify => |row| unsafe { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.wine.language = WineLang::from_ordinal_unsafe(row.selected() as i8); - config::update(config); + Config::update(config); } } } @@ -103,10 +106,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_state_notify => |switch| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.wine.borderless = switch.state(); - config::update(config); + Config::update(config); } } } @@ -130,13 +133,13 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_selected_notify => |row| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { let (width, height) = Resolution::try_from(row.selected()).unwrap().get_pair(); config.game.wine.virtual_desktop.width = width; config.game.wine.virtual_desktop.height = height; - config::update(config); + Config::update(config); } } }, @@ -148,10 +151,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_state_notify => |switch| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.wine.virtual_desktop.enabled = switch.state(); - config::update(config); + Config::update(config); } } } @@ -176,10 +179,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_selected_notify => |row| unsafe { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.hud = HUD::from_ordinal_unsafe(row.selected() as i8); - config::update(config); + Config::update(config); } } } @@ -209,10 +212,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_selected_notify => |row| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.fsr.strength = 5 - row.selected() as u64; - config::update(config); + Config::update(config); } } }, @@ -224,10 +227,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_state_notify => |switch| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.fsr.enabled = switch.state(); - config::update(config); + Config::update(config); } } } @@ -247,10 +250,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_state_notify => |switch| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.gamemode = switch.state(); - config::update(config); + Config::update(config); } } } @@ -279,10 +282,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_state_notify => |switch| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.gamescope.enabled = switch.state(); - config::update(config); + Config::update(config); } } } @@ -303,10 +306,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_state_notify => |switch| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.launcher.discord_rpc.enabled = switch.state(); - config::update(config); + Config::update(config); } } } @@ -319,10 +322,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_changed: |row| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.launcher.discord_rpc.title = row.text().to_string(); - config::update(config); + Config::update(config); } } } @@ -334,10 +337,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_changed: |row| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.launcher.discord_rpc.subtitle = row.text().to_string(); - config::update(config); + Config::update(config); } } } @@ -377,10 +380,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_selected_notify => |row| { if is_ready() && row.selected() < Fps::list().len() as u32 - 1 { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.fps_unlocker.config.fps = Fps::list()[row.selected() as usize].to_num(); - config::update(config); + Config::update(config); } } }, @@ -392,10 +395,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_state_notify => |switch| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.fps_unlocker.enabled = switch.state(); - config::update(config); + Config::update(config); } } } @@ -413,10 +416,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_state_notify => |switch| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.fps_unlocker.config.power_saving = switch.state(); - config::update(config); + Config::update(config); } } } @@ -435,10 +438,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_changed => |row| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.fps_unlocker.config.monitor = row.value() as u64; - config::update(config); + Config::update(config); } } } @@ -459,10 +462,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_selected_notify => |row| unsafe { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.fps_unlocker.config.window_mode = WindowMode::from_ordinal_unsafe(row.selected() as i8); - config::update(config); + Config::update(config); } } } @@ -486,10 +489,10 @@ impl SimpleAsyncComponent for EnhancementsApp { connect_selected_notify => |row| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.fps_unlocker.config.priority = row.selected() as u64; - config::update(config); + Config::update(config); } } } diff --git a/src/ui/preferences/environment.rs b/src/ui/preferences/environment.rs index e94d7bd..a41ecbd 100644 --- a/src/ui/preferences/environment.rs +++ b/src/ui/preferences/environment.rs @@ -4,8 +4,6 @@ use relm4::factory::*; use adw::prelude::*; -use anime_launcher_sdk::config; - use crate::i18n::tr; use crate::*; @@ -64,8 +62,8 @@ impl AsyncFactoryComponent for Variable { pub struct EnvironmentApp { variables: AsyncFactoryVecDeque, - name: gtk::Entry, - value: gtk::Entry + name_entry: adw::EntryRow, + value_entry: adw::EntryRow } #[derive(Debug, Clone)] @@ -94,16 +92,16 @@ impl SimpleAsyncComponent for EnvironmentApp { set_text: CONFIG.game.command.as_ref().unwrap_or(&String::new()).trim(), connect_changed => |entry| { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { let command = entry.text().trim().to_string(); - + config.game.command = if command.is_empty() { None } else { Some(command) }; - - config::update(config); + + Config::update(config); } } } @@ -112,24 +110,19 @@ impl SimpleAsyncComponent for EnvironmentApp { add = &adw::PreferencesGroup { set_title: &tr("new-variable"), - gtk::Box { - set_orientation: gtk::Orientation::Horizontal, - set_spacing: 8, + #[local_ref] + name_entry -> adw::EntryRow { + set_title: &tr("name") + }, - #[local_ref] - name -> gtk::Entry { - set_placeholder_text: Some(&tr("name")) - }, - - #[local_ref] - value -> gtk::Entry { - set_placeholder_text: Some(&tr("value")), - set_hexpand: true - } + #[local_ref] + value_entry -> adw::EntryRow { + set_title: &tr("value") }, gtk::Button { set_label: &tr("add"), + add_css_class: "pill", set_margin_top: 8, set_halign: gtk::Align::Start, @@ -153,8 +146,8 @@ impl SimpleAsyncComponent for EnvironmentApp { let mut model = Self { variables: AsyncFactoryVecDeque::new(adw::PreferencesGroup::new(), sender.input_sender()), - name: gtk::Entry::new(), - value: gtk::Entry::new() + name_entry: adw::EntryRow::new(), + value_entry: adw::EntryRow::new() }; for (name, value) in &CONFIG.game.environment { @@ -163,8 +156,8 @@ impl SimpleAsyncComponent for EnvironmentApp { let variables = model.variables.widget(); - let name = &model.name; - let value = &model.value; + let name_entry = &model.name_entry; + let value_entry = &model.value_entry; let widgets = view_output!(); @@ -174,24 +167,26 @@ impl SimpleAsyncComponent for EnvironmentApp { async fn update(&mut self, msg: Self::Input, _sender: AsyncComponentSender) { match msg { EnvironmentMsg::Add => { - if let Ok(mut config) = config::get() { - let name = self.name.text().trim().to_string(); - let value = self.value.text().trim().to_string(); + if let Ok(mut config) = Config::get() { + let name = self.name_entry.text().trim().to_string(); + let value = self.value_entry.text().trim().to_string(); - config.game.environment.insert(name.clone(), value.clone()); + if !name.is_empty() && !value.is_empty() { + config.game.environment.insert(name.clone(), value.clone()); - config::update(config); + Config::update(config); - self.variables.guard().push_back((name, value)); + self.variables.guard().push_back((name, value)); + } } } EnvironmentMsg::Remove(index) => { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { if let Some(var) = self.variables.guard().get(index.current_index()) { config.game.environment.remove(&var.key); - config::update(config); + Config::update(config); } self.variables.guard().remove(index.current_index()); diff --git a/src/ui/preferences/gamescope.rs b/src/ui/preferences/gamescope.rs index 9770c27..b91480f 100644 --- a/src/ui/preferences/gamescope.rs +++ b/src/ui/preferences/gamescope.rs @@ -3,8 +3,10 @@ use relm4::component::*; use adw::prelude::*; -use anime_launcher_sdk::config; -use anime_launcher_sdk::config::prelude::*; +use anime_launcher_sdk::config::ConfigExt; +use anime_launcher_sdk::genshin::config::Config; + +use anime_launcher_sdk::config::schema_blanks::prelude::*; use crate::i18n::tr; use crate::*; @@ -43,10 +45,10 @@ impl SimpleAsyncComponent for GamescopeApp { connect_changed => |row| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.gamescope.game.width = row.text().parse().unwrap_or_default(); - config::update(config); + Config::update(config); } } } @@ -64,10 +66,10 @@ impl SimpleAsyncComponent for GamescopeApp { connect_changed => |row| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.gamescope.game.height = row.text().parse().unwrap_or_default(); - config::update(config); + Config::update(config); } } } @@ -89,10 +91,10 @@ impl SimpleAsyncComponent for GamescopeApp { connect_changed => |row| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.gamescope.gamescope.width = row.text().parse().unwrap_or_default(); - config::update(config); + Config::update(config); } } } @@ -110,10 +112,10 @@ impl SimpleAsyncComponent for GamescopeApp { connect_changed => |row| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.gamescope.gamescope.height = row.text().parse().unwrap_or_default(); - config::update(config); + Config::update(config); } } } @@ -133,10 +135,10 @@ impl SimpleAsyncComponent for GamescopeApp { connect_state_notify => |switch| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.gamescope.integer_scaling = switch.state(); - config::update(config); + Config::update(config); } } } @@ -153,10 +155,10 @@ impl SimpleAsyncComponent for GamescopeApp { connect_state_notify => |switch| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.gamescope.fsr = switch.state(); - config::update(config); + Config::update(config); } } } @@ -173,10 +175,10 @@ impl SimpleAsyncComponent for GamescopeApp { connect_state_notify => |switch| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.gamescope.nis = switch.state(); - config::update(config); + Config::update(config); } } } @@ -201,10 +203,10 @@ impl SimpleAsyncComponent for GamescopeApp { connect_changed => |row| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.gamescope.framerate.focused = row.text().parse().unwrap_or_default(); - config::update(config); + Config::update(config); } } } @@ -222,10 +224,10 @@ impl SimpleAsyncComponent for GamescopeApp { connect_changed => |row| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.gamescope.framerate.unfocused = row.text().parse().unwrap_or_default(); - config::update(config); + Config::update(config); } } } @@ -244,10 +246,10 @@ impl SimpleAsyncComponent for GamescopeApp { connect_selected_notify => |row| unsafe { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.game.enhancements.gamescope.window_type = WindowType::from_ordinal_unsafe(row.selected() as i8); - config::update(config); + Config::update(config); } } } diff --git a/src/ui/preferences/general.rs b/src/ui/preferences/general.rs index 7d9bc8a..9376a68 100644 --- a/src/ui/preferences/general.rs +++ b/src/ui/preferences/general.rs @@ -10,14 +10,18 @@ use gtk::prelude::*; use adw::prelude::*; use anime_launcher_sdk::anime_game_core::prelude::*; +use anime_launcher_sdk::anime_game_core::genshin::consts::GameEdition; + use anime_launcher_sdk::wincompatlib::prelude::*; -use anime_launcher_sdk::config; -use anime_launcher_sdk::config::launcher::LauncherStyle; + use anime_launcher_sdk::components::*; use anime_launcher_sdk::components::wine::WincompatlibWine; -use anime_launcher_sdk::env_emulation::Environment; -use anime_launcher_sdk::config::launcher::GameEdition; -use anime_launcher_sdk::anime_game_core::genshin::consts::GameEdition as CoreGameEdition; + +use anime_launcher_sdk::config::ConfigExt; +use anime_launcher_sdk::genshin::config::Config; + +use anime_launcher_sdk::genshin::config::schema::launcher::LauncherStyle; +use anime_launcher_sdk::genshin::env_emulation::Environment; use super::main::PreferencesAppMsg; use crate::ui::migrate_installation::MigrateInstallationApp; @@ -291,12 +295,12 @@ impl SimpleAsyncComponent for GeneralApp { connect_selected_notify => |row| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.launcher.language = crate::i18n::format_lang(SUPPORTED_LANGUAGES .get(row.selected() as usize) .unwrap_or(&SUPPORTED_LANGUAGES[0])); - config::update(config); + Config::update(config); } } } @@ -318,7 +322,7 @@ impl SimpleAsyncComponent for GeneralApp { connect_selected_notify[sender] => move |row| { if is_ready() { #[allow(unused_must_use)] - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.launcher.edition = match row.selected() { 0 => GameEdition::Global, 1 => GameEdition::China, @@ -327,9 +331,9 @@ impl SimpleAsyncComponent for GeneralApp { }; // Select new game edition - CoreGameEdition::from(config.launcher.edition).select(); + config.launcher.edition.select(); - config::update(config); + Config::update(config); sender.output(PreferencesAppMsg::UpdateLauncherState); } @@ -355,7 +359,7 @@ impl SimpleAsyncComponent for GeneralApp { connect_selected_notify => |row| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.launcher.environment = match row.selected() { 0 => Environment::PC, 1 => Environment::Android, @@ -363,7 +367,7 @@ impl SimpleAsyncComponent for GeneralApp { _ => unreachable!() }; - config::update(config); + Config::update(config); } } } @@ -478,7 +482,7 @@ impl SimpleAsyncComponent for GeneralApp { PatchStatus::Preparation { .. } | PatchStatus::Testing { .. } => &["warning"], PatchStatus::Available { .. } => unsafe { - let path = match config::get() { + let path = match Config::get() { Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(), Err(_) => CONFIG.game.path.for_edition(CONFIG.launcher.edition).to_path_buf(), }; @@ -505,7 +509,7 @@ impl SimpleAsyncComponent for GeneralApp { PatchStatus::Preparation { .. } => tr("patch-preparation-tooltip"), PatchStatus::Testing { .. } => tr("patch-testing-tooltip"), PatchStatus::Available { .. } => unsafe { - let path = match config::get() { + let path = match Config::get() { Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(), Err(_) => CONFIG.game.path.for_edition(CONFIG.launcher.edition).to_path_buf(), }; @@ -549,7 +553,7 @@ impl SimpleAsyncComponent for GeneralApp { PatchStatus::Preparation { .. } | PatchStatus::Testing { .. } => &["warning"], PatchStatus::Available { .. } => unsafe { - let path = match config::get() { + let path = match Config::get() { Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(), Err(_) => CONFIG.game.path.for_edition(CONFIG.launcher.edition).to_path_buf(), }; @@ -576,7 +580,7 @@ impl SimpleAsyncComponent for GeneralApp { PatchStatus::Preparation { .. } => tr("patch-preparation-tooltip"), PatchStatus::Testing { .. } => tr("patch-testing-tooltip"), PatchStatus::Available { .. } => unsafe { - let path = match config::get() { + let path = match Config::get() { Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(), Err(_) => CONFIG.game.path.for_edition(CONFIG.launcher.edition).to_path_buf(), }; @@ -607,10 +611,10 @@ impl SimpleAsyncComponent for GeneralApp { connect_state_notify[sender] => move |switch| { if is_ready() { #[allow(unused_must_use)] - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.patch.apply_xlua = switch.state(); - config::update(config); + Config::update(config); sender.output(PreferencesAppMsg::UpdateLauncherState); } @@ -630,10 +634,10 @@ impl SimpleAsyncComponent for GeneralApp { connect_state_notify => |switch| { if is_ready() { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.patch.root = switch.state(); - config::update(config); + Config::update(config); } } } @@ -701,11 +705,11 @@ impl SimpleAsyncComponent for GeneralApp { add_row = &adw::ActionRow { set_title: &tr("command-line"), - set_subtitle: "start cmd", + set_subtitle: "wineconsole", set_activatable: true, - connect_activated => GeneralAppMsg::WineOpen(&["start", "cmd"]) + connect_activated => GeneralAppMsg::WineOpen(&["wineconsole"]) }, add_row = &adw::ActionRow { @@ -742,6 +746,15 @@ impl SimpleAsyncComponent for GeneralApp { set_activatable: true, connect_activated => GeneralAppMsg::WineOpen(&["winecfg"]) + }, + + add_row = &adw::ActionRow { + set_title: &tr("debugger"), + set_subtitle: "start winedbg", + + set_activatable: true, + + connect_activated => GeneralAppMsg::WineOpen(&["start", "winedbg"]) } } }, @@ -951,11 +964,11 @@ impl SimpleAsyncComponent for GeneralApp { #[allow(unused_must_use)] GeneralAppMsg::AddVoicePackage(index) => { if let Some(package) = self.voice_packages.get(index.current_index()) { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { if !config.game.voices.iter().any(|voice| VoiceLocale::from_str(voice) == Some(package.locale)) { config.game.voices.push(package.locale.to_code().to_string()); - config::update(config); + Config::update(config); sender.output(PreferencesAppMsg::UpdateLauncherState); } @@ -966,12 +979,12 @@ impl SimpleAsyncComponent for GeneralApp { #[allow(unused_must_use)] GeneralAppMsg::RemoveVoicePackage(index) => { if let Some(package) = self.voice_packages.guard().get_mut(index.current_index()) { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { package.sensitive = false; config.game.voices.retain(|voice| VoiceLocale::from_str(voice) != Some(package.locale)); - config::update(config.clone()); + Config::update(config.clone()); let package = VoicePackage::with_locale(package.locale).unwrap(); let game_path = config.game.path.for_edition(config.launcher.edition).to_path_buf(); @@ -1019,7 +1032,7 @@ impl SimpleAsyncComponent for GeneralApp { } GeneralAppMsg::WineOpen(executable) => { - let config = config::get().unwrap_or_else(|_| CONFIG.clone()); + let config = Config::get().unwrap_or_else(|_| CONFIG.clone()); if let Ok(Some(wine)) = config.get_selected_wine() { let result = wine.to_wine(config.components.path, Some(config.game.wine.builds.join(&wine.name))) @@ -1056,10 +1069,10 @@ impl SimpleAsyncComponent for GeneralApp { } } - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { config.launcher.style = style; - config::update(config); + Config::update(config); } self.style = style; @@ -1135,7 +1148,7 @@ impl SimpleAsyncComponent for GeneralApp { } GeneralAppMsg::SelectWine(index) => { - if let Ok(mut config) = config::get() { + if let Ok(mut config) = Config::get() { if let Some((version, features)) = self.downloaded_wine_versions.get(index) { if config.game.wine.selected.as_ref() != Some(&version.title) { self.selecting_wine_version = true; @@ -1154,7 +1167,7 @@ impl SimpleAsyncComponent for GeneralApp { Ok(_) => { config.game.wine.selected = Some(wine_name); - config::update(config); + Config::update(config); } Err(err) => { @@ -1178,7 +1191,7 @@ impl SimpleAsyncComponent for GeneralApp { } GeneralAppMsg::SelectDxvk(index) => { - if let Ok(config) = config::get() { + if let Ok(config) = Config::get() { if let Some(version) = self.downloaded_dxvk_versions.get(index) { if let Ok(selected) = config.get_selected_dxvk() { if selected.is_none() || selected.unwrap().name != version.name { diff --git a/src/ui/preferences/main.rs b/src/ui/preferences/main.rs index a95c80c..545ef80 100644 --- a/src/ui/preferences/main.rs +++ b/src/ui/preferences/main.rs @@ -6,12 +6,16 @@ use adw::prelude::*; use anime_launcher_sdk::anime_game_core::prelude::*; use anime_launcher_sdk::anime_game_core::genshin::prelude::*; -use anime_launcher_sdk::config::launcher::LauncherStyle; + +use anime_launcher_sdk::config::ConfigExt; +use anime_launcher_sdk::genshin::config::Config; +use anime_launcher_sdk::genshin::config::schema::launcher::LauncherStyle; use crate::i18n::tr; use super::general::*; use super::enhancements::*; +use super::sandbox::*; use super::environment::*; pub static mut PREFERENCES_WINDOW: Option = None; @@ -19,6 +23,7 @@ pub static mut PREFERENCES_WINDOW: Option = None; pub struct PreferencesApp { general: AsyncController, enhancements: AsyncController, + sandbox: AsyncController, environment: AsyncController } @@ -64,10 +69,11 @@ impl SimpleAsyncComponent for PreferencesApp { add = model.general.widget(), add = model.enhancements.widget(), + add = model.sandbox.widget(), add = model.environment.widget(), connect_close_request[sender] => move |_| { - if let Err(err) = anime_launcher_sdk::config::flush() { + if let Err(err) = Config::flush() { sender.input(PreferencesAppMsg::Toast { title: tr("config-update-error"), description: Some(err.to_string()) @@ -95,6 +101,10 @@ impl SimpleAsyncComponent for PreferencesApp { .launch(()) .detach(), + sandbox: SandboxApp::builder() + .launch(()) + .detach(), + environment: EnvironmentApp::builder() .launch(()) .detach() diff --git a/src/ui/preferences/mod.rs b/src/ui/preferences/mod.rs index 292d031..c066563 100644 --- a/src/ui/preferences/mod.rs +++ b/src/ui/preferences/mod.rs @@ -1,5 +1,6 @@ pub mod main; pub mod general; pub mod enhancements; +pub mod sandbox; pub mod environment; pub mod gamescope; diff --git a/src/ui/preferences/sandbox.rs b/src/ui/preferences/sandbox.rs new file mode 100644 index 0000000..439cc4a --- /dev/null +++ b/src/ui/preferences/sandbox.rs @@ -0,0 +1,395 @@ +use relm4::prelude::*; +use relm4::component::*; +use relm4::factory::*; + +use adw::prelude::*; + +use anime_launcher_sdk::is_available; + +use crate::i18n::tr; +use crate::*; + +#[derive(Debug)] +struct PrivateDirectory { + path: String +} + +#[relm4::factory(async)] +impl AsyncFactoryComponent for PrivateDirectory { + type Init = String; + type Input = SandboxAppMsg; + type Output = SandboxAppMsg; + type CommandOutput = (); + type ParentInput = SandboxAppMsg; + type ParentWidget = adw::PreferencesGroup; + + view! { + root = adw::ActionRow { + set_title: &self.path, + + add_suffix = >k::Button { + set_icon_name: "user-trash-symbolic", + add_css_class: "flat", + set_valign: gtk::Align::Center, + + connect_clicked[sender, index] => move |_| { + sender.input(SandboxAppMsg::RemovePrivate(index.clone())); + } + } + } + } + + fn output_to_parent_input(output: Self::Output) -> Option { + Some(output) + } + + async fn init_model( + init: Self::Init, + _index: &DynamicIndex, + _sender: AsyncFactorySender, + ) -> Self { + Self { + path: init + } + } + + async fn update(&mut self, msg: Self::Input, sender: AsyncFactorySender) { + sender.output(msg); + } +} + +#[derive(Debug)] +struct SharedDirectory { + mount_from: String, + mount_to: String +} + +#[relm4::factory(async)] +impl AsyncFactoryComponent for SharedDirectory { + type Init = (String, String); + type Input = SandboxAppMsg; + type Output = SandboxAppMsg; + type CommandOutput = (); + type ParentInput = SandboxAppMsg; + type ParentWidget = adw::PreferencesGroup; + + view! { + root = adw::ActionRow { + set_title: &self.mount_to, + set_subtitle: &self.mount_from, + + add_suffix = >k::Button { + set_icon_name: "user-trash-symbolic", + add_css_class: "flat", + set_valign: gtk::Align::Center, + + connect_clicked[sender, index] => move |_| { + sender.input(SandboxAppMsg::RemoveShared(index.clone())); + } + } + } + } + + fn output_to_parent_input(output: Self::Output) -> Option { + Some(output) + } + + async fn init_model( + init: Self::Init, + _index: &DynamicIndex, + _sender: AsyncFactorySender, + ) -> Self { + Self { + mount_from: init.0, + mount_to: init.1 + } + } + + async fn update(&mut self, msg: Self::Input, sender: AsyncFactorySender) { + sender.output(msg); + } +} + +pub struct SandboxApp { + private_paths: AsyncFactoryVecDeque, + shared_paths: AsyncFactoryVecDeque, + + private_path_entry: adw::EntryRow, + + shared_path_from_entry: adw::EntryRow, + shared_path_to_entry: adw::EntryRow, + read_only_switch: gtk::Switch +} + +#[derive(Debug, Clone)] +pub enum SandboxAppMsg { + AddPrivate, + RemovePrivate(DynamicIndex), + + AddShared, + RemoveShared(DynamicIndex) +} + +#[relm4::component(async, pub)] +impl SimpleAsyncComponent for SandboxApp { + type Init = (); + type Input = SandboxAppMsg; + type Output = (); + + view! { + adw::PreferencesPage { + set_title: &tr("sandbox"), + set_icon_name: Some("folder-symbolic"), + + set_sensitive: is_available("bwrap"), + + add = &adw::PreferencesGroup { + set_title: &tr("sandbox"), + set_description: Some(&tr("sandbox-description")), + + adw::ActionRow { + set_title: &tr("enable-sandboxing"), + set_subtitle: &tr("enable-sandboxing-description"), + + add_suffix = >k::Switch { + set_valign: gtk::Align::Center, + + set_state: CONFIG.sandbox.enabled, + + connect_state_notify => |switch| { + if is_ready() { + if let Ok(mut config) = Config::get() { + config.sandbox.enabled = switch.state(); + + Config::update(config); + } + } + } + } + }, + + adw::ActionRow { + set_title: &tr("hide-home-directory"), + set_subtitle: &tr("hide-home-directory-description"), + + add_suffix = >k::Switch { + set_valign: gtk::Align::Center, + + set_state: CONFIG.sandbox.isolate_home, + + connect_state_notify => |switch| { + if is_ready() { + if let Ok(mut config) = Config::get() { + config.sandbox.isolate_home = switch.state(); + + Config::update(config); + } + } + } + } + }, + + adw::EntryRow { + set_title: &tr("hostname"), + set_text: CONFIG.sandbox.hostname.as_ref().unwrap_or(&String::new()).trim(), + + connect_changed => |entry| { + if let Ok(mut config) = Config::get() { + let command = entry.text().trim().to_string(); + + config.sandbox.hostname = if command.is_empty() { + None + } else { + Some(command) + }; + + Config::update(config); + } + } + } + }, + + add = &adw::PreferencesGroup { + set_title: &tr("private-directories"), + set_description: Some(&tr("private-directories-description")), + + #[local_ref] + private_path_entry -> adw::EntryRow { + set_title: &tr("path") + }, + + gtk::Button { + set_label: &tr("add"), + add_css_class: "pill", + + set_margin_top: 8, + set_halign: gtk::Align::Start, + + connect_clicked => SandboxAppMsg::AddPrivate + } + }, + + #[local_ref] + add = private_paths -> adw::PreferencesGroup {}, + + add = &adw::PreferencesGroup { + set_title: &tr("shared-directories"), + set_description: Some(&tr("shared-directories-description")), + + #[local_ref] + shared_path_from_entry -> adw::EntryRow { + set_title: &tr("original-path") + }, + + #[local_ref] + shared_path_to_entry -> adw::EntryRow { + set_title: &tr("new-path") + }, + + adw::ActionRow { + set_title: &tr("read-only"), + set_subtitle: &tr("read-only-description"), + + #[local_ref] + add_suffix = read_only_switch -> gtk::Switch { + set_valign: gtk::Align::Center + } + }, + + gtk::Button { + set_label: &tr("add"), + add_css_class: "pill", + + set_margin_top: 8, + set_halign: gtk::Align::Start, + + connect_clicked => SandboxAppMsg::AddShared + } + }, + + #[local_ref] + add = shared_paths -> adw::PreferencesGroup {} + } + } + + async fn init( + _init: Self::Init, + root: Self::Root, + sender: AsyncComponentSender, + ) -> AsyncComponentParts { + tracing::info!("Initializing environment settings"); + + let mut model = Self { + private_paths: AsyncFactoryVecDeque::new(adw::PreferencesGroup::new(), sender.input_sender()), + shared_paths: AsyncFactoryVecDeque::new(adw::PreferencesGroup::new(), sender.input_sender()), + + private_path_entry: adw::EntryRow::new(), + + shared_path_from_entry: adw::EntryRow::new(), + shared_path_to_entry: adw::EntryRow::new(), + read_only_switch: gtk::Switch::new() + }; + + for path in &CONFIG.sandbox.private { + model.private_paths.guard().push_back(path.trim().to_string()); + } + + for (from, to) in &CONFIG.sandbox.mounts.read_only { + model.shared_paths.guard().push_back(( + from.trim().to_string(), + format!("[read-only] {}", to.trim()) + )); + } + + for (from, to) in &CONFIG.sandbox.mounts.bind { + model.shared_paths.guard().push_back(( + from.trim().to_string(), + to.trim().to_string() + )); + } + + let private_paths = model.private_paths.widget(); + let shared_paths = model.shared_paths.widget(); + + let private_path_entry = &model.private_path_entry; + + let shared_path_from_entry = &model.shared_path_from_entry; + let shared_path_to_entry = &model.shared_path_to_entry; + let read_only_switch = &model.read_only_switch; + + let widgets = view_output!(); + + AsyncComponentParts { model, widgets } + } + + async fn update(&mut self, msg: Self::Input, _sender: AsyncComponentSender) { + match msg { + SandboxAppMsg::AddPrivate => { + if let Ok(mut config) = Config::get() { + let path = self.private_path_entry.text().trim().to_string(); + + if !path.is_empty() { + config.sandbox.private.push(path.clone()); + + Config::update(config); + + self.private_paths.guard().push_back(path); + } + } + } + + SandboxAppMsg::RemovePrivate(index) => { + if let Ok(mut config) = Config::get() { + if let Some(var) = self.private_paths.guard().get(index.current_index()) { + config.sandbox.private.retain(|item| item != &var.path); + + Config::update(config); + } + + self.private_paths.guard().remove(index.current_index()); + } + }, + + SandboxAppMsg::AddShared => { + if let Ok(mut config) = Config::get() { + let from = self.shared_path_from_entry.text().trim().to_string(); + let to = self.shared_path_to_entry.text().trim().to_string(); + + let read_only = self.read_only_switch.state(); + + if !from.is_empty() && !to.is_empty() { + if read_only { + config.sandbox.mounts.read_only.insert(from.clone(), to.clone()); + } else { + config.sandbox.mounts.bind.insert(from.clone(), to.clone()); + } + + Config::update(config); + + self.shared_paths.guard().push_back(( + from, + if read_only { + format!("[read-only] {}", to) + } else { + to + } + )); + } + } + } + + SandboxAppMsg::RemoveShared(index) => { + if let Ok(mut config) = Config::get() { + if let Some(var) = self.shared_paths.guard().get(index.current_index()) { + config.sandbox.mounts.read_only.remove(&var.mount_from); + config.sandbox.mounts.bind.remove(&var.mount_from); + + Config::update(config); + } + + self.shared_paths.guard().remove(index.current_index()); + } + } + } + } +}