refactor(tauri): use qbz-app graphics settings
cx
This commit is contained in:
1 file changed
+5
-236
@@ -1,19 +1,14 @@
|
||||
//! Graphics settings persistence
|
||||
//! Tauri graphics settings commands and startup status.
|
||||
//!
|
||||
//! Stores GPU/rendering preferences that take effect before WebView initialization.
|
||||
//! These settings are device-level (not per-user) and persist across sessions.
|
||||
//!
|
||||
//! - hardware_acceleration: GPU rendering toggle (default: on). Read at startup
|
||||
//! as the default value; env var QBZ_HARDWARE_ACCEL=0|1 overrides.
|
||||
//! - force_x11: force X11/XWayland backend on Wayland sessions (default: off)
|
||||
//! Env var QBZ_FORCE_X11=1|0 always overrides the stored value.
|
||||
//! - gdk_scale / gdk_dpi_scale: display scaling overrides for XWayland
|
||||
//! Portable graphics settings persistence lives in `qbz-app`. This module keeps
|
||||
//! Tauri command wrappers and process-local startup status only.
|
||||
|
||||
use rusqlite::{params, Connection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub use qbz_app::settings::graphics::{GraphicsSettings, GraphicsSettingsStore};
|
||||
|
||||
// --- Global startup state (set once in main.rs, read-only thereafter) ---
|
||||
static GRAPHICS_USING_FALLBACK: AtomicBool = AtomicBool::new(false);
|
||||
static GRAPHICS_IS_WAYLAND: AtomicBool = AtomicBool::new(false);
|
||||
@@ -50,232 +45,6 @@ pub fn is_using_graphics_fallback() -> bool {
|
||||
GRAPHICS_USING_FALLBACK.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct GraphicsSettings {
|
||||
/// GPU rendering toggle. Read at startup as default; env var QBZ_HARDWARE_ACCEL overrides.
|
||||
pub hardware_acceleration: bool,
|
||||
/// Force X11 (XWayland) backend on Wayland sessions (requires restart)
|
||||
pub force_x11: bool,
|
||||
/// GDK_SCALE override for XWayland (None = auto). Integer values: "1", "2"
|
||||
pub gdk_scale: Option<String>,
|
||||
/// GDK_DPI_SCALE override for XWayland (None = auto). Float values: "0.5", "1", "1.5"
|
||||
pub gdk_dpi_scale: Option<String>,
|
||||
/// GSK_RENDERER override (None = auto). Values: "gl", "ngl", "vulkan", "cairo"
|
||||
pub gsk_renderer: Option<String>,
|
||||
/// Rendering GPU selection. Values:
|
||||
/// "auto" — let the system pick (default).
|
||||
/// "integrated" — pin to the iGPU (Intel/AMD).
|
||||
/// "discrete" — pin to the dGPU (NVIDIA, or AMD/Intel discrete).
|
||||
/// "software" — software rendering via llvmpipe.
|
||||
/// "<pci-slot>" — pin to a specific detected GPU (e.g. "0000:01:00.0").
|
||||
/// main.rs applies the choice via env vars (EGL vendor pin, DRI_PRIME,
|
||||
/// NV_PRIME_RENDER_OFFLOAD) before WebKit init. Requires restart to
|
||||
/// take effect.
|
||||
pub preferred_gpu: String,
|
||||
/// Opt-in NVIDIA Wayland compatibility mode. When ON:
|
||||
/// - Sets `__NV_DISABLE_EXPLICIT_SYNC=1` to bypass the buggy
|
||||
/// implicit-sync paths in libnvidia-egl-gbm that produce
|
||||
/// "Failed to create GBM buffer of size NxN: Invalid argument"
|
||||
/// under WebKit on NVIDIA + Wayland (driver 550+).
|
||||
/// - Lifts the defensive compositing-mode shutdown that QBZ
|
||||
/// applies to NVIDIA-only Wayland sessions — the user is
|
||||
/// signaling that the real fix is now in place, so the
|
||||
/// safety net is no longer needed.
|
||||
/// Off by default. Requires a driver+system that ships
|
||||
/// libnvidia-egl-gbm to be useful.
|
||||
pub nvidia_compat_mode: bool,
|
||||
}
|
||||
|
||||
impl Default for GraphicsSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hardware_acceleration: true,
|
||||
force_x11: false,
|
||||
gdk_scale: None,
|
||||
gdk_dpi_scale: None,
|
||||
gsk_renderer: None,
|
||||
preferred_gpu: "auto".to_string(),
|
||||
nvidia_compat_mode: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GraphicsSettingsStore {
|
||||
conn: Connection,
|
||||
}
|
||||
|
||||
impl GraphicsSettingsStore {
|
||||
/// Lightweight read-only open for startup (before Tauri state).
|
||||
/// Opens existing DB without creating tables/running migrations.
|
||||
pub fn new_readonly() -> Result<Self, String> {
|
||||
let db_path = dirs::data_dir()
|
||||
.ok_or("Could not determine data directory")?
|
||||
.join("qbz")
|
||||
.join("graphics_settings.db");
|
||||
|
||||
if !db_path.exists() {
|
||||
return Err("Graphics settings DB does not exist yet".to_string());
|
||||
}
|
||||
|
||||
let conn = Connection::open_with_flags(
|
||||
&db_path,
|
||||
rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY | rusqlite::OpenFlags::SQLITE_OPEN_NO_MUTEX,
|
||||
)
|
||||
.map_err(|e| format!("Failed to open graphics settings database (readonly): {}", e))?;
|
||||
|
||||
Ok(Self { conn })
|
||||
}
|
||||
|
||||
pub fn new() -> Result<Self, String> {
|
||||
let data_dir = dirs::data_dir()
|
||||
.ok_or("Could not determine data directory")?
|
||||
.join("qbz");
|
||||
|
||||
std::fs::create_dir_all(&data_dir)
|
||||
.map_err(|e| format!("Failed to create data directory: {}", e))?;
|
||||
|
||||
let db_path = data_dir.join("graphics_settings.db");
|
||||
let conn = Connection::open(&db_path)
|
||||
.map_err(|e| format!("Failed to open graphics settings database: {}", e))?;
|
||||
|
||||
conn.execute_batch("PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL;")
|
||||
.map_err(|e| format!("Failed to enable WAL for graphics settings database: {}", e))?;
|
||||
|
||||
// Default hardware_acceleration = 1. Earlier QBZ versions defaulted
|
||||
// to 0 "for maximum compatibility", but WebKitGTK without GPU
|
||||
// compositing is severely degraded (multi-second component mounts
|
||||
// on 4K / tiled windows; observed 2026-05-12 perf session). The
|
||||
// mitigation logic in main.rs already auto-detects NVIDIA-only
|
||||
// Wayland configs and disables compositing for them; for every
|
||||
// other config the GPU path is the better default. Users who
|
||||
// explicitly toggle off in Settings keep their preference (the
|
||||
// INSERT OR IGNORE below only seeds a row that doesn't exist).
|
||||
conn.execute_batch(
|
||||
"CREATE TABLE IF NOT EXISTS graphics_settings (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
hardware_acceleration INTEGER NOT NULL DEFAULT 1
|
||||
);
|
||||
INSERT OR IGNORE INTO graphics_settings (id, hardware_acceleration) VALUES (1, 1);",
|
||||
)
|
||||
.map_err(|e| format!("Failed to create graphics settings table: {}", e))?;
|
||||
|
||||
// Migrations: add columns (no-op if already present)
|
||||
let _ = conn.execute_batch(
|
||||
"ALTER TABLE graphics_settings ADD COLUMN force_x11 INTEGER NOT NULL DEFAULT 0;",
|
||||
);
|
||||
let _ = conn.execute_batch("ALTER TABLE graphics_settings ADD COLUMN gdk_scale TEXT;");
|
||||
let _ = conn.execute_batch("ALTER TABLE graphics_settings ADD COLUMN gdk_dpi_scale TEXT;");
|
||||
let _ = conn.execute_batch("ALTER TABLE graphics_settings ADD COLUMN gsk_renderer TEXT;");
|
||||
// 1.2.13: rendering GPU selection. Default 'auto' so existing
|
||||
// installs keep their current behaviour after migration. Stored
|
||||
// as TEXT to accept both the well-known sentinels (auto /
|
||||
// integrated / discrete / software) and specific PCI slot ids.
|
||||
let _ = conn.execute_batch(
|
||||
"ALTER TABLE graphics_settings ADD COLUMN preferred_gpu TEXT NOT NULL DEFAULT 'auto';",
|
||||
);
|
||||
// 1.2.13: NVIDIA Wayland compatibility opt-in. Default OFF so
|
||||
// existing installs don't pick up a behaviour change on migration
|
||||
// — users explicitly enable it after installing libnvidia-egl-gbm.
|
||||
let _ = conn.execute_batch(
|
||||
"ALTER TABLE graphics_settings ADD COLUMN nvidia_compat_mode INTEGER NOT NULL DEFAULT 0;",
|
||||
);
|
||||
|
||||
Ok(Self { conn })
|
||||
}
|
||||
|
||||
pub fn get_settings(&self) -> Result<GraphicsSettings, String> {
|
||||
self.conn
|
||||
.query_row(
|
||||
"SELECT hardware_acceleration, force_x11, gdk_scale, gdk_dpi_scale, gsk_renderer, preferred_gpu, nvidia_compat_mode FROM graphics_settings WHERE id = 1",
|
||||
[],
|
||||
|row| {
|
||||
Ok(GraphicsSettings {
|
||||
hardware_acceleration: row.get::<_, i64>(0)? != 0,
|
||||
force_x11: row.get::<_, i64>(1)? != 0,
|
||||
gdk_scale: row.get::<_, Option<String>>(2)?,
|
||||
gdk_dpi_scale: row.get::<_, Option<String>>(3)?,
|
||||
gsk_renderer: row.get::<_, Option<String>>(4)?,
|
||||
preferred_gpu: row
|
||||
.get::<_, Option<String>>(5)?
|
||||
.unwrap_or_else(|| "auto".to_string()),
|
||||
nvidia_compat_mode: row.get::<_, i64>(6).unwrap_or(0) != 0,
|
||||
})
|
||||
},
|
||||
)
|
||||
.map_err(|e| format!("Failed to get graphics settings: {}", e))
|
||||
}
|
||||
|
||||
pub fn set_hardware_acceleration(&self, enabled: bool) -> Result<(), String> {
|
||||
self.conn
|
||||
.execute(
|
||||
"UPDATE graphics_settings SET hardware_acceleration = ?1 WHERE id = 1",
|
||||
params![enabled as i64],
|
||||
)
|
||||
.map_err(|e| format!("Failed to set hardware_acceleration: {}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_force_x11(&self, enabled: bool) -> Result<(), String> {
|
||||
self.conn
|
||||
.execute(
|
||||
"UPDATE graphics_settings SET force_x11 = ?1 WHERE id = 1",
|
||||
params![enabled as i64],
|
||||
)
|
||||
.map_err(|e| format!("Failed to set force_x11: {}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_gdk_scale(&self, value: Option<String>) -> Result<(), String> {
|
||||
self.conn
|
||||
.execute(
|
||||
"UPDATE graphics_settings SET gdk_scale = ?1 WHERE id = 1",
|
||||
params![value],
|
||||
)
|
||||
.map_err(|e| format!("Failed to set gdk_scale: {}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_gdk_dpi_scale(&self, value: Option<String>) -> Result<(), String> {
|
||||
self.conn
|
||||
.execute(
|
||||
"UPDATE graphics_settings SET gdk_dpi_scale = ?1 WHERE id = 1",
|
||||
params![value],
|
||||
)
|
||||
.map_err(|e| format!("Failed to set gdk_dpi_scale: {}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_gsk_renderer(&self, value: Option<String>) -> Result<(), String> {
|
||||
self.conn
|
||||
.execute(
|
||||
"UPDATE graphics_settings SET gsk_renderer = ?1 WHERE id = 1",
|
||||
params![value],
|
||||
)
|
||||
.map_err(|e| format!("Failed to set gsk_renderer: {}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_preferred_gpu(&self, value: &str) -> Result<(), String> {
|
||||
self.conn
|
||||
.execute(
|
||||
"UPDATE graphics_settings SET preferred_gpu = ?1 WHERE id = 1",
|
||||
params![value],
|
||||
)
|
||||
.map_err(|e| format!("Failed to set preferred_gpu: {}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_nvidia_compat_mode(&self, enabled: bool) -> Result<(), String> {
|
||||
self.conn
|
||||
.execute(
|
||||
"UPDATE graphics_settings SET nvidia_compat_mode = ?1 WHERE id = 1",
|
||||
params![enabled as i64],
|
||||
)
|
||||
.map_err(|e| format!("Failed to set nvidia_compat_mode: {}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Thread-safe wrapper for Tauri state management
|
||||
pub struct GraphicsSettingsState {
|
||||
pub store: Arc<Mutex<Option<GraphicsSettingsStore>>>,
|
||||
|
||||
Reference in new issue
Block a user