1use winit::{
2 event::{Event, WindowEvent},
3 event_loop::{ControlFlow, EventLoop},
4 window::{Window, WindowBuilder},
5};
6
7use crate::prelude::*;
8use std::sync::{Arc, Mutex};
9use std::task::Wake;
10use winit::event_loop::EventLoopProxy;
11
12pub struct Sandbox<M: 'static + Component> {
14 pub ui: crate::backend::wgpu::Ui<M>,
16 event_loop: Option<EventLoop<PollUi>>,
17 surface: wgpu::Surface,
18 #[allow(unused)]
19 adapter: wgpu::Adapter,
20 device: wgpu::Device,
21 queue: wgpu::Queue,
22 surface_config: wgpu::SurfaceConfiguration,
23 window: Window,
24}
25
26#[derive(Clone)]
27struct PollUi;
28
29struct Waker<T: 'static> {
30 message: T,
31 event_loop: Mutex<EventLoopProxy<T>>,
32}
33
34impl<T: 'static + Clone> Wake for Waker<T> {
35 fn wake(self: Arc<Self>) {
36 self.event_loop.lock().unwrap().send_event(self.message.clone()).ok();
37 }
38}
39
40impl<T> Sandbox<T>
41where
42 T: 'static + Component,
43{
44 pub async fn new<S, E>(root_component: T, style: S, window: WindowBuilder) -> anyhow::Result<Self>
48 where
49 S: TryInto<Style, Error = E>,
50 anyhow::Error: From<E>,
51 {
52 let event_loop = EventLoop::with_user_event();
53 let window = window.build(&event_loop).unwrap();
54 let size = window.inner_size();
55
56 let swapchain_format = wgpu::TextureFormat::Bgra8Unorm;
57
58 let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY);
59 let surface = unsafe { instance.create_surface(&window) };
60 let adapter = instance
61 .request_adapter(&wgpu::RequestAdapterOptions {
62 power_preference: wgpu::PowerPreference::LowPower,
63 force_fallback_adapter: false,
64 compatible_surface: Some(&surface),
65 })
66 .await
67 .expect("Failed to find an appropriate adapter");
68
69 let trace_dir = std::env::var("WGPU_TRACE");
71 let (device, queue) = adapter
72 .request_device(
73 &wgpu::DeviceDescriptor {
74 label: None,
75 features: Default::default(),
76 limits: wgpu::Limits::default(),
77 },
78 trace_dir.ok().as_ref().map(std::path::Path::new),
79 )
80 .await
81 .expect("Failed retrieve device and queue");
82
83 let surface_config = wgpu::SurfaceConfiguration {
84 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
85 format: swapchain_format,
86 width: size.width,
87 height: size.height,
88 present_mode: wgpu::PresentMode::Mailbox,
89 };
90
91 surface.configure(&device, &surface_config);
92
93 let ui = crate::backend::wgpu::Ui::new(
94 root_component,
95 Rectangle::from_wh(size.width as f32, size.height as f32),
96 window.scale_factor() as f32,
97 style,
98 swapchain_format,
99 &device,
100 )?;
101
102 Ok(Sandbox {
103 ui,
104 event_loop: Some(event_loop),
105 surface,
106 adapter,
107 device,
108 queue,
109 surface_config,
110 window,
111 })
112 }
113
114 pub fn update(&mut self, message: T::Message) -> Vec<T::Output> {
117 let waker = self
118 .event_loop
119 .as_ref()
120 .map(|event_loop| {
121 std::task::Waker::from(Arc::new(Waker {
122 message: PollUi,
123 event_loop: Mutex::new(event_loop.create_proxy()),
124 }))
125 })
126 .unwrap();
127
128 self.ui.update_and_poll(message, waker)
129 }
130
131 pub async fn run(mut self) {
133 let event_loop = self.event_loop.take().unwrap();
134 let waker = std::task::Waker::from(Arc::new(Waker {
135 message: PollUi,
136 event_loop: Mutex::new(event_loop.create_proxy()),
137 }));
138
139 event_loop.run(move |event, _, control_flow| {
140 *control_flow = ControlFlow::Wait;
141 match event {
142 Event::UserEvent(_) => {
143 self.ui.poll(waker.clone());
144 }
145 Event::WindowEvent {
146 event: WindowEvent::Resized(size),
147 ..
148 } => {
149 self.surface_config.width = size.width;
151 self.surface_config.height = size.height;
152 self.surface.configure(&self.device, &self.surface_config);
153 self.ui
154 .resize(Rectangle::from_wh(size.width as f32, size.height as f32));
155 }
156 Event::RedrawRequested(_) => {
157 let frame = self
158 .surface
159 .get_current_texture()
160 .expect("Failed to acquire next swap chain texture");
161 let view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default());
162 let mut encoder = self
163 .device
164 .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
165 {
166 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
167 label: None,
168 color_attachments: &[wgpu::RenderPassColorAttachment {
169 view: &view,
170 resolve_target: None,
171 ops: wgpu::Operations {
172 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
173 store: true,
174 },
175 }],
176 depth_stencil_attachment: None,
177 });
178
179 self.ui.draw(&self.device, &self.queue, &mut pass);
180 }
181
182 self.queue.submit(Some(encoder.finish()));
183 frame.present();
184 }
185 Event::WindowEvent {
186 event: WindowEvent::CloseRequested,
187 ..
188 } => *control_flow = ControlFlow::Exit,
189 other => {
190 if let Some(event) = crate::backend::winit::convert_event(other) {
191 self.ui.handle_event_and_poll(event, waker.clone());
192 }
193 }
194 }
195
196 if self.ui.needs_redraw() {
197 self.window.request_redraw();
198 }
199 });
200 }
201}