use crate::parser::WasmaConfig;
use crate::wasma_protocol_universal_client_unix_posix_window::{ArchKind, CURRENT_ARCH};
use std::sync::{Arc, RwLock};
#[derive(Debug, Clone, PartialEq)]
pub enum ToolkitTheme {
Gtk(GtkThemeOpt),
Iced(IcedThemeOpt),
Qt(QtThemeOpt),
None,
}
impl ToolkitTheme {
pub fn name(&self) -> &'static str {
match self {
Self::Gtk(_) => "GTK",
Self::Iced(_) => "Iced",
Self::Qt(_) => "Qt",
Self::None => "None (raw)",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct GtkThemeOpt {
pub theme_name: String,
pub version: u8,
pub dark_mode: bool,
pub font_scale: f32,
pub icon_theme: Option<String>,
}
impl Default for GtkThemeOpt {
fn default() -> Self {
Self {
theme_name: "Adwaita".to_string(),
version: 4,
dark_mode: false,
font_scale: 1.0,
icon_theme: None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct IcedThemeOpt {
pub variant: IcedThemeVariant,
pub accent_color: Option<[u8; 4]>,
pub font_family: Option<String>,
pub text_size: f32,
}
#[derive(Debug, Clone, PartialEq)]
pub enum IcedThemeVariant {
Light,
Dark,
Dracula,
Nord,
SolarizedLight,
SolarizedDark,
GruvboxLight,
GruvboxDark,
CatppuccinLatte,
CatppuccinMocha,
Custom(String),
}
impl Default for IcedThemeOpt {
fn default() -> Self {
Self {
variant: IcedThemeVariant::Dark,
accent_color: None,
font_family: None,
text_size: 16.0,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct QtThemeOpt {
pub style_name: String,
pub version: u8,
pub dark_mode: bool,
pub platform_theme: Option<String>,
pub font_pt: f32,
}
impl Default for QtThemeOpt {
fn default() -> Self {
Self {
style_name: "Fusion".to_string(),
version: 6,
dark_mode: false,
platform_theme: None,
font_pt: 10.0,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DrawSizeOpt {
pub min_width: u32,
pub min_height: u32,
pub max_width: Option<u32>,
pub max_height: Option<u32>,
pub default_width: u32,
pub default_height: u32,
pub aspect_ratio: Option<f32>,
pub resizable: bool,
}
impl Default for DrawSizeOpt {
fn default() -> Self {
Self {
min_width: 100,
min_height: 100,
max_width: None,
max_height: None,
default_width: 800,
default_height: 600,
aspect_ratio: None,
resizable: true,
}
}
}
#[derive(Debug, Clone)]
pub struct DrawOpt {
pub size: DrawSizeOpt,
pub toolkit: ToolkitTheme,
pub antialiasing: bool,
pub subpixel_rendering: bool,
pub vsync: bool,
pub target_fps: Option<u32>,
}
impl Default for DrawOpt {
fn default() -> Self {
Self {
size: DrawSizeOpt::default(),
toolkit: ToolkitTheme::Iced(IcedThemeOpt::default()),
antialiasing: true,
subpixel_rendering: false,
vsync: true,
target_fps: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PixelColorFormat {
Rgb888,
Rgba8888,
Bgr888,
Bgra8888,
Rgb565,
Rgb101010,
Rgba16Float,
Luma8,
}
impl PixelColorFormat {
pub fn bytes_per_pixel(&self) -> u8 {
match self {
Self::Rgb888 => 3,
Self::Rgba8888 => 4,
Self::Bgr888 => 3,
Self::Bgra8888 => 4,
Self::Rgb565 => 2,
Self::Rgb101010 => 4,
Self::Rgba16Float => 8,
Self::Luma8 => 1,
}
}
pub fn name(&self) -> &'static str {
match self {
Self::Rgb888 => "RGB888",
Self::Rgba8888 => "RGBA8888",
Self::Bgr888 => "BGR888",
Self::Bgra8888 => "BGRA8888",
Self::Rgb565 => "RGB565",
Self::Rgb101010 => "RGB101010",
Self::Rgba16Float => "RGBA16F",
Self::Luma8 => "Luma8",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PixelGridScreenDep {
pub cell_width_px: u32,
pub cell_height_px: u32,
pub line_color: [u8; 4],
pub line_width_phys: f32,
pub visible: bool,
pub snap_to_grid: bool,
}
impl Default for PixelGridScreenDep {
fn default() -> Self {
Self {
cell_width_px: 8,
cell_height_px: 8,
line_color: [200, 200, 200, 64],
line_width_phys: 0.5,
visible: false,
snap_to_grid: false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PixelGridScreenIndep {
pub cell_size_logical: f32,
pub subdivisions: u8,
pub major_every: u32,
pub major_color: [u8; 4],
pub minor_color: [u8; 4],
pub visible: bool,
}
impl Default for PixelGridScreenIndep {
fn default() -> Self {
Self {
cell_size_logical: 16.0,
subdivisions: 4,
major_every: 8,
major_color: [150, 150, 150, 128],
minor_color: [200, 200, 200, 64],
visible: false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PixelSizeOpt {
pub logical_scale: f32,
pub physical_size_um: f32,
pub min_size_logical: f32,
}
impl Default for PixelSizeOpt {
fn default() -> Self {
Self {
logical_scale: 1.0,
physical_size_um: 0.0,
min_size_logical: 0.5,
}
}
}
#[derive(Debug, Clone)]
pub struct PixelOpt {
pub size: PixelSizeOpt,
pub color_format: PixelColorFormat,
pub background_color: [u8; 4],
pub clear_color: [u8; 4],
pub grid_screen_dep: PixelGridScreenDep,
pub grid_screen_indep: PixelGridScreenIndep,
pub pixel_perfect: bool,
pub gamma: f32,
}
impl Default for PixelOpt {
fn default() -> Self {
Self {
size: PixelSizeOpt::default(),
color_format: PixelColorFormat::Rgba8888,
background_color: [30, 30, 30, 255],
clear_color: [0, 0, 0, 255],
grid_screen_dep: PixelGridScreenDep::default(),
grid_screen_indep: PixelGridScreenIndep::default(),
pixel_perfect: true,
gamma: 2.2,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum XlinxCacheMode {
Auto,
NoCache,
Manual,
Aggressive,
}
impl XlinxCacheMode {
pub fn name(&self) -> &'static str {
match self {
Self::Auto => "auto",
Self::NoCache => "no_cache",
Self::Manual => "manual",
Self::Aggressive => "aggressive",
}
}
pub fn from_str(s: &str) -> Self {
match s {
"no_cache" | "none" => Self::NoCache,
"manual" => Self::Manual,
"aggressive" | "full" => Self::Aggressive,
_ => Self::Auto,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum RasterMode {
Debug,
Hardware,
Software,
Auto,
}
impl RasterMode {
pub fn name(&self) -> &'static str {
match self {
Self::Debug => "debug (rasterization)",
Self::Hardware => "hardware",
Self::Software => "software (xlinx fallback)",
Self::Auto => "auto",
}
}
pub fn from_str(s: &str) -> Self {
match s {
"debug" | "dbg" => Self::Debug,
"hardware" | "hw" => Self::Hardware,
"software" | "sw" => Self::Software,
_ => Self::Auto,
}
}
pub fn is_debug_only(&self) -> bool {
matches!(self, Self::Debug)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum XlinxArch {
X86_64,
Aarch64,
RiscV,
Generic,
SoftwareOnly,
Auto,
}
impl XlinxArch {
pub fn auto_detect() -> Self {
match CURRENT_ARCH {
ArchKind::Amd64 => Self::X86_64,
ArchKind::Aarch64 => Self::Aarch64,
ArchKind::RiscV64 => Self::RiscV,
ArchKind::Sisd | ArchKind::Unknown => Self::SoftwareOnly,
_ => Self::Generic,
}
}
pub fn name(&self) -> &'static str {
match self {
Self::X86_64 => "x86_64",
Self::Aarch64 => "aarch64",
Self::RiscV => "riscv",
Self::Generic => "generic",
Self::SoftwareOnly => "software_only",
Self::Auto => "auto",
}
}
pub fn from_str(s: &str) -> Self {
match s {
"x86_64" | "amd64" => Self::X86_64,
"aarch64" | "arm64" => Self::Aarch64,
"riscv" | "riscv64" => Self::RiscV,
"generic" => Self::Generic,
"software" | "sw" => Self::SoftwareOnly,
_ => Self::Auto,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct XlinxUsetPermission {
pub allow_cache_change: bool,
pub allow_raster_change: bool,
pub allow_arch_change: bool,
pub allow_sw_fallback: bool,
pub allow_log_config: bool,
}
impl Default for XlinxUsetPermission {
fn default() -> Self {
Self {
allow_cache_change: true,
allow_raster_change: true,
allow_arch_change: false, allow_sw_fallback: true,
allow_log_config: true,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct XlinxAutoLog {
pub enabled: bool,
pub level: u8,
pub output_path: Option<String>,
pub log_frame_timing: bool,
pub log_cache_stats: bool,
pub log_raster_ops: bool,
}
impl Default for XlinxAutoLog {
fn default() -> Self {
Self {
enabled: false,
level: 2,
output_path: None,
log_frame_timing: false,
log_cache_stats: false,
log_raster_ops: false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct XlinxSoftFallback {
pub enabled: bool,
pub thread_count: u8,
pub tile_width: u32,
pub tile_height: u32,
pub software_msaa: bool,
pub msaa_samples: u8,
}
impl Default for XlinxSoftFallback {
fn default() -> Self {
Self {
enabled: true,
thread_count: 4,
tile_width: 64,
tile_height: 64,
software_msaa: false,
msaa_samples: 1,
}
}
}
#[derive(Debug, Clone)]
pub struct XlinxOpt {
pub cache_mode: XlinxCacheMode,
pub raster_mode: RasterMode,
pub arch: XlinxArch,
pub soft_fallback: XlinxSoftFallback,
pub auto_log: XlinxAutoLog,
pub uset_permission: XlinxUsetPermission,
pub hw_raster_supported: bool,
pub pipeline_version: u8,
}
impl Default for XlinxOpt {
fn default() -> Self {
let arch = XlinxArch::auto_detect();
let hw_supported = !matches!(arch, XlinxArch::SoftwareOnly | XlinxArch::Generic);
Self {
cache_mode: XlinxCacheMode::Auto,
raster_mode: RasterMode::Auto,
arch,
soft_fallback: XlinxSoftFallback::default(),
auto_log: XlinxAutoLog::default(),
uset_permission: XlinxUsetPermission::default(),
hw_raster_supported: hw_supported,
pipeline_version: 1,
}
}
}
impl XlinxOpt {
pub fn effective_raster_mode(&self) -> RasterMode {
match self.raster_mode {
RasterMode::Auto => {
if self.hw_raster_supported {
RasterMode::Hardware
} else {
RasterMode::Software
}
}
other => other,
}
}
pub fn is_debug_raster(&self) -> bool {
cfg!(debug_assertions) && matches!(self.raster_mode, RasterMode::Debug)
}
pub fn check_uset(&self, change: &str) -> Result<(), String> {
match change {
"cache" if !self.uset_permission.allow_cache_change => {
Err("USET: cache change not permitted".to_string())
}
"raster" if !self.uset_permission.allow_raster_change => {
Err("USET: raster mode change not permitted".to_string())
}
"arch" if !self.uset_permission.allow_arch_change => {
Err("USET: arch change not permitted".to_string())
}
"sw_fallback" if !self.uset_permission.allow_sw_fallback => {
Err("USET: software fallback change not permitted".to_string())
}
"log" if !self.uset_permission.allow_log_config => {
Err("USET: log config change not permitted".to_string())
}
_ => Ok(()),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum DisplayBackend {
X11,
Wayland,
XWayland,
Auto,
Headless,
}
impl DisplayBackend {
pub fn name(&self) -> &'static str {
match self {
Self::X11 => "X11 (Xorg/XCB)",
Self::Wayland => "Wayland",
Self::XWayland => "XWayland (X11 over Wayland, debug)",
Self::Auto => "Auto (Wayland preferred)",
Self::Headless => "Headless",
}
}
pub fn from_str(s: &str) -> Self {
match s {
"x11" | "xorg" | "xcb" => Self::X11,
"wayland" | "wl" => Self::Wayland,
"xwayland" => Self::XWayland,
"headless" => Self::Headless,
_ => Self::Auto,
}
}
pub fn is_xwayland(&self) -> bool {
matches!(self, Self::XWayland)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct X11DebugOpt {
pub xcb_debug: bool,
pub log_events: bool,
pub sync_mode: bool,
pub debug_border_width: u8,
pub decorations: bool,
pub use_composite: bool,
pub override_redirect: bool,
pub backing_store: X11BackingStore,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum X11BackingStore {
NotUseful,
WhenMapped,
Always,
}
impl Default for X11DebugOpt {
fn default() -> Self {
Self {
xcb_debug: false,
log_events: false,
sync_mode: false,
debug_border_width: 0,
decorations: true,
use_composite: true,
override_redirect: false,
backing_store: X11BackingStore::NotUseful,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct WaylandSurfaceOpt {
pub subsurface_support: bool,
pub preferred_format: PixelColorFormat,
pub fractional_scale: bool,
pub server_side_decorations: bool,
pub layer_shell: bool,
pub presentation_time: bool,
pub explicit_sync: bool,
pub dmabuf: bool,
pub output_scale: u32,
pub force_shm: bool,
}
impl Default for WaylandSurfaceOpt {
fn default() -> Self {
Self {
subsurface_support: true,
preferred_format: PixelColorFormat::Rgba8888,
fractional_scale: true,
server_side_decorations: true,
layer_shell: false,
presentation_time: true,
explicit_sync: false,
dmabuf: true,
output_scale: 1,
force_shm: false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct X11ToWaylandOpt {
pub auto_switch: bool,
pub min_compositor_version: u32,
pub fallback_to_x11: bool,
pub xwayland_app_ids: Vec<String>,
}
impl Default for X11ToWaylandOpt {
fn default() -> Self {
Self {
auto_switch: true,
min_compositor_version: 1,
fallback_to_x11: true,
xwayland_app_ids: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct BackendOpt {
pub backend: DisplayBackend,
pub x11: X11DebugOpt,
pub wayland: WaylandSurfaceOpt,
pub x11_to_wayland: X11ToWaylandOpt,
pub x11_active: bool,
pub wayland_available: bool,
}
impl Default for BackendOpt {
fn default() -> Self {
let wayland_available = std::env::var("WAYLAND_DISPLAY").is_ok();
let x11_active = std::env::var("DISPLAY").is_ok();
Self {
backend: DisplayBackend::Auto,
x11: X11DebugOpt::default(),
wayland: WaylandSurfaceOpt::default(),
x11_to_wayland: X11ToWaylandOpt::default(),
x11_active,
wayland_available,
}
}
}
impl BackendOpt {
pub fn effective_backend(&self) -> DisplayBackend {
match &self.backend {
DisplayBackend::Auto => {
if self.wayland_available {
DisplayBackend::Wayland
} else if self.x11_active {
DisplayBackend::X11
} else {
DisplayBackend::Headless
}
}
other => other.clone(),
}
}
pub fn x11_settings_active(&self) -> bool {
self.x11_active
&& matches!(
self.effective_backend(),
DisplayBackend::X11 | DisplayBackend::XWayland
)
}
pub fn wayland_settings_active(&self) -> bool {
self.wayland_available
&& matches!(
self.effective_backend(),
DisplayBackend::Wayland | DisplayBackend::XWayland
)
}
pub fn validate_xwayland(&self) -> Result<(), String> {
if matches!(self.backend, DisplayBackend::XWayland) {
if !self.x11_active {
return Err(
"XWayland requires X11 backend to be active (DISPLAY not set)".to_string(),
);
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct PosixOpt {
pub draw: DrawOpt,
pub pixel: PixelOpt,
pub xlinx: XlinxOpt,
pub backend: BackendOpt,
pub source: String,
}
impl Default for PosixOpt {
fn default() -> Self {
Self {
draw: DrawOpt::default(),
pixel: PixelOpt::default(),
xlinx: XlinxOpt::default(),
backend: BackendOpt::default(),
source: "default".to_string(),
}
}
}
impl PosixOpt {
pub fn from_config(config: &WasmaConfig) -> Self {
let mut opt = Self::default();
opt.source = "wasma.in.conf".to_string();
opt.draw.size.default_width = 800;
opt.draw.size.default_height = 600;
opt.xlinx.arch = XlinxArch::auto_detect();
opt.xlinx.raster_mode = match config.resource_limits.renderer.as_str() {
"glx_renderer" | "opencl" | "renderer_opencl" => RasterMode::Hardware,
"cpu_renderer" | "cpu" => RasterMode::Software,
_ => RasterMode::Auto,
};
if config.resource_limits.scope_level == 0 {
opt.xlinx.cache_mode = XlinxCacheMode::NoCache;
}
opt.draw.toolkit = ToolkitTheme::Iced(IcedThemeOpt::default());
if config.uri_handling.singularity_instances {
opt.draw.vsync = true;
}
opt
}
pub fn validate(&self) -> Result<(), Vec<String>> {
let mut errors = Vec::new();
if let Err(e) = self.backend.validate_xwayland() {
errors.push(e);
}
if matches!(self.xlinx.raster_mode, RasterMode::Debug) {
#[cfg(not(debug_assertions))]
errors.push("RasterMode::Debug is only allowed in debug builds".to_string());
}
let s = self.xlinx.soft_fallback.msaa_samples;
if s != 1 && s != 2 && s != 4 && s != 8 {
errors.push(format!(
"Invalid MSAA samples: {} (must be 1, 2, 4, or 8)",
s
));
}
if self.draw.size.min_width > self.draw.size.default_width {
errors.push("min_width > default_width".to_string());
}
if self.draw.size.min_height > self.draw.size.default_height {
errors.push("min_height > default_height".to_string());
}
if self.pixel.gamma < 0.1 || self.pixel.gamma > 10.0 {
errors.push(format!(
"Gamma out of range: {} (expected 0.1–10.0)",
self.pixel.gamma
));
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
pub fn print_summary(&self) {
println!("╔═══════════════════════════════════════════════════════════╗");
println!("║ WASMA PosixOpt Summary ║");
println!("╚═══════════════════════════════════════════════════════════╝");
println!("Source: {}", self.source);
println!("\n[DrawOpt]");
println!(" Toolkit: {}", self.draw.toolkit.name());
println!(
" Size: {}x{} (min {}x{})",
self.draw.size.default_width,
self.draw.size.default_height,
self.draw.size.min_width,
self.draw.size.min_height
);
println!(
" Antialiasing: {} | VSync: {} | FPS: {:?}",
self.draw.antialiasing, self.draw.vsync, self.draw.target_fps
);
println!("\n[PixelOpt]");
println!(
" Color format: {} ({} bpp)",
self.pixel.color_format.name(),
self.pixel.color_format.bytes_per_pixel()
);
println!(
" Gamma: {} | Pixel-perfect: {}",
self.pixel.gamma, self.pixel.pixel_perfect
);
println!(
" Grid (screen-dep): visible={} snap={}",
self.pixel.grid_screen_dep.visible, self.pixel.grid_screen_dep.snap_to_grid
);
println!(
" Grid (screen-indep): visible={} cell={}lpx",
self.pixel.grid_screen_indep.visible, self.pixel.grid_screen_indep.cell_size_logical
);
println!("\n[XlinxOpt]");
println!(" Arch: {}", self.xlinx.arch.name());
println!(" Cache: {}", self.xlinx.cache_mode.name());
println!(
" Raster: {} (effective: {})",
self.xlinx.raster_mode.name(),
self.xlinx.effective_raster_mode().name()
);
println!(" HW raster: {}", self.xlinx.hw_raster_supported);
println!(
" SW fallback: {} ({} threads, {}x{} tiles)",
self.xlinx.soft_fallback.enabled,
self.xlinx.soft_fallback.thread_count,
self.xlinx.soft_fallback.tile_width,
self.xlinx.soft_fallback.tile_height
);
println!(
" Auto-log: {} (level {})",
self.xlinx.auto_log.enabled, self.xlinx.auto_log.level
);
println!("\n[BackendOpt]");
println!(
" Backend: {} → effective: {}",
self.backend.backend.name(),
self.backend.effective_backend().name()
);
println!(
" X11 active: {} | Wayland available: {}",
self.backend.x11_active, self.backend.wayland_available
);
println!(" X11 settings: {}", self.backend.x11_settings_active());
println!(
" Wayland surface: {}",
self.backend.wayland_settings_active()
);
println!(
" DMA-BUF: {} | Frac.scale: {}",
self.backend.wayland.dmabuf, self.backend.wayland.fractional_scale
);
}
}
pub struct RuntimeOptStore {
inner: Arc<RwLock<PosixOpt>>,
}
impl RuntimeOptStore {
pub fn new(base: PosixOpt) -> Self {
Self {
inner: Arc::new(RwLock::new(base)),
}
}
pub fn read(&self) -> std::sync::RwLockReadGuard<'_, PosixOpt> {
self.inner.read().unwrap()
}
pub fn override_with<F>(&self, f: F)
where
F: FnOnce(&mut PosixOpt),
{
let mut opt = self.inner.write().unwrap();
f(&mut opt);
opt.source = format!("{} + runtime", opt.source);
}
pub fn set_toolkit(&self, toolkit: ToolkitTheme) {
self.override_with(|o| o.draw.toolkit = toolkit);
}
pub fn set_backend(&self, backend: DisplayBackend) {
self.override_with(|o| o.backend.backend = backend);
}
pub fn set_cache_mode(&self, mode: XlinxCacheMode) -> Result<(), String> {
let check = self.read().xlinx.check_uset("cache")?;
let _ = check;
self.override_with(|o| o.xlinx.cache_mode = mode);
Ok(())
}
pub fn set_raster_mode(&self, mode: RasterMode) -> Result<(), String> {
#[cfg(not(debug_assertions))]
if matches!(mode, RasterMode::Debug) {
return Err("RasterMode::Debug not allowed in release builds".to_string());
}
self.read().xlinx.check_uset("raster")?;
self.override_with(|o| o.xlinx.raster_mode = mode);
Ok(())
}
pub fn set_color_format(&self, fmt: PixelColorFormat) {
self.override_with(|o| o.pixel.color_format = fmt);
}
pub fn set_screen_dep_grid(&self, visible: bool) {
self.override_with(|o| o.pixel.grid_screen_dep.visible = visible);
}
pub fn set_screen_indep_grid(&self, visible: bool) {
self.override_with(|o| o.pixel.grid_screen_indep.visible = visible);
}
pub fn set_auto_log(&self, enabled: bool, level: u8) -> Result<(), String> {
self.read().xlinx.check_uset("log")?;
self.override_with(|o| {
o.xlinx.auto_log.enabled = enabled;
o.xlinx.auto_log.level = level;
});
Ok(())
}
pub fn clone_store(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
pub struct PosixOptBuilder {
opt: PosixOpt,
}
impl PosixOptBuilder {
pub fn new() -> Self {
Self {
opt: PosixOpt::default(),
}
}
pub fn from_config(config: &WasmaConfig) -> Self {
Self {
opt: PosixOpt::from_config(config),
}
}
pub fn toolkit(mut self, toolkit: ToolkitTheme) -> Self {
self.opt.draw.toolkit = toolkit;
self
}
pub fn default_size(mut self, width: u32, height: u32) -> Self {
self.opt.draw.size.default_width = width;
self.opt.draw.size.default_height = height;
self
}
pub fn min_size(mut self, width: u32, height: u32) -> Self {
self.opt.draw.size.min_width = width;
self.opt.draw.size.min_height = height;
self
}
pub fn max_size(mut self, width: u32, height: u32) -> Self {
self.opt.draw.size.max_width = Some(width);
self.opt.draw.size.max_height = Some(height);
self
}
pub fn aspect_ratio(mut self, ratio: f32) -> Self {
self.opt.draw.size.aspect_ratio = Some(ratio);
self
}
pub fn antialiasing(mut self, enabled: bool) -> Self {
self.opt.draw.antialiasing = enabled;
self
}
pub fn vsync(mut self, enabled: bool) -> Self {
self.opt.draw.vsync = enabled;
self
}
pub fn target_fps(mut self, fps: u32) -> Self {
self.opt.draw.target_fps = Some(fps);
self
}
pub fn color_format(mut self, fmt: PixelColorFormat) -> Self {
self.opt.pixel.color_format = fmt;
self
}
pub fn background_color(mut self, rgba: [u8; 4]) -> Self {
self.opt.pixel.background_color = rgba;
self
}
pub fn gamma(mut self, gamma: f32) -> Self {
self.opt.pixel.gamma = gamma;
self
}
pub fn pixel_perfect(mut self, enabled: bool) -> Self {
self.opt.pixel.pixel_perfect = enabled;
self
}
pub fn screen_dep_grid(mut self, visible: bool, snap: bool) -> Self {
self.opt.pixel.grid_screen_dep.visible = visible;
self.opt.pixel.grid_screen_dep.snap_to_grid = snap;
self
}
pub fn screen_indep_grid(mut self, visible: bool, cell_size: f32) -> Self {
self.opt.pixel.grid_screen_indep.visible = visible;
self.opt.pixel.grid_screen_indep.cell_size_logical = cell_size;
self
}
pub fn xlinx_cache(mut self, mode: XlinxCacheMode) -> Self {
self.opt.xlinx.cache_mode = mode;
self
}
pub fn xlinx_raster(mut self, mode: RasterMode) -> Self {
self.opt.xlinx.raster_mode = mode;
self
}
pub fn xlinx_arch(mut self, arch: XlinxArch) -> Self {
self.opt.xlinx.arch = arch;
self.opt.xlinx.hw_raster_supported =
!matches!(arch, XlinxArch::SoftwareOnly | XlinxArch::Generic);
self
}
pub fn xlinx_soft_fallback(mut self, enabled: bool, threads: u8) -> Self {
self.opt.xlinx.soft_fallback.enabled = enabled;
self.opt.xlinx.soft_fallback.thread_count = threads;
self
}
pub fn xlinx_auto_log(mut self, enabled: bool, level: u8) -> Self {
self.opt.xlinx.auto_log.enabled = enabled;
self.opt.xlinx.auto_log.level = level;
self
}
pub fn xlinx_uset(mut self, perm: XlinxUsetPermission) -> Self {
self.opt.xlinx.uset_permission = perm;
self
}
pub fn backend(mut self, backend: DisplayBackend) -> Self {
self.opt.backend.backend = backend;
self
}
pub fn x11_debug(mut self, xcb_debug: bool, sync_mode: bool) -> Self {
self.opt.backend.x11.xcb_debug = xcb_debug;
self.opt.backend.x11.sync_mode = sync_mode;
self
}
pub fn wayland_surface(mut self, dmabuf: bool, frac_scale: bool) -> Self {
self.opt.backend.wayland.dmabuf = dmabuf;
self.opt.backend.wayland.fractional_scale = frac_scale;
self
}
pub fn x11_to_wayland(mut self, auto_switch: bool, fallback: bool) -> Self {
self.opt.backend.x11_to_wayland.auto_switch = auto_switch;
self.opt.backend.x11_to_wayland.fallback_to_x11 = fallback;
self
}
pub fn build(self) -> Result<PosixOpt, Vec<String>> {
self.opt.validate()?;
Ok(self.opt)
}
pub fn build_unchecked(self) -> PosixOpt {
self.opt
}
}
impl Default for PosixOptBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::ConfigParser;
fn make_config() -> WasmaConfig {
let parser = ConfigParser::new(None);
let content = parser.generate_default_config();
parser.parse(&content).unwrap()
}
#[test]
fn test_default_posix_opt() {
let opt = PosixOpt::default();
assert!(opt.validate().is_ok());
println!("✅ Default PosixOpt valid");
}
#[test]
fn test_from_config() {
let config = make_config();
let opt = PosixOpt::from_config(&config);
assert_eq!(opt.source, "wasma.in.conf");
assert!(opt.validate().is_ok());
println!(
"✅ PosixOpt::from_config working, raster: {}",
opt.xlinx.raster_mode.name()
);
}
#[test]
fn test_builder_basic() {
let opt = PosixOptBuilder::new()
.default_size(1920, 1080)
.min_size(320, 240)
.antialiasing(true)
.vsync(false)
.color_format(PixelColorFormat::Rgba8888)
.gamma(2.2)
.xlinx_cache(XlinxCacheMode::Auto)
.xlinx_raster(RasterMode::Hardware)
.backend(DisplayBackend::Wayland)
.build()
.unwrap();
assert_eq!(opt.draw.size.default_width, 1920);
assert_eq!(opt.draw.size.default_height, 1080);
assert!(!opt.draw.vsync);
assert_eq!(opt.pixel.color_format, PixelColorFormat::Rgba8888);
assert!((opt.pixel.gamma - 2.2).abs() < f32::EPSILON);
println!("✅ Builder basic working");
}
#[test]
fn test_builder_from_config() {
let config = make_config();
let opt = PosixOptBuilder::from_config(&config)
.toolkit(ToolkitTheme::Gtk(GtkThemeOpt::default()))
.target_fps(60)
.build_unchecked();
assert_eq!(opt.draw.target_fps, Some(60));
assert!(matches!(opt.draw.toolkit, ToolkitTheme::Gtk(_)));
println!("✅ Builder from_config + override working");
}
#[test]
fn test_xlinx_effective_raster() {
let mut xlinx = XlinxOpt::default();
xlinx.raster_mode = RasterMode::Auto;
xlinx.hw_raster_supported = true;
assert_eq!(xlinx.effective_raster_mode(), RasterMode::Hardware);
xlinx.hw_raster_supported = false;
assert_eq!(xlinx.effective_raster_mode(), RasterMode::Software);
println!("✅ XlinxOpt::effective_raster_mode working");
}
#[test]
fn test_xlinx_uset_permission() {
let mut xlinx = XlinxOpt::default();
xlinx.uset_permission.allow_cache_change = false;
assert!(xlinx.check_uset("cache").is_err());
assert!(xlinx.check_uset("raster").is_ok());
println!("✅ USET permission check working");
}
#[test]
fn test_backend_effective() {
let mut backend = BackendOpt::default();
backend.backend = DisplayBackend::Auto;
backend.wayland_available = true;
assert_eq!(backend.effective_backend(), DisplayBackend::Wayland);
backend.wayland_available = false;
backend.x11_active = true;
assert_eq!(backend.effective_backend(), DisplayBackend::X11);
backend.x11_active = false;
assert_eq!(backend.effective_backend(), DisplayBackend::Headless);
println!("✅ BackendOpt::effective_backend working");
}
#[test]
fn test_xwayland_validation() {
let mut backend = BackendOpt::default();
backend.backend = DisplayBackend::XWayland;
backend.x11_active = false;
assert!(backend.validate_xwayland().is_err());
backend.x11_active = true;
assert!(backend.validate_xwayland().is_ok());
println!("✅ XWayland validation working");
}
#[test]
fn test_x11_settings_active_flag() {
let mut backend = BackendOpt::default();
backend.backend = DisplayBackend::X11;
backend.x11_active = true;
backend.wayland_available = false;
assert!(backend.x11_settings_active());
backend.backend = DisplayBackend::Wayland;
assert!(!backend.x11_settings_active());
println!("✅ X11 settings active flag working");
}
#[test]
fn test_runtime_opt_store() {
let opt = PosixOpt::default();
let store = RuntimeOptStore::new(opt);
store.set_toolkit(ToolkitTheme::Qt(QtThemeOpt::default()));
assert!(matches!(store.read().draw.toolkit, ToolkitTheme::Qt(_)));
store.set_color_format(PixelColorFormat::Rgb565);
assert_eq!(store.read().pixel.color_format, PixelColorFormat::Rgb565);
store.set_cache_mode(XlinxCacheMode::Aggressive).unwrap();
assert_eq!(store.read().xlinx.cache_mode, XlinxCacheMode::Aggressive);
assert!(store.read().source.contains("runtime"));
println!("✅ RuntimeOptStore working");
}
#[test]
fn test_runtime_uset_block() {
let opt = PosixOptBuilder::new()
.xlinx_uset(XlinxUsetPermission {
allow_cache_change: false,
..XlinxUsetPermission::default()
})
.build_unchecked();
let store = RuntimeOptStore::new(opt);
let result = store.set_cache_mode(XlinxCacheMode::NoCache);
assert!(result.is_err());
println!("✅ USET runtime block working");
}
#[test]
fn test_pixel_color_format_bpp() {
assert_eq!(PixelColorFormat::Rgba8888.bytes_per_pixel(), 4);
assert_eq!(PixelColorFormat::Rgb565.bytes_per_pixel(), 2);
assert_eq!(PixelColorFormat::Rgba16Float.bytes_per_pixel(), 8);
assert_eq!(PixelColorFormat::Luma8.bytes_per_pixel(), 1);
println!("✅ PixelColorFormat bytes_per_pixel working");
}
#[test]
fn test_validation_failures() {
let result = PosixOptBuilder::new()
.min_size(1000, 1000) .gamma(0.0) .build();
assert!(result.is_err());
let errors = result.unwrap_err();
assert!(errors.len() >= 2);
println!("✅ Validation correctly caught {} errors", errors.len());
}
#[test]
fn test_xlinx_arch_auto_detect() {
let arch = XlinxArch::auto_detect();
println!("✅ XlinxArch auto-detected: {}", arch.name());
assert!(arch.name().len() > 0);
}
#[test]
fn test_grid_options() {
let opt = PosixOptBuilder::new()
.screen_dep_grid(true, true)
.screen_indep_grid(true, 32.0)
.build_unchecked();
assert!(opt.pixel.grid_screen_dep.visible);
assert!(opt.pixel.grid_screen_dep.snap_to_grid);
assert!(opt.pixel.grid_screen_indep.visible);
assert!((opt.pixel.grid_screen_indep.cell_size_logical - 32.0).abs() < f32::EPSILON);
println!("✅ Grid options working");
}
#[test]
fn test_print_summary() {
let opt = PosixOpt::default();
opt.print_summary();
println!("✅ PosixOpt::print_summary working");
}
}