Skip to main content

truce_gpu/
platform.rs

1//! Platform window bridging for baseview / wgpu.
2//!
3//! `WgpuBackend::from_window` consumes a `baseview::Window`; that
4//! window exposes raw-window-handle 0.5 handles but wgpu wants 0.6,
5//! so the bridge re-encodes per platform here. Lives in `truce-gpu`
6//! so the wgpu pipeline crate is self-contained; the per-OS
7//! HWND/NSView lookups + DPI queries live next door in
8//! `truce_gui::platform`.
9
10#[cfg(not(target_os = "ios"))]
11#[allow(unused_imports)]
12use raw_window_handle::{
13    HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle as RwhRawDisplayHandle,
14    RawWindowHandle as RwhRawWindowHandle,
15};
16
17#[cfg(target_os = "windows")]
18fn current_module_hinstance() -> Option<std::num::NonZeroIsize> {
19    unsafe extern "system" {
20        fn GetModuleHandleW(lpModuleName: *const u16) -> isize;
21    }
22    // SAFETY: `GetModuleHandleW(NULL)` is documented to return the running
23    // EXE's HMODULE without acquiring a refcount; no threading or aliasing
24    // concerns. Returns 0 only in pathological cases (kernel32 missing).
25    let hmodule = unsafe { GetModuleHandleW(std::ptr::null()) };
26    std::num::NonZeroIsize::new(hmodule)
27}
28
29/// Bridge a baseview raw-window-handle 0.5 to a wgpu-compatible
30/// `SurfaceTargetUnsafe` using rwh 0.6 types.
31///
32/// # Safety
33/// The window handle must be valid for the lifetime of the returned surface.
34#[cfg(not(target_os = "ios"))]
35#[must_use]
36pub unsafe fn create_wgpu_surface(
37    instance: &wgpu::Instance,
38    window: &baseview::Window,
39) -> Option<wgpu::Surface<'static>> {
40    unsafe {
41        let rwh = window.raw_window_handle();
42        let surface_target = match rwh {
43            #[cfg(target_os = "macos")]
44            RwhRawWindowHandle::AppKit(handle) => {
45                let ns_view = handle.ns_view;
46                if ns_view.is_null() {
47                    return None;
48                }
49                let rwh6_window = wgpu::rwh::RawWindowHandle::AppKit(
50                    wgpu::rwh::AppKitWindowHandle::new(std::ptr::NonNull::new(ns_view)?),
51                );
52                let rwh6_display =
53                    wgpu::rwh::RawDisplayHandle::AppKit(wgpu::rwh::AppKitDisplayHandle::new());
54                wgpu::SurfaceTargetUnsafe::RawHandle {
55                    raw_display_handle: Some(rwh6_display),
56                    raw_window_handle: rwh6_window,
57                }
58            }
59            #[cfg(target_os = "windows")]
60            RwhRawWindowHandle::Win32(handle) => {
61                let hwnd = handle.hwnd;
62                if hwnd.is_null() {
63                    return None;
64                }
65                let mut win32 =
66                    wgpu::rwh::Win32WindowHandle::new(std::num::NonZeroIsize::new(hwnd as isize)?);
67                // wgpu's Vulkan backend requires `hinstance` to be set
68                // (`vkCreateWin32SurfaceKHR` rejects a null HINSTANCE).
69                // baseview leaves the rwh 0.5 `hinstance` field at null,
70                // so populate it here with the running module's HMODULE.
71                win32.hinstance = current_module_hinstance();
72                let rwh6_window = wgpu::rwh::RawWindowHandle::Win32(win32);
73                let rwh6_display =
74                    wgpu::rwh::RawDisplayHandle::Windows(wgpu::rwh::WindowsDisplayHandle::new());
75                wgpu::SurfaceTargetUnsafe::RawHandle {
76                    raw_display_handle: Some(rwh6_display),
77                    raw_window_handle: rwh6_window,
78                }
79            }
80            #[cfg(target_os = "linux")]
81            RwhRawWindowHandle::Xlib(handle) => {
82                let RwhRawDisplayHandle::Xlib(display_handle) = window.raw_display_handle() else {
83                    return None;
84                };
85                let display_ptr = std::ptr::NonNull::new(display_handle.display);
86                let rwh6_window = wgpu::rwh::RawWindowHandle::Xlib(
87                    wgpu::rwh::XlibWindowHandle::new(handle.window),
88                );
89                let rwh6_display = wgpu::rwh::RawDisplayHandle::Xlib(
90                    wgpu::rwh::XlibDisplayHandle::new(display_ptr, display_handle.screen),
91                );
92                wgpu::SurfaceTargetUnsafe::RawHandle {
93                    raw_display_handle: Some(rwh6_display),
94                    raw_window_handle: rwh6_window,
95                }
96            }
97            _ => return None,
98        };
99
100        instance.create_surface_unsafe(surface_target).ok()
101    }
102}