1use std::sync::Arc;
31use winit::{
32 application::ApplicationHandler,
33 event::WindowEvent,
34 event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
35 window::{Window, WindowId},
36};
37
38pub struct NativeRenderer {
41 window: Arc<Window>,
42 gpu: Option<cvkg_render_gpu::SurtrRenderer>,
43}
44
45impl NativeRenderer {
46 fn new(window: Arc<Window>) -> Self {
48 Self { window, gpu: None }
49 }
50
51 pub fn run<V: cvkg_core::View + 'static>(view: V) {
54 let event_loop = EventLoop::new().expect("Failed to create event loop");
55 event_loop.set_control_flow(ControlFlow::Poll);
56
57 let mut app = App {
58 view,
59 renderer: None,
60 accesskit_adapter: None,
61 vdom: Some(cvkg_vdom::VDom::new()),
62 asset_manager: std::sync::Arc::new(NativeAssetManager::new()),
63 cursor_pos: [0.0, 0.0],
64 };
65
66 event_loop.run_app(&mut app).expect("Event loop error");
67 }
68}
69
70struct App<V: cvkg_core::View> {
71 view: V,
72 renderer: Option<NativeRenderer>,
73 accesskit_adapter: Option<accesskit_winit::Adapter>,
74 vdom: Option<cvkg_vdom::VDom>,
75 asset_manager: std::sync::Arc<NativeAssetManager>,
76 cursor_pos: [f32; 2],
77}
78
79impl<V: cvkg_core::View + 'static> ApplicationHandler for App<V> {
80 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
81 let window_attrs = Window::default_attributes()
82 .with_title("CVKG Forge")
83 .with_inner_size(winit::dpi::LogicalSize::new(1280.0, 720.0));
84
85 let window = Arc::new(
86 event_loop
87 .create_window(window_attrs)
88 .expect("Failed to create window"),
89 );
90
91 let adapter = accesskit_winit::Adapter::with_direct_handlers(
93 event_loop, &window, ShieldWall, ShieldWall, ShieldWall,
94 );
95 self.accesskit_adapter = Some(adapter);
96
97 let mut renderer = NativeRenderer::new(window.clone());
98
99 let rt = tokio::runtime::Runtime::new().unwrap();
101 renderer.gpu = Some(rt.block_on(cvkg_render_gpu::SurtrRenderer::forge(window.clone())));
102
103 self.renderer = Some(renderer);
104
105 cvkg_core::env::insert::<cvkg_core::AssetKey>(self.asset_manager.clone());
107 }
108
109 fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
110 let renderer = if let Some(r) = &mut self.renderer {
111 r
112 } else {
113 return;
114 };
115
116 match event {
117 WindowEvent::CloseRequested => event_loop.exit(),
118 WindowEvent::Resized(physical_size) => {
119 if let Some(gpu) = &mut renderer.gpu {
120 gpu.resize(physical_size.width, physical_size.height);
121 }
122 renderer.window.request_redraw();
123 }
124 WindowEvent::RedrawRequested => {
125 if let Some(gpu) = &mut renderer.gpu {
126 let encoder = gpu.begin_frame();
127
128 let size = renderer.window.inner_size();
129 let rect = cvkg_core::Rect {
130 x: 0.0,
131 y: 0.0,
132 width: size.width as f32,
133 height: size.height as f32,
134 };
135
136 self.view.render(gpu, rect);
138
139 let new_vdom = cvkg_vdom::VDom::build(&self.view, rect);
141 if let (Some(prev_vdom), Some(adapter)) =
142 (&mut self.vdom, &mut self.accesskit_adapter)
143 {
144 let patches = new_vdom.diff(prev_vdom);
145
146 let mut nodes = Vec::new();
148 for patch in patches {
149 match patch {
150 cvkg_vdom::VDomPatch::Create(node)
151 | cvkg_vdom::VDomPatch::Replace { node, .. } => {
152 nodes.push((
153 accesskit::NodeId(node.id.0 as u64),
154 node.to_accesskit_node(),
155 ));
156 }
157 _ => {}
158 }
159 }
160
161 if !nodes.is_empty() {
162 adapter.update_if_active(|| accesskit::TreeUpdate {
163 nodes,
164 tree: None,
165 focus: accesskit::NodeId(1),
166 });
167 }
168 }
169 self.vdom = Some(new_vdom);
170
171 gpu.end_frame(encoder);
172 }
173 }
174 WindowEvent::CursorMoved { position, .. } => {
175 let scale = renderer.window.scale_factor();
176 let logical = position.to_logical::<f32>(scale);
177 self.cursor_pos = [logical.x, logical.y];
178 if let Some(vdom) = &self.vdom {
179 vdom.dispatch_event(cvkg_core::Event::PointerMove {
180 x: self.cursor_pos[0],
181 y: self.cursor_pos[1],
182 });
183 }
184 }
185 WindowEvent::MouseInput { state, .. } => {
186 if let Some(vdom) = &self.vdom {
187 let event = match state {
188 winit::event::ElementState::Pressed => cvkg_core::Event::PointerDown {
189 x: self.cursor_pos[0],
190 y: self.cursor_pos[1],
191 },
192 winit::event::ElementState::Released => cvkg_core::Event::PointerUp {
193 x: self.cursor_pos[0],
194 y: self.cursor_pos[1],
195 },
196 };
197 vdom.dispatch_event(event);
198 }
199 }
200 WindowEvent::KeyboardInput { event, .. } => {
201 if let Some(vdom) = &self.vdom {
202 if let winit::keyboard::PhysicalKey::Code(code) = event.physical_key {
203 let key_str = format!("{:?}", code);
204 let cvkg_event = if event.state == winit::event::ElementState::Pressed {
205 cvkg_core::Event::KeyDown { key: key_str }
206 } else {
207 cvkg_core::Event::KeyUp { key: key_str }
208 };
209 vdom.dispatch_event(cvkg_event);
210 }
211
212 if event.state == winit::event::ElementState::Pressed {
214 if let Some(text) = event.text {
215 for c in text.chars() {
216 vdom.dispatch_event(cvkg_core::Event::KeyDown {
217 key: c.to_string(),
218 });
219 }
220 }
221 }
222 }
223 }
224 _ => (),
225 }
226 }
227
228 fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
229 if let Some(renderer) = &self.renderer {
230 renderer.window.request_redraw();
231 }
232 }
233}
234
235impl cvkg_core::Renderer for NativeRenderer {
236 fn fill_rect(&mut self, rect: cvkg_core::Rect, color: [f32; 4]) {
237 if let Some(gpu) = &mut self.gpu {
238 gpu.fill_rect(rect, color);
239 }
240 }
241 fn fill_rounded_rect(&mut self, rect: cvkg_core::Rect, radius: f32, color: [f32; 4]) {
242 if let Some(gpu) = &mut self.gpu {
243 gpu.fill_rounded_rect(rect, radius, color);
244 }
245 }
246 fn fill_ellipse(&mut self, rect: cvkg_core::Rect, color: [f32; 4]) {
247 if let Some(gpu) = &mut self.gpu {
248 gpu.fill_ellipse(rect, color);
249 }
250 }
251 fn stroke_rect(&mut self, rect: cvkg_core::Rect, color: [f32; 4], stroke_width: f32) {
252 if let Some(gpu) = &mut self.gpu {
253 gpu.stroke_rect(rect, color, stroke_width);
254 }
255 }
256 fn stroke_rounded_rect(
257 &mut self,
258 rect: cvkg_core::Rect,
259 radius: f32,
260 color: [f32; 4],
261 stroke_width: f32,
262 ) {
263 if let Some(gpu) = &mut self.gpu {
264 gpu.stroke_rounded_rect(rect, radius, color, stroke_width);
265 }
266 }
267 fn stroke_ellipse(&mut self, rect: cvkg_core::Rect, color: [f32; 4], stroke_width: f32) {
268 if let Some(gpu) = &mut self.gpu {
269 gpu.stroke_ellipse(rect, color, stroke_width);
270 }
271 }
272 fn draw_line(
273 &mut self,
274 x1: f32,
275 y1: f32,
276 x2: f32,
277 y2: f32,
278 color: [f32; 4],
279 stroke_width: f32,
280 ) {
281 if let Some(gpu) = &mut self.gpu {
282 gpu.draw_line(x1, y1, x2, y2, color, stroke_width);
283 }
284 }
285 fn draw_text(&mut self, text: &str, x: f32, y: f32, size: f32, color: [f32; 4]) {
286 if let Some(gpu) = &mut self.gpu {
287 gpu.draw_text(text, x, y, size, color);
288 }
289 }
290 fn measure_text(&mut self, text: &str, size: f32) -> (f32, f32) {
291 if let Some(gpu) = &mut self.gpu {
292 gpu.measure_text(text, size)
293 } else {
294 (0.0, 0.0)
295 }
296 }
297 fn draw_texture(&mut self, texture_id: u32, rect: cvkg_core::Rect) {
298 if let Some(gpu) = &mut self.gpu {
299 gpu.draw_texture(texture_id, rect);
300 }
301 }
302 fn draw_image(&mut self, image_name: &str, rect: cvkg_core::Rect) {
303 if let Some(gpu) = &mut self.gpu {
304 gpu.draw_image(image_name, rect);
305 }
306 }
307 fn load_image(&mut self, name: &str, data: &[u8]) {
308 if let Some(gpu) = &mut self.gpu {
309 gpu.load_image(name, data);
310 }
311 }
312 fn push_clip_rect(&mut self, rect: cvkg_core::Rect) {
313 if let Some(gpu) = &mut self.gpu {
314 gpu.push_clip_rect(rect);
315 }
316 }
317 fn pop_clip_rect(&mut self) {
318 if let Some(gpu) = &mut self.gpu {
319 gpu.pop_clip_rect();
320 }
321 }
322 fn push_opacity(&mut self, opacity: f32) {
323 if let Some(gpu) = &mut self.gpu {
324 gpu.push_opacity(opacity);
325 }
326 }
327 fn pop_opacity(&mut self) {
328 if let Some(gpu) = &mut self.gpu {
329 gpu.pop_opacity();
330 }
331 }
332 fn bifrost(&mut self, rect: cvkg_core::Rect, blur: f32, saturation: f32, opacity: f32) {
333 if let Some(gpu) = &mut self.gpu {
334 gpu.bifrost(rect, blur, saturation, opacity);
335 }
336 }
337 fn push_mjolnir_slice(&mut self, angle: f32, offset: f32) {
338 if let Some(gpu) = &mut self.gpu {
339 gpu.push_mjolnir_slice(angle, offset);
340 }
341 }
342 fn pop_mjolnir_slice(&mut self) {
343 if let Some(gpu) = &mut self.gpu {
344 gpu.pop_mjolnir_slice();
345 }
346 }
347 fn register_shared_element(&mut self, id: &str, rect: cvkg_core::Rect) {
348 if let Some(gpu) = &mut self.gpu {
349 gpu.register_shared_element(id, rect);
350 }
351 }
352}
353
354
355
356struct ShieldWall;
359
360impl accesskit::ActionHandler for ShieldWall {
361 fn do_action(&mut self, _request: accesskit::ActionRequest) {}
362}
363
364impl accesskit::ActivationHandler for ShieldWall {
365 fn request_initial_tree(&mut self) -> Option<accesskit::TreeUpdate> {
366 let mut root = accesskit::Node::new(accesskit::Role::Window);
367 root.set_label("CVKG Application");
368
369 let root_id = accesskit::NodeId(1);
370 Some(accesskit::TreeUpdate {
371 nodes: vec![(root_id, root)],
372 tree: Some(accesskit::Tree::new(root_id)),
373 focus: root_id,
374 })
375 }
376}
377
378impl accesskit::DeactivationHandler for ShieldWall {
379 fn deactivate_accessibility(&mut self) {}
380}
381
382pub struct NativeAssetManager {
384 cache: std::sync::Arc<
385 std::sync::RwLock<
386 std::collections::HashMap<String, cvkg_core::AssetState<std::sync::Arc<Vec<u8>>>>,
387 >,
388 >,
389}
390
391impl NativeAssetManager {
392 pub fn new() -> Self {
393 Self {
394 cache: std::sync::Arc::new(std::sync::RwLock::new(std::collections::HashMap::new())),
395 }
396 }
397}
398
399impl cvkg_core::AssetManager for NativeAssetManager {
400 fn load_image(&self, url: &str) -> cvkg_core::AssetState<std::sync::Arc<Vec<u8>>> {
401 {
402 let cache = self.cache.read().unwrap();
403 if let Some(state) = cache.get(url) {
404 return state.clone();
405 }
406 }
407
408 match std::fs::read(url) {
410 Ok(data) => {
411 let state = cvkg_core::AssetState::Ready(std::sync::Arc::new(data));
412 let mut cache = self.cache.write().unwrap();
413 cache.insert(url.to_string(), state.clone());
414 state
415 }
416 Err(e) => {
417 let state = cvkg_core::AssetState::Error(e.to_string());
418 let mut cache = self.cache.write().unwrap();
419 cache.insert(url.to_string(), state.clone());
420 state
421 }
422 }
423 }
424
425 fn preload_image(&self, _url: &str) {
426 }
428}