Skip to main content

surface_wasmtime/
surface.rs

1use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
2use shared::channel_to_stream;
3use std::{fmt::Debug, future::Future, marker::PhantomData, pin::Pin, sync::Arc};
4use wasi_gfx::surface::surface;
5pub use wasi_gfx::surface::surface::{
6    FrameEvent, KeyEvent, PointerEvent, {CreateDesc as SurfaceDesc, ResizeEvent},
7};
8use wasmtime::component::{Access, HasData, Resource, StreamReader};
9
10wasmtime::component::bindgen!({
11    world: "wasi-gfx:surface/imports",
12    require_store_data_send: true,
13    imports: {
14        "wasi-gfx:surface/surface.[method]surface.on-pointer-down": store | trappable,
15        "wasi-gfx:surface/surface.[method]surface.on-pointer-move": store | trappable,
16        "wasi-gfx:surface/surface.[method]surface.on-key-up": store | trappable,
17        "wasi-gfx:surface/surface.[method]surface.on-pointer-up": store | trappable,
18        "wasi-gfx:surface/surface.[method]surface.on-key-down": store | trappable,
19        "wasi-gfx:surface/surface.[method]surface.on-resize": store | trappable,
20        "wasi-gfx:surface/surface.[method]surface.on-frame": store | trappable,
21        default: trappable,
22    },
23    with: {
24        "wasi-gfx:surface/surface.surface": Surface,
25    },
26});
27
28// types
29
30/// Any type implementing GfxWindow can be used to back a wasi-gfx:surface
31pub trait GfxWindow: HasDisplayHandle + HasWindowHandle {
32    fn height(&self) -> u32;
33    fn width(&self) -> u32;
34    fn request_set_size(&self, width: Option<u32>, height: Option<u32>);
35}
36
37#[derive(Clone, Debug)]
38pub struct Surface(Arc<SurfaceInner>);
39
40impl Surface {
41    pub fn new(window: Box<dyn GfxWindow + Send + Sync + 'static>) -> Self {
42        let (mut pointer_up_sender, pointer_up_receiver) = async_broadcast::broadcast(5);
43        let pointer_up_receiver = pointer_up_receiver.deactivate();
44        pointer_up_sender.set_overflow(true);
45        let (mut pointer_down_sender, pointer_down_receiver) = async_broadcast::broadcast(5);
46        let pointer_down_receiver = pointer_down_receiver.deactivate();
47        pointer_down_sender.set_overflow(true);
48        let (mut pointer_move_sender, pointer_move_receiver) = async_broadcast::broadcast(1);
49        let pointer_move_receiver = pointer_move_receiver.deactivate();
50        pointer_move_sender.set_overflow(true);
51        let (mut key_up_sender, key_up_receiver) = async_broadcast::broadcast(5);
52        let key_up_receiver = key_up_receiver.deactivate();
53        key_up_sender.set_overflow(true);
54        let (mut key_down_sender, key_down_receiver) = async_broadcast::broadcast(5);
55        let key_down_receiver = key_down_receiver.deactivate();
56        key_down_sender.set_overflow(true);
57        let (mut resize_sender, resize_receiver) = async_broadcast::broadcast(5);
58        let resize_receiver = resize_receiver.deactivate();
59        resize_sender.set_overflow(true);
60        let (mut frame_sender, frame_receiver) = async_broadcast::broadcast(1);
61        let frame_receiver = frame_receiver.deactivate();
62        frame_sender.set_overflow(true);
63        Surface(Arc::new(SurfaceInner {
64            window,
65            pointer_up_sender,
66            _pointer_up_receiver: pointer_up_receiver,
67            pointer_down_sender,
68            _pointer_down_receiver: pointer_down_receiver,
69            pointer_move_sender,
70            _pointer_move_receiver: pointer_move_receiver,
71            key_up_sender,
72            _key_up_receiver: key_up_receiver,
73            key_down_sender,
74            _key_down_receiver: key_down_receiver,
75            resize_sender,
76            _resize_receiver: resize_receiver,
77            frame_sender,
78            _frame_receiver: frame_receiver,
79        }))
80    }
81
82    pub fn height(&self) -> u32 {
83        self.0.window.height()
84    }
85
86    pub fn width(&self) -> u32 {
87        self.0.window.width()
88    }
89
90    pub fn request_set_size(&self, width: Option<u32>, height: Option<u32>) {
91        self.0.window.request_set_size(width, height);
92    }
93
94    /// clone the Arc reference. i.e shallow clone
95    pub fn arc_clone(&self) -> Self {
96        Surface(Arc::clone(&self.0))
97    }
98
99    pub fn pointer_up(&self, event: PointerEvent) {
100        shared::unwrap_unless_inactive(self.0.pointer_up_sender.try_broadcast(event));
101    }
102    pub fn pointer_down(&self, event: PointerEvent) {
103        shared::unwrap_unless_inactive(self.0.pointer_down_sender.try_broadcast(event));
104    }
105    pub fn pointer_move(&self, event: PointerEvent) {
106        shared::unwrap_unless_inactive_or_full(self.0.pointer_move_sender.try_broadcast(event));
107    }
108    pub fn key_up(&self, event: KeyEvent) {
109        shared::unwrap_unless_inactive(self.0.key_up_sender.try_broadcast(event));
110    }
111    pub fn key_down(&self, event: KeyEvent) {
112        shared::unwrap_unless_inactive(self.0.key_down_sender.try_broadcast(event));
113    }
114    pub fn canvas_resize(&self, event: ResizeEvent) {
115        shared::unwrap_unless_inactive(self.0.resize_sender.try_broadcast(event));
116    }
117    pub fn animation_frame(&self) {
118        shared::unwrap_unless_inactive_or_full(
119            self.0
120                .frame_sender
121                .try_broadcast(FrameEvent { nothing: true }),
122        );
123    }
124}
125
126impl HasDisplayHandle for Surface {
127    fn display_handle(
128        &self,
129    ) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
130        self.0.window.display_handle()
131    }
132}
133impl HasWindowHandle for Surface {
134    fn window_handle(
135        &self,
136    ) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
137        self.0.window.window_handle()
138    }
139}
140
141struct SurfaceInner {
142    pub window: Box<dyn GfxWindow + Send + Sync + 'static>,
143
144    // Keeping inactive receivers to keep channels open.
145    // See https://docs.rs/async-broadcast/0.7.1/async_broadcast/struct.InactiveReceiver.html
146    pointer_up_sender: async_broadcast::Sender<PointerEvent>,
147    _pointer_up_receiver: async_broadcast::InactiveReceiver<PointerEvent>,
148    pointer_down_sender: async_broadcast::Sender<PointerEvent>,
149    _pointer_down_receiver: async_broadcast::InactiveReceiver<PointerEvent>,
150    pointer_move_sender: async_broadcast::Sender<PointerEvent>,
151    _pointer_move_receiver: async_broadcast::InactiveReceiver<PointerEvent>,
152    key_up_sender: async_broadcast::Sender<KeyEvent>,
153    _key_up_receiver: async_broadcast::InactiveReceiver<KeyEvent>,
154    key_down_sender: async_broadcast::Sender<KeyEvent>,
155    _key_down_receiver: async_broadcast::InactiveReceiver<KeyEvent>,
156    resize_sender: async_broadcast::Sender<ResizeEvent>,
157    _resize_receiver: async_broadcast::InactiveReceiver<ResizeEvent>,
158    frame_sender: async_broadcast::Sender<FrameEvent>,
159    _frame_receiver: async_broadcast::InactiveReceiver<FrameEvent>,
160}
161
162impl Debug for SurfaceInner {
163    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164        f.debug_struct("Surface")
165            .field("window", &"<Boxed window>")
166            .field("pointer_up_sender", &self.pointer_up_sender)
167            .field("_pointer_up_receiver", &self._pointer_up_receiver)
168            .field("pointer_down_sender", &self.pointer_down_sender)
169            .field("_pointer_down_receiver", &self._pointer_down_receiver)
170            .field("pointer_move_sender", &self.pointer_move_sender)
171            .field("_pointer_move_receiver", &self._pointer_move_receiver)
172            .field("key_up_sender", &self.key_up_sender)
173            .field("_key_up_receiver", &self._key_up_receiver)
174            .field("key_down_sender", &self.key_down_sender)
175            .field("_key_down_receiver", &self._key_down_receiver)
176            .field("resize_sender", &self.resize_sender)
177            .field("_resize_receiver", &self._resize_receiver)
178            .field("frame_sender", &self.frame_sender)
179            .field("_frame_receiver", &self._frame_receiver)
180            .finish()
181    }
182}
183
184// linker connection
185pub fn add_to_linker<T>(l: &mut wasmtime::component::Linker<T>) -> wasmtime::Result<()>
186where
187    T: SurfaceCtxView,
188{
189    wasi_gfx::surface::surface::add_to_linker::<_, HasSurfaceCtx<T::Spawner>>(l, T::surface_ctx)?;
190    Ok(())
191}
192
193pub trait SurfaceCtxView: Send {
194    /// Spawner used to run main-thread-only windowing operations.
195    type Spawner: MainThreadSpawner;
196    /// returns a struct of references.
197    /// Returning all references in a struct allows us to use multiple mutable references at the same time.
198    fn surface_ctx(&mut self) -> SurfaceCtx<'_, Self::Spawner>;
199}
200
201pub struct SurfaceCtx<'a, S: MainThreadSpawner> {
202    pub table: &'a mut wasmtime_wasi::ResourceTable,
203    pub main_thread_spawner: &'a S,
204}
205
206struct HasSurfaceCtx<S>(PhantomData<S>);
207
208impl<S: MainThreadSpawner> HasData for HasSurfaceCtx<S> {
209    type Data<'a> = SurfaceCtx<'a, S>;
210}
211
212/// Runs main-thread-only operations, as required by some operating systems
213/// (e.g. macOS). On platforms without that requirement the implementation may run them in place.
214pub trait MainThreadSpawner: Send + Sync + 'static {
215    /// Run an arbitrary closure on the main thread and resolve to its result.
216    fn spawn<F, T>(&self, f: F) -> impl Future<Output = T>
217    where
218        F: FnOnce() -> T + Send + 'static,
219        T: Send + 'static;
220
221    /// Create a surface (window) on the main thread.
222    fn create_surface(&self, desc: SurfaceDesc) -> Pin<Box<dyn Future<Output = Surface> + Send>>;
223}
224
225// wasmtime trait impls
226impl<'a, S: MainThreadSpawner> surface::Host for SurfaceCtx<'a, S> {}
227
228impl<'a, S: MainThreadSpawner> surface::HostSurface for SurfaceCtx<'a, S> {
229    fn new(&mut self, desc: SurfaceDesc) -> wasmtime::Result<Resource<Surface>> {
230        let surface = futures::executor::block_on(self.main_thread_spawner.create_surface(desc));
231        Ok(self.table.push(surface)?)
232    }
233
234    fn height(&mut self, surface: Resource<Surface>) -> wasmtime::Result<u32> {
235        let surface = self.table.get(&surface)?;
236        Ok(surface.height())
237    }
238
239    fn width(&mut self, surface: Resource<Surface>) -> wasmtime::Result<u32> {
240        let surface = self.table.get(&surface)?;
241        Ok(surface.width())
242    }
243
244    fn request_set_size(
245        &mut self,
246        surface: Resource<Surface>,
247        width: Option<u32>,
248        height: Option<u32>,
249    ) -> wasmtime::Result<()> {
250        let surface = self.table.get(&surface)?;
251        surface.request_set_size(width, height);
252        Ok(())
253    }
254
255    fn drop(&mut self, surface: Resource<Surface>) -> wasmtime::Result<()> {
256        self.table.delete(surface)?;
257        Ok(())
258    }
259}
260
261impl<T, S: MainThreadSpawner> surface::HostSurfaceWithStore<T> for HasSurfaceCtx<S> {
262    fn on_pointer_down(
263        mut access: Access<T, Self>,
264        surface: Resource<surface::Surface>,
265    ) -> wasmtime::Result<StreamReader<PointerEvent>> {
266        let ctx = access.get();
267        let surface = ctx.table.get(&surface)?;
268        let receiver = surface.0.pointer_down_sender.new_receiver();
269        Ok(channel_to_stream(access, receiver))
270    }
271
272    fn on_pointer_move(
273        mut access: Access<T, Self>,
274        surface: Resource<surface::Surface>,
275    ) -> wasmtime::Result<StreamReader<PointerEvent>> {
276        let ctx = access.get();
277        let surface = ctx.table.get(&surface)?;
278        let receiver = surface.0.pointer_move_sender.new_receiver();
279        Ok(channel_to_stream(access, receiver))
280    }
281
282    fn on_pointer_up(
283        mut access: Access<T, Self>,
284        surface: Resource<surface::Surface>,
285    ) -> wasmtime::Result<StreamReader<PointerEvent>> {
286        let ctx = access.get();
287        let surface = ctx.table.get(&surface)?;
288        let receiver = surface.0.pointer_up_sender.new_receiver();
289        Ok(channel_to_stream(access, receiver))
290    }
291
292    fn on_key_up(
293        mut access: Access<T, Self>,
294        surface: Resource<surface::Surface>,
295    ) -> wasmtime::Result<StreamReader<KeyEvent>> {
296        let ctx = access.get();
297        let surface = ctx.table.get(&surface)?;
298        let receiver = surface.0.key_up_sender.new_receiver();
299        Ok(channel_to_stream(access, receiver))
300    }
301
302    fn on_key_down(
303        mut access: Access<T, Self>,
304        surface: Resource<surface::Surface>,
305    ) -> wasmtime::Result<StreamReader<KeyEvent>> {
306        let ctx = access.get();
307        let surface = ctx.table.get(&surface)?;
308        let receiver = surface.0.key_down_sender.new_receiver();
309        Ok(channel_to_stream(access, receiver))
310    }
311
312    fn on_resize(
313        mut access: Access<T, Self>,
314        surface: Resource<surface::Surface>,
315    ) -> wasmtime::Result<StreamReader<ResizeEvent>> {
316        let ctx = access.get();
317        let surface = ctx.table.get(&surface)?;
318        let receiver = surface.0.resize_sender.new_receiver();
319        Ok(channel_to_stream(access, receiver))
320    }
321
322    fn on_frame(
323        mut access: Access<T, Self>,
324        surface: Resource<surface::Surface>,
325    ) -> wasmtime::Result<StreamReader<FrameEvent>> {
326        let ctx = access.get();
327        let surface = ctx.table.get(&surface)?;
328        let receiver = surface.0.frame_sender.new_receiver();
329        Ok(channel_to_stream(access, receiver))
330    }
331}