From eca39344cd9d2a37fb52ef1c5f4fe3c11f6525cb Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 1 Nov 2026 22:02:52 +0100 Subject: v4.3.1: render Windows through wgpu and keep the installer window open Windows 10 1809 sessions with only the Microsoft display driver expose OpenGL 1.1, which egui_glow refuses. The Windows build now uses wgpu on Direct3D 12 and its adapter selector accepts WARP, the software rasterizer, after every hardware class, so a VM or Remote Desktop session still starts. Linux keeps the smaller glow renderer. The GUI is a windows-subsystem binary now, so launching it from the Start menu does not flash a console; startup failures and --help go to a native message box. The PowerShell installer pauses before closing unless -NoPause is passed. --- AGENTS.md | 6 +- Cargo.toml | 2 +- docs/GUI-PLAN.md | 7 ++- gui/Cargo.toml | 17 ++++-- gui/src/main.rs | 125 ++++++++++++++++++++++++++++++++------ scripts/installers/windows.ps1.in | 14 ++++- web/README.md | 5 +- 7 files changed, 147 insertions(+), 29 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7258710..cd5c71b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -87,7 +87,11 @@ renders as tofu. - `scripts/publish.sh` builds the GUI with `gui-release` and renders the installers with the published SHA-256. Installers verify the hash and install - per-user; never install system-wide or skip the check. + per-user; never install system-wide or skip the check. The Windows installer + waits for Enter before closing; `-NoPause` is only for automation. +- Windows renders through wgpu on Direct3D 12, including the WARP software + adapter, so a machine with only OpenGL 1.1 (VM, Remote Desktop) still runs. + Linux keeps the smaller glow renderer. Do not assume OpenGL 2.0 on Windows. - The download page links the public git repository (https://git.2137697.xyz/zagan-bet.git) for the GPL corresponding source. Run publish from a clean tree so the linked history matches the binaries. diff --git a/Cargo.toml b/Cargo.toml index 22a6ec7..8897f9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["gui"] [package] name = "zagan-bet" -version = "4.3.0" +version = "4.3.1" edition = "2024" license = "GPL-3.0-or-later" diff --git a/docs/GUI-PLAN.md b/docs/GUI-PLAN.md index 4ddf98f..a322057 100644 --- a/docs/GUI-PLAN.md +++ b/docs/GUI-PLAN.md @@ -121,6 +121,9 @@ they show the same layout with the real session and the pinned action bars. "remember this login" choice, lobby with the update notice, history table, and the four games. "Log out" and "forget saved login" are separate. Leaving a live hand or round asks first and forfeits the reservation. +- Linux draws with glow (OpenGL); Windows draws with wgpu on Direct3D 12 and + accepts the WARP software adapter, so a Windows 10 machine with only OpenGL + 1.1 (Remote Desktop, a VM) still starts. - Games are painted from `View`: slots with a 0.9 s reel animation and payline highlights, roulette with the wheel spin, the hover lattice, chip placement and payouts, blackjack with vector-drawn suits and legal actions, crash with @@ -153,8 +156,8 @@ Measured on Linux at 1080p with llvmpipe software GL: | Check | Result | | --- | --- | -| Linux binary | 8.3 MB | -| Windows x86_64 binary | 6.8 MB | +| Linux binary (glow) | 8.3 MB | +| Windows x86_64 binary (wgpu/DX12) | 9.8 MB | | Cold start to window | 72 ms | | Idle RSS | 130 MB | | Idle CPU | 0% | diff --git a/gui/Cargo.toml b/gui/Cargo.toml index 8bcbda0..24bf560 100644 --- a/gui/Cargo.toml +++ b/gui/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zagan-bet-gui" -version = "4.3.0" +version = "4.3.1" edition = "2024" license = "GPL-3.0-or-later" description = "Desktop frontend for zagan-bet" @@ -12,12 +12,19 @@ path = "src/main.rs" [dependencies] # No default features: the GUI never links the server's native-tls. zagan-bet = { path = "..", default-features = false } -# glow (OpenGL) rather than wgpu: no Vulkan runtime, and it cross-compiles to -# Windows with the mingw toolchain. Fonts are embedded, so no default_fonts. +rusqlite = { version = "0.29", features = ["bundled"] } +sha2 = "0.10" + +# Linux uses glow (OpenGL): small, and every desktop has a usable driver. +# Windows uses wgpu on Direct3D 12, which also has the WARP software adapter on +# machines with no OpenGL driver (Windows 10 1809 over Remote Desktop, VMs). +[target.'cfg(windows)'.dependencies] +eframe = { version = "0.37", default-features = false, features = ["wgpu"] } +windows-sys = { version = "0.59", features = ["Win32_UI_WindowsAndMessaging"] } + +[target.'cfg(not(windows))'.dependencies] eframe = { version = "0.37", default-features = false, features = [ "glow", "wayland", "x11", ] } -rusqlite = { version = "0.29", features = ["bundled"] } -sha2 = "0.10" diff --git a/gui/src/main.rs b/gui/src/main.rs index 82babb6..e0338fa 100644 --- a/gui/src/main.rs +++ b/gui/src/main.rs @@ -1,3 +1,5 @@ +#![cfg_attr(windows, windows_subsystem = "windows")] + //! Desktop frontend for zagan-bet. //! //! Phase 4: the egui shell over the shared `ClientCore`, with the transport on @@ -15,13 +17,15 @@ fn main() -> eframe::Result { } }; if args.help { - println!( - "usage: zagan-bet-gui [-d ] [--unsafe] + let usage = "usage: zagan-bet-gui [-d ] [--unsafe] -d, --data-dir where to keep client.db (default: XDG data dir) --unsafe skip certificate verification - -h, --help show this message" - ); + -h, --help show this message"; + #[cfg(windows)] + show_dialog(usage, false); + #[cfg(not(windows))] + println!("{usage}"); return Ok(()); } @@ -31,23 +35,110 @@ fn main() -> eframe::Result { std::process::exit(1); } - let options = eframe::NativeOptions { - viewport: eframe::egui::ViewportBuilder::default() - .with_title("Zagan-Bet") - .with_app_id("zagan-bet") - .with_inner_size([1440.0, 900.0]) - .with_min_inner_size([1100.0, 720.0]), - renderer: eframe::Renderer::Glow, - ..Default::default() - }; - let startup = Startup { data_dir, unsafe_mode: args.unsafe_mode, }; - eframe::run_native( + let result = eframe::run_native( "Zagan-Bet", - options, + native_options(), Box::new(move |cc| Ok(Box::new(ZaganApp::new(cc, startup)))), - ) + ); + #[cfg(windows)] + if let Err(error) = &result { + // The binary is a GUI-subsystem app, so this is the only way a startup + // failure reaches the player. + show_dialog(&format!("Zagan-Bet could not start.\n\n{error}"), true); + } + result +} + +/// A native Windows message box, for help and for startup failures (there is +/// no console attached). +#[cfg(windows)] +fn show_dialog(message: &str, error: bool) { + use std::ffi::OsStr; + use std::os::windows::ffi::OsStrExt; + + use windows_sys::Win32::UI::WindowsAndMessaging::{ + MB_ICONERROR, MB_ICONINFORMATION, MessageBoxW, + }; + + let wide = |text: &str| -> Vec { + OsStr::new(text) + .encode_wide() + .chain(std::iter::once(0)) + .collect() + }; + let text = wide(message); + let title = wide("Zagan-Bet"); + let icon = if error { + MB_ICONERROR + } else { + MB_ICONINFORMATION + }; + // SAFETY: both strings are NUL-terminated and outlive the call. + unsafe { + MessageBoxW(std::ptr::null_mut(), text.as_ptr(), title.as_ptr(), icon); + } +} + +fn viewport() -> eframe::egui::ViewportBuilder { + eframe::egui::ViewportBuilder::default() + .with_title("Zagan-Bet") + .with_app_id("zagan-bet") + .with_inner_size([1440.0, 900.0]) + .with_min_inner_size([1100.0, 720.0]) +} + +/// Windows renders through Direct3D 12. egui's default adapter choice prefers +/// hardware; this selector also accepts WARP, the software rasterizer, so a +/// Remote Desktop or VM session with no OpenGL driver still runs the app. +#[cfg(windows)] +fn native_options() -> eframe::NativeOptions { + use std::sync::Arc; + + use eframe::egui_wgpu::{WgpuConfiguration, WgpuSetup}; + use eframe::wgpu; + + let mut wgpu_options = WgpuConfiguration::default(); + if let WgpuSetup::CreateNew(setup) = &mut wgpu_options.wgpu_setup { + setup.instance_descriptor.backends = wgpu::Backends::DX12; + setup.native_adapter_selector = Some(Arc::new(|adapters, surface| { + let usable = |adapter: &wgpu::Adapter| { + surface.is_none_or(|surface| adapter.is_surface_supported(surface)) + }; + for kind in [ + wgpu::DeviceType::DiscreteGpu, + wgpu::DeviceType::IntegratedGpu, + wgpu::DeviceType::VirtualGpu, + wgpu::DeviceType::Cpu, + wgpu::DeviceType::Other, + ] { + if let Some(adapter) = adapters + .iter() + .find(|adapter| adapter.get_info().device_type == kind && usable(adapter)) + { + return Ok(adapter.clone()); + } + } + Err("no Direct3D 12 adapter (hardware or WARP) is available".to_string()) + })); + } + + eframe::NativeOptions { + viewport: viewport(), + renderer: eframe::Renderer::Wgpu, + wgpu_options, + ..Default::default() + } +} + +#[cfg(not(windows))] +fn native_options() -> eframe::NativeOptions { + eframe::NativeOptions { + viewport: viewport(), + renderer: eframe::Renderer::Glow, + ..Default::default() + } } diff --git a/scripts/installers/windows.ps1.in b/scripts/installers/windows.ps1.in index fd2b8b3..4b8e61d 100644 --- a/scripts/installers/windows.ps1.in +++ b/scripts/installers/windows.ps1.in @@ -6,6 +6,9 @@ # # Run it with: # powershell -ExecutionPolicy Bypass -File .\zagan-bet-gui-*-install.ps1 +# Add -NoPause to run it unattended. +param([switch]$NoPause) + $ErrorActionPreference = "Stop" $Version = "__VERSION__" @@ -13,9 +16,16 @@ $Binary = "__BINARY__" $Sha256 = "__SHA256__" $Url = "__URL__" +function Finish([int]$Code) { + if (-not $NoPause) { + Read-Host "Press Enter to close" | Out-Null + } + exit $Code +} + function Stop-Install([string]$Message) { Write-Host "install: $Message" -ForegroundColor Red - exit 1 + Finish 1 } $here = Split-Path -Parent $MyInvocation.MyCommand.Path @@ -73,3 +83,5 @@ try { Remove-Item -LiteralPath $temporary -Force } } + +Finish 0 diff --git a/web/README.md b/web/README.md index bd87e11..1ee794f 100644 --- a/web/README.md +++ b/web/README.md @@ -35,8 +35,9 @@ bin/server/zagan-bet-server---[.exe] The installers are generated next to their app. They embed the app's SHA-256, check the file (or download it) and install it for the current user: Linux writes `~/.local/bin/zagan-bet-gui` plus a `.desktop` launcher, Windows writes -`%LOCALAPPDATA%\Programs\Zagan-Bet` plus a Start-menu shortcut. The source -archives satisfy the GPL's corresponding-source requirement. +`%LOCALAPPDATA%\Programs\Zagan-Bet` plus a Start-menu shortcut. The Windows +installer waits for Enter before closing (`-NoPause` for automation). The page +links the public git repository for the GPL corresponding source. ## Publishing a build -- cgit v1.2.3