Skip to main content

truce_iced/
platform.rs

1//! Platform glue for truce-iced.
2//!
3//! `ParentWindow`, `query_backing_scale`, and `note_linux_scale_factor`
4//! are re-exported from `truce-gui` so call sites have one canonical
5//! source. The wgpu-surface bridge can't follow the same pattern: iced
6//! pulls in `iced_wgpu` 0.13 which depends on **wgpu 0.19**, while
7//! the rest of truce is on **wgpu 24**. `wgpu::Surface` is therefore
8//! a different type in each dep tree, so the canonical helper
9//! produces a value `iced_wgpu` can't ingest. `create_wgpu_surface`
10//! below is the per-version copy required to bridge baseview's
11//! rwh-0.5 handle to the wgpu-0.19 surface type `iced_wgpu` expects.
12
13use iced_wgpu::wgpu;
14use raw_window_handle::HasRawWindowHandle;
15
16pub use truce_gui::platform::{ParentWindow, note_linux_scale_factor, query_backing_scale};
17
18#[cfg(target_os = "windows")]
19fn current_module_hinstance() -> Option<std::num::NonZeroIsize> {
20    unsafe extern "system" {
21        fn GetModuleHandleW(lpModuleName: *const u16) -> isize;
22    }
23    // SAFETY: `GetModuleHandleW(NULL)` is documented to return the running
24    // EXE's HMODULE without acquiring a refcount; no threading or aliasing
25    // concerns. Returns 0 only in pathological cases (kernel32 missing).
26    let hmodule = unsafe { GetModuleHandleW(std::ptr::null()) };
27    std::num::NonZeroIsize::new(hmodule)
28}
29
30/// Bridge a baseview raw-window-handle 0.5 to the wgpu-0.19
31/// `SurfaceTargetUnsafe` type that `iced_wgpu` 0.13 expects.
32///
33/// # Safety
34/// The window handle must be valid for the lifetime of the returned
35/// surface.
36#[must_use]
37pub unsafe fn create_wgpu_surface(
38    instance: &wgpu::Instance,
39    window: &baseview::Window,
40) -> Option<wgpu::Surface<'static>> {
41    unsafe {
42        let rwh = window.raw_window_handle();
43        let target = match rwh {
44            #[cfg(target_os = "macos")]
45            raw_window_handle::RawWindowHandle::AppKit(h) => {
46                let ns_view = std::ptr::NonNull::new(h.ns_view)?;
47                wgpu::SurfaceTargetUnsafe::RawHandle {
48                    raw_display_handle: wgpu::rwh::RawDisplayHandle::AppKit(
49                        wgpu::rwh::AppKitDisplayHandle::new(),
50                    ),
51                    raw_window_handle: wgpu::rwh::RawWindowHandle::AppKit(
52                        wgpu::rwh::AppKitWindowHandle::new(ns_view),
53                    ),
54                }
55            }
56            #[cfg(target_os = "windows")]
57            raw_window_handle::RawWindowHandle::Win32(h) => {
58                let mut win32 =
59                    wgpu::rwh::Win32WindowHandle::new(std::num::NonZero::new(h.hwnd as isize)?);
60                // wgpu's Vulkan backend requires `hinstance` to be set
61                // (`vkCreateWin32SurfaceKHR` rejects a null HINSTANCE).
62                // See `truce-gui::platform::create_wgpu_surface` for the
63                // matching fix in the egui/gui/slint shared path.
64                win32.hinstance = current_module_hinstance();
65                wgpu::SurfaceTargetUnsafe::RawHandle {
66                    raw_display_handle: wgpu::rwh::RawDisplayHandle::Windows(
67                        wgpu::rwh::WindowsDisplayHandle::new(),
68                    ),
69                    raw_window_handle: wgpu::rwh::RawWindowHandle::Win32(win32),
70                }
71            }
72            #[cfg(target_os = "linux")]
73            raw_window_handle::RawWindowHandle::Xlib(h) => {
74                use raw_window_handle::HasRawDisplayHandle;
75                let raw_window_handle::RawDisplayHandle::Xlib(display) =
76                    window.raw_display_handle()
77                else {
78                    return None;
79                };
80                let display_ptr = std::ptr::NonNull::new(display.display);
81                wgpu::SurfaceTargetUnsafe::RawHandle {
82                    raw_display_handle: wgpu::rwh::RawDisplayHandle::Xlib(
83                        wgpu::rwh::XlibDisplayHandle::new(display_ptr, display.screen),
84                    ),
85                    raw_window_handle: wgpu::rwh::RawWindowHandle::Xlib(
86                        wgpu::rwh::XlibWindowHandle::new(h.window as std::ffi::c_ulong),
87                    ),
88                }
89            }
90            _ => return None,
91        };
92        instance.create_surface_unsafe(target).ok()
93    }
94}