feat(settings): add Windows toggle to disable WebView2 hardware acceleration
This commit is contained in:
@@ -8,6 +8,10 @@ use crate::db::service::app_metadata_service;
|
||||
use crate::db::AppDatabase;
|
||||
use crate::models::{SystemLanguageSettings, SystemProxySettings};
|
||||
#[cfg(feature = "tauri-runtime")]
|
||||
use crate::models::SystemRenderingSettings;
|
||||
#[cfg(feature = "tauri-runtime")]
|
||||
use crate::preferences;
|
||||
#[cfg(feature = "tauri-runtime")]
|
||||
use crate::network::proxy;
|
||||
|
||||
const SYSTEM_PROXY_SETTINGS_KEY: &str = "system_proxy_settings";
|
||||
@@ -147,3 +151,26 @@ pub async fn update_system_language_settings(
|
||||
|
||||
Ok(settings)
|
||||
}
|
||||
|
||||
#[cfg(feature = "tauri-runtime")]
|
||||
#[cfg_attr(feature = "tauri-runtime", tauri::command)]
|
||||
pub async fn get_system_rendering_settings() -> Result<SystemRenderingSettings, AppCommandError> {
|
||||
let prefs = preferences::load();
|
||||
Ok(SystemRenderingSettings {
|
||||
disable_hardware_acceleration: prefs.disable_hardware_acceleration,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "tauri-runtime")]
|
||||
#[cfg_attr(feature = "tauri-runtime", tauri::command)]
|
||||
pub async fn update_system_rendering_settings(
|
||||
settings: SystemRenderingSettings,
|
||||
) -> Result<SystemRenderingSettings, AppCommandError> {
|
||||
let mut prefs = preferences::load();
|
||||
prefs.disable_hardware_acceleration = settings.disable_hardware_acceleration;
|
||||
preferences::save(&prefs).map_err(|err| {
|
||||
AppCommandError::io_error("Failed to persist rendering settings")
|
||||
.with_detail(err.to_string())
|
||||
})?;
|
||||
Ok(settings)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ pub mod keyring_store;
|
||||
mod models;
|
||||
mod network;
|
||||
mod parsers;
|
||||
#[cfg(feature = "tauri-runtime")]
|
||||
pub mod preferences;
|
||||
pub mod process;
|
||||
mod terminal;
|
||||
pub mod web;
|
||||
@@ -35,8 +37,45 @@ mod tauri_app {
|
||||
|
||||
static APP_QUITTING: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// On Windows, opt-out users can disable WebView2 hardware acceleration to
|
||||
/// work around AMD/Intel GPU driver bugs that produce a black-screen
|
||||
/// webview. The flag is stored in a tiny sidecar file at
|
||||
/// `~/.codeg/preferences.json` so it can be read **before** the Tauri
|
||||
/// builder, plugins, or tokio runtime start — once a tokio worker is alive,
|
||||
/// `std::env::set_var` would race with concurrent `getenv` calls from
|
||||
/// libraries like reqwest/rustls that read `HTTP_PROXY` etc.
|
||||
#[cfg(target_os = "windows")]
|
||||
fn apply_webview2_rendering_override() {
|
||||
const DISABLE_GPU_ARGS: [&str; 2] = ["--disable-gpu", "--disable-software-rasterizer"];
|
||||
const ENV_KEY: &str = "WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS";
|
||||
|
||||
let prefs = crate::preferences::load();
|
||||
if !prefs.disable_hardware_acceleration {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut tokens: Vec<String> = match std::env::var(ENV_KEY) {
|
||||
Ok(prev) => prev
|
||||
.split_whitespace()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
Err(_) => Vec::new(),
|
||||
};
|
||||
for arg in DISABLE_GPU_ARGS {
|
||||
if !tokens.iter().any(|t| t == arg) {
|
||||
tokens.push(arg.to_string());
|
||||
}
|
||||
}
|
||||
std::env::set_var(ENV_KEY, tokens.join(" "));
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
// Apply the WebView2 rendering override before *any* tokio worker
|
||||
// exists or any plugin reads the env. See doc comment above.
|
||||
#[cfg(target_os = "windows")]
|
||||
apply_webview2_rendering_override();
|
||||
|
||||
if let Err(err) = fix_path_env::fix() {
|
||||
eprintln!("[PATH] fix_path_env failed: {err}");
|
||||
}
|
||||
@@ -302,6 +341,8 @@ mod tauri_app {
|
||||
system_settings::update_system_proxy_settings,
|
||||
system_settings::get_system_language_settings,
|
||||
system_settings::update_system_language_settings,
|
||||
system_settings::get_system_rendering_settings,
|
||||
system_settings::update_system_rendering_settings,
|
||||
version_control::detect_git,
|
||||
version_control::test_git_path,
|
||||
version_control::get_git_settings,
|
||||
|
||||
@@ -25,3 +25,5 @@ pub use system::{
|
||||
GitCredentials, GitDetectResult, GitHubAccountsSettings, GitHubTokenValidation, GitSettings,
|
||||
SystemLanguageSettings, SystemProxySettings,
|
||||
};
|
||||
#[cfg(feature = "tauri-runtime")]
|
||||
pub use system::SystemRenderingSettings;
|
||||
|
||||
@@ -37,6 +37,13 @@ pub struct SystemLanguageSettings {
|
||||
pub language: AppLocale,
|
||||
}
|
||||
|
||||
#[cfg(feature = "tauri-runtime")]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
#[serde(default)]
|
||||
pub struct SystemRenderingSettings {
|
||||
pub disable_hardware_acceleration: bool,
|
||||
}
|
||||
|
||||
// --- Version Control ---
|
||||
|
||||
/// Explicit credentials for a single git remote operation.
|
||||
|
||||
56
src-tauri/src/preferences.rs
Normal file
56
src-tauri/src/preferences.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
//! User-scoped preferences stored at `~/.codeg/preferences.json`.
|
||||
//!
|
||||
//! These are settings that must be readable **before** the Tauri builder and
|
||||
//! tokio runtime start (e.g. WebView2 rendering flags applied via
|
||||
//! `WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS`). All access is synchronous I/O so
|
||||
//! the data must stay tiny.
|
||||
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const PREFERENCES_FILE_NAME: &str = "preferences.json";
|
||||
const CODEG_DIR_NAME: &str = ".codeg";
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
#[serde(default)]
|
||||
pub struct AppPreferences {
|
||||
pub disable_hardware_acceleration: bool,
|
||||
}
|
||||
|
||||
pub fn preferences_file_path() -> Option<PathBuf> {
|
||||
dirs::home_dir().map(|h| h.join(CODEG_DIR_NAME).join(PREFERENCES_FILE_NAME))
|
||||
}
|
||||
|
||||
/// Read preferences synchronously. Missing / unreadable / malformed file
|
||||
/// returns `Default::default()`. Errors are intentionally swallowed because
|
||||
/// this is called on the startup hot-path; callers log if needed.
|
||||
pub fn load() -> AppPreferences {
|
||||
let Some(path) = preferences_file_path() else {
|
||||
return AppPreferences::default();
|
||||
};
|
||||
match fs::read_to_string(&path) {
|
||||
Ok(raw) => serde_json::from_str(&raw).unwrap_or_default(),
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => AppPreferences::default(),
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
"[Preferences] failed to read {}: {err}",
|
||||
path.display()
|
||||
);
|
||||
AppPreferences::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save(prefs: &AppPreferences) -> io::Result<()> {
|
||||
let path = preferences_file_path()
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "home directory unavailable"))?;
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
let serialized = serde_json::to_string_pretty(prefs)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err.to_string()))?;
|
||||
fs::write(&path, serialized)
|
||||
}
|
||||
Reference in New Issue
Block a user