Merge refactor/qbz-app-graphics-settings: graphics settings store
This commit is contained in:
3 files changed
+344
-236
No files matched your search
@@ -0,0 +1,338 @@
|
||||
//! Graphics settings persistence.
|
||||
//!
|
||||
//! This module stores portable host rendering preferences only. Startup
|
||||
//! detection, environment variable application, crash recovery, and command
|
||||
//! transport stay outside `qbz-app`.
|
||||
|
||||
use rusqlite::{params, Connection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::Path;
|
||||
|
||||
#[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.
|
||||
///
|
||||
/// Valid values are "auto", "integrated", "discrete", "software", or a
|
||||
/// host-specific GPU id such as a PCI slot.
|
||||
pub preferred_gpu: String,
|
||||
/// Opt-in NVIDIA Wayland compatibility mode. The host applies the runtime
|
||||
/// environment changes before graphics initialization.
|
||||
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 host-managed state exists.
|
||||
/// Opens existing DB without creating tables or 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");
|
||||
Self::new_readonly_at_path(&db_path)
|
||||
}
|
||||
|
||||
pub fn new_readonly_at_path(db_path: &Path) -> Result<Self, String> {
|
||||
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 })
|
||||
}
|
||||
|
||||
fn open_at(dir: &Path, db_name: &str) -> Result<Self, String> {
|
||||
std::fs::create_dir_all(dir)
|
||||
.map_err(|e| format!("Failed to create data directory: {}", e))?;
|
||||
|
||||
let db_path = dir.join(db_name);
|
||||
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))?;
|
||||
|
||||
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))?;
|
||||
|
||||
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;");
|
||||
let _ = conn.execute_batch(
|
||||
"ALTER TABLE graphics_settings ADD COLUMN preferred_gpu TEXT NOT NULL DEFAULT 'auto';",
|
||||
);
|
||||
let _ = conn.execute_batch(
|
||||
"ALTER TABLE graphics_settings ADD COLUMN nvidia_compat_mode INTEGER NOT NULL DEFAULT 0;",
|
||||
);
|
||||
|
||||
Ok(Self { conn })
|
||||
}
|
||||
|
||||
pub fn new() -> Result<Self, String> {
|
||||
let data_dir = dirs::data_dir()
|
||||
.ok_or("Could not determine data directory")?
|
||||
.join("qbz");
|
||||
Self::open_at(&data_dir, "graphics_settings.db")
|
||||
}
|
||||
|
||||
pub fn new_at(base_dir: &Path) -> Result<Self, String> {
|
||||
Self::open_at(base_dir, "graphics_settings.db")
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn unique_test_dir(name: &str) -> std::path::PathBuf {
|
||||
let nonce = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
std::env::temp_dir().join(format!("qbz-app-{name}-{}-{nonce}", std::process::id()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graphics_settings_default_values_are_stable() {
|
||||
let settings = GraphicsSettings::default();
|
||||
|
||||
assert!(settings.hardware_acceleration);
|
||||
assert!(!settings.force_x11);
|
||||
assert_eq!(settings.gdk_scale, None);
|
||||
assert_eq!(settings.gdk_dpi_scale, None);
|
||||
assert_eq!(settings.gsk_renderer, None);
|
||||
assert_eq!(settings.preferred_gpu, "auto");
|
||||
assert!(!settings.nvidia_compat_mode);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graphics_settings_store_returns_defaults() {
|
||||
let dir = unique_test_dir("graphics-default");
|
||||
let store = GraphicsSettingsStore::new_at(&dir).expect("open store");
|
||||
|
||||
let settings = store.get_settings().expect("get settings");
|
||||
|
||||
assert!(settings.hardware_acceleration);
|
||||
assert!(!settings.force_x11);
|
||||
assert_eq!(settings.preferred_gpu, "auto");
|
||||
assert!(!settings.nvidia_compat_mode);
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graphics_settings_persist_all_fields() {
|
||||
let dir = unique_test_dir("graphics-persist");
|
||||
{
|
||||
let store = GraphicsSettingsStore::new_at(&dir).expect("open store");
|
||||
store
|
||||
.set_hardware_acceleration(false)
|
||||
.expect("set hardware acceleration");
|
||||
store.set_force_x11(true).expect("set force x11");
|
||||
store
|
||||
.set_gdk_scale(Some("2".to_string()))
|
||||
.expect("set gdk scale");
|
||||
store
|
||||
.set_gdk_dpi_scale(Some("0.5".to_string()))
|
||||
.expect("set gdk dpi scale");
|
||||
store
|
||||
.set_gsk_renderer(Some("ngl".to_string()))
|
||||
.expect("set gsk renderer");
|
||||
store
|
||||
.set_preferred_gpu("discrete")
|
||||
.expect("set preferred gpu");
|
||||
store
|
||||
.set_nvidia_compat_mode(true)
|
||||
.expect("set nvidia compat mode");
|
||||
}
|
||||
|
||||
let reopened = GraphicsSettingsStore::new_at(&dir).expect("reopen store");
|
||||
let settings = reopened.get_settings().expect("get settings");
|
||||
|
||||
assert!(!settings.hardware_acceleration);
|
||||
assert!(settings.force_x11);
|
||||
assert_eq!(settings.gdk_scale.as_deref(), Some("2"));
|
||||
assert_eq!(settings.gdk_dpi_scale.as_deref(), Some("0.5"));
|
||||
assert_eq!(settings.gsk_renderer.as_deref(), Some("ngl"));
|
||||
assert_eq!(settings.preferred_gpu, "discrete");
|
||||
assert!(settings.nvidia_compat_mode);
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graphics_settings_reopen_does_not_overwrite_existing_row() {
|
||||
let dir = unique_test_dir("graphics-no-overwrite");
|
||||
{
|
||||
let store = GraphicsSettingsStore::new_at(&dir).expect("open store");
|
||||
store
|
||||
.set_hardware_acceleration(false)
|
||||
.expect("set hardware acceleration");
|
||||
store
|
||||
.set_preferred_gpu("software")
|
||||
.expect("set preferred gpu");
|
||||
}
|
||||
|
||||
let reopened = GraphicsSettingsStore::new_at(&dir).expect("reopen store");
|
||||
let settings = reopened.get_settings().expect("get settings");
|
||||
|
||||
assert!(!settings.hardware_acceleration);
|
||||
assert_eq!(settings.preferred_gpu, "software");
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graphics_settings_readonly_opens_existing_db() {
|
||||
let dir = unique_test_dir("graphics-readonly");
|
||||
let db_path = dir.join("graphics_settings.db");
|
||||
{
|
||||
let store = GraphicsSettingsStore::new_at(&dir).expect("open store");
|
||||
store
|
||||
.set_preferred_gpu("integrated")
|
||||
.expect("set preferred gpu");
|
||||
}
|
||||
|
||||
let readonly =
|
||||
GraphicsSettingsStore::new_readonly_at_path(&db_path).expect("open readonly store");
|
||||
let settings = readonly.get_settings().expect("get readonly settings");
|
||||
|
||||
assert_eq!(settings.preferred_gpu, "integrated");
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod download;
|
||||
pub mod favorites;
|
||||
pub mod graphics;
|
||||
pub mod image_cache;
|
||||
pub mod legal;
|
||||
pub mod library;
|
||||
|
||||
@@ -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