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