diff --git a/bin/darkwallet/pydrk/api.py b/bin/darkwallet/pydrk/api.py index a7b21acdf..012d2a726 100644 --- a/bin/darkwallet/pydrk/api.py +++ b/bin/darkwallet/pydrk/api.py @@ -92,6 +92,7 @@ class PropertySubType: NULL = 0 COLOR = 1 PIXEL = 2 + RESOURCE_ID = 3 @staticmethod def to_str(prop_type): @@ -102,6 +103,8 @@ class PropertySubType: return "color" case PropertySubType.PIXEL: return "pixel" + case PropertySubType.RESOURCE_ID: + return "resource_id" class PropertyStatus: OK = 0 diff --git a/bin/darkwallet/src/gfx.rs b/bin/darkwallet/src/gfx.rs index 040b45efe..0da99f81b 100644 --- a/bin/darkwallet/src/gfx.rs +++ b/bin/darkwallet/src/gfx.rs @@ -4,12 +4,17 @@ use fontdue::{ Font, FontSettings, }; use miniquad::*; -use std::{fmt, io::Cursor}; +use std::{ + fmt, + io::Cursor, + sync::mpsc, + time::{Duration, Instant}, +}; use crate::{ error::{Error, Result}, prop::{Property, PropertySubType, PropertyType}, - scene::{SceneGraph, SceneGraphPtr, SceneNode, SceneNodeId, SceneNodeType}, + scene::{MethodResponseFn, SceneGraph, SceneGraphPtr, SceneNode, SceneNodeId, SceneNodeType}, shader, }; @@ -88,6 +93,15 @@ impl ResourceManager { } } +#[derive(Debug)] +enum GraphicsMethodEvent { + CreateText, + LoadTexture, + CreateMesh, + DeleteTexture, + DeleteMesh, +} + struct Stage { ctx: Box, pipeline: Pipeline, @@ -96,6 +110,9 @@ struct Stage { textures: ResourceManager, font: Font, + + method_recvr: mpsc::Receiver<(GraphicsMethodEvent, SceneNodeId, Vec, MethodResponseFn)>, + method_sender: mpsc::SyncSender<(GraphicsMethodEvent, SceneNodeId, Vec, MethodResponseFn)>, } impl Stage { @@ -146,6 +163,8 @@ impl Stage { params, ); + let (method_sender, method_recvr) = mpsc::sync_channel(100); + let font = { let mut scene_graph = scene_graph.lock().unwrap(); @@ -157,24 +176,39 @@ impl Stage { let font = include_bytes!("../Inter-Regular.ttf") as &[u8]; let font = Font::from_bytes(font, FontSettings::default()).unwrap(); - let line_metrics = font.horizontal_line_metrics(1.).unwrap(); + //let line_metrics = font.horizontal_line_metrics(1.).unwrap(); //inter_regular.add_property_f32("ascent", line_metrics.ascent).unwrap(); //inter_regular.add_property_f32("descent", line_metrics.descent).unwrap(); //inter_regular.add_property_f32("line_gap", line_metrics.line_gap).unwrap(); //inter_regular.add_property_f32("new_line_size", line_metrics.new_line_size).unwrap(); + let sender = method_sender.clone(); + let inter_regular_id = inter_regular.id; + let method_fn = Box::new(move |arg_data, response_fn| { + sender.send(( + GraphicsMethodEvent::CreateText, + inter_regular_id, + arg_data, + response_fn, + )); + }); inter_regular.add_method( "create_text", - vec![("text", "", PropertyType::Str), ("font_size", "", PropertyType::Float32)], + vec![ + ("node_name", "", PropertyType::Str), + ("text", "", PropertyType::Str), + ("font_size", "", PropertyType::Float32), + ], vec![("node_id", "", PropertyType::SceneNodeId)], + method_fn, ); - let inter_regular_id = inter_regular.id; scene_graph.link(inter_regular_id, font_id).unwrap(); font }; - let mut stage = Stage { ctx, pipeline, scene_graph, textures, font }; + let mut stage = + Stage { ctx, pipeline, scene_graph, textures, font, method_recvr, method_sender }; stage.setup_scene_graph_window(); stage } @@ -194,20 +228,26 @@ impl Stage { window .add_signal( "resize", + "Screen resize event", vec![ ("screen_width", "", PropertyType::Float32), ("screen_height", "", PropertyType::Float32), ], ) .unwrap(); + let sender = self.method_sender.clone(); + let window_id = window.id; + let method_fn = Box::new(move |arg_data, response_fn| { + sender.send((GraphicsMethodEvent::LoadTexture, window_id, arg_data, response_fn)); + }); window .add_method( "load_texture", vec![("node_name", "", PropertyType::Str), ("path", "", PropertyType::Str)], vec![("node_id", "", PropertyType::SceneNodeId)], + method_fn, ) .unwrap(); - let window_id = window.id; scene_graph.link(window_id, SceneGraph::ROOT_ID).unwrap(); let input = scene_graph.add_node("input", SceneNodeType::WindowInput); @@ -217,6 +257,7 @@ impl Stage { let keyb = scene_graph.add_node("keyboard", SceneNodeType::Keyboard); keyb.add_signal( "key_down", + "Key press down event", vec![ ("shift", "", PropertyType::Bool), ("ctrl", "", PropertyType::Bool), @@ -234,6 +275,7 @@ impl Stage { mouse .add_signal( "button_up", + "Mouse button up event", vec![ ("button", "", PropertyType::Enum), ("x", "", PropertyType::Float32), @@ -244,6 +286,7 @@ impl Stage { mouse .add_signal( "button_down", + "Mouse button down event", vec![ ("button", "", PropertyType::Enum), ("x", "", PropertyType::Float32), @@ -254,12 +297,14 @@ impl Stage { mouse .add_signal( "wheel", + "Mouse wheel scroll event", vec![("x", "", PropertyType::Float32), ("y", "", PropertyType::Float32)], ) .unwrap(); mouse .add_signal( "move", + "Mouse cursor move event", vec![("x", "", PropertyType::Float32), ("y", "", PropertyType::Float32)], ) .unwrap(); @@ -424,7 +469,94 @@ impl Stage { Ok(text_node_id) } - fn load_texture(&mut self, node_name: String, filepath: String) -> Result { + fn method_create_text(&mut self, node_id: SceneNodeId, arg_data: Vec) -> Result> { + let mut cur = Cursor::new(&arg_data); + let node_name = String::decode(&mut cur).unwrap(); + let text = String::decode(&mut cur).unwrap(); + let font_size = f32::decode(&mut cur).unwrap(); + + let mut scene_graph = self.scene_graph.lock().unwrap(); + let font_node = scene_graph.get_node(node_id).ok_or(Error::NodeNotFound)?; + let font_name = font_node.name.clone(); + let font_node_id = font_node.id; + let text_node = scene_graph.add_node(node_name, SceneNodeType::RenderText); + + let mut prop = Property::new("text", PropertyType::Str, PropertySubType::Null); + text_node.add_property(prop)?; + + let mut prop = Property::new("font_size", PropertyType::Float32, PropertySubType::Pixel); + text_node.add_property(prop)?; + + let mut prop = Property::new("color", PropertyType::Float32, PropertySubType::Color); + prop.set_array_len(4); + text_node.add_property(prop)?; + + let mut layout = Layout::new(CoordinateSystem::PositiveYDown); + layout.reset(&LayoutSettings { ..LayoutSettings::default() }); + let font = match font_name.as_str() { + "inter-regular" => &self.font, + _ => panic!("unknown font name!"), + }; + let fonts = [font]; + layout.append(&fonts, &TextStyle::new(&text, font_size, 0)); + + // Calculate the text width + // std::cmp::max() not impl for f32 + let max_f32 = |x: f32, y: f32| { + if x > y { + x + } else { + y + } + }; + + // TODO: this calc isn't multiline, we should add width property to each line + let mut total_width = 0.; + for glyph_pos in layout.glyphs() { + let right = glyph_pos.x + glyph_pos.width as f32; + total_width = max_f32(total_width, right); + } + + let mut prop = Property::new("size", PropertyType::Float32, PropertySubType::Pixel); + prop.set_array_len(2); + prop.set_f32(0, total_width).unwrap(); + prop.set_f32(1, layout.height()).unwrap(); + + let text_node_id = text_node.id; + + /* + let lines = layout.lines(); + if lines.is_some() { + for (idx, line) in lines.unwrap().into_iter().enumerate() { + let line_node_name = format!("line.{}", idx); + let line_node = scene_graph.add_node(line_node_name, SceneNodeType::LinePosition); + //line_node.add_property_u32("idx", idx as u32).unwrap(); + //line_node.add_property_f32("baseline_y", line.baseline_y).unwrap(); + //line_node.add_property_f32("padding", line.padding).unwrap(); + //line_node.add_property_f32("max_ascent", line.max_ascent).unwrap(); + //line_node.add_property_f32("min_descent", line.min_descent).unwrap(); + //line_node.add_property_f32("max_line_gap", line.max_line_gap).unwrap(); + //line_node.add_property_u32("glyph_start", line.glyph_start as u32).unwrap(); + //line_node.add_property_u32("glyph_end", line.glyph_end as u32).unwrap(); + + let line_node_id = line_node.id; + scene_graph.link(line_node_id, text_node_id)?; + } + } + */ + + scene_graph.link(font_node_id, text_node_id)?; + + let mut reply = vec![]; + text_node_id.encode(&mut reply).unwrap(); + + Ok(reply) + } + fn method_load_texture(&mut self, node_id: SceneNodeId, arg_data: Vec) -> Result> { + let mut cur = Cursor::new(&arg_data); + let node_name = String::decode(&mut cur).unwrap(); + let filepath = String::decode(&mut cur).unwrap(); + let Ok(img) = image::open(filepath) else { return Err(Error::FileNotFound) }; let img = img.to_rgba8(); @@ -437,9 +569,6 @@ impl Stage { let mut scene_graph = self.scene_graph.lock().unwrap(); let img_node = scene_graph.add_node(node_name, SceneNodeType::RenderTexture); - //img_node.add_property_u32("width", width).unwrap(); - //img_node.add_property_u32("height", height).unwrap(); - //img_node.add_property_u32("texture_id", id).unwrap(); let mut prop = Property::new("size", PropertyType::Uint32, PropertySubType::Pixel); prop.set_array_len(2); @@ -447,11 +576,28 @@ impl Stage { prop.set_u32(1, height).unwrap(); img_node.add_property(prop)?; - let mut prop = Property::new("texture_id", PropertyType::Uint32, PropertySubType::Null); + let mut prop = + Property::new("texture_id", PropertyType::Uint32, PropertySubType::ResourceId); prop.set_u32(0, id).unwrap(); img_node.add_property(prop)?; - Ok(img_node.id) + let mut reply = vec![]; + img_node.id.encode(&mut reply).unwrap(); + + Ok(reply) + } + fn method_create_mesh(&mut self, node_id: SceneNodeId, arg_data: Vec) -> Result> { + Ok(vec![]) + } + fn method_delete_texture( + &mut self, + node_id: SceneNodeId, + arg_data: Vec, + ) -> Result> { + Ok(vec![]) + } + fn method_delete_mesh(&mut self, node_id: SceneNodeId, arg_data: Vec) -> Result> { + Ok(vec![]) } } @@ -479,78 +625,22 @@ fn get_text_props(render_text: &SceneNode) -> Result<(String, f32, [f32; 4])> { impl EventHandler for Stage { fn update(&mut self) { - // check /font:create_text() queue - - let mut scene_graph = self.scene_graph.lock().unwrap(); - let font_root = scene_graph.lookup_node("/font").unwrap(); - let font_ids: Vec<_> = font_root - .iter_children(&scene_graph, SceneNodeType::Font) - .map(|node| node.id) - .collect(); - - let mut calls = vec![]; - for font_id in font_ids { - let font_node = scene_graph.get_node_mut(font_id).unwrap(); - for method in &mut font_node.methods { - for (arg_data, response_fn) in std::mem::take(&mut method.queue) { - calls.push(( - font_node.id, - font_node.name.clone(), - method.name.clone(), - arg_data, - response_fn, - )); - } - } - } - drop(scene_graph); - - for (font_node_id, font_node_name, method_name, arg_data, response_fn) in calls { - assert_eq!(method_name, "create_text"); - - let mut cur = Cursor::new(&arg_data); - let mut reply = vec![]; - let node_name = String::decode(&mut cur).unwrap(); - let text = String::decode(&mut cur).unwrap(); - let font_size = f32::decode(&mut cur).unwrap(); - debug!(target: "win", "/font:{}({}, {})", method_name, text, font_size); - - let node_id_result = - self.create_text_node(font_node_id, &font_node_name, node_name, text, font_size); - let node_id_result = node_id_result.map(|node_id| { - node_id.encode(&mut reply).unwrap(); - reply - }); - response_fn(node_id_result) - } - - // check /window:load_texture() queue - - let mut scene_graph = self.scene_graph.lock().unwrap(); - let mut calls = vec![]; - let window = scene_graph.lookup_node_mut("/window").unwrap(); - for method in &mut window.methods { - for (arg_data, response_fn) in std::mem::take(&mut method.queue) { - calls.push((method.name.clone(), arg_data, response_fn)); - } - } - drop(scene_graph); - - for (method_name, arg_data, response_fn) in calls { - assert_eq!(method_name, "load_texture"); - - let mut cur = Cursor::new(&arg_data); - let mut reply = vec![]; - let node_name = String::decode(&mut cur).unwrap(); - let filepath = String::decode(&mut cur).unwrap(); - debug!(target: "win", "/window:{}({}, {})", method_name, node_name, filepath); - - let node_id_result = self.load_texture(node_name, filepath); - let node_id_result = node_id_result.map(|node_id| { - node_id.encode(&mut reply).unwrap(); - reply - }); - response_fn(node_id_result) + // Only block for 20 ms, process as much as we can during that time + let deadline = Instant::now() + Duration::from_millis(400); + loop { + let Ok((event, node_id, arg_data, response_fn)) = + self.method_recvr.recv_deadline(deadline) + else { + break + }; + let res = match event { + GraphicsMethodEvent::CreateText => self.method_create_text(node_id, arg_data), + GraphicsMethodEvent::LoadTexture => self.method_load_texture(node_id, arg_data), + GraphicsMethodEvent::CreateMesh => self.method_create_mesh(node_id, arg_data), + GraphicsMethodEvent::DeleteTexture => self.method_delete_texture(node_id, arg_data), + GraphicsMethodEvent::DeleteMesh => self.method_delete_mesh(node_id, arg_data), + }; + response_fn(res); } } @@ -746,8 +836,9 @@ impl EventHandler for Stage { //win.set_property_bool("repeat", repeat).unwrap(); let send_key_down = |key: &str| { - //win.set_property_str("keycode", key).unwrap(); - win.trigger("key_down").unwrap(); + let mut data = vec![]; + key.encode(&mut data).unwrap(); + win.trigger("key_down", data).unwrap(); }; match keycode { @@ -876,41 +967,46 @@ impl EventHandler for Stage { } fn mouse_motion_event(&mut self, x: f32, y: f32) { let mut scene_graph = self.scene_graph.lock().unwrap(); + let mut data = vec![]; + x.encode(&mut data).unwrap(); + y.encode(&mut data).unwrap(); let mouse = scene_graph.lookup_node_mut("/window/input/mouse").unwrap(); - //mouse.set_property_f32("motion_x", x).unwrap(); - //mouse.set_property_f32("motion_y", y).unwrap(); - mouse.trigger("move").unwrap(); + mouse.trigger("move", data).unwrap(); } fn mouse_wheel_event(&mut self, x: f32, y: f32) { let mut scene_graph = self.scene_graph.lock().unwrap(); + let mut data = vec![]; + x.encode(&mut data).unwrap(); + y.encode(&mut data).unwrap(); let mouse = scene_graph.lookup_node_mut("/window/input/mouse").unwrap(); - //mouse.set_property_f32("x", x).unwrap(); - //mouse.set_property_f32("wheel_y", y).unwrap(); - mouse.trigger("wheel").unwrap(); + mouse.trigger("wheel", data).unwrap(); } fn mouse_button_down_event(&mut self, button: MouseButton, x: f32, y: f32) { let mut scene_graph = self.scene_graph.lock().unwrap(); + let mut data = vec![]; + button.to_u8().encode(&mut data).unwrap(); + x.encode(&mut data).unwrap(); + y.encode(&mut data).unwrap(); let mouse = scene_graph.lookup_node_mut("/window/input/mouse").unwrap(); - //mouse.set_property_u32("button", button.to_u8() as u32).unwrap(); - //mouse.set_property_f32("click_x", x).unwrap(); - //mouse.set_property_f32("click_y", y).unwrap(); - mouse.trigger("button_down").unwrap(); + mouse.trigger("button_down", data).unwrap(); } fn mouse_button_up_event(&mut self, button: MouseButton, x: f32, y: f32) { let mut scene_graph = self.scene_graph.lock().unwrap(); + let mut data = vec![]; + button.to_u8().encode(&mut data).unwrap(); + x.encode(&mut data).unwrap(); + y.encode(&mut data).unwrap(); let mouse = scene_graph.lookup_node_mut("/window/input/mouse").unwrap(); - //mouse.set_property_u32("button", button.to_u8() as u32).unwrap(); - //mouse.set_property_f32("click_x", x).unwrap(); - //mouse.set_property_f32("click_y", y).unwrap(); - mouse.trigger("button_up").unwrap(); + mouse.trigger("button_up", data).unwrap(); } fn resize_event(&mut self, width: f32, height: f32) { let mut scene_graph = self.scene_graph.lock().unwrap(); + let mut data = vec![]; + width.encode(&mut data).unwrap(); + height.encode(&mut data).unwrap(); let win = scene_graph.lookup_node_mut("/window").unwrap(); - //win.set_property_f32("width", width).unwrap(); - //win.set_property_f32("height", height).unwrap(); - win.trigger("resize").unwrap(); + win.trigger("resize", data).unwrap(); } } diff --git a/bin/darkwallet/src/main.rs b/bin/darkwallet/src/main.rs index 8c2037674..d3f7454b9 100644 --- a/bin/darkwallet/src/main.rs +++ b/bin/darkwallet/src/main.rs @@ -1,3 +1,5 @@ +#![feature(deadline_api)] + use std::{ sync::{Arc, Mutex}, thread, diff --git a/bin/darkwallet/src/net.rs b/bin/darkwallet/src/net.rs index 6f8b5b194..9db69db3a 100644 --- a/bin/darkwallet/src/net.rs +++ b/bin/darkwallet/src/net.rs @@ -52,8 +52,8 @@ pub struct ZeroMQAdapter { req_socket: zmq::Socket, // We cannot share zmq sockets across threads, and we cannot quickly spawn // pub sockets due to address reuse errors. - slot_sender: mpsc::SyncSender>, - slot_recvr: Option>>, + slot_sender: mpsc::SyncSender<(Vec, Vec)>, + slot_recvr: Option, Vec)>>, scene_graph: SceneGraphPtr, } @@ -78,8 +78,8 @@ impl ZeroMQAdapter { pub_socket.bind("tcp://*:9485").unwrap(); loop { - let user_data = rx.recv().unwrap(); - pub_socket.send(&user_data, 0).unwrap(); + let (signal_data, user_data) = rx.recv().unwrap(); + pub_socket.send_multipart(&[signal_data, user_data], zmq::DONTWAIT).unwrap(); } }); @@ -394,8 +394,8 @@ impl ZeroMQAdapter { let sender = self.slot_sender.clone(); let slot = Slot { name: slot_name, - func: Box::new(move || { - sender.send(user_data.clone()).unwrap(); + func: Box::new(move |signal_data| { + sender.send((signal_data, user_data.clone())).unwrap(); }), }; diff --git a/bin/darkwallet/src/prop.rs b/bin/darkwallet/src/prop.rs index 422234a62..1226cdabf 100644 --- a/bin/darkwallet/src/prop.rs +++ b/bin/darkwallet/src/prop.rs @@ -52,6 +52,7 @@ pub enum PropertySubType { Color = 1, // Size of something in pixels Pixel = 2, + ResourceId = 3, } #[derive(Debug, Clone)] diff --git a/bin/darkwallet/src/scene.rs b/bin/darkwallet/src/scene.rs index 57173bbff..cfa89e86e 100644 --- a/bin/darkwallet/src/scene.rs +++ b/bin/darkwallet/src/scene.rs @@ -453,6 +453,7 @@ impl SceneNode { pub fn add_signal>( &mut self, name: S, + desc: S, fmt: Vec<(S, S, PropertyType)>, ) -> Result<()> { let name = name.into(); @@ -463,7 +464,13 @@ impl SceneNode { .into_iter() .map(|(n, d, t)| CallArg { name: n.into(), desc: d.into(), typ: t }) .collect(); - self.sigs.push(Signal { name: name.into(), fmt, slots: vec![], freed: vec![] }); + self.sigs.push(Signal { + name: name.into(), + desc: desc.into(), + fmt, + slots: vec![], + freed: vec![], + }); Ok(()) } @@ -498,11 +505,11 @@ impl SceneNode { sig.freed.push(slot_id); Ok(()) } - pub fn trigger(&self, sig_name: &str) -> Result<()> { + pub fn trigger(&self, sig_name: &str, data: Vec) -> Result<()> { let sig = self.get_signal(sig_name).ok_or(Error::SignalNotFound)?; for (_, slot) in sig.get_slots() { // Trigger the slot - slot.call(); + slot.call(data.clone()); } Ok(()) } @@ -512,6 +519,7 @@ impl SceneNode { name: S, args: Vec<(S, S, PropertyType)>, result: Vec<(S, S, PropertyType)>, + method_fn: MethodRequestFn, ) -> Result<()> { let name = name.into(); if self.has_signal(&name) { @@ -525,7 +533,7 @@ impl SceneNode { .into_iter() .map(|(n, d, t)| CallArg { name: n.into(), desc: d.into(), typ: t }) .collect(); - self.methods.push(Method { name: name.into(), args, result, queue: vec![] }); + self.methods.push(Method { name: name.into(), args, result, method_fn }); Ok(()) } @@ -542,8 +550,8 @@ impl SceneNode { arg_data: Vec, response_fn: MethodResponseFn, ) -> Result<()> { - let method = self.get_method_mut(name).ok_or(Error::MethodNotFound)?; - method.queue.push((arg_data, response_fn)); + let method = self.get_method(name).ok_or(Error::MethodNotFound)?; + (method.method_fn)(arg_data, response_fn); Ok(()) } } @@ -555,7 +563,7 @@ pub struct CallArg { pub typ: PropertyType, } -type SlotFn = Box; +type SlotFn = Box) + Send>; pub type SlotId = u32; pub struct Slot { @@ -564,13 +572,14 @@ pub struct Slot { } impl Slot { - fn call(&self) { - (self.func)() + fn call(&self, data: Vec) { + (self.func)(data) } } pub struct Signal { pub name: String, + pub desc: String, pub fmt: Vec, slots: Vec, freed: Vec, @@ -602,11 +611,12 @@ impl Signal { } } -type MethodResponseFn = Box>) + Send>; +type MethodRequestFn = Box, MethodResponseFn) + Send>; +pub type MethodResponseFn = Box>) + Send>; pub struct Method { pub name: String, pub args: Vec, pub result: Vec, - pub queue: Vec<(Vec, MethodResponseFn)>, + method_fn: MethodRequestFn, }