From 751037d44f79b1dfff3fec115b488900a2ae02c2 Mon Sep 17 00:00:00 2001 From: darkfi Date: Thu, 20 Jun 2024 11:27:45 +0200 Subject: [PATCH] wallet: migrate widgets to src/ui/ --- bin/darkwallet/src/app.rs | 510 +-------------------------------- bin/darkwallet/src/chatapp.rs | 2 +- bin/darkwallet/src/editbox.rs | 4 +- bin/darkwallet/src/gfx2.rs | 5 +- bin/darkwallet/src/main.rs | 5 + bin/darkwallet/src/scene.rs | 7 +- bin/darkwallet/src/ui/layer.rs | 163 +++++++++++ bin/darkwallet/src/ui/mesh.rs | 123 ++++++++ bin/darkwallet/src/ui/mod.rs | 190 ++++++++++++ bin/darkwallet/src/ui/win.rs | 147 ++++++++++ 10 files changed, 643 insertions(+), 513 deletions(-) create mode 100644 bin/darkwallet/src/ui/layer.rs create mode 100644 bin/darkwallet/src/ui/mesh.rs create mode 100644 bin/darkwallet/src/ui/mod.rs create mode 100644 bin/darkwallet/src/ui/win.rs diff --git a/bin/darkwallet/src/app.rs b/bin/darkwallet/src/app.rs index 58a82620a..9c14e158e 100644 --- a/bin/darkwallet/src/app.rs +++ b/bin/darkwallet/src/app.rs @@ -12,7 +12,9 @@ use crate::{ error::{Error, Result}, expr::{Op, SExprMachine, SExprVal}, gfx::Rectangle, - gfx2::{self, DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, RenderApiPtr, Vertex}, + gfx2::{ + self, DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, RenderApiPtr, Vertex, + }, prop::{ Property, PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyStr, PropertySubType, PropertyType, PropertyUint32, @@ -22,12 +24,12 @@ use crate::{ MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNode, SceneNodeId, SceneNodeInfo, SceneNodeType, }, + ui::{ + eval_rect, get_parent_rect, read_rect, DrawUpdate, Mesh, OnModify, RenderLayer, + RenderLayerPtr, Stoppable, Window, WindowPtr, + }, }; -trait Stoppable { - async fn stop(&self); -} - pub type AsyncRuntimePtr = Arc; pub struct AsyncRuntime { @@ -299,501 +301,3 @@ impl App { fn print_type_of(_: &T) { println!("{}", std::any::type_name::()) } - -struct OnModify { - ex: Arc>, - node_name: String, - node_id: SceneNodeId, - me: Weak, - tasks: Vec>, -} - -impl OnModify { - fn new( - ex: Arc>, - node_name: String, - node_id: SceneNodeId, - me: Weak, - ) -> Self { - Self { ex, node_name, node_id, me, tasks: vec![] } - } - - fn when_change(&mut self, prop: PropertyPtr, f: impl Fn(Arc) -> F + Send + 'static) - where - F: std::future::Future + Send + 'static, - { - let node_name = self.node_name.clone(); - let node_id = self.node_id; - let on_modify_sub = prop.subscribe_modify(); - let prop_name = prop.name.clone(); - let me = self.me.clone(); - let task = self.ex.spawn(async move { - loop { - let _ = on_modify_sub.receive().await; - debug!(target: "app", "Property '{}':{}/'{}' modified", node_name, node_id, prop_name); - - let Some(self_) = me.upgrade() else { - // Should not happen - panic!( - "'{}':{}/'{}' self destroyed before modify_task was stopped!", - node_name, node_id, prop_name - ); - }; - - debug!(target: "app", "property modified"); - f(self_).await; - } - }); - self.tasks.push(task); - } -} - -fn eval_rect(rect: PropertyPtr, parent_rect: &Rectangle) -> Result<()> { - if rect.array_len != 4 { - return Err(Error::PropertyWrongLen) - } - - for i in 0..4 { - if !rect.is_expr(i)? { - continue - } - - let expr = rect.get_expr(i).unwrap(); - - let machine = SExprMachine { - globals: vec![ - ("w".to_string(), SExprVal::Float32(parent_rect.w)), - ("h".to_string(), SExprVal::Float32(parent_rect.h)), - ], - stmts: &expr, - }; - - let v = machine.call()?.as_f32()?; - rect.set_cache_f32(i, v).unwrap(); - } - Ok(()) -} - -fn read_rect(rect_prop: PropertyPtr) -> Result> { - if rect_prop.array_len != 4 { - return Err(Error::PropertyWrongLen) - } - - let mut rect = [0.; 4]; - for i in 0..4 { - if rect_prop.is_expr(i)? { - rect[i] = rect_prop.get_cached(i)?.as_f32()?; - } else { - rect[i] = rect_prop.get_f32(i)?; - } - } - Ok(Rectangle::from_array(rect)) -} - -fn get_parent_rect(sg: &SceneGraph, node: &SceneNode) -> Option> { - // read our parent - if node.parents.is_empty() { - info!("RenderLayer {:?} has no parents so skipping", node); - return None - } - if node.parents.len() != 1 { - error!("RenderLayer {:?} has too many parents so skipping", node); - return None - } - let parent_id = node.parents[0].id; - let parent_node = sg.get_node(parent_id).unwrap(); - let parent_rect = match parent_node.typ { - SceneNodeType::Window => { - let Some(screen_size_prop) = parent_node.get_property("screen_size") else { - error!( - "RenderLayer {:?} parent node {:?} missing screen_size property", - node, parent_node - ); - return None - }; - let screen_width = screen_size_prop.get_f32(0).unwrap(); - let screen_height = screen_size_prop.get_f32(1).unwrap(); - - let parent_rect = Rectangle { x: 0., y: 0., w: screen_width, h: screen_height }; - parent_rect - } - SceneNodeType::RenderLayer => { - // get their rect property - let Some(parent_rect) = parent_node.get_property("rect") else { - error!( - "RenderLayer {:?} parent node {:?} missing rect property", - node, parent_node - ); - return None - }; - // read parent's rect - let Ok(parent_rect) = read_rect(parent_rect) else { - error!( - "RenderLayer {:?} parent node {:?} malformed rect property", - node, parent_node - ); - return None - }; - parent_rect - } - _ => { - error!( - "RenderLayer {:?} parent node {:?} wrong type {:?}", - node, parent_node, parent_node.typ - ); - return None - } - }; - Some(parent_rect) -} - -struct DrawUpdate { - key: u64, - draw_calls: Vec<(u64, DrawCall)>, -} - -pub type WindowPtr = Arc; - -pub struct Window { - node_id: SceneNodeId, - tasks: Vec>, - screen_size_prop: PropertyPtr, - render_api: RenderApiPtr, -} - -impl Window { - pub async fn new( - sg: SceneGraphPtr2, - node_id: SceneNodeId, - ex: Arc>, - render_api: RenderApiPtr, - event_pub: GraphicsEventPublisherPtr, - ) -> Pimpl { - debug!(target: "app", "Window::new()"); - - let scene_graph = sg.lock().await; - let node = scene_graph.get_node(node_id).unwrap(); - let node_name = node.name.clone(); - let screen_size_prop = node.get_property("screen_size").unwrap(); - let scale_prop = node.get_property("scale").unwrap(); - drop(scene_graph); - - let self_ = Arc::new_cyclic(|me: &Weak| { - // Start a task monitoring for window resize events - // which updates screen_size - let ev_sub = event_pub.subscribe_resize(); - let screen_size_prop2 = screen_size_prop.clone(); - let me2 = me.clone(); - let sg2 = sg.clone(); - let resize_task = ex.spawn(async move { - loop { - let Ok((w, h)) = ev_sub.receive().await else { - debug!(target: "app", "Event relayer closed"); - break - }; - - debug!(target: "app", "Window resized ({w}, {h})"); - // Now update the properties - screen_size_prop2.set_f32(0, w); - screen_size_prop2.set_f32(1, h); - - let Some(self_) = me2.upgrade() else { - // Should not happen - panic!("self destroyed before modify_task was stopped!"); - }; - - let sg = sg2.lock().await; - self_.draw(&sg).await; - } - }); - - let sg2 = sg.clone(); - let redraw_fn = move |self_: Arc| { - let sg = sg2.clone(); - async move { - let sg = sg.lock().await; - self_.draw(&sg).await; - } - }; - - let mut on_modify = OnModify::new(ex.clone(), node_name, node_id, me.clone()); - on_modify.when_change(scale_prop, redraw_fn); - - let mut tasks = on_modify.tasks; - tasks.push(resize_task); - - Self { node_id, tasks, screen_size_prop, render_api } - }); - - Pimpl::Window(self_) - } - - async fn draw(&self, sg: &SceneGraph) { - debug!(target: "app", "Window::draw()"); - // SceneGraph should remain locked for the entire draw - let self_node = sg.get_node(self.node_id).unwrap(); - - let screen_width = self.screen_size_prop.get_f32(0).unwrap(); - let screen_height = self.screen_size_prop.get_f32(1).unwrap(); - - let parent_rect = Rectangle { x: 0., y: 0., w: screen_width, h: screen_height }; - - let mut draw_calls = vec![]; - let mut child_calls = vec![]; - for child_inf in self_node.get_children2() { - let node = sg.get_node(child_inf.id).unwrap(); - debug!(target: "app", "Window::draw() calling draw() for node '{}':{}", node.name, node.id); - - let dcs = match &node.pimpl { - Pimpl::RenderLayer(layer) => layer.draw(sg, &parent_rect).await, - _ => { - error!(target: "app", "unhandled pimpl type"); - continue - } - }; - let Some(mut draw_update) = dcs else { continue }; - draw_calls.append(&mut draw_update.draw_calls); - child_calls.push(draw_update.key); - } - - let root_dc = DrawCall { instrs: vec![], dcs: child_calls }; - draw_calls.push((0, root_dc)); - //debug!(" => {:?}", draw_calls); - - self.render_api.replace_draw_calls(draw_calls).await; - debug!("Window::draw() - replaced draw call"); - } -} - -// Nodes should be stopped before being removed -impl Stoppable for Window { - async fn stop(&self) {} -} - -pub type RenderLayerPtr = Arc; - -pub struct RenderLayer { - sg: SceneGraphPtr2, - node_id: SceneNodeId, - tasks: Vec>, - render_api: RenderApiPtr, - - dc_key: u64, - - is_visible: PropertyBool, - rect: PropertyPtr, - - parent_rect: Mutex>, -} - -impl RenderLayer { - pub async fn new( - sg_ptr: SceneGraphPtr2, - node_id: SceneNodeId, - ex: Arc>, - render_api: RenderApiPtr, - ) -> Pimpl { - let sg = sg_ptr.lock().await; - let node = sg.get_node(node_id).unwrap(); - let node_name = node.name.clone(); - - let is_visible = - PropertyBool::wrap(node, "is_visible", 0).expect("RenderLayer::is_visible"); - let rect = node.get_property("rect").expect("RenderLayer::rect"); - drop(sg); - - // Monitor for changes to screen_size or scale properties - // If so then trigger draw - let rect_sub = rect.subscribe_modify(); - - let self_ = Arc::new_cyclic(|me: &Weak| { - let mut on_modify = OnModify::new(ex.clone(), node_name, node_id, me.clone()); - on_modify.when_change(rect.clone(), Self::redraw); - - Self { - sg: sg_ptr, - node_id, - tasks: on_modify.tasks, - render_api, - dc_key: OsRng.gen(), - is_visible, - rect, - parent_rect: Mutex::new(Rectangle { x: 0., y: 0., w: 0., h: 0. }), - } - }); - - Pimpl::RenderLayer(self_) - } - - async fn redraw(self: Arc) { - let sg = self.sg.lock().await; - let node = sg.get_node(self.node_id).unwrap(); - - let Some(parent_rect) = get_parent_rect(&sg, node) else { - return; - }; - - let Some(draw_update) = self.draw(&sg, &parent_rect).await else { - error!("RenderLayer {:?} failed to draw", node); - return; - }; - self.render_api.replace_draw_calls(draw_update.draw_calls).await; - debug!("replace draw calls done"); - } - - #[async_recursion] - pub async fn draw(&self, sg: &SceneGraph, parent_rect: &Rectangle) -> Option { - debug!(target: "app", "RenderLayer::draw()"); - let node = sg.get_node(self.node_id).unwrap(); - - if !self.is_visible.get() { - debug!(target: "app", "invisible layer node '{}':{}", node.name, node.id); - return None - } - - if let Err(err) = eval_rect(self.rect.clone(), parent_rect) { - panic!("Node {:?} bad rect property: {}", node, err); - } - - let Ok(mut rect) = read_rect(self.rect.clone()) else { - panic!("Node {:?} bad rect property", node); - }; - - rect.x += parent_rect.x; - rect.y += parent_rect.x; - - if !parent_rect.includes(&rect) { - error!( - target: "app", - "layer '{}':{} rect {:?} is not inside parent {:?}", - node.name, node.id, rect, parent_rect - ); - return None - } - - debug!(target: "app", "Parent rect: {:?}", parent_rect); - debug!(target: "app", "Viewport rect: {:?}", rect); - - // Apply viewport - - let mut draw_calls = vec![]; - let mut child_calls = vec![]; - for child_inf in node.get_children2() { - let node = sg.get_node(child_inf.id).unwrap(); - - let dcs = match &node.pimpl { - Pimpl::RenderLayer(layer) => layer.draw(&sg, &rect).await, - Pimpl::Mesh(mesh) => mesh.draw(&sg, &rect), - _ => { - error!(target: "app", "unhandled pimpl type"); - continue - } - }; - let Some(mut draw_update) = dcs else { continue }; - draw_calls.append(&mut draw_update.draw_calls); - child_calls.push(draw_update.key); - } - - let dc = DrawCall { instrs: vec![DrawInstruction::ApplyViewport(rect)], dcs: child_calls }; - draw_calls.push((self.dc_key, dc)); - Some(DrawUpdate { key: self.dc_key, draw_calls }) - } -} - -impl Stoppable for RenderLayer { - async fn stop(&self) {} -} - -pub struct Mesh { - render_api: RenderApiPtr, - vertex_buffer: miniquad::BufferId, - index_buffer: miniquad::BufferId, - // Texture - num_elements: i32, - - dc_key: u64, - - node_id: SceneNodeId, - rect: PropertyPtr, -} - -impl Mesh { - pub async fn new( - sg: SceneGraphPtr2, - node_id: SceneNodeId, - render_api: RenderApiPtr, - verts: Vec, - indices: Vec, - ) -> Pimpl { - let num_elements = indices.len() as i32; - let vertex_buffer = render_api.new_vertex_buffer(verts).await.unwrap(); - let index_buffer = render_api.new_index_buffer(indices).await.unwrap(); - - let mut sg = sg.lock().await; - let node = sg.get_node_mut(node_id).unwrap(); - let rect = node.get_property("rect").expect("RenderLayer::rect"); - - Pimpl::Mesh(Self { - render_api, - vertex_buffer, - index_buffer, - num_elements, - dc_key: OsRng.gen(), - node_id, - rect, - }) - } - - pub fn draw(&self, sg: &SceneGraph, parent_rect: &Rectangle) -> Option { - debug!(target: "app", "Mesh::draw()"); - // Only used for debug messages - let node = sg.get_node(self.node_id).unwrap(); - - let mesh = DrawMesh { - vertex_buffer: self.vertex_buffer, - index_buffer: self.index_buffer, - texture: None, - num_elements: self.num_elements, - }; - - if let Err(err) = eval_rect(self.rect.clone(), parent_rect) { - panic!("Node {:?} bad rect property: {}", node, err); - } - - let Ok(mut rect) = read_rect(self.rect.clone()) else { - panic!("Node {:?} bad rect property", node); - }; - - rect.x += parent_rect.x; - rect.y += parent_rect.x; - - let off_x = rect.x / parent_rect.w; - let off_y = rect.y / parent_rect.h; - let scale_x = rect.w / parent_rect.w; - let scale_y = rect.h / parent_rect.h; - let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) * - glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.)); - - Some(DrawUpdate { - key: self.dc_key, - draw_calls: vec![( - self.dc_key, - DrawCall { - instrs: vec![DrawInstruction::ApplyMatrix(model), DrawInstruction::Draw(mesh)], - dcs: vec![], - }, - )], - }) - } -} - -impl Stoppable for Mesh { - async fn stop(&self) { - // TODO: Delete own draw call - - // Free buffers - // Should this be in drop? - self.render_api.delete_buffer(self.vertex_buffer); - self.render_api.delete_buffer(self.index_buffer); - } -} diff --git a/bin/darkwallet/src/chatapp.rs b/bin/darkwallet/src/chatapp.rs index 91f48ad14..de5393464 100644 --- a/bin/darkwallet/src/chatapp.rs +++ b/bin/darkwallet/src/chatapp.rs @@ -232,7 +232,7 @@ fn create_chatview(sg: &mut SceneGraph, name: &str, layer_node_id: SceneNodeId) let mut arg_data = vec![]; node_id.encode(&mut arg_data).unwrap(); //let (tx, rx) = mpsc::sync_channel::>>(0); - let response_fn = Box::new(move |result| { + let response_fn = Box::new(move |_result| { //tx.send(result).unwrap(); }); let win_node = sg.lookup_node_mut("/window").unwrap(); diff --git a/bin/darkwallet/src/editbox.rs b/bin/darkwallet/src/editbox.rs index 9087f3e09..74b3e34c4 100644 --- a/bin/darkwallet/src/editbox.rs +++ b/bin/darkwallet/src/editbox.rs @@ -590,7 +590,7 @@ impl EditBox { "Delete" => { let cursor_pos = self.cursor_pos.get(); - let text = if !self.selected.is_null(0).unwrap() { + let _text = if !self.selected.is_null(0).unwrap() { self.delete_highlighted(); } else { let glyphs = &*self.glyphs.lock().unwrap(); @@ -618,7 +618,7 @@ impl EditBox { "Backspace" => { let cursor_pos = self.cursor_pos.get(); - let text = if !self.selected.is_null(0).unwrap() { + let _text = if !self.selected.is_null(0).unwrap() { self.delete_highlighted(); } else { if cursor_pos == 0 { diff --git a/bin/darkwallet/src/gfx2.rs b/bin/darkwallet/src/gfx2.rs index 657699c5a..f0a7bfc51 100644 --- a/bin/darkwallet/src/gfx2.rs +++ b/bin/darkwallet/src/gfx2.rs @@ -230,10 +230,7 @@ pub struct GraphicsEventPublisher { impl GraphicsEventPublisher { pub fn new() -> Arc { - Arc::new(Self { - key_down: Publisher::new(), - resize: Publisher::new(), - }) + Arc::new(Self { key_down: Publisher::new(), resize: Publisher::new() }) } fn notify_key_down(&self, key: KeyCode, mods: KeyMods, repeat: bool) { diff --git a/bin/darkwallet/src/main.rs b/bin/darkwallet/src/main.rs index b9a8b88de..7a5df1b83 100644 --- a/bin/darkwallet/src/main.rs +++ b/bin/darkwallet/src/main.rs @@ -1,6 +1,10 @@ #![feature(deadline_api)] #![feature(str_split_whitespace_remainder)] +// Use these to incrementally fix warnings with cargo fix +//#![allow(warnings, unused)] +//#![deny(unused_imports)] + use async_lock::Mutex; use futures::{stream::FuturesUnordered, StreamExt}; use std::{ @@ -31,6 +35,7 @@ mod res; mod scene; mod shader; mod text; +mod ui; use crate::{ error::{Error, Result}, diff --git a/bin/darkwallet/src/scene.rs b/bin/darkwallet/src/scene.rs index 33de0089b..5b8920154 100644 --- a/bin/darkwallet/src/scene.rs +++ b/bin/darkwallet/src/scene.rs @@ -16,6 +16,7 @@ use crate::{ app, chatview, editbox, error::{Error, Result}, prop::{Property, PropertyPtr, PropertyType}, + ui, }; pub type SceneNodeId = u32; @@ -637,9 +638,9 @@ pub enum Pimpl { Null, EditBox(editbox::EditBoxPtr), ChatView(chatview::ChatViewPtr), - Window(app::WindowPtr), - RenderLayer(app::RenderLayerPtr), - Mesh(app::Mesh), + Window(ui::WindowPtr), + RenderLayer(ui::RenderLayerPtr), + Mesh(ui::Mesh), } impl std::fmt::Debug for SceneNode { diff --git a/bin/darkwallet/src/ui/layer.rs b/bin/darkwallet/src/ui/layer.rs new file mode 100644 index 000000000..def3be941 --- /dev/null +++ b/bin/darkwallet/src/ui/layer.rs @@ -0,0 +1,163 @@ +use async_lock::Mutex; +use async_recursion::async_recursion; +use futures::{stream::FuturesUnordered, StreamExt}; +use rand::{rngs::OsRng, Rng}; +use std::{ + sync::{mpsc, Arc, Weak}, + thread, +}; + +use crate::{ + chatapp, + error::{Error, Result}, + expr::{Op, SExprMachine, SExprVal}, + gfx::Rectangle, + gfx2::{ + self, DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, RenderApiPtr, Vertex, + }, + prop::{ + Property, PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyStr, + PropertySubType, PropertyType, PropertyUint32, + }, + pubsub::PublisherPtr, + scene::{ + MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNode, SceneNodeId, SceneNodeInfo, + SceneNodeType, + }, +}; + +use super::{eval_rect, get_parent_rect, read_rect, DrawUpdate, OnModify, Stoppable}; + +pub type RenderLayerPtr = Arc; + +pub struct RenderLayer { + sg: SceneGraphPtr2, + node_id: SceneNodeId, + tasks: Vec>, + render_api: RenderApiPtr, + + dc_key: u64, + + is_visible: PropertyBool, + rect: PropertyPtr, + + parent_rect: Mutex>, +} + +impl RenderLayer { + pub async fn new( + sg_ptr: SceneGraphPtr2, + node_id: SceneNodeId, + ex: Arc>, + render_api: RenderApiPtr, + ) -> Pimpl { + let sg = sg_ptr.lock().await; + let node = sg.get_node(node_id).unwrap(); + let node_name = node.name.clone(); + + let is_visible = + PropertyBool::wrap(node, "is_visible", 0).expect("RenderLayer::is_visible"); + let rect = node.get_property("rect").expect("RenderLayer::rect"); + drop(sg); + + // Monitor for changes to screen_size or scale properties + // If so then trigger draw + let rect_sub = rect.subscribe_modify(); + + let self_ = Arc::new_cyclic(|me: &Weak| { + let mut on_modify = OnModify::new(ex.clone(), node_name, node_id, me.clone()); + on_modify.when_change(rect.clone(), Self::redraw); + + Self { + sg: sg_ptr, + node_id, + tasks: on_modify.tasks, + render_api, + dc_key: OsRng.gen(), + is_visible, + rect, + parent_rect: Mutex::new(Rectangle { x: 0., y: 0., w: 0., h: 0. }), + } + }); + + Pimpl::RenderLayer(self_) + } + + async fn redraw(self: Arc) { + let sg = self.sg.lock().await; + let node = sg.get_node(self.node_id).unwrap(); + + let Some(parent_rect) = get_parent_rect(&sg, node) else { + return; + }; + + let Some(draw_update) = self.draw(&sg, &parent_rect).await else { + error!("RenderLayer {:?} failed to draw", node); + return; + }; + self.render_api.replace_draw_calls(draw_update.draw_calls).await; + debug!("replace draw calls done"); + } + + #[async_recursion] + pub async fn draw(&self, sg: &SceneGraph, parent_rect: &Rectangle) -> Option { + debug!(target: "app", "RenderLayer::draw()"); + let node = sg.get_node(self.node_id).unwrap(); + + if !self.is_visible.get() { + debug!(target: "app", "invisible layer node '{}':{}", node.name, node.id); + return None + } + + if let Err(err) = eval_rect(self.rect.clone(), parent_rect) { + panic!("Node {:?} bad rect property: {}", node, err); + } + + let Ok(mut rect) = read_rect(self.rect.clone()) else { + panic!("Node {:?} bad rect property", node); + }; + + rect.x += parent_rect.x; + rect.y += parent_rect.x; + + if !parent_rect.includes(&rect) { + error!( + target: "app", + "layer '{}':{} rect {:?} is not inside parent {:?}", + node.name, node.id, rect, parent_rect + ); + return None + } + + debug!(target: "app", "Parent rect: {:?}", parent_rect); + debug!(target: "app", "Viewport rect: {:?}", rect); + + // Apply viewport + + let mut draw_calls = vec![]; + let mut child_calls = vec![]; + for child_inf in node.get_children2() { + let node = sg.get_node(child_inf.id).unwrap(); + + let dcs = match &node.pimpl { + Pimpl::RenderLayer(layer) => layer.draw(&sg, &rect).await, + Pimpl::Mesh(mesh) => mesh.draw(&sg, &rect), + _ => { + error!(target: "app", "unhandled pimpl type"); + continue + } + }; + let Some(mut draw_update) = dcs else { continue }; + draw_calls.append(&mut draw_update.draw_calls); + child_calls.push(draw_update.key); + } + + let dc = DrawCall { instrs: vec![DrawInstruction::ApplyViewport(rect)], dcs: child_calls }; + draw_calls.push((self.dc_key, dc)); + Some(DrawUpdate { key: self.dc_key, draw_calls }) + } +} + +impl Stoppable for RenderLayer { + async fn stop(&self) {} +} diff --git a/bin/darkwallet/src/ui/mesh.rs b/bin/darkwallet/src/ui/mesh.rs new file mode 100644 index 000000000..a18e9b81a --- /dev/null +++ b/bin/darkwallet/src/ui/mesh.rs @@ -0,0 +1,123 @@ +use async_lock::Mutex; +use async_recursion::async_recursion; +use futures::{stream::FuturesUnordered, StreamExt}; +use rand::{rngs::OsRng, Rng}; +use std::{ + sync::{mpsc, Arc, Weak}, + thread, +}; + +use crate::{ + chatapp, + error::{Error, Result}, + expr::{Op, SExprMachine, SExprVal}, + gfx::Rectangle, + gfx2::{ + self, DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, RenderApiPtr, Vertex, + }, + prop::{ + Property, PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyStr, + PropertySubType, PropertyType, PropertyUint32, + }, + pubsub::PublisherPtr, + scene::{ + MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNode, SceneNodeId, SceneNodeInfo, + SceneNodeType, + }, +}; + +use super::{eval_rect, read_rect, DrawUpdate, Stoppable}; + +pub struct Mesh { + render_api: RenderApiPtr, + vertex_buffer: miniquad::BufferId, + index_buffer: miniquad::BufferId, + // Texture + num_elements: i32, + + dc_key: u64, + + node_id: SceneNodeId, + rect: PropertyPtr, +} + +impl Mesh { + pub async fn new( + sg: SceneGraphPtr2, + node_id: SceneNodeId, + render_api: RenderApiPtr, + verts: Vec, + indices: Vec, + ) -> Pimpl { + let num_elements = indices.len() as i32; + let vertex_buffer = render_api.new_vertex_buffer(verts).await.unwrap(); + let index_buffer = render_api.new_index_buffer(indices).await.unwrap(); + + let mut sg = sg.lock().await; + let node = sg.get_node_mut(node_id).unwrap(); + let rect = node.get_property("rect").expect("RenderLayer::rect"); + + Pimpl::Mesh(Self { + render_api, + vertex_buffer, + index_buffer, + num_elements, + dc_key: OsRng.gen(), + node_id, + rect, + }) + } + + pub fn draw(&self, sg: &SceneGraph, parent_rect: &Rectangle) -> Option { + debug!(target: "app", "Mesh::draw()"); + // Only used for debug messages + let node = sg.get_node(self.node_id).unwrap(); + + let mesh = DrawMesh { + vertex_buffer: self.vertex_buffer, + index_buffer: self.index_buffer, + texture: None, + num_elements: self.num_elements, + }; + + if let Err(err) = eval_rect(self.rect.clone(), parent_rect) { + panic!("Node {:?} bad rect property: {}", node, err); + } + + let Ok(mut rect) = read_rect(self.rect.clone()) else { + panic!("Node {:?} bad rect property", node); + }; + + rect.x += parent_rect.x; + rect.y += parent_rect.x; + + let off_x = rect.x / parent_rect.w; + let off_y = rect.y / parent_rect.h; + let scale_x = rect.w / parent_rect.w; + let scale_y = rect.h / parent_rect.h; + let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) * + glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.)); + + Some(DrawUpdate { + key: self.dc_key, + draw_calls: vec![( + self.dc_key, + DrawCall { + instrs: vec![DrawInstruction::ApplyMatrix(model), DrawInstruction::Draw(mesh)], + dcs: vec![], + }, + )], + }) + } +} + +impl Stoppable for Mesh { + async fn stop(&self) { + // TODO: Delete own draw call + + // Free buffers + // Should this be in drop? + self.render_api.delete_buffer(self.vertex_buffer); + self.render_api.delete_buffer(self.index_buffer); + } +} diff --git a/bin/darkwallet/src/ui/mod.rs b/bin/darkwallet/src/ui/mod.rs new file mode 100644 index 000000000..d300321ff --- /dev/null +++ b/bin/darkwallet/src/ui/mod.rs @@ -0,0 +1,190 @@ +use async_lock::Mutex; +use async_recursion::async_recursion; +use futures::{stream::FuturesUnordered, StreamExt}; +use rand::{rngs::OsRng, Rng}; +use std::{ + sync::{mpsc, Arc, Weak}, + thread, +}; + +use crate::{ + chatapp, + error::{Error, Result}, + expr::{Op, SExprMachine, SExprVal}, + gfx::Rectangle, + gfx2::{ + self, DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, RenderApiPtr, Vertex, + }, + prop::{ + Property, PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyStr, + PropertySubType, PropertyType, PropertyUint32, + }, + pubsub::PublisherPtr, + scene::{ + MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNode, SceneNodeId, SceneNodeInfo, + SceneNodeType, + }, +}; + +mod mesh; +pub use mesh::Mesh; +mod layer; +pub use layer::{RenderLayer, RenderLayerPtr}; +mod win; +pub use win::{Window, WindowPtr}; + +pub trait Stoppable { + async fn stop(&self); +} + +pub struct DrawUpdate { + pub key: u64, + pub draw_calls: Vec<(u64, DrawCall)>, +} + +pub struct OnModify { + ex: Arc>, + node_name: String, + node_id: SceneNodeId, + me: Weak, + pub tasks: Vec>, +} + +impl OnModify { + pub fn new( + ex: Arc>, + node_name: String, + node_id: SceneNodeId, + me: Weak, + ) -> Self { + Self { ex, node_name, node_id, me, tasks: vec![] } + } + + pub fn when_change(&mut self, prop: PropertyPtr, f: impl Fn(Arc) -> F + Send + 'static) + where + F: std::future::Future + Send + 'static, + { + let node_name = self.node_name.clone(); + let node_id = self.node_id; + let on_modify_sub = prop.subscribe_modify(); + let prop_name = prop.name.clone(); + let me = self.me.clone(); + let task = self.ex.spawn(async move { + loop { + let _ = on_modify_sub.receive().await; + debug!(target: "app", "Property '{}':{}/'{}' modified", node_name, node_id, prop_name); + + let Some(self_) = me.upgrade() else { + // Should not happen + panic!( + "'{}':{}/'{}' self destroyed before modify_task was stopped!", + node_name, node_id, prop_name + ); + }; + + debug!(target: "app", "property modified"); + f(self_).await; + } + }); + self.tasks.push(task); + } +} + +pub fn eval_rect(rect: PropertyPtr, parent_rect: &Rectangle) -> Result<()> { + if rect.array_len != 4 { + return Err(Error::PropertyWrongLen) + } + + for i in 0..4 { + if !rect.is_expr(i)? { + continue + } + + let expr = rect.get_expr(i).unwrap(); + + let machine = SExprMachine { + globals: vec![ + ("w".to_string(), SExprVal::Float32(parent_rect.w)), + ("h".to_string(), SExprVal::Float32(parent_rect.h)), + ], + stmts: &expr, + }; + + let v = machine.call()?.as_f32()?; + rect.set_cache_f32(i, v).unwrap(); + } + Ok(()) +} + +pub fn read_rect(rect_prop: PropertyPtr) -> Result> { + if rect_prop.array_len != 4 { + return Err(Error::PropertyWrongLen) + } + + let mut rect = [0.; 4]; + for i in 0..4 { + if rect_prop.is_expr(i)? { + rect[i] = rect_prop.get_cached(i)?.as_f32()?; + } else { + rect[i] = rect_prop.get_f32(i)?; + } + } + Ok(Rectangle::from_array(rect)) +} + +pub fn get_parent_rect(sg: &SceneGraph, node: &SceneNode) -> Option> { + // read our parent + if node.parents.is_empty() { + info!("RenderLayer {:?} has no parents so skipping", node); + return None + } + if node.parents.len() != 1 { + error!("RenderLayer {:?} has too many parents so skipping", node); + return None + } + let parent_id = node.parents[0].id; + let parent_node = sg.get_node(parent_id).unwrap(); + let parent_rect = match parent_node.typ { + SceneNodeType::Window => { + let Some(screen_size_prop) = parent_node.get_property("screen_size") else { + error!( + "RenderLayer {:?} parent node {:?} missing screen_size property", + node, parent_node + ); + return None + }; + let screen_width = screen_size_prop.get_f32(0).unwrap(); + let screen_height = screen_size_prop.get_f32(1).unwrap(); + + let parent_rect = Rectangle { x: 0., y: 0., w: screen_width, h: screen_height }; + parent_rect + } + SceneNodeType::RenderLayer => { + // get their rect property + let Some(parent_rect) = parent_node.get_property("rect") else { + error!( + "RenderLayer {:?} parent node {:?} missing rect property", + node, parent_node + ); + return None + }; + // read parent's rect + let Ok(parent_rect) = read_rect(parent_rect) else { + error!( + "RenderLayer {:?} parent node {:?} malformed rect property", + node, parent_node + ); + return None + }; + parent_rect + } + _ => { + error!( + "RenderLayer {:?} parent node {:?} wrong type {:?}", + node, parent_node, parent_node.typ + ); + return None + } + }; + Some(parent_rect) +} diff --git a/bin/darkwallet/src/ui/win.rs b/bin/darkwallet/src/ui/win.rs new file mode 100644 index 000000000..9cb5b2ed3 --- /dev/null +++ b/bin/darkwallet/src/ui/win.rs @@ -0,0 +1,147 @@ +use async_lock::Mutex; +use async_recursion::async_recursion; +use futures::{stream::FuturesUnordered, StreamExt}; +use rand::{rngs::OsRng, Rng}; +use std::{ + sync::{mpsc, Arc, Weak}, + thread, +}; + +use crate::{ + chatapp, + error::{Error, Result}, + expr::{Op, SExprMachine, SExprVal}, + gfx::Rectangle, + gfx2::{ + self, DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, RenderApiPtr, Vertex, + }, + prop::{ + Property, PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyStr, + PropertySubType, PropertyType, PropertyUint32, + }, + pubsub::PublisherPtr, + scene::{ + MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNode, SceneNodeId, SceneNodeInfo, + SceneNodeType, + }, +}; + +use super::{eval_rect, read_rect, DrawUpdate, OnModify, Stoppable}; + +pub type WindowPtr = Arc; + +pub struct Window { + node_id: SceneNodeId, + tasks: Vec>, + screen_size_prop: PropertyPtr, + render_api: RenderApiPtr, +} + +impl Window { + pub async fn new( + sg: SceneGraphPtr2, + node_id: SceneNodeId, + ex: Arc>, + render_api: RenderApiPtr, + event_pub: GraphicsEventPublisherPtr, + ) -> Pimpl { + debug!(target: "app", "Window::new()"); + + let scene_graph = sg.lock().await; + let node = scene_graph.get_node(node_id).unwrap(); + let node_name = node.name.clone(); + let screen_size_prop = node.get_property("screen_size").unwrap(); + let scale_prop = node.get_property("scale").unwrap(); + drop(scene_graph); + + let self_ = Arc::new_cyclic(|me: &Weak| { + // Start a task monitoring for window resize events + // which updates screen_size + let ev_sub = event_pub.subscribe_resize(); + let screen_size_prop2 = screen_size_prop.clone(); + let me2 = me.clone(); + let sg2 = sg.clone(); + let resize_task = ex.spawn(async move { + loop { + let Ok((w, h)) = ev_sub.receive().await else { + debug!(target: "app", "Event relayer closed"); + break + }; + + debug!(target: "app", "Window resized ({w}, {h})"); + // Now update the properties + screen_size_prop2.set_f32(0, w); + screen_size_prop2.set_f32(1, h); + + let Some(self_) = me2.upgrade() else { + // Should not happen + panic!("self destroyed before modify_task was stopped!"); + }; + + let sg = sg2.lock().await; + self_.draw(&sg).await; + } + }); + + let sg2 = sg.clone(); + let redraw_fn = move |self_: Arc| { + let sg = sg2.clone(); + async move { + let sg = sg.lock().await; + self_.draw(&sg).await; + } + }; + + let mut on_modify = OnModify::new(ex.clone(), node_name, node_id, me.clone()); + on_modify.when_change(scale_prop, redraw_fn); + + let mut tasks = on_modify.tasks; + tasks.push(resize_task); + + Self { node_id, tasks, screen_size_prop, render_api } + }); + + Pimpl::Window(self_) + } + + pub async fn draw(&self, sg: &SceneGraph) { + debug!(target: "app", "Window::draw()"); + // SceneGraph should remain locked for the entire draw + let self_node = sg.get_node(self.node_id).unwrap(); + + let screen_width = self.screen_size_prop.get_f32(0).unwrap(); + let screen_height = self.screen_size_prop.get_f32(1).unwrap(); + + let parent_rect = Rectangle { x: 0., y: 0., w: screen_width, h: screen_height }; + + let mut draw_calls = vec![]; + let mut child_calls = vec![]; + for child_inf in self_node.get_children2() { + let node = sg.get_node(child_inf.id).unwrap(); + debug!(target: "app", "Window::draw() calling draw() for node '{}':{}", node.name, node.id); + + let dcs = match &node.pimpl { + Pimpl::RenderLayer(layer) => layer.draw(sg, &parent_rect).await, + _ => { + error!(target: "app", "unhandled pimpl type"); + continue + } + }; + let Some(mut draw_update) = dcs else { continue }; + draw_calls.append(&mut draw_update.draw_calls); + child_calls.push(draw_update.key); + } + + let root_dc = DrawCall { instrs: vec![], dcs: child_calls }; + draw_calls.push((0, root_dc)); + //debug!(" => {:?}", draw_calls); + + self.render_api.replace_draw_calls(draw_calls).await; + debug!("Window::draw() - replaced draw call"); + } +} + +// Nodes should be stopped before being removed +impl Stoppable for Window { + async fn stop(&self) {} +}