diff --git a/bin/darkwallet/src/app.rs b/bin/darkwallet/src/app.rs index 9c8945635..56441dd67 100644 --- a/bin/darkwallet/src/app.rs +++ b/bin/darkwallet/src/app.rs @@ -32,13 +32,13 @@ use crate::{ darkirc::{DarkIrcBackendPtr, Privmsg}, error::Error, expr::Op, - gfx2::{GraphicsEventPublisherPtr, RenderApiPtr, Vertex}, + gfx::{GraphicsEventPublisherPtr, RenderApiPtr, Vertex}, prop::{Property, PropertyBool, PropertyStr, PropertySubType, PropertyType, Role}, scene::{ CallArgType, MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId, SceneNodeType, Slot, }, - text2::TextShaperPtr, + text::TextShaperPtr, ui::{chatview, Button, ChatView, EditBox, Image, Mesh, RenderLayer, Stoppable, Text, Window}, ExecutorPtr, }; diff --git a/bin/darkwallet/src/chatapp.rs b/bin/darkwallet/src/chatapp.rs deleted file mode 100644 index a40a15e23..000000000 --- a/bin/darkwallet/src/chatapp.rs +++ /dev/null @@ -1,436 +0,0 @@ -/* This file is part of DarkFi (https://dark.fi) - * - * Copyright (C) 2020-2024 Dyne.org foundation - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -use darkfi_serial::Encodable; - -use crate::{ - expr::Op, - gfx::Rectangle, - prop::{Property, PropertySubType, PropertyType}, - scene::{ - SceneGraph, SceneNodeId, - SceneNodeType, - }, -}; - -struct Buffer { - verts: Vec, - faces: Vec, - verts_len: u32, -} - -impl Buffer { - pub fn new() -> Self { - Self { verts: vec![], faces: vec![], verts_len: 0 } - } - - fn vertex(&mut self, x: f32, y: f32, r: f32, g: f32, b: f32, a: f32, u: f32, v: f32) { - // xy - x.encode(&mut self.verts).unwrap(); - y.encode(&mut self.verts).unwrap(); - // rgba - r.encode(&mut self.verts).unwrap(); - g.encode(&mut self.verts).unwrap(); - b.encode(&mut self.verts).unwrap(); - a.encode(&mut self.verts).unwrap(); - // uv - u.encode(&mut self.verts).unwrap(); - v.encode(&mut self.verts).unwrap(); - self.verts_len += 1 - } - - fn face(&mut self, i1: u32, i2: u32, i3: u32) { - i1.encode(&mut self.faces).unwrap(); - i2.encode(&mut self.faces).unwrap(); - i3.encode(&mut self.faces).unwrap(); - } - - pub fn draw_box(&mut self, rect: Rectangle, color: [f32; 4]) { - let k = self.verts_len; - let x = rect.x; - let y = rect.y; - let w = rect.w; - let h = rect.h; - let r = color[0]; - let g = color[1]; - let b = color[2]; - let a = color[3]; - - self.vertex(x, y, r, g, b, a, 0., 0.); - self.vertex(x + w, y, r, g, b, a, 1., 0.); - self.vertex(x, y + h, r, g, b, a, 0., 1.); - self.vertex(x + w, y + h, r, g, b, a, 1., 1.); - - self.face(k, k + 2, k + 1); - self.face(k + 1, k + 2, k + 3); - } - - pub fn draw_outline( - &mut self, - rect: Rectangle, - color: [f32; 4], - pad: f32, - layer_w: f32, - layer_h: f32, - ) { - let pad_x = pad / layer_w; - let pad_y = pad / layer_h; - - let x = rect.x; - let y = rect.y; - let w = rect.w; - let h = rect.h; - - // left - self.draw_box(Rectangle { x, y, w: pad_x, h }, color); - // top - self.draw_box(Rectangle { x, y, w, h: pad_y }, color); - // right - let rhs = x + w; - self.draw_box(Rectangle { x: rhs - pad_x, y, w: pad_x, h }, color); - // bottom - let bhs = y + h; - self.draw_box(Rectangle { x, y: bhs - pad_y, w, h: pad_y }, color); - } -} - -pub fn create_layer(sg: &mut SceneGraph, name: &str) -> SceneNodeId { - let win_id = sg.lookup_node("/window").unwrap().id; - - let node = sg.add_node(name, SceneNodeType::RenderLayer); - let mut prop = Property::new("is_visible", PropertyType::Bool, PropertySubType::Null); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel); - prop.set_array_len(4); - prop.allow_exprs(); - node.add_property(prop).unwrap(); - - node.id -} - -pub fn create_mesh(sg: &mut SceneGraph, name: &str) -> SceneNodeId { - let node = sg.add_node(name, SceneNodeType::RenderMesh); - - let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel); - prop.set_array_len(4); - prop.allow_exprs(); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("z_index", PropertyType::Uint32, PropertySubType::Null); - node.add_property(prop).unwrap(); - - node.id -} - -fn create_text(sg: &mut SceneGraph, name: &str, layer_node_id: SceneNodeId) -> SceneNodeId { - let node = sg.add_node(name, SceneNodeType::RenderText); - - let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel); - prop.set_array_len(4); - prop.allow_exprs(); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("baseline", PropertyType::Float32, PropertySubType::Pixel); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("font_size", PropertyType::Float32, PropertySubType::Pixel); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("text", PropertyType::Str, PropertySubType::Null); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("color", PropertyType::Float32, PropertySubType::Color); - prop.set_array_len(4); - prop.set_range_f32(0., 1.); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("z_index", PropertyType::Uint32, PropertySubType::Null); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("debug", PropertyType::Bool, PropertySubType::Null); - node.add_property(prop).unwrap(); - - let node_id = node.id; - sg.link(node_id, layer_node_id).unwrap(); - node_id -} - -fn create_editbox(sg: &mut SceneGraph, name: &str, layer_node_id: SceneNodeId) -> SceneNodeId { - let node = sg.add_node(name, SceneNodeType::EditBox); - - let mut prop = Property::new("is_active", PropertyType::Bool, PropertySubType::Null); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel); - prop.set_array_len(4); - prop.allow_exprs(); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("baseline", PropertyType::Float32, PropertySubType::Pixel); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("scroll", PropertyType::Float32, PropertySubType::Pixel); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("cursor_pos", PropertyType::Uint32, PropertySubType::Pixel); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("font_size", PropertyType::Float32, PropertySubType::Pixel); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("text", PropertyType::Str, PropertySubType::Null); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("text_color", PropertyType::Float32, PropertySubType::Color); - prop.set_array_len(4); - prop.set_range_f32(0., 1.); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("cursor_color", PropertyType::Float32, PropertySubType::Color); - prop.set_array_len(4); - prop.set_range_f32(0., 1.); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("hi_bg_color", PropertyType::Float32, PropertySubType::Color); - prop.set_array_len(4); - prop.set_range_f32(0., 1.); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("selected", PropertyType::Uint32, PropertySubType::Color); - prop.set_array_len(2); - prop.allow_null_values(); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("z_index", PropertyType::Uint32, PropertySubType::Null); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("debug", PropertyType::Bool, PropertySubType::Null); - node.add_property(prop).unwrap(); - - let node_id = node.id; - sg.link(node_id, layer_node_id).unwrap(); - node_id -} - -fn create_chatview(sg: &mut SceneGraph, name: &str, layer_node_id: SceneNodeId) -> SceneNodeId { - let node = sg.add_node(name, SceneNodeType::ChatView); - - let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel); - prop.set_array_len(4); - prop.allow_exprs(); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("z_index", PropertyType::Uint32, PropertySubType::Null); - node.add_property(prop).unwrap(); - - let mut prop = Property::new("debug", PropertyType::Bool, PropertySubType::Null); - node.add_property(prop).unwrap(); - - let node_id = node.id; - - 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| { - //tx.send(result).unwrap(); - }); - let win_node = sg.lookup_node_mut("/window").unwrap(); - win_node.call_method("create_chat_view", arg_data, response_fn).unwrap(); - //let _result = rx.recv().unwrap(); - - sg.link(node_id, layer_node_id).unwrap(); - node_id -} - -pub fn setup(sg: &mut SceneGraph) { - let layer_node_id = create_layer(sg, "view"); - let node = sg.get_node(layer_node_id).unwrap(); - let prop = node.get_property("rect").unwrap(); - prop.set_u32(0, 0).unwrap(); - prop.set_u32(1, 0).unwrap(); - let code = vec![Op::Float32ToUint32((Box::new(Op::LoadVar("sw".to_string()))))]; - prop.set_expr(2, code).unwrap(); - let code = vec![Op::Float32ToUint32((Box::new(Op::LoadVar("sh".to_string()))))]; - prop.set_expr(3, code).unwrap(); - - // Make the black background - // Maybe we should use a RenderPass for this instead - let node_id = create_mesh(sg, "bg"); - let node = sg.get_node(node_id).unwrap(); - - let prop = node.get_property("rect").unwrap(); - prop.set_f32(0, 0.).unwrap(); - prop.set_f32(1, 0.).unwrap(); - let code = vec![Op::LoadVar("lw".to_string())]; - prop.set_expr(2, code).unwrap(); - let code = vec![Op::LoadVar("lh".to_string())]; - prop.set_expr(3, code).unwrap(); - - let prop = node.get_property("data").unwrap(); - - let mut buff = Buffer::new(); - buff.draw_box(Rectangle { x: 0., y: 0., w: 1., h: 1. }, [0.05, 0.05, 0.05, 1.]); - prop.set_buf(0, buff.verts).unwrap(); - prop.set_buf(1, buff.faces).unwrap(); - - // Make the chatedit bg - let node_id = create_mesh(sg, "chateditbg"); - let node = sg.get_node(node_id).unwrap(); - let prop = node.get_property("rect").unwrap(); - - let prop = node.get_property("rect").unwrap(); - prop.set_f32(0, 140.).unwrap(); - let code = - vec![Op::Sub((Box::new(Op::LoadVar("lh".to_string())), Box::new(Op::ConstFloat32(60.))))]; - prop.set_expr(1, code).unwrap(); - let code = - vec![Op::Sub((Box::new(Op::LoadVar("lw".to_string())), Box::new(Op::ConstFloat32(140.))))]; - prop.set_expr(2, code).unwrap(); - prop.set_f32(3, 60.).unwrap(); - - let prop = node.get_property("data").unwrap(); - - let mut buff = Buffer::new(); - buff.draw_box(Rectangle { x: 0., y: 0., w: 1., h: 1. }, [0., 0.13, 0.08, 1.]); - // FIXME: layer dim is passed here manually! - // we should just use separate objs - buff.draw_outline( - Rectangle { x: 0., y: 0., w: 1., h: 1. }, - [0.22, 0.22, 0.22, 1.], - 1., - 1000., - 50., - ); - prop.set_buf(0, buff.verts).unwrap(); - prop.set_buf(1, buff.faces).unwrap(); - - // Make the nicktext border - let node_id = create_mesh(sg, "nickbg"); - let node = sg.get_node(node_id).unwrap(); - let prop = node.get_property("rect").unwrap(); - prop.set_f32(0, 0.).unwrap(); - let code = - vec![Op::Sub((Box::new(Op::LoadVar("lh".to_string())), Box::new(Op::ConstFloat32(60.))))]; - prop.set_expr(1, code).unwrap(); - prop.set_f32(2, 130.).unwrap(); - prop.set_f32(3, 60.).unwrap(); - - let prop = node.get_property("data").unwrap(); - - let mut buff = Buffer::new(); - // FIXME: layer dim is passed here manually! - // we should just use separate objs - buff.draw_outline( - Rectangle { x: 0., y: 0., w: 1., h: 1. }, - [0., 0.13, 0.08, 1.], - 1., - 1000., - 50., - ); - prop.set_buf(0, buff.verts).unwrap(); - prop.set_buf(1, buff.faces).unwrap(); - - // Nickname - let node_id = create_text(sg, "nick", layer_node_id); - let node = sg.get_node(node_id).unwrap(); - let prop = node.get_property("rect").unwrap(); - prop.set_f32(0, 20.).unwrap(); - let code = - vec![Op::Sub((Box::new(Op::LoadVar("lh".to_string())), Box::new(Op::ConstFloat32(60.))))]; - prop.set_expr(1, code).unwrap(); - prop.set_f32(2, 120.).unwrap(); - prop.set_f32(3, 60.).unwrap(); - node.set_property_f32("baseline", 40.).unwrap(); - node.set_property_f32("font_size", 20.).unwrap(); - node.set_property_str("text", "anon1").unwrap(); - let prop = node.get_property("color").unwrap(); - prop.set_f32(0, 0.).unwrap(); - prop.set_f32(1, 1.).unwrap(); - prop.set_f32(2, 0.).unwrap(); - prop.set_f32(3, 1.).unwrap(); - node.set_property_u32("z_index", 1).unwrap(); - - // Text edit - let node_id = create_editbox(sg, "editz", layer_node_id); - let node = sg.get_node(node_id).unwrap(); - node.set_property_bool("is_active", true).unwrap(); - let prop = node.get_property("rect").unwrap(); - prop.set_f32(0, 150.).unwrap(); - let code = - vec![Op::Sub((Box::new(Op::LoadVar("lh".to_string())), Box::new(Op::ConstFloat32(60.))))]; - prop.set_expr(1, code).unwrap(); - let code = - vec![Op::Sub((Box::new(Op::LoadVar("lw".to_string())), Box::new(Op::ConstFloat32(120.))))]; - prop.set_expr(2, code).unwrap(); - prop.set_f32(3, 60.).unwrap(); - node.set_property_f32("baseline", 40.).unwrap(); - node.set_property_f32("font_size", 20.).unwrap(); - //node.set_property_str("text", "hello king!😁🍆jelly 🍆1234").unwrap(); - node.set_property_str("text", "hello king! jelly 1234").unwrap(); - let prop = node.get_property("text_color").unwrap(); - prop.set_f32(0, 1.).unwrap(); - prop.set_f32(1, 1.).unwrap(); - prop.set_f32(2, 1.).unwrap(); - prop.set_f32(3, 1.).unwrap(); - let prop = node.get_property("cursor_color").unwrap(); - prop.set_f32(0, 1.).unwrap(); - prop.set_f32(1, 0.5).unwrap(); - prop.set_f32(2, 0.5).unwrap(); - prop.set_f32(3, 1.).unwrap(); - let prop = node.get_property("hi_bg_color").unwrap(); - prop.set_f32(0, 1.).unwrap(); - prop.set_f32(1, 1.).unwrap(); - prop.set_f32(2, 1.).unwrap(); - prop.set_f32(3, 0.5).unwrap(); - let prop = node.get_property("selected").unwrap(); - prop.set_null(0).unwrap(); - prop.set_null(1).unwrap(); - node.set_property_u32("z_index", 1).unwrap(); - - let node_id = node.id; - 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| { - //tx.send(result).unwrap(); - }); - let win_node = sg.lookup_node_mut("/window").unwrap(); - win_node.call_method("create_edit_box", arg_data, response_fn).unwrap(); - //let _result = rx.recv().unwrap(); - - // ChatView - let node_id = create_chatview(sg, "chatty", layer_node_id); - let node = sg.get_node(node_id).unwrap(); - let prop = node.get_property("rect").unwrap(); - prop.set_f32(0, 0.).unwrap(); - prop.set_f32(1, 0.).unwrap(); - let code = vec![Op::LoadVar("lw".to_string())]; - prop.set_expr(2, code).unwrap(); - let code = - vec![Op::Sub((Box::new(Op::LoadVar("lh".to_string())), Box::new(Op::ConstFloat32(50.))))]; - prop.set_expr(3, code).unwrap(); - node.set_property_u32("z_index", 1).unwrap(); - - // On android lets scale the UI up - let win_node = sg.lookup_node_mut("/window").unwrap(); - win_node.set_property_f32("scale", 1.6).unwrap(); - - let layer_node = sg.get_node(layer_node_id).unwrap(); - layer_node.set_property_bool("is_visible", true).unwrap(); -} diff --git a/bin/darkwallet/src/chatview.rs b/bin/darkwallet/src/chatview.rs deleted file mode 100644 index 37ebc7a33..000000000 --- a/bin/darkwallet/src/chatview.rs +++ /dev/null @@ -1,339 +0,0 @@ -/* This file is part of DarkFi (https://dark.fi) - * - * Copyright (C) 2020-2024 Dyne.org foundation - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -use atomic_float::AtomicF32; -use miniquad::{TextureId, UniformType}; -use std::{ - collections::HashMap, - io::{BufRead, BufReader}, - path::Path, - sync::{ - atomic::Ordering, - Arc, Mutex, - }, -}; - -use crate::{ - error::Result, - gfx::{ - FreetypeFace, Point, Rectangle, RenderContext, - COLOR_RED, COLOR_WHITE, - }, - prop::PropertyBool, - scene::{Pimpl, SceneGraph, SceneNodeId}, - text::{Glyph, TextShaper}, -}; - -fn read_lines

(filename: P) -> Vec -where - P: AsRef, -{ - //let file = File::open(filename).unwrap(); - //BufReader::new(file).lines().map(|l| l.unwrap()).collect() - // Just so we can package for android easily - // Later this will be all replaced anyway - let file = include_bytes!("../chat.txt"); - BufReader::new(&file[..]).lines().map(|l| l.unwrap()).collect() -} - -pub type ChatViewPtr = Arc; - -pub struct ChatView { - node_name: String, - debug: PropertyBool, - // Used for mouse interaction - world_rect: Mutex>, - mouse_pos: Mutex>, - text_shaper: TextShaper, - scroll: AtomicF32, - lines: Vec, - glyph_lines: Mutex>>, - atlas: Mutex>, -} - -impl ChatView { - pub fn new( - scene_graph: &mut SceneGraph, - node_id: SceneNodeId, - font_faces: Vec, - ) -> Result { - let node = scene_graph.get_node(node_id).unwrap(); - let node_name = node.name.clone(); - let debug = PropertyBool::wrap(node, "debug", 0)?; - - let text_shaper = TextShaper { font_faces }; - - let lines = read_lines("chat.txt"); - let mut glyph_lines = vec![]; - glyph_lines.resize(lines.len(), vec![]); - - let self_ = Arc::new(Self { - node_name: node_name.clone(), - debug, - world_rect: Mutex::new(Rectangle { x: 0., y: 0., w: 0., h: 0. }), - mouse_pos: Mutex::new(Point { x: 0., y: 0. }), - text_shaper, - scroll: AtomicF32::new(0.), - lines, - glyph_lines: Mutex::new(glyph_lines), - atlas: Mutex::new(HashMap::new()), - }); - - /* - let weak_self = Arc::downgrade(&self_); - let slot_move = Slot { - name: format!("{}::mouse_move", node_name), - func: Box::new(move |data| { - let mut cur = Cursor::new(&data); - let x = f32::decode(&mut cur).unwrap(); - let y = f32::decode(&mut cur).unwrap(); - - let self_ = weak_self.upgrade(); - if let Some(self_) = self_ { - let pos = &mut *self_.mouse_pos.lock().unwrap(); - pos.x = x; - pos.y = y; - } - }), - }; - - let weak_self = Arc::downgrade(&self_); - let slot_wheel = Slot { - name: format!("{}::mouse_wheel", node_name), - func: Box::new(move |data| { - let mut cur = Cursor::new(&data); - let x = f32::decode(&mut cur).unwrap(); - let y = f32::decode(&mut cur).unwrap(); - - let self_ = weak_self.upgrade(); - if let Some(self_) = self_ { - self_.mouse_scroll(x, y); - } - }), - }; - */ - - let mouse_node = - scene_graph.lookup_node_mut("/window/input/mouse").expect("no mouse attached!"); - //mouse_node.register("wheel", slot_wheel); - //mouse_node.register("move", slot_move); - - // Save any properties we use - Ok(Pimpl::ChatView(self_)) - } - - pub fn render<'a>( - &self, - render: &mut RenderContext<'a>, - node_id: SceneNodeId, - layer_rect: &Rectangle, - ) -> Result<()> { - let debug = self.debug.get(); - - let node = render.scene_graph.get_node(node_id).unwrap(); - let rect = RenderContext::get_dim(node, layer_rect)?; - - // Used for detecting mouse clicks - let mut world_rect = rect.clone(); - world_rect.x += layer_rect.x as f32; - world_rect.y += layer_rect.y as f32; - *self.world_rect.lock().unwrap() = world_rect; - - let layer_w = layer_rect.w as f32; - let layer_h = layer_rect.h as f32; - let off_x = rect.x / layer_w; - let off_y = rect.y / layer_h; - // Use absolute pixel scale - let scale_x = 1. / layer_w; - let scale_y = 1. / layer_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.)); - - let mut uniforms_data = [0u8; 128]; - let data: [u8; 64] = unsafe { std::mem::transmute_copy(&render.proj) }; - uniforms_data[0..64].copy_from_slice(&data); - let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) }; - uniforms_data[64..].copy_from_slice(&data); - assert_eq!(128, 2 * UniformType::Mat4.size()); - - render.ctx.apply_uniforms_from_bytes(uniforms_data.as_ptr(), uniforms_data.len()); - - // Used for scaling the font size - let window = render.scene_graph.lookup_node("/window").expect("no window attached!"); - let window_scale = window.get_property_f32("scale")?; - let font_size = window_scale * 20.; - - let bound = Rectangle { x: 0., y: 0., w: rect.w, h: rect.h }; - - let glyph_lines = &mut self.glyph_lines.lock().unwrap(); - let atlas = &mut self.atlas.lock().unwrap(); - - let scroll = self.scroll.load(Ordering::Relaxed); - for (i, (line, glyph_line)) in self.lines.iter().zip(glyph_lines.iter_mut()).enumerate() { - let line = line.replace("\t", " "); - - // Split time and nick from the line - let mut iter = line.split_whitespace(); - let Some(time) = iter.next() else { - error!("line missing time"); - continue - }; - let Some(nick) = iter.next() else { - error!("line missing nick"); - continue - }; - let Some(line) = iter.remainder() else { - error!("line missing remainder"); - continue - }; - - let linespacing = window_scale * 30.; - let off_y = linespacing * i as f32 + scroll; - if off_y + linespacing < 0. || off_y - linespacing > rect.h { - continue; - } - - if glyph_line.is_empty() { - *glyph_line = self.text_shaper.shape(line.to_string(), font_size, COLOR_WHITE); - } - - let times_color = [0.4, 0.4, 0.4, 1.]; - let times_color_u8 = - [(255. * 0.4) as u8, (255. * 0.4) as u8, (255. * 0.4) as u8, (255. * 1.) as u8]; - let glyphs_time = self.text_shaper.shape(time.to_string(), font_size, times_color); - let mut rhs = 0.; - for glyph in glyphs_time { - let mut pos = glyph.pos.clone(); - pos.y += off_y; - rhs = pos.x + pos.w; - - assert_eq!(glyph.bmp.len() as u16, glyph.bmp_width * glyph.bmp_height * 4); - //debug!("gly {} {}", glyph.substr, glyph.bmp.len()); - let texture = if atlas.contains_key(&(glyph.id, times_color_u8.clone())) { - *atlas.get(&(glyph.id, times_color_u8.clone())).unwrap() - } else { - let texture = render.ctx.new_texture_from_rgba8( - glyph.bmp_width, - glyph.bmp_height, - &glyph.bmp, - ); - atlas.insert((glyph.id, times_color_u8.clone()), texture); - texture - }; - render.render_clipped_box_with_texture2(&bound, &pos, COLOR_WHITE, texture); - //render.ctx.delete_texture(texture); - } - - let nick_colors = [ - [0.00, 0.94, 1.00, 1.], - [0.36, 1.00, 0.69, 1.], - [0.29, 1.00, 0.45, 1.], - [0.00, 0.73, 0.38, 1.], - [0.21, 0.67, 0.67, 1.], - [0.56, 0.61, 1.00, 1.], - [0.84, 0.48, 1.00, 1.], - [1.00, 0.61, 0.94, 1.], - [1.00, 0.36, 0.48, 1.], - [1.00, 0.30, 0.00, 1.], - ]; - let nick_colors_u8 = [ - [(255. * 0.00) as u8, (255. * 0.94) as u8, (255. * 1.00) as u8, (255. * 1.) as u8], - [(255. * 0.36) as u8, (255. * 1.00) as u8, (255. * 0.69) as u8, (255. * 1.) as u8], - [(255. * 0.29) as u8, (255. * 1.00) as u8, (255. * 0.45) as u8, (255. * 1.) as u8], - [(255. * 0.00) as u8, (255. * 0.73) as u8, (255. * 0.38) as u8, (255. * 1.) as u8], - [(255. * 0.21) as u8, (255. * 0.67) as u8, (255. * 0.67) as u8, (255. * 1.) as u8], - [(255. * 0.56) as u8, (255. * 0.61) as u8, (255. * 1.00) as u8, (255. * 1.) as u8], - [(255. * 0.84) as u8, (255. * 0.48) as u8, (255. * 1.00) as u8, (255. * 1.) as u8], - [(255. * 1.00) as u8, (255. * 0.61) as u8, (255. * 0.94) as u8, (255. * 1.) as u8], - [(255. * 1.00) as u8, (255. * 0.36) as u8, (255. * 0.48) as u8, (255. * 1.) as u8], - [(255. * 1.00) as u8, (255. * 0.30) as u8, (255. * 0.00) as u8, (255. * 1.) as u8], - ]; - - let nick_color = nick_colors[nick.len() % nick_colors.len()]; - let nick_color_u8 = nick_colors_u8[nick.len() % nick_colors.len()]; - let glyphs_nick = self.text_shaper.shape(nick.to_string(), font_size, nick_color); - let off_x = rhs + window_scale * 20.; - for glyph in glyphs_nick { - let mut pos = glyph.pos.clone(); - pos.x += off_x; - pos.y += off_y; - rhs = pos.x + pos.w; - - assert_eq!(glyph.bmp.len() as u16, glyph.bmp_width * glyph.bmp_height * 4); - //debug!("gly {} {}", glyph.substr, glyph.bmp.len()); - //let texture = render.ctx.new_texture_from_rgba8(glyph.bmp_width, glyph.bmp_height, &glyph.bmp); - let texture = if atlas.contains_key(&(glyph.id, nick_color_u8.clone())) { - *atlas.get(&(glyph.id, nick_color_u8.clone())).unwrap() - } else { - let texture = render.ctx.new_texture_from_rgba8( - glyph.bmp_width, - glyph.bmp_height, - &glyph.bmp, - ); - atlas.insert((glyph.id, nick_color_u8.clone()), texture); - texture - }; - render.render_clipped_box_with_texture2(&bound, &pos, COLOR_WHITE, texture); - //render.ctx.delete_texture(texture); - } - - let off_x = rhs + window_scale * 20.; - for glyph in glyph_line { - let mut pos = glyph.pos.clone(); - pos.x += off_x; - pos.y += off_y; - - assert_eq!(glyph.bmp.len() as u16, glyph.bmp_width * glyph.bmp_height * 4); - //debug!("gly {} {}", glyph.substr, glyph.bmp.len()); - //let texture = render.ctx.new_texture_from_rgba8(glyph.bmp_width, glyph.bmp_height, &glyph.bmp); - let texture = if atlas.contains_key(&(glyph.id, [255, 255, 255, 255])) { - *atlas.get(&(glyph.id, [255, 255, 255, 255])).unwrap() - } else { - let texture = render.ctx.new_texture_from_rgba8( - glyph.bmp_width, - glyph.bmp_height, - &glyph.bmp, - ); - atlas.insert((glyph.id, [255, 255, 255, 255]), texture); - texture - }; - render.render_clipped_box_with_texture2(&bound, &pos, COLOR_WHITE, texture); - //render.ctx.delete_texture(texture); - } - } - - if debug { - render.outline(0., 0., rect.w, rect.h, COLOR_RED, 1.); - } - - Ok(()) - } - - fn mouse_scroll(self: Arc, _x: f32, y: f32) { - let mouse_pos = &*self.mouse_pos.lock().unwrap(); - if !self.world_rect.lock().unwrap().contains(mouse_pos) { - return; - } - drop(mouse_pos); - - self.scroll.fetch_add(y * 10., Ordering::Relaxed); - // y = 1 for scroll up - // y = -1 for scroll down - //println!("{}", y); - } -} diff --git a/bin/darkwallet/src/darkirc.rs b/bin/darkwallet/src/darkirc.rs index 608b38873..b897d5857 100644 --- a/bin/darkwallet/src/darkirc.rs +++ b/bin/darkwallet/src/darkirc.rs @@ -39,7 +39,7 @@ use darkfi_serial::{ use crate::{ net::ZeroMQAdapter, scene::{SceneGraph, SceneGraphPtr2}, - text2::TextShaper, + text::TextShaper, ExecutorPtr, }; diff --git a/bin/darkwallet/src/editbox.rs b/bin/darkwallet/src/editbox.rs deleted file mode 100644 index 31c0711c2..000000000 --- a/bin/darkwallet/src/editbox.rs +++ /dev/null @@ -1,865 +0,0 @@ -/* This file is part of DarkFi (https://dark.fi) - * - * Copyright (C) 2020-2024 Dyne.org foundation - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -use log::{debug, info}; -use miniquad::{window, KeyMods, MouseButton, TextureId, UniformType}; -use std::{ - collections::HashMap, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, Mutex, - }, - time::Instant, -}; - -use crate::{ - error::{Error, Result}, - gfx::{ - FreetypeFace, Point, Rectangle, RenderContext, COLOR_DARKGREY, COLOR_GREEN, COLOR_WHITE, - }, - prop::{Property, PropertyBool, PropertyColor, PropertyFloat32, PropertyStr, PropertyUint32}, - scene::{Pimpl, SceneGraph, SceneNodeId}, - text::{Glyph, TextShaper}, -}; - -const CURSOR_WIDTH: f32 = 4.; - -struct PressedKeysSmoothRepeat { - /// When holding keys, we track from start and last sent time. - /// This is useful for initial delay and smooth scrolling. - pressed_keys: HashMap, - /// Initial delay before allowing keys - start_delay: u32, - /// Minimum time between repeated keys - step_time: u32, -} - -impl PressedKeysSmoothRepeat { - fn new(start_delay: u32, step_time: u32) -> Self { - Self { pressed_keys: HashMap::new(), start_delay, step_time } - } - - fn key_down(&mut self, key: &str, repeat: bool) -> u32 { - if !repeat { - return 1; - } - - // Insert key if not exists - if !self.pressed_keys.contains_key(key) { - self.pressed_keys.insert(key.to_string(), RepeatingKeyTimer::new()); - } - - let repeater = self.pressed_keys.get_mut(key).expect("repeat map"); - repeater.update(self.start_delay, self.step_time) - } - - fn key_up(&mut self, key: &str) { - self.pressed_keys.remove(key); - } -} - -struct RepeatingKeyTimer { - start: Instant, - actions: u32, -} - -impl RepeatingKeyTimer { - fn new() -> Self { - Self { start: Instant::now(), actions: 0 } - } - - fn update(&mut self, start_delay: u32, step_time: u32) -> u32 { - let elapsed = self.start.elapsed().as_millis(); - if elapsed < start_delay as u128 { - return 0 - } - let total_actions = ((elapsed - start_delay as u128) / step_time as u128) as u32; - let remaining_actions = total_actions - self.actions; - self.actions = total_actions; - remaining_actions - } -} - -pub type EditBoxPtr = Arc; - -pub struct EditBox { - node_name: String, - is_active: PropertyBool, - debug: PropertyBool, - baseline: PropertyFloat32, - scroll: PropertyFloat32, - cursor_pos: PropertyUint32, - selected: Arc, - text: PropertyStr, - font_size: PropertyFloat32, - text_color: PropertyColor, - cursor_color: PropertyColor, - hi_bg_color: PropertyColor, - // Used for mouse clicks - world_rect: Mutex>, - glyphs: Mutex>, - text_shaper: TextShaper, - key_repeat: Mutex, - mouse_btn_held: AtomicBool, - window_scale: f32, - screen_size: Arc, - atlas: Mutex>, -} - -impl EditBox { - pub fn new( - scene_graph: &mut SceneGraph, - node_id: SceneNodeId, - font_faces: Vec, - ) -> Result { - let node = scene_graph.get_node(node_id).unwrap(); - let node_name = node.name.clone(); - let is_active = PropertyBool::wrap(node, "is_active", 0)?; - let debug = PropertyBool::wrap(node, "debug", 0)?; - let baseline = PropertyFloat32::wrap(node, "baseline", 0)?; - let scroll = PropertyFloat32::wrap(node, "scroll", 0)?; - let cursor_pos = PropertyUint32::wrap(node, "cursor_pos", 0)?; - let selected = node.get_property("selected").ok_or(Error::PropertyNotFound)?; - let text = PropertyStr::wrap(node, "text", 0)?; - let font_size = PropertyFloat32::wrap(node, "font_size", 0)?; - let text_color = PropertyColor::wrap(node, "text_color")?; - let cursor_color = PropertyColor::wrap(node, "cursor_color")?; - let hi_bg_color = PropertyColor::wrap(node, "hi_bg_color")?; - - let text_shaper = TextShaper { font_faces }; - - // TODO: catch window resize event and regen glyphs - // Used for scaling the font size - let window_node = scene_graph.lookup_node("/window").expect("no window attached!"); - let window_scale = window_node.get_property_f32("scale")?; - let screen_size = window_node.get_property("screen_size").ok_or(Error::PropertyNotFound)?; - - let self_ = Arc::new(Self { - node_name: node_name.clone(), - is_active, - debug, - baseline, - scroll, - cursor_pos, - selected, - text, - font_size, - text_color, - cursor_color, - hi_bg_color, - world_rect: Mutex::new(Rectangle { x: 0., y: 0., w: 0., h: 0. }), - glyphs: Mutex::new(vec![]), - text_shaper, - key_repeat: Mutex::new(PressedKeysSmoothRepeat::new(400, 50)), - mouse_btn_held: AtomicBool::new(false), - window_scale, - screen_size, - atlas: Mutex::new(HashMap::new()), - }); - self_.regen_glyphs().unwrap(); - - /* - let weak_self = Arc::downgrade(&self_); - let slot_key_down = Slot { - name: format!("{}::key_down", node_name), - func: Box::new(move |data| { - let mut cur = Cursor::new(&data); - let keymods = KeyMods { - shift: Decodable::decode(&mut cur).unwrap(), - ctrl: Decodable::decode(&mut cur).unwrap(), - alt: Decodable::decode(&mut cur).unwrap(), - logo: Decodable::decode(&mut cur).unwrap(), - }; - let repeat = bool::decode(&mut cur).unwrap(); - let key = String::decode(&mut cur).unwrap(); - - let self_ = weak_self.upgrade(); - if let Some(self_) = self_ { - self_.key_down(key, keymods, repeat); - } - }), - }; - let weak_self = Arc::downgrade(&self_); - let slot_key_up = Slot { - name: format!("{}::key_up", node_name), - func: Box::new(move |data| { - let mut cur = Cursor::new(&data); - let keymods = KeyMods { - shift: Decodable::decode(&mut cur).unwrap(), - ctrl: Decodable::decode(&mut cur).unwrap(), - alt: Decodable::decode(&mut cur).unwrap(), - logo: Decodable::decode(&mut cur).unwrap(), - }; - let key = String::decode(&mut cur).unwrap(); - - let self_ = weak_self.upgrade(); - if let Some(self_) = self_ { - self_.key_up(key, keymods); - } - }), - }; - - let keyb_node = - scene_graph - .lookup_node_mut("/window/input/keyboard") - .expect("no keyboard attached!"); - keyb_node.register("key_down", slot_key_down).unwrap(); - keyb_node.register("key_up", slot_key_up).unwrap(); - - let weak_self = Arc::downgrade(&self_); - let slot_btn_down = Slot { - name: format!("{}::mouse_button_down", node_name), - func: Box::new(move |data| { - let mut cur = Cursor::new(&data); - let button = MouseButton::from_u8(u8::decode(&mut cur).unwrap()); - let x = f32::decode(&mut cur).unwrap(); - let y = f32::decode(&mut cur).unwrap(); - - let self_ = weak_self.upgrade(); - if let Some(self_) = self_ { - self_.mouse_button_down(button, x, y); - } - }), - }; - - let weak_self = Arc::downgrade(&self_); - let slot_btn_up = Slot { - name: format!("{}::mouse_button_up", node_name), - func: Box::new(move |data| { - let mut cur = Cursor::new(&data); - let button = MouseButton::from_u8(u8::decode(&mut cur).unwrap()); - let x = f32::decode(&mut cur).unwrap(); - let y = f32::decode(&mut cur).unwrap(); - - let self_ = weak_self.upgrade(); - if let Some(self_) = self_ { - self_.mouse_button_up(button, x, y); - } - }), - }; - - let weak_self = Arc::downgrade(&self_); - let slot_move = Slot { - name: format!("{}::mouse_move", node_name), - func: Box::new(move |data| { - let mut cur = Cursor::new(&data); - let x = f32::decode(&mut cur).unwrap(); - let y = f32::decode(&mut cur).unwrap(); - - let self_ = weak_self.upgrade(); - if let Some(self_) = self_ { - self_.mouse_move(x, y); - } - }), - }; - - let mouse_node = - scene_graph - .lookup_node_mut("/window/input/mouse") - .expect("no mouse attached!"); - mouse_node.register("button_down", slot_btn_down).unwrap(); - mouse_node.register("button_up", slot_btn_up).unwrap(); - mouse_node.register("move", slot_move).unwrap(); - - let weak_self = Arc::downgrade(&self_); - let slot_resize = Slot { - name: format!("{}::window_resize", node_name), - func: Box::new(move |data| { - let mut cur = Cursor::new(&data); - let w = f32::decode(&mut cur).unwrap(); - let h = f32::decode(&mut cur).unwrap(); - - let self_ = weak_self.upgrade(); - if let Some(self_) = self_ { - self_.window_resize(w, h); - } - }), - }; - */ - - let window_node = scene_graph.lookup_node_mut("/window").expect("no window attached!"); - //window_node.register("resize", slot_resize).unwrap(); - - // Save any properties we use - Ok(Pimpl::EditBox(self_)) - } - - pub fn render<'a>( - &self, - render: &mut RenderContext<'a>, - node_id: SceneNodeId, - layer_rect: &Rectangle, - ) -> Result<()> { - let node = render.scene_graph.get_node(node_id).unwrap(); - - let rect = RenderContext::get_dim(node, layer_rect)?; - - // Used for detecting mouse clicks - let mut world_rect = rect.clone(); - world_rect.x += layer_rect.x as f32; - world_rect.y += layer_rect.y as f32; - world_rect.x *= self.window_scale; - world_rect.y *= self.window_scale; - world_rect.w *= self.window_scale; - world_rect.h *= self.window_scale; - *self.world_rect.lock().unwrap() = world_rect; - - let layer_w = layer_rect.w as f32; - let layer_h = layer_rect.h as f32; - let off_x = rect.x / layer_w; - let off_y = rect.y / layer_h; - // Use absolute pixel scale - let scale_x = 1. / layer_w; - let scale_y = 1. / layer_h; - //let model = glam::Mat4::IDENTITY; - 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.)); - - let mut uniforms_data = [0u8; 128]; - let data: [u8; 64] = unsafe { std::mem::transmute_copy(&render.proj) }; - uniforms_data[0..64].copy_from_slice(&data); - let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) }; - uniforms_data[64..].copy_from_slice(&data); - assert_eq!(128, 2 * UniformType::Mat4.size()); - - render.ctx.apply_uniforms_from_bytes(uniforms_data.as_ptr(), uniforms_data.len()); - - self.apply_cursor_scrolling(&rect); - - let node = render.scene_graph.get_node(node_id).unwrap(); - let debug = self.debug.get(); - let baseline = self.baseline.get(); - let scroll = self.scroll.get(); - let cursor_pos = self.cursor_pos.get() as usize; - let cursor_color = self.cursor_color.get(); - let text_color = self.text_color.get(); - - let glyphs = &*self.glyphs.lock().unwrap(); - let atlas = &mut self.atlas.lock().unwrap(); - - if !self.selected.is_null(0)? && !self.selected.is_null(1)? { - self.render_selected(render, &rect, glyphs)?; - } - - let bound = Rectangle { x: 0., y: 0., w: rect.w, h: rect.h }; - - let mut rhs = 0.; - for (glyph_idx, glyph) in glyphs.iter().enumerate() { - let x1 = glyph.pos.x + scroll; - let y1 = glyph.pos.y + baseline; - let x2 = x1 + glyph.pos.w; - let y2 = y1 + glyph.pos.h; - - let texture = if atlas.contains_key(&glyph.id) { - *atlas.get(&glyph.id).unwrap() - } else { - let texture = render.ctx.new_texture_from_rgba8( - glyph.bmp_width, - glyph.bmp_height, - &glyph.bmp, - ); - atlas.insert(glyph.id, texture); - texture - }; - //let texture = render.ctx.new_texture_from_rgba8(glyph.bmp_width, glyph.bmp_height, &glyph.bmp); - render.render_clipped_box_with_texture(&bound, x1, y1, x2, y2, COLOR_WHITE, texture); - //render.render_box_with_texture(x1, y1, x2, y2, COLOR_WHITE, texture); - //render.ctx.delete_texture(texture); - - // Glyph outlines - //if debug { - // render.outline(x1, y1, x2, y2, COLOR_BLUE, 1.); - //} - - if cursor_pos != 0 && cursor_pos == glyph_idx { - render.render_box(x1 - CURSOR_WIDTH, 0., x1, rect.h, cursor_color); - } - - rhs = x2; - } - - if cursor_pos == 0 { - render.render_box(0., 0., CURSOR_WIDTH, rect.h, cursor_color); - } else if cursor_pos == glyphs.len() { - render.render_box(rhs - CURSOR_WIDTH, 0., rhs, rect.h, cursor_color); - } - - if debug { - let outline_color = if self.is_active.get() { COLOR_GREEN } else { COLOR_DARKGREY }; - // Baseline - //render.hline(0., rhs, 0., COLOR_RED, 1.); - render.outline(0., 0., rect.w, rect.h, outline_color, 1.); - } - - Ok(()) - } - - pub fn render_selected<'a>( - &self, - render: &mut RenderContext<'a>, - rect: &Rectangle, - glyphs: &Vec, - ) -> Result<()> { - let start = self.selected.get_u32(0)? as usize; - let end = self.selected.get_u32(1)? as usize; - - let sel_start = std::cmp::min(start, end); - let sel_end = std::cmp::max(start, end); - - let scroll = self.scroll.get(); - let hi_bg_color = self.hi_bg_color.get(); - - let mut start_x = 0.; - let mut end_x = 0.; - - for (glyph_idx, glyph) in glyphs.iter().enumerate() { - let x1 = glyph.pos.x + scroll; - - if glyph_idx == sel_start { - start_x = x1; - } - if glyph_idx == sel_end { - end_x = x1; - } - } - if sel_start == 0 { - start_x = scroll; - } - if sel_end == glyphs.len() { - let glyph = &glyphs.last().unwrap(); - let x2 = glyph.pos.x + scroll + glyph.pos.w; - end_x = x2; - } - - // Apply clipping - if start_x < 0. { - start_x = 0.; - } - if end_x > rect.w { - end_x = rect.w; - } - render.render_box(start_x, 0., end_x, rect.h, hi_bg_color); - Ok(()) - } - - fn delete_highlighted(&self) { - assert!(!self.selected.is_null(0).unwrap()); - assert!(!self.selected.is_null(1).unwrap()); - - let start = self.selected.get_u32(0).unwrap() as usize; - let end = self.selected.get_u32(1).unwrap() as usize; - - let sel_start = std::cmp::min(start, end); - let sel_end = std::cmp::max(start, end); - - let glyphs = &*self.glyphs.lock().unwrap(); - - // Regen text - let mut text = String::new(); - for (i, glyph) in glyphs.iter().enumerate() { - let mut substr = glyph.substr.clone(); - if sel_start <= i && i < sel_end { - continue - } - text.push_str(&substr); - } - debug!( - "EditBox(\"{}\")::delete_highlighted() text=\"{}\", cursor_pos={}", - self.node_name, text, sel_start - ); - self.text.set(text); - - self.selected.set_null(0).unwrap(); - self.selected.set_null(1).unwrap(); - self.cursor_pos.set(sel_start as u32); - } - - fn apply_cursor_scrolling(&self, rect: &Rectangle) { - let cursor_pos = self.cursor_pos.get() as usize; - let mut scroll = self.scroll.get(); - - let cursor_x = { - let glyphs = &*self.glyphs.lock().unwrap(); - if cursor_pos == 0 { - 0. - } else if cursor_pos == glyphs.len() { - let glyph = &glyphs.last().unwrap(); - glyph.pos.x + glyph.pos.w - } else { - assert!(cursor_pos < glyphs.len()); - let glyph = &glyphs[cursor_pos]; - glyph.pos.x - } - }; - - let cursor_lhs = cursor_x + scroll; - let cursor_rhs = cursor_lhs + CURSOR_WIDTH; - - if cursor_rhs > rect.w { - scroll = rect.w - cursor_x; - } else if cursor_lhs < 0. { - scroll = -cursor_x + CURSOR_WIDTH; - } - - self.scroll.set(scroll); - } - - fn regen_glyphs(&self) -> Result<()> { - let font_size = self.window_scale * self.font_size.get(); - - debug!("shape start"); - let glyphs = self.text_shaper.shape(self.text.get(), font_size, self.text_color.get()); - debug!("shape end"); - if self.cursor_pos.get() > glyphs.len() as u32 { - self.cursor_pos.set(glyphs.len() as u32); - } - *self.glyphs.lock().unwrap() = glyphs; - Ok(()) - } - - fn key_down(self: Arc, key: String, mods: KeyMods, repeat: bool) { - if !self.is_active.get() { - return - } - let actions = { - let mut repeater = self.key_repeat.lock().unwrap(); - repeater.key_down(&key, repeat) - }; - for _ in 0..actions { - self.do_key_down(&key, &mods) - } - } - - fn do_key_down(&self, key: &str, mods: &KeyMods) { - match key { - "Left" => { - let mut cursor_pos = self.cursor_pos.get(); - - // Start selection if shift is held - if !mods.shift { - self.selected.set_null(0).unwrap(); - self.selected.set_null(1).unwrap(); - } else if self.selected.is_null(0).unwrap() { - assert!(self.selected.is_null(1).unwrap()); - self.selected.set_u32(0, cursor_pos).unwrap(); - } - - if cursor_pos > 0 { - cursor_pos -= 1; - debug!( - "EditBox(\"{}\")::key_down(Left) cursor_pos={}", - self.node_name, cursor_pos - ); - self.cursor_pos.set(cursor_pos); - } - - // Update selection - if mods.shift { - self.selected.set_u32(1, cursor_pos).unwrap(); - } - } - "Right" => { - let mut cursor_pos = self.cursor_pos.get(); - - // Start selection if shift is held - if !mods.shift { - self.selected.set_null(0).unwrap(); - self.selected.set_null(1).unwrap(); - } else if self.selected.is_null(0).unwrap() { - assert!(self.selected.is_null(1).unwrap()); - self.selected.set_u32(0, cursor_pos).unwrap(); - } - - let glyphs_len = self.glyphs.lock().unwrap().len() as u32; - if cursor_pos < glyphs_len { - cursor_pos += 1; - debug!( - "EditBox(\"{}\")::key_down(Right) cursor_pos={}", - self.node_name, cursor_pos - ); - self.cursor_pos.set(cursor_pos); - } - - // Update selection - if mods.shift { - self.selected.set_u32(1, cursor_pos).unwrap(); - } - } - "Delete" => { - let cursor_pos = self.cursor_pos.get(); - - let _text = if !self.selected.is_null(0).unwrap() { - self.delete_highlighted(); - } else { - let glyphs = &*self.glyphs.lock().unwrap(); - - if cursor_pos == glyphs.len() as u32 { - return; - } - - // Regen text - let mut text = String::new(); - for (i, glyph) in glyphs.iter().enumerate() { - let mut substr = glyph.substr.clone(); - if cursor_pos as usize == i { - // Lmk if anyone knows a better way to do substr.pop_front() - let mut chars = substr.chars(); - chars.next(); - substr = chars.as_str().to_string(); - } - text.push_str(&substr); - } - self.text.set(text); - }; - self.regen_glyphs().unwrap(); - } - "Backspace" => { - let cursor_pos = self.cursor_pos.get(); - - let _text = if !self.selected.is_null(0).unwrap() { - self.delete_highlighted(); - } else { - if cursor_pos == 0 { - return; - } - - let glyphs = &*self.glyphs.lock().unwrap(); - - let mut text = String::new(); - for (i, glyph) in glyphs.iter().enumerate() { - let mut substr = glyph.substr.clone(); - if cursor_pos as usize - 1 == i { - substr.pop().unwrap(); - } - text.push_str(&substr); - } - self.text.set(text); - self.cursor_pos.set(cursor_pos - 1); - }; - self.regen_glyphs().unwrap(); - } - "C" => { - if mods.ctrl { - self.copy_highlighted_text().unwrap(); - } else { - self.insert_char(key, mods); - } - } - "V" => { - if mods.ctrl { - if let Some(text) = window::clipboard_get() { - self.insert_text(text); - } - } else { - self.insert_char(key, mods); - } - } - _ => { - self.insert_char(key, mods); - } - } - } - - fn insert_char(&self, key: &str, mods: &KeyMods) { - // First filter for only single digit keys - let allowed_keys = [ - "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", - "R", "S", "T", "U", "V", "W", "X", "Y", "Z", " ", ":", ";", "'", "-", ".", "/", "=", - "(", "\\", ")", "`", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", - ]; - if !allowed_keys.contains(&key) { - return - } - - // If we want to only allow specific chars in a String here - //let ch = key.chars().next().unwrap(); - // if !self.allowed_chars.chars().any(|c| c == ch) { return } - - let key = if mods.shift { key.to_string() } else { key.to_lowercase() }; - - self.insert_text(key); - } - - fn insert_text(&self, key: String) { - let mut text = String::new(); - - let cursor_pos = self.cursor_pos.get(); - - if cursor_pos == 0 { - text = key; - } else { - let glyphs = &*self.glyphs.lock().unwrap(); - for (glyph_idx, glyph) in glyphs.iter().enumerate() { - text.push_str(&glyph.substr); - if cursor_pos == glyph_idx as u32 + 1 { - text.push_str(&key); - } - } - } - self.text.set(text); - // Not always true lol - self.cursor_pos.set(cursor_pos + 1); - self.regen_glyphs().unwrap(); - } - - fn copy_highlighted_text(&self) -> Result<()> { - let start = self.selected.get_u32(0)? as usize; - let end = self.selected.get_u32(1)? as usize; - - let sel_start = std::cmp::min(start, end); - let sel_end = std::cmp::max(start, end); - - let mut text = String::new(); - - let glyphs = &*self.glyphs.lock().unwrap(); - for (glyph_idx, glyph) in glyphs.iter().enumerate() { - if sel_start <= glyph_idx && glyph_idx < sel_end { - text.push_str(&glyph.substr); - } - } - - info!("Copied '{}'", text); - window::clipboard_set(&text); - Ok(()) - } - - fn key_up(self: Arc, key: String, mods: KeyMods) { - let mut repeater = self.key_repeat.lock().unwrap(); - repeater.key_up(&key); - } - - fn mouse_button_down(self: Arc, button: MouseButton, x: f32, y: f32) { - let mouse_pos = Point { x, y }; - let rect = self.world_rect.lock().unwrap().clone(); - debug!("mouse down event {} {} {} {} {} {}", x, y, rect.x, rect.y, rect.w, rect.h); - - // clicking inside box will: - // 1. make it active - // 2. begin selection - if rect.contains(&mouse_pos) { - debug!("showkeyb!"); - window::show_keyboard(true); - if !self.is_active.get() { - self.is_active.set(true); - println!("inside!"); - // Send signal - } - - let cpos = match self.find_closest_glyph_idx(x) { - MouseClickGlyph::Pos(cpos) => cpos, - _ => panic!("shouldn't be possible to reach here!"), - }; - - // set cursor pos - self.cursor_pos.set(cpos); - - // begin selection - self.selected.set_u32(0, cpos).unwrap(); - self.selected.set_u32(1, cpos).unwrap(); - self.mouse_btn_held.store(true, Ordering::Relaxed); - } - // click outside the box will: - // 1. make it inactive - else { - if self.is_active.get() { - self.is_active.set(false); - // Send signal - } - } - } - fn mouse_button_up(self: Arc, button: MouseButton, x: f32, y: f32) { - // releasing mouse button will: - // 1. end selection - self.mouse_btn_held.store(false, Ordering::Relaxed); - } - fn mouse_move(self: Arc, x: f32, y: f32) { - if !self.mouse_btn_held.load(Ordering::Relaxed) { - return; - } - - // if active and selection_active, then use x to modify the selection. - // also implement scrolling when cursor is to the left or right - // just scroll to the end - // also set cursor_pos too - - let cpos = match self.find_closest_glyph_idx(x) { - MouseClickGlyph::Lhs => 0, - MouseClickGlyph::Pos(cpos) => cpos, - MouseClickGlyph::Rhs(cpos) => cpos, - }; - - self.cursor_pos.set(cpos); - self.selected.set_u32(1, cpos).unwrap(); - } - - // Uses screen x pos - fn find_closest_glyph_idx(&self, x: f32) -> MouseClickGlyph { - let rect = self.world_rect.lock().unwrap().clone(); - let glyphs = &*self.glyphs.lock().unwrap(); - - let mouse_x = x - rect.x; - - if mouse_x > rect.w { - // Highlight to the end - let cpos = glyphs.len() as u32; - return MouseClickGlyph::Rhs(cpos); - // Scroll to the right handled in render - } else if mouse_x < 0. { - return MouseClickGlyph::Lhs; - } - - let scroll = self.scroll.get(); - - let mouse_x = x - rect.x; - let mut cpos = 0; - let lhs = 0.; - let mut last_d = (lhs - mouse_x).abs(); - - for (i, glyph) in glyphs.iter().skip(1).enumerate() { - // Because we skip the first item - let glyph_idx = (i + 1) as u32; - - let x1 = glyph.pos.x + scroll; - let curr_d = (x1 - mouse_x).abs(); - if curr_d < last_d { - last_d = curr_d; - cpos = glyph_idx; - } - } - // also check the right hand side - let rhs = { - let glyph = &glyphs.last().unwrap(); - glyph.pos.x + scroll + glyph.pos.w - }; - let curr_d = (rhs - mouse_x).abs(); - if curr_d < last_d { - //last_d = curr_d; - cpos = glyphs.len() as u32; - } - - MouseClickGlyph::Pos(cpos) - } - - fn window_resize(self: Arc, w: f32, h: f32) {} -} - -enum MouseClickGlyph { - Lhs, - Pos(u32), - Rhs(u32), -} diff --git a/bin/darkwallet/src/gfx.rs b/bin/darkwallet/src/gfx.rs deleted file mode 100644 index c34338644..000000000 --- a/bin/darkwallet/src/gfx.rs +++ /dev/null @@ -1,1352 +0,0 @@ -/* This file is part of DarkFi (https://dark.fi) - * - * Copyright (C) 2020-2024 Dyne.org foundation - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable}; -use freetype as ft; -use miniquad::{ - conf, window, Backend, Bindings, BlendFactor, BlendState, BlendValue, BufferId, BufferLayout, - BufferSource, BufferType, BufferUsage, Equation, EventHandler, KeyCode, KeyMods, MouseButton, - PassAction, Pipeline, PipelineParams, RenderingBackend, ShaderMeta, ShaderSource, TextureId, - UniformDesc, UniformType, VertexAttribute, VertexFormat, -}; -use std::{ - array::IntoIter, - collections::HashMap, - fmt, - io::Cursor, - sync::{mpsc, Mutex, MutexGuard}, - time::{Duration, Instant}, -}; - -use crate::{ - chatview, editbox, - error::{Error, Result}, - expr::{SExprMachine, SExprVal}, - keysym::{KeyCodeAsStr, MouseButtonAsU8}, - prop::{Property, PropertySubType, PropertyType}, - res::{ResourceId, ResourceManager}, - scene::{ - MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr, SceneNode, SceneNodeId, SceneNodeInfo, - SceneNodeType, - }, - shader, -}; - -type Color = [f32; 4]; - -pub const COLOR_RED: Color = [1., 0., 0., 1.]; -pub const COLOR_DARKGREY: Color = [0.2, 0.2, 0.2, 1.]; -pub const COLOR_GREEN: Color = [0., 1., 0., 1.]; -pub const COLOR_BLUE: Color = [0., 0., 1., 1.]; -pub const COLOR_WHITE: Color = [1., 1., 1., 1.]; - -#[derive(Debug, SerialEncodable, SerialDecodable)] -#[repr(C)] -struct Vertex { - pos: [f32; 2], - color: [f32; 4], - uv: [f32; 2], -} - -#[derive(SerialEncodable, SerialDecodable)] -#[repr(C)] -struct Face { - idxs: [u32; 3], -} - -impl fmt::Debug for Face { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.idxs) - } -} - -struct Mesh { - pub verts: Vec, - pub faces: Vec, - pub vertex_buffer: BufferId, - pub index_buffer: BufferId, -} - -pub struct Point { - pub x: T, - pub y: T, -} - -#[derive(Debug, Clone)] -pub struct Rectangle< - T: Copy + std::ops::Add + std::ops::Sub + std::cmp::PartialOrd, -> { - pub x: T, - pub y: T, - pub w: T, - pub h: T, -} - -impl< - T: Copy - + std::ops::Add - + std::ops::Sub - + std::ops::AddAssign - + std::cmp::PartialOrd, - > Rectangle -{ - pub fn from_array(arr: [T; 4]) -> Self { - let mut iter = IntoIter::new(arr); - Self { - x: iter.next().unwrap(), - y: iter.next().unwrap(), - w: iter.next().unwrap(), - h: iter.next().unwrap(), - } - } - - pub fn clip(&self, other: &Self) -> Option { - if other.x + other.w < self.x || - other.x > self.x + self.w || - other.y + other.h < self.y || - other.y > self.y + self.h - { - return None - } - - let mut clipped = other.clone(); - if clipped.x < self.x { - clipped.x = self.x; - clipped.w = other.x + other.w - clipped.x; - } - if clipped.y < self.y { - clipped.y = self.y; - clipped.h = other.y + other.h - clipped.y; - } - if clipped.x + clipped.w > self.x + self.w { - clipped.w = self.x + self.w - clipped.x; - } - if clipped.y + clipped.h > self.y + self.h { - clipped.h = self.y + self.h - clipped.y; - } - Some(clipped) - } - - pub fn contains(&self, point: &Point) -> bool { - self.x <= point.x && - point.x <= self.x + self.w && - self.y <= point.y && - point.y <= self.y + self.h - } - - pub fn top_left(&self) -> Point { - Point { x: self.x, y: self.y } - } - pub fn bottom_right(&self) -> Point { - Point { x: self.x + self.w, y: self.y + self.h } - } - - pub fn includes(&self, child: &Self) -> bool { - self.contains(&child.top_left()) && self.contains(&child.bottom_right()) - } -} - -pub type FreetypeFace = ft::Face<&'static [u8]>; - -#[derive(Debug)] -enum GraphicsMethodEvent { - LoadTexture, - DeleteTexture, - CreateChatView, - CreateEditBox, -} - -struct Stage { - ctx: Box, - pipeline: Pipeline, - - scene_graph: SceneGraphPtr, - - textures: ResourceManager, - font_faces: Vec, - - method_recvr: mpsc::Receiver<(GraphicsMethodEvent, SceneNodeId, Vec, MethodResponseFn)>, - method_sender: mpsc::SyncSender<(GraphicsMethodEvent, SceneNodeId, Vec, MethodResponseFn)>, - - last_draw_time: Option, - atlas: Mutex>, -} - -impl Stage { - const WHITE_TEXTURE_ID: ResourceId = 0; - - pub fn new(scene_graph: SceneGraphPtr) -> Self { - let mut ctx: Box = window::new_rendering_backend(); - - let white_texture = ctx.new_texture_from_rgba8(1, 1, &[255, 255, 255, 255]); - let mut textures = ResourceManager::new(); - let white_texture_id = textures.alloc(white_texture); - assert_eq!(white_texture_id, Self::WHITE_TEXTURE_ID); - - let mut shader_meta: ShaderMeta = shader::meta(); - shader_meta.uniforms.uniforms.push(UniformDesc::new("Projection", UniformType::Mat4)); - shader_meta.uniforms.uniforms.push(UniformDesc::new("Model", UniformType::Mat4)); - - let shader = ctx - .new_shader( - match ctx.info().backend { - Backend::OpenGl => ShaderSource::Glsl { - vertex: shader::GL_VERTEX, - fragment: shader::GL_FRAGMENT, - }, - Backend::Metal => ShaderSource::Msl { program: shader::METAL }, - }, - shader_meta, - ) - .unwrap(); - - let params = PipelineParams { - color_blend: Some(BlendState::new( - Equation::Add, - BlendFactor::Value(BlendValue::SourceAlpha), - BlendFactor::OneMinusValue(BlendValue::SourceAlpha), - )), - ..Default::default() - }; - - let pipeline = ctx.new_pipeline( - &[BufferLayout::default()], - &[ - VertexAttribute::new("in_pos", VertexFormat::Float2), - VertexAttribute::new("in_color", VertexFormat::Float4), - VertexAttribute::new("in_uv", VertexFormat::Float2), - ], - shader, - params, - ); - - let (method_sender, method_recvr) = mpsc::sync_channel(100); - - let ftlib = ft::Library::init().unwrap(); - let mut font_faces = vec![]; - - let font_data = include_bytes!("../ibm-plex-mono-light.otf") as &[u8]; - let ft_face = ftlib.new_memory_face2(font_data, 0).unwrap(); - font_faces.push(ft_face); - - let font_data = include_bytes!("../NotoColorEmoji.ttf") as &[u8]; - let ft_face = ftlib.new_memory_face2(font_data, 0).unwrap(); - font_faces.push(ft_face); - - let mut stage = Stage { - ctx, - pipeline, - scene_graph, - textures, - font_faces, - method_recvr, - method_sender, - last_draw_time: None, - atlas: Mutex::new(HashMap::new()), - }; - stage.setup_scene_graph_window(); - - stage.setup_scene(); - - debug!("Finished loading GUI"); - stage - } - - fn setup_scene_graph_window(&mut self) { - let mut scene_graph = self.scene_graph.lock().unwrap(); - - let window = scene_graph.add_node("window", SceneNodeType::Window); - let (screen_width, screen_height) = window::screen_size(); - - let mut prop = Property::new("screen_size", PropertyType::Float32, PropertySubType::Pixel); - prop.set_array_len(2); - prop.set_f32(0, screen_width); - prop.set_f32(1, screen_height); - window.add_property(prop).unwrap(); - - let mut prop = Property::new("scale", PropertyType::Float32, PropertySubType::Pixel); - prop.set_defaults_f32(vec![1.]).unwrap(); - window.add_property(prop).unwrap(); - - window - .add_signal( - "resize", - "Screen resize event", - vec![ - ("screen_width", "", PropertyType::Float32), - ("screen_height", "", PropertyType::Float32), - ], - ) - .unwrap(); - - let window_id = window.id; - - let sender = self.method_sender.clone(); - 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 sender = self.method_sender.clone(); - let method_fn = Box::new(move |arg_data, response_fn| { - sender.send((GraphicsMethodEvent::CreateChatView, window_id, arg_data, response_fn)); - }); - window - .add_method( - "create_chat_view", - vec![("node_id", "", PropertyType::SceneNodeId)], - vec![], - method_fn, - ) - .unwrap(); - - let sender = self.method_sender.clone(); - let method_fn = Box::new(move |arg_data, response_fn| { - sender.send((GraphicsMethodEvent::CreateEditBox, window_id, arg_data, response_fn)); - }); - window - .add_method( - "create_edit_box", - vec![("node_id", "", PropertyType::SceneNodeId)], - vec![], - method_fn, - ) - .unwrap(); - - scene_graph.link(window_id, SceneGraph::ROOT_ID).unwrap(); - - let input = scene_graph.add_node("input", SceneNodeType::WindowInput); - let input_id = input.id; - scene_graph.link(input_id, window_id).unwrap(); - - 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), - ("alt", "", PropertyType::Bool), - ("logo", "", PropertyType::Bool), - ("repeat", "", PropertyType::Bool), - ("keycode", "", PropertyType::Enum), - ], - ) - .unwrap(); - keyb.add_signal( - "key_up", - "Key press up event", - vec![ - ("shift", "", PropertyType::Bool), - ("ctrl", "", PropertyType::Bool), - ("alt", "", PropertyType::Bool), - ("logo", "", PropertyType::Bool), - ("repeat", "", PropertyType::Bool), - ("keycode", "", PropertyType::Enum), - ], - ) - .unwrap(); - let keyb_id = keyb.id; - scene_graph.link(keyb_id, input_id).unwrap(); - - let mouse = scene_graph.add_node("mouse", SceneNodeType::Mouse); - mouse - .add_signal( - "button_up", - "Mouse button up event", - vec![ - ("button", "", PropertyType::Enum), - ("x", "", PropertyType::Float32), - ("y", "", PropertyType::Float32), - ], - ) - .unwrap(); - mouse - .add_signal( - "button_down", - "Mouse button down event", - vec![ - ("button", "", PropertyType::Enum), - ("x", "", PropertyType::Float32), - ("y", "", PropertyType::Float32), - ], - ) - .unwrap(); - 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(); - let mouse_id = mouse.id; - scene_graph.link(mouse_id, input_id).unwrap(); - } - - 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(); - let width = img.width(); - let height = img.height(); - let bmp = img.into_raw(); - - let texture = self.ctx.new_texture_from_rgba8(width as u16, height as u16, &bmp); - let id = self.textures.alloc(texture); - - let mut scene_graph = self.scene_graph.lock().unwrap(); - let img_node = scene_graph.add_node(node_name, SceneNodeType::RenderTexture); - - let mut prop = Property::new("size", PropertyType::Uint32, PropertySubType::Pixel); - prop.set_array_len(2); - prop.set_u32(0, width).unwrap(); - prop.set_u32(1, height).unwrap(); - img_node.add_property(prop)?; - - let mut prop = - Property::new("texture_rid", PropertyType::Uint32, PropertySubType::ResourceId); - prop.set_u32(0, id).unwrap(); - img_node.add_property(prop)?; - - let mut reply = vec![]; - img_node.id.encode(&mut reply).unwrap(); - - Ok(reply) - } - fn method_delete_texture( - &mut self, - node_id: SceneNodeId, - arg_data: Vec, - ) -> Result> { - let mut cur = Cursor::new(&arg_data); - let texture_id = ResourceId::decode(&mut cur).unwrap(); - let texture = self.textures.get(texture_id).ok_or(Error::ResourceNotFound)?; - self.ctx.delete_texture(*texture); - self.textures.free(texture_id); - Ok(vec![]) - } - - fn method_create_chatview(&mut self, _: SceneNodeId, arg_data: Vec) -> Result> { - debug!("gfx::create_chatview()"); - let mut cur = Cursor::new(&arg_data); - let node_id = SceneNodeId::decode(&mut cur).unwrap(); - - let mut scene_graph = self.scene_graph.lock().unwrap(); - let editbox = chatview::ChatView::new(&mut scene_graph, node_id, self.font_faces.clone())?; - - let node = scene_graph.get_node_mut(node_id).ok_or(Error::NodeNotFound)?; - node.pimpl = editbox; - - let mut reply = vec![]; - Ok(reply) - } - - fn method_create_editbox(&mut self, _: SceneNodeId, arg_data: Vec) -> Result> { - debug!("gfx::create_editbox()"); - let mut cur = Cursor::new(&arg_data); - let node_id = SceneNodeId::decode(&mut cur).unwrap(); - - let mut scene_graph = self.scene_graph.lock().unwrap(); - let editbox = editbox::EditBox::new(&mut scene_graph, node_id, self.font_faces.clone())?; - - let node = scene_graph.get_node_mut(node_id).ok_or(Error::NodeNotFound)?; - node.pimpl = editbox; - - let mut reply = vec![]; - Ok(reply) - } - - fn setup_scene(&mut self) { - let mut sg = self.scene_graph.lock().unwrap(); - crate::chatapp::setup(&mut sg); - } -} - -pub struct RenderContext<'a> { - pub scene_graph: MutexGuard<'a, SceneGraph>, - pub ctx: &'a mut Box, - pub pipeline: &'a Pipeline, - pub proj: glam::Mat4, - pub textures: &'a ResourceManager, - pub font_faces: &'a Vec, - pub atlas: &'a mut HashMap<(u32, [u8; 4]), TextureId>, -} - -impl<'a> RenderContext<'a> { - fn render_window(&mut self) { - for layer in self - .scene_graph - .lookup_node("/window") - .expect("no window attached!") - .get_children(&[SceneNodeType::RenderLayer]) - { - if let Err(err) = self.render_layer(layer.id) { - error!("error rendering layer '{}': {}", layer.name, err) - } - } - - self.ctx.commit_frame(); - } - - fn get_rect(layer: &SceneNode) -> Result> { - let prop = layer.get_property("rect").ok_or(Error::PropertyNotFound)?; - if prop.array_len != 4 { - return Err(Error::PropertyWrongLen) - } - - let mut rect = [0; 4]; - for i in 0..4 { - if prop.is_expr(i)? { - let (screen_width, screen_height) = window::screen_size(); - - let expr = prop.get_expr(i).unwrap(); - - let machine = SExprMachine { - globals: vec![ - ("sw".to_string(), SExprVal::Float32(screen_width)), - ("sh".to_string(), SExprVal::Float32(screen_height)), - ], - stmts: &expr, - }; - - rect[i] = machine.call()?.as_u32()? as i32; - } else { - rect[i] = prop.get_u32(i)? as i32; - } - } - Ok(Rectangle::from_array(rect)) - } - - fn render_layer( - &mut self, - layer_id: SceneNodeId, - // parent rect - ) -> Result<()> { - let layer = self.scene_graph.get_node(layer_id).unwrap(); - - if !layer.get_property_bool("is_visible")? { - return Ok(()) - } - - self.ctx.begin_default_pass(PassAction::Nothing); - self.ctx.apply_pipeline(&self.pipeline); - - let (_, screen_height) = window::screen_size(); - - let rect = Self::get_rect(&layer)?; - - let mut view = rect.clone(); - view.y = screen_height as i32 - (rect.y + rect.h); - self.ctx.apply_viewport(view.x, view.y, view.w, view.h); - self.ctx.apply_scissor_rect(view.x, view.y, view.w, view.h); - - let window = self.scene_graph.lookup_node("/window").expect("no window attached!"); - let window_scale = window.get_property_f32("scale")?; - - let rect = Rectangle { - x: (rect.x as f32) / window_scale, - y: (rect.y as f32) / window_scale, - w: (rect.w as f32) / window_scale, - h: (rect.h as f32) / window_scale, - }; - - let layer_children = layer.get_children(&[ - SceneNodeType::RenderMesh, - SceneNodeType::RenderText, - SceneNodeType::EditBox, - SceneNodeType::ChatView, - ]); - let layer_children = self.order_by_z_index(layer_children); - - // get the rectangle - // make sure it's inside the parent's rect - for child in layer_children { - // x, y, w, h as pixels - - // note that (x, y) is offset by layer rect so it is the pos within layer - // layer coords are (0, 0) -> (1, 1) - - // optionally evaluated using sexpr - - // mesh data is (0, 0) to (1, 1) - // so scale by (w, h) - - match child.typ { - SceneNodeType::RenderMesh => { - if let Err(err) = self.render_mesh(child.id, &rect) { - error!("error rendering mesh '{}': {}", child.name, err); - } - } - SceneNodeType::RenderText => { - if let Err(err) = self.render_text(child.id, &rect) { - error!("error rendering text '{}': {}", child.name, err); - } - } - SceneNodeType::ChatView => { - let node = self.scene_graph.get_node(child.id).unwrap(); - let chatview = match &node.pimpl { - Pimpl::Null => { - // Maybe the chatview isn't initialized fully yet - continue; - } - Pimpl::ChatView(e) => e.clone(), - _ => panic!("wrong pimpl for editbox"), - }; - if let Err(err) = chatview.render(self, child.id, &rect) { - error!("error rendering chatview '{}': {}", child.name, err); - } - } - SceneNodeType::EditBox => { - let node = self.scene_graph.get_node(child.id).unwrap(); - let editbox = match &node.pimpl { - Pimpl::Null => { - // Maybe the editbox isn't initialized fully yet - continue; - } - Pimpl::EditBox(e) => e.clone(), - _ => panic!("wrong pimpl for editbox"), - }; - if let Err(err) = editbox.render(self, child.id, &rect) { - error!("error rendering editbox '{}': {}", child.name, err); - } - } - _ => panic!("render_layer(): unknown type"), - } - } - - Ok(()) - } - - fn order_by_z_index(&self, nodes: Vec) -> Vec { - let mut nodes: Vec<_> = nodes - .into_iter() - .filter_map(|node_inf| { - let node = self.scene_graph.get_node(node_inf.id).unwrap(); - //if !node.get_property_bool("is_visible").ok()? { - // return None - //} - let z_index = node.get_property_u32("z_index").ok()?; - Some((z_index, node_inf)) - }) - .collect(); - nodes.sort_unstable_by_key(|(z_index, node_inf)| *z_index); - nodes.into_iter().map(|(z_index, node_inf)| node_inf).collect() - } - - pub fn get_dim(mesh: &SceneNode, layer_rect: &Rectangle) -> Result> { - let prop = mesh.get_property("rect").ok_or(Error::PropertyNotFound)?; - if prop.array_len != 4 { - return Err(Error::PropertyWrongLen) - } - - let mut rect = [0.; 4]; - for i in 0..4 { - if prop.is_expr(i)? { - let expr = prop.get_expr(i).unwrap(); - - let machine = SExprMachine { - globals: vec![ - ("lw".to_string(), SExprVal::Uint32(layer_rect.w as u32)), - ("lh".to_string(), SExprVal::Uint32(layer_rect.h as u32)), - ], - stmts: &expr, - }; - - rect[i] = machine.call()?.coerce_f32()?; - } else { - rect[i] = prop.get_f32(i)?; - } - } - Ok(Rectangle::from_array(rect)) - } - - fn render_mesh(&mut self, node_id: SceneNodeId, layer_rect: &Rectangle) -> Result<()> { - let mesh = self.scene_graph.get_node(node_id).unwrap(); - - let z_index = mesh.get_property_u32("z_index")?; - - let data = mesh.get_property("data").ok_or(Error::PropertyNotFound)?; - let verts = data.get_buf(0)?; - let faces = data.get_buf(1)?; - - let vertex_buffer = self.ctx.new_buffer( - BufferType::VertexBuffer, - BufferUsage::Immutable, - BufferSource::slice(&verts), - ); - - let bufsrc = unsafe { - BufferSource::pointer( - faces.as_ptr() as _, - std::mem::size_of_val(&faces[..]), - std::mem::size_of::(), - ) - }; - - let index_buffer = - self.ctx.new_buffer(BufferType::IndexBuffer, BufferUsage::Immutable, bufsrc); - - // temp - let texture = self.textures.get(Stage::WHITE_TEXTURE_ID).unwrap(); - - let rect = Self::get_dim(mesh, layer_rect)?; - //debug!("mesh rect: {:?}", rect); - - let layer_w = layer_rect.w as f32; - let layer_h = layer_rect.h as f32; - let off_x = rect.x / layer_w; - let off_y = rect.y / layer_h; - let scale_x = rect.w / layer_w; - let scale_y = rect.h / layer_h; - //let model = glam::Mat4::IDENTITY; - 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.)); - - let mut uniforms_data = [0u8; 128]; - let data: [u8; 64] = unsafe { std::mem::transmute_copy(&self.proj) }; - uniforms_data[0..64].copy_from_slice(&data); - let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) }; - uniforms_data[64..].copy_from_slice(&data); - assert_eq!(128, 2 * UniformType::Mat4.size()); - - let bindings = - Bindings { vertex_buffers: vec![vertex_buffer], index_buffer, images: vec![*texture] }; - - self.ctx.apply_bindings(&bindings); - - self.ctx.apply_uniforms_from_bytes(uniforms_data.as_ptr(), uniforms_data.len()); - - self.ctx.draw(0, 3 * faces.len() as i32, 1); - - self.ctx.delete_buffer(index_buffer); - self.ctx.delete_buffer(vertex_buffer); - - Ok(()) - } - - pub fn render_clipped_box_with_texture2( - &mut self, - bound: &Rectangle, - obj: &Rectangle, - color: Color, - texture: TextureId, - ) { - let Some(clipped) = bound.clip(&obj) else { return }; - - let x1 = clipped.x; - let y1 = clipped.y; - let x2 = clipped.x + clipped.w; - let y2 = clipped.y + clipped.h; - - let u1 = (clipped.x - obj.x) / obj.w; - let u2 = (clipped.x + clipped.w - obj.x) / obj.w; - let v1 = (clipped.y - obj.y) / obj.h; - let v2 = (clipped.y + clipped.h - obj.y) / obj.h; - - let vertices: [Vertex; 4] = [ - // top left - Vertex { pos: [x1, y1], color, uv: [u1, v1] }, - // top right - Vertex { pos: [x2, y1], color, uv: [u2, v1] }, - // bottom left - Vertex { pos: [x1, y2], color, uv: [u1, v2] }, - // bottom right - Vertex { pos: [x2, y2], color, uv: [u2, v2] }, - ]; - - let vertex_buffer = self.ctx.new_buffer( - BufferType::VertexBuffer, - BufferUsage::Immutable, - BufferSource::slice(&vertices), - ); - - let indices: [u16; 6] = [0, 2, 1, 1, 2, 3]; - let index_buffer = self.ctx.new_buffer( - BufferType::IndexBuffer, - BufferUsage::Immutable, - BufferSource::slice(&indices), - ); - - let bindings = - Bindings { vertex_buffers: vec![vertex_buffer], index_buffer, images: vec![texture] }; - - self.ctx.apply_bindings(&bindings); - self.ctx.draw(0, 6, 1); - self.ctx.delete_buffer(vertex_buffer); - self.ctx.delete_buffer(index_buffer); - } - - pub fn render_clipped_box_with_texture( - &mut self, - bound_rect: &Rectangle, - x1: f32, - y1: f32, - x2: f32, - y2: f32, - color: Color, - texture: TextureId, - ) { - let obj = Rectangle { x: x1, y: y1, w: x2 - x1, h: y2 - y1 }; - if obj.w == 0. || obj.h == 0. { - return - } - let Some(clipped) = bound_rect.clip(&obj) else { return }; - - let x1 = clipped.x; - let y1 = clipped.y; - let x2 = clipped.x + clipped.w; - let y2 = clipped.y + clipped.h; - - let u1 = (clipped.x - obj.x) / obj.w; - let u2 = (clipped.x + clipped.w - obj.x) / obj.w; - let v1 = (clipped.y - obj.y) / obj.h; - let v2 = (clipped.y + clipped.h - obj.y) / obj.h; - - let vertices: [Vertex; 4] = [ - // top left - Vertex { pos: [x1, y1], color, uv: [u1, v1] }, - // top right - Vertex { pos: [x2, y1], color, uv: [u2, v1] }, - // bottom left - Vertex { pos: [x1, y2], color, uv: [u1, v2] }, - // bottom right - Vertex { pos: [x2, y2], color, uv: [u2, v2] }, - ]; - - let vertex_buffer = self.ctx.new_buffer( - BufferType::VertexBuffer, - BufferUsage::Immutable, - BufferSource::slice(&vertices), - ); - - let indices: [u16; 6] = [0, 2, 1, 1, 2, 3]; - let index_buffer = self.ctx.new_buffer( - BufferType::IndexBuffer, - BufferUsage::Immutable, - BufferSource::slice(&indices), - ); - - let bindings = - Bindings { vertex_buffers: vec![vertex_buffer], index_buffer, images: vec![texture] }; - - self.ctx.apply_bindings(&bindings); - self.ctx.draw(0, 6, 1); - self.ctx.delete_buffer(vertex_buffer); - self.ctx.delete_buffer(index_buffer); - } - - pub fn render_box_with_texture( - &mut self, - x1: f32, - y1: f32, - x2: f32, - y2: f32, - color: Color, - texture: TextureId, - ) { - let vertices: [Vertex; 4] = [ - // top left - Vertex { pos: [x1, y1], color, uv: [0., 0.] }, - // top right - Vertex { pos: [x2, y1], color, uv: [1., 0.] }, - // bottom left - Vertex { pos: [x1, y2], color, uv: [0., 1.] }, - // bottom right - Vertex { pos: [x2, y2], color, uv: [1., 1.] }, - ]; - - let vertex_buffer = self.ctx.new_buffer( - BufferType::VertexBuffer, - BufferUsage::Immutable, - BufferSource::slice(&vertices), - ); - - let indices: [u16; 6] = [0, 2, 1, 1, 2, 3]; - let index_buffer = self.ctx.new_buffer( - BufferType::IndexBuffer, - BufferUsage::Immutable, - BufferSource::slice(&indices), - ); - - let bindings = - Bindings { vertex_buffers: vec![vertex_buffer], index_buffer, images: vec![texture] }; - - self.ctx.apply_bindings(&bindings); - self.ctx.draw(0, 6, 1); - self.ctx.delete_buffer(vertex_buffer); - self.ctx.delete_buffer(index_buffer); - } - - pub fn render_box(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: Color) { - let texture = self.textures.get(Stage::WHITE_TEXTURE_ID).unwrap(); - self.render_box_with_texture(x1, y1, x2, y2, color, *texture) - } - - pub fn hline(&mut self, min_x: f32, max_x: f32, y: f32, color: Color, w: f32) { - self.render_box(min_x, y - w / 2., max_x, y + w / 2., color) - } - - pub fn vline(&mut self, x: f32, min_y: f32, max_y: f32, color: Color, w: f32) { - self.render_box(x - w / 2., min_y, x + w / 2., max_y, color) - } - - pub fn outline(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: Color, w: f32) { - // top - self.render_box(x1, y1, x2, y1 + w, color); - // left - self.render_box(x1, y1, x1 + w, y2, color); - // right - self.render_box(x2 - w, y1, x2, y2, color); - // bottom - self.render_box(x1, y2 - w, x2, y2, color); - } - - fn render_text(&mut self, node_id: SceneNodeId, layer_rect: &Rectangle) -> Result<()> { - let node = self.scene_graph.get_node(node_id).unwrap(); - - let text = node.get_property_str("text")?; - let font_size = node.get_property_f32("font_size")?; - let debug = node.get_property_bool("debug")?; - let rect = Self::get_dim(node, layer_rect)?; - let baseline = node.get_property_f32("baseline")?; - - let color_prop = node.get_property("color").ok_or(Error::PropertyNotFound)?; - let color_r = color_prop.get_f32(0)?; - let color_g = color_prop.get_f32(1)?; - let color_b = color_prop.get_f32(2)?; - let color_a = color_prop.get_f32(3)?; - - let layer_w = layer_rect.w as f32; - let layer_h = layer_rect.h as f32; - let off_x = rect.x / layer_w; - let off_y = rect.y / layer_h; - // Use absolute pixel scale - let scale_x = 1. / layer_w; - let scale_y = 1. / layer_h; - //let model = glam::Mat4::IDENTITY; - 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.)); - - let mut uniforms_data = [0u8; 128]; - let data: [u8; 64] = unsafe { std::mem::transmute_copy(&self.proj) }; - uniforms_data[0..64].copy_from_slice(&data); - let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) }; - uniforms_data[64..].copy_from_slice(&data); - assert_eq!(128, 2 * UniformType::Mat4.size()); - - self.ctx.apply_uniforms_from_bytes(uniforms_data.as_ptr(), uniforms_data.len()); - - // Used for scaling the font size - let window = self.scene_graph.lookup_node("/window").expect("no window attached!"); - let window_scale = window.get_property_f32("scale")?; - let font_size = window_scale * font_size; - - //let mut strings = vec![]; - //let mut current_str = String::new(); - //let mut current_idx = 0; - //for chr in text.chars() { - // let ft_face = self.font_faces[current_idx]; - // if ft_face.get_char_index(chr as usize).is_some() { - // } - //} - - let mut current_idx = 0; - let mut current_str = String::new(); - let mut substrs = vec![]; - 'next_char: for chr in text.chars() { - let idx = 'get_idx: { - for i in 0..self.font_faces.len() { - let ft_face = &self.font_faces[i]; - if ft_face.get_char_index(chr as usize).is_some() { - break 'get_idx i - } - } - - warn!("no font fallback for char: {}", chr); - // Skip this char - continue 'next_char - }; - if current_idx != idx { - if !current_str.is_empty() { - // Push - substrs.push((current_idx, current_str.clone())); - } - - current_str.clear(); - current_idx = idx; - } - current_str.push(chr); - } - if !current_str.is_empty() { - // Push - substrs.push((current_idx, current_str)); - } - - let mut current_x = 0.; - let mut current_y = baseline; - - for (face_idx, text) in substrs { - let face = &self.font_faces[face_idx]; - if face.has_fixed_sizes() { - // emojis required a fixed size - //face.set_char_size(109 * 64, 0, 72, 72).unwrap(); - face.select_size(0).unwrap(); - } else { - face.set_char_size(font_size as isize * 64, 0, 72, 72).unwrap(); - } - - let hb_font = harfbuzz_rs::Font::from_freetype_face(face.clone()); - let buffer = harfbuzz_rs::UnicodeBuffer::new().add_str(&text); - let output = harfbuzz_rs::shape(&hb_font, buffer, &[]); - - let positions = output.get_glyph_positions(); - let infos = output.get_glyph_infos(); - - for (position, info) in positions.iter().zip(infos) { - let gid = info.codepoint; - // Index within this substr - // let cluster = info.cluster; - - let mut flags = ft::face::LoadFlag::DEFAULT; - if face.has_color() { - flags |= ft::face::LoadFlag::COLOR; - } - face.load_glyph(gid, flags).unwrap(); - - let glyph = face.glyph(); - glyph.render_glyph(ft::RenderMode::Normal).unwrap(); - - // https://gist.github.com/jokertarot/7583938?permalink_comment_id=3327566#gistcomment-3327566 - - let bmp = glyph.bitmap(); - let buffer = bmp.buffer(); - let bmp_width = bmp.width() as usize; - let bmp_height = bmp.rows() as usize; - let bearing_x = glyph.bitmap_left() as f32; - let bearing_y = glyph.bitmap_top() as f32; - - //assert_eq!(bmp.pixel_mode().unwrap(), ft::bitmap::PixelMode::Bgra); - //assert_eq!(bmp.pixel_mode().unwrap(), ft::bitmap::PixelMode::Lcd); - //assert_eq!(bmp.pixel_mode().unwrap(), ft::bitmap::PixelMode::Gray); - - let pixel_mode = bmp.pixel_mode().unwrap(); - let tdata = match pixel_mode { - ft::bitmap::PixelMode::Bgra => { - let mut tdata = vec![]; - tdata.resize(4 * bmp_width * bmp_height, 0); - // Convert from BGRA to RGBA - for i in 0..bmp_width * bmp_height as usize { - let idx = i * 4; - let b = buffer[idx]; - let g = buffer[idx + 1]; - let r = buffer[idx + 2]; - let a = buffer[idx + 3]; - tdata[idx] = r; - tdata[idx + 1] = g; - tdata[idx + 2] = b; - tdata[idx + 3] = a; - } - tdata - } - ft::bitmap::PixelMode::Gray => { - // Convert from greyscale to RGBA8 - let tdata: Vec<_> = buffer - .iter() - .flat_map(|coverage| { - let r = (255. * color_r) as u8; - let g = (255. * color_g) as u8; - let b = (255. * color_b) as u8; - let a = ((*coverage as f32) * color_a) as u8; - vec![r, g, b, a] - }) - .collect(); - tdata - } - _ => panic!("unsupport pixel mode: {:?}", pixel_mode), - }; - - let (x1, y1, x2, y2) = if face.has_fixed_sizes() { - // Downscale by height - let width = (bmp_width as f32 * font_size) / bmp_height as f32; - let height = font_size; - - let x1 = current_x; - let y1 = current_y - height; - - let x2 = current_x + width; - let y2 = current_y; - - current_x += width; - - (x1, y1, x2, y2) - } else { - let (width, height) = (bmp_width as f32, bmp_height as f32); - - let off_x = position.x_offset as f32 / 64.; - let off_y = position.y_offset as f32 / 64.; - - let x1 = current_x + off_x + bearing_x; - let y1 = current_y - off_y - bearing_y; - let x2 = x1 + width as f32; - let y2 = y1 + height as f32; - - let x_advance = position.x_advance as f32 / 64.; - let y_advance = position.y_advance as f32 / 64.; - current_x += x_advance; - current_y += y_advance; - - (x1, y1, x2, y2) - }; - - let key = ( - gid, - [ - (255. * color_r) as u8, - (255. * color_g) as u8, - (255. * color_b) as u8, - (255. * color_a) as u8, - ], - ); - let texture = if self.atlas.contains_key(&key) { - *self.atlas.get(&key).unwrap() - } else { - let texture = self.ctx.new_texture_from_rgba8( - bmp_width as u16, - bmp_height as u16, - &tdata, - ); - self.atlas.insert(key, texture); - texture - }; - - //let texture = self.ctx.new_texture_from_rgba8(bmp_width as u16, bmp_height as u16, &tdata); - self.render_box_with_texture(x1, y1, x2, y2, COLOR_WHITE, texture); - //self.ctx.delete_texture(texture); - - if debug { - self.outline(x1, y1, x2, y2, COLOR_BLUE, 1.); - } - } - if debug { - self.hline(0., current_x, 0., COLOR_RED, 1.); - } - } - - Ok(()) - } - - fn render_glyph(&mut self, glyph_id: u32, font_size: f32, x: f32, y: f32) -> Result<()> { - Ok(()) - } -} - -impl EventHandler for Stage { - fn update(&mut self) { - if self.last_draw_time.is_none() { - return - } - - // Only allow 20 ms, process as much as we can during that time - let elapsed_since_draw = self.last_draw_time.unwrap().elapsed(); - // We're long overdue a redraw. Exit for now - if elapsed_since_draw > Duration::from_millis(20) { - return - } - // The next redraw must happen 20ms since its last one. - // Calculate how much time is remaining until then. - let allowed_time = Duration::from_millis(20) - elapsed_since_draw; - let deadline = Instant::now() + allowed_time; - - loop { - let Ok((event, node_id, arg_data, response_fn)) = - self.method_recvr.recv_deadline(deadline) - else { - break - }; - let res = match event { - GraphicsMethodEvent::LoadTexture => self.method_load_texture(node_id, arg_data), - GraphicsMethodEvent::DeleteTexture => self.method_delete_texture(node_id, arg_data), - GraphicsMethodEvent::CreateChatView => { - self.method_create_chatview(node_id, arg_data) - } - GraphicsMethodEvent::CreateEditBox => self.method_create_editbox(node_id, arg_data), - }; - response_fn(res); - } - } - - // Only do drawing here. Apps might not call this when minimized. - fn draw(&mut self) { - self.last_draw_time = Some(Instant::now()); - - // This will make the top left (0, 0) and the bottom right (1, 1) - // Default is (-1, 1) -> (1, -1) - let proj = glam::Mat4::from_translation(glam::Vec3::new(-1., 1., 0.)) * - glam::Mat4::from_scale(glam::Vec3::new(2., -2., 1.)); - //let proj = glam::Mat4::IDENTITY; - - let scene_graph = self.scene_graph.lock().unwrap(); - let atlas = &mut self.atlas.lock().unwrap(); - - // We need this because scene_graph must remain locked for the duration of the rendering - let mut render_context = RenderContext { - scene_graph, - ctx: &mut self.ctx, - pipeline: &self.pipeline, - proj, - textures: &self.textures, - font_faces: &self.font_faces, - atlas, - }; - - render_context.render_window(); - - drop(render_context); - } - - fn key_down_event(&mut self, keycode: KeyCode, modifiers: KeyMods, repeat: bool) { - let mut scene_graph = self.scene_graph.lock().unwrap(); - let win = scene_graph.lookup_node_mut("/window/input/keyboard").unwrap(); - - let key = keycode.to_str(); - - let mut data = vec![]; - modifiers.shift.encode(&mut data).unwrap(); - modifiers.ctrl.encode(&mut data).unwrap(); - modifiers.alt.encode(&mut data).unwrap(); - modifiers.logo.encode(&mut data).unwrap(); - repeat.encode(&mut data).unwrap(); - key.encode(&mut data).unwrap(); - //win.trigger("key_down", data).unwrap(); - } - fn key_up_event(&mut self, keycode: KeyCode, modifiers: KeyMods) { - let mut scene_graph = self.scene_graph.lock().unwrap(); - let win = scene_graph.lookup_node_mut("/window/input/keyboard").unwrap(); - - let key = keycode.to_str(); - - let mut data = vec![]; - modifiers.shift.encode(&mut data).unwrap(); - modifiers.ctrl.encode(&mut data).unwrap(); - modifiers.alt.encode(&mut data).unwrap(); - modifiers.logo.encode(&mut data).unwrap(); - key.encode(&mut data).unwrap(); - //win.trigger("key_up", data).unwrap(); - } - 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.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.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.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.trigger("button_up", data).unwrap(); - } - - fn resize_event(&mut self, width: f32, height: f32) { - let mut data = vec![]; - width.encode(&mut data).unwrap(); - height.encode(&mut data).unwrap(); - - let mut scene_graph = self.scene_graph.lock().unwrap(); - let win = scene_graph.lookup_node_mut("/window").unwrap(); - let prop = win.get_property("screen_size").unwrap(); - prop.set_f32(0, width).unwrap(); - prop.set_f32(1, height).unwrap(); - //win.trigger("resize", data).unwrap(); - } -} - -pub fn run_gui(scene_graph: SceneGraphPtr) { - #[cfg(target_os = "android")] - { - android_logger::init_once( - android_logger::Config::default().with_max_level(LevelFilter::Debug).with_tag("darkfi"), - ); - } - - #[cfg(target_os = "linux")] - { - let term_logger = simplelog::TermLogger::new( - simplelog::LevelFilter::Debug, - simplelog::Config::default(), - simplelog::TerminalMode::Mixed, - simplelog::ColorChoice::Auto, - ); - simplelog::CombinedLogger::init(vec![term_logger]).expect("logger"); - } - - let mut conf = miniquad::conf::Conf { - high_dpi: true, - window_resizable: true, - platform: miniquad::conf::Platform { - linux_backend: miniquad::conf::LinuxBackend::WaylandWithX11Fallback, - wayland_use_fallback_decorations: false, - ..Default::default() - }, - ..Default::default() - }; - let metal = std::env::args().nth(1).as_deref() == Some("metal"); - conf.platform.apple_gfx_api = - if metal { conf::AppleGfxApi::Metal } else { conf::AppleGfxApi::OpenGl }; - - miniquad::start(conf, || Box::new(Stage::new(scene_graph))); -} diff --git a/bin/darkwallet/src/gfx2.rs b/bin/darkwallet/src/gfx/mod.rs similarity index 97% rename from bin/darkwallet/src/gfx2.rs rename to bin/darkwallet/src/gfx/mod.rs index 7bc321b13..cc5486924 100644 --- a/bin/darkwallet/src/gfx2.rs +++ b/bin/darkwallet/src/gfx/mod.rs @@ -32,11 +32,12 @@ use std::{ time::{Duration, Instant}, }; +mod shader; + use crate::{ app::{AppPtr, AsyncRuntime}, error::{Error, Result}, pubsub::{Publisher, PublisherPtr, Subscription, SubscriptionId}, - shader, util::ansi_texture, }; @@ -734,15 +735,15 @@ impl Stage { sendr: async_channel::Sender, ) { let texture = self.ctx.new_texture_from_rgba8(width, height, &data); - //debug!(target: "gfx2", "Invoked method: new_texture({}, {}, ...) -> {:?}", + //debug!(target: "gfx", "Invoked method: new_texture({}, {}, ...) -> {:?}", // width, height, texture); - //debug!(target: "gfx2", "Invoked method: new_texture({}, {}, ...) -> {:?}\n{}", + //debug!(target: "gfx", "Invoked method: new_texture({}, {}, ...) -> {:?}\n{}", // width, height, texture, // ansi_texture(width as usize, height as usize, &data)); sendr.try_send(texture).unwrap(); } fn method_delete_texture(&mut self, texture: TextureId) { - //debug!(target: "gfx2", "Invoked method: delete_texture({:?})", texture); + //debug!(target: "gfx", "Invoked method: delete_texture({:?})", texture); self.ctx.delete_texture(texture); } fn method_new_vertex_buffer( @@ -755,7 +756,7 @@ impl Stage { BufferUsage::Immutable, BufferSource::slice(&verts), ); - //debug!(target: "gfx2", "Invoked method: new_vertex_buffer({:?}) -> {:?}", verts, buffer); + //debug!(target: "gfx", "Invoked method: new_vertex_buffer({:?}) -> {:?}", verts, buffer); sendr.try_send(buffer).unwrap(); } fn method_new_index_buffer( @@ -768,15 +769,15 @@ impl Stage { BufferUsage::Immutable, BufferSource::slice(&indices), ); - //debug!(target: "gfx2", "Invoked method: new_index_buffer({:?}) -> {:?}", indices, buffer); + //debug!(target: "gfx", "Invoked method: new_index_buffer({:?}) -> {:?}", indices, buffer); sendr.try_send(buffer).unwrap(); } fn method_delete_buffer(&mut self, buffer: BufferId) { - //debug!(target: "gfx2", "Invoked method: delete_buffer({:?})", buffer); + //debug!(target: "gfx", "Invoked method: delete_buffer({:?})", buffer); self.ctx.delete_buffer(buffer); } fn method_replace_draw_calls(&mut self, dcs: Vec<(u64, DrawCall)>) { - //debug!(target: "gfx2", "Invoked method: replace_draw_calls({:?})", dcs); + //debug!(target: "gfx", "Invoked method: replace_draw_calls({:?})", dcs); for (key, val) in dcs { self.draw_calls.insert(key, val); } diff --git a/bin/darkwallet/src/shader.rs b/bin/darkwallet/src/gfx/shader.rs similarity index 100% rename from bin/darkwallet/src/shader.rs rename to bin/darkwallet/src/gfx/shader.rs diff --git a/bin/darkwallet/src/keysym.rs b/bin/darkwallet/src/keysym.rs deleted file mode 100644 index 853bab644..000000000 --- a/bin/darkwallet/src/keysym.rs +++ /dev/null @@ -1,434 +0,0 @@ -/* This file is part of DarkFi (https://dark.fi) - * - * Copyright (C) 2020-2024 Dyne.org foundation - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -use miniquad::{KeyCode, MouseButton}; - -pub trait KeyCodeAsStr { - fn to_str(&self) -> &str; -} - -impl KeyCodeAsStr for KeyCode { - fn to_str(&self) -> &str { - match self { - Self::Space => " ", - Self::Apostrophe => "'", - Self::Comma => ",", - Self::Minus => "-", - Self::Period => ".", - Self::Slash => "/", - Self::Key0 => "0", - Self::Key1 => "1", - Self::Key2 => "2", - Self::Key3 => "3", - Self::Key4 => "4", - Self::Key5 => "5", - Self::Key6 => "6", - Self::Key7 => "7", - Self::Key8 => "8", - Self::Key9 => "9", - Self::Semicolon => ";", - Self::Equal => "=", - Self::A => "A", - Self::B => "B", - Self::C => "C", - Self::D => "D", - Self::E => "E", - Self::F => "F", - Self::G => "G", - Self::H => "H", - Self::I => "I", - Self::J => "J", - Self::K => "K", - Self::L => "L", - Self::M => "M", - Self::N => "N", - Self::O => "O", - Self::P => "P", - Self::Q => "Q", - Self::R => "R", - Self::S => "S", - Self::T => "T", - Self::U => "U", - Self::V => "V", - Self::W => "W", - Self::X => "X", - Self::Y => "Y", - Self::Z => "Z", - Self::LeftBracket => "(", - Self::Backslash => "\\", - Self::RightBracket => ")", - Self::GraveAccent => "`", - Self::World1 => "World1", - Self::World2 => "World2", - Self::Escape => "Escape", - Self::Enter => "\n", - Self::Tab => "Tab", - Self::Backspace => "Backspace", - Self::Insert => "Insert", - Self::Delete => "Delete", - Self::Right => "Right", - Self::Left => "Left", - Self::Down => "Down", - Self::Up => "Up", - Self::PageUp => "PageUp", - Self::PageDown => "PageDown", - Self::Home => "Home", - Self::End => "End", - Self::CapsLock => "CapsLock", - Self::ScrollLock => "ScrollLock", - Self::NumLock => "NumLock", - Self::PrintScreen => "PrintScreen", - Self::Pause => "Pause", - Self::F1 => "F1", - Self::F2 => "F2", - Self::F3 => "F3", - Self::F4 => "F4", - Self::F5 => "F5", - Self::F6 => "F6", - Self::F7 => "F7", - Self::F8 => "F8", - Self::F9 => "F9", - Self::F10 => "F10", - Self::F11 => "F11", - Self::F12 => "F12", - Self::F13 => "F13", - Self::F14 => "F14", - Self::F15 => "F15", - Self::F16 => "F16", - Self::F17 => "F17", - Self::F18 => "F18", - Self::F19 => "F19", - Self::F20 => "F20", - Self::F21 => "F21", - Self::F22 => "F22", - Self::F23 => "F23", - Self::F24 => "F24", - Self::F25 => "F25", - Self::Kp0 => "Kp0", - Self::Kp1 => "Kp1", - Self::Kp2 => "Kp2", - Self::Kp3 => "Kp3", - Self::Kp4 => "Kp4", - Self::Kp5 => "Kp5", - Self::Kp6 => "Kp6", - Self::Kp7 => "Kp7", - Self::Kp8 => "Kp8", - Self::Kp9 => "Kp9", - Self::KpDecimal => "KpDecimal", - Self::KpDivide => "KpDivide", - Self::KpMultiply => "KpMultiply", - Self::KpSubtract => "KpSubtract", - Self::KpAdd => "KpAdd", - Self::KpEnter => "KpEnter", - Self::KpEqual => "KpEqual", - Self::LeftShift => "LeftShift", - Self::LeftControl => "LeftControl", - Self::LeftAlt => "LeftAlt", - Self::LeftSuper => "LeftSuper", - Self::RightShift => "RightShift", - Self::RightControl => "RightControl", - Self::RightAlt => "RightAlt", - Self::RightSuper => "RightSuper", - Self::Menu => "Menu", - Self::Unknown => "Unknown", - } - } -} - -pub trait KeyCodeAsU16 { - fn to_u16(&self) -> u16; - fn from_u16(keysym: u16) -> Self; -} - -impl KeyCodeAsU16 for KeyCode { - fn to_u16(&self) -> u16 { - match self { - Self::Space => 0x0020, - Self::Apostrophe => 0x0027, - Self::Comma => 0x002c, - Self::Minus => 0x002d, - Self::Period => 0x002e, - Self::Slash => 0x002f, - Self::Key0 => 0x0030, - Self::Key1 => 0x0031, - Self::Key2 => 0x0032, - Self::Key3 => 0x0033, - Self::Key4 => 0x0034, - Self::Key5 => 0x0035, - Self::Key6 => 0x0036, - Self::Key7 => 0x0037, - Self::Key8 => 0x0038, - Self::Key9 => 0x0039, - Self::Semicolon => 0x003b, - Self::Equal => 0x003d, - Self::A => 0x0041, - Self::B => 0x0042, - Self::C => 0x0043, - Self::D => 0x0044, - Self::E => 0x0045, - Self::F => 0x0046, - Self::G => 0x0047, - Self::H => 0x0048, - Self::I => 0x0049, - Self::J => 0x004a, - Self::K => 0x004b, - Self::L => 0x004c, - Self::M => 0x004d, - Self::N => 0x004e, - Self::O => 0x004f, - Self::P => 0x0050, - Self::Q => 0x0051, - Self::R => 0x0052, - Self::S => 0x0053, - Self::T => 0x0054, - Self::U => 0x0055, - Self::V => 0x0056, - Self::W => 0x0057, - Self::X => 0x0058, - Self::Y => 0x0059, - Self::Z => 0x005a, - Self::LeftBracket => 0x005b, - Self::Backslash => 0x005c, - Self::RightBracket => 0x005d, - Self::GraveAccent => 0x0060, - Self::World1 => 0x0100, - Self::World2 => 0x0101, - Self::Escape => 0xff1b, - Self::Enter => 0xff0d, - Self::Tab => 0xff09, - Self::Backspace => 0xff08, - Self::Insert => 0xff63, - Self::Delete => 0xffff, - Self::Right => 0xff53, - Self::Left => 0xff51, - Self::Down => 0xff54, - Self::Up => 0xff52, - Self::PageUp => 0xff55, - Self::PageDown => 0xff56, - Self::Home => 0xff50, - Self::End => 0xff57, - Self::CapsLock => 0xffe5, - Self::ScrollLock => 0xff14, - Self::NumLock => 0xff7f, - Self::PrintScreen => 0xfd1d, - Self::Pause => 0xff13, - Self::F1 => 0xffbe, - Self::F2 => 0xffbf, - Self::F3 => 0xffc0, - Self::F4 => 0xffc1, - Self::F5 => 0xffc2, - Self::F6 => 0xffc3, - Self::F7 => 0xffc4, - Self::F8 => 0xffc5, - Self::F9 => 0xffc6, - Self::F10 => 0xffc7, - Self::F11 => 0xffc8, - Self::F12 => 0xffc9, - Self::F13 => 0xffca, - Self::F14 => 0xffcb, - Self::F15 => 0xffcc, - Self::F16 => 0xffcd, - Self::F17 => 0xffce, - Self::F18 => 0xffcf, - Self::F19 => 0xffd0, - Self::F20 => 0xffd1, - Self::F21 => 0xffd2, - Self::F22 => 0xffd3, - Self::F23 => 0xffd4, - Self::F24 => 0xffd5, - Self::F25 => 0xffd6, - Self::Kp0 => 0xffb0, - Self::Kp1 => 0xffb1, - Self::Kp2 => 0xffb2, - Self::Kp3 => 0xffb3, - Self::Kp4 => 0xffb4, - Self::Kp5 => 0xffb5, - Self::Kp6 => 0xffb6, - Self::Kp7 => 0xffb7, - Self::Kp8 => 0xffb8, - Self::Kp9 => 0xffb9, - Self::KpDecimal => 0xffae, - Self::KpDivide => 0xffaf, - Self::KpMultiply => 0xffaa, - Self::KpSubtract => 0xffad, - Self::KpAdd => 0xffab, - Self::KpEnter => 0xff8d, - Self::KpEqual => 0xffbd, - Self::LeftShift => 0xffe1, - Self::LeftControl => 0xffe3, - Self::LeftAlt => 0xffe9, - Self::LeftSuper => 0xffeb, - Self::RightShift => 0xffe2, - Self::RightControl => 0xffe4, - Self::RightAlt => 0xffea, - Self::RightSuper => 0xffec, - Self::Menu => 0xff67, - Self::Unknown => 0x01ff, - } - } - fn from_u16(keysym: u16) -> Self { - match keysym { - 0x0020 => Self::Space, - 0x0027 => Self::Apostrophe, - 0x002c => Self::Comma, - 0x002d => Self::Minus, - 0x002e => Self::Period, - 0x002f => Self::Slash, - 0x0030 => Self::Key0, - 0x0031 => Self::Key1, - 0x0032 => Self::Key2, - 0x0033 => Self::Key3, - 0x0034 => Self::Key4, - 0x0035 => Self::Key5, - 0x0036 => Self::Key6, - 0x0037 => Self::Key7, - 0x0038 => Self::Key8, - 0x0039 => Self::Key9, - 0x003b => Self::Semicolon, - 0x003d => Self::Equal, - 0x0041 => Self::A, - 0x0042 => Self::B, - 0x0043 => Self::C, - 0x0044 => Self::D, - 0x0045 => Self::E, - 0x0046 => Self::F, - 0x0047 => Self::G, - 0x0048 => Self::H, - 0x0049 => Self::I, - 0x004a => Self::J, - 0x004b => Self::K, - 0x004c => Self::L, - 0x004d => Self::M, - 0x004e => Self::N, - 0x004f => Self::O, - 0x0050 => Self::P, - 0x0051 => Self::Q, - 0x0052 => Self::R, - 0x0053 => Self::S, - 0x0054 => Self::T, - 0x0055 => Self::U, - 0x0056 => Self::V, - 0x0057 => Self::W, - 0x0058 => Self::X, - 0x0059 => Self::Y, - 0x005a => Self::Z, - 0x005b => Self::LeftBracket, - 0x005c => Self::Backslash, - 0x005d => Self::RightBracket, - 0x0060 => Self::GraveAccent, - 0x0100 => Self::World1, - 0x0101 => Self::World2, - 0xff1b => Self::Escape, - 0xff0d => Self::Enter, - 0xff09 => Self::Tab, - 0xff08 => Self::Backspace, - 0xff63 => Self::Insert, - 0xffff => Self::Delete, - 0xff53 => Self::Right, - 0xff51 => Self::Left, - 0xff54 => Self::Down, - 0xff52 => Self::Up, - 0xff55 => Self::PageUp, - 0xff56 => Self::PageDown, - 0xff50 => Self::Home, - 0xff57 => Self::End, - 0xffe5 => Self::CapsLock, - 0xff14 => Self::ScrollLock, - 0xff7f => Self::NumLock, - 0xfd1d => Self::PrintScreen, - 0xff13 => Self::Pause, - 0xffbe => Self::F1, - 0xffbf => Self::F2, - 0xffc0 => Self::F3, - 0xffc1 => Self::F4, - 0xffc2 => Self::F5, - 0xffc3 => Self::F6, - 0xffc4 => Self::F7, - 0xffc5 => Self::F8, - 0xffc6 => Self::F9, - 0xffc7 => Self::F10, - 0xffc8 => Self::F11, - 0xffc9 => Self::F12, - 0xffca => Self::F13, - 0xffcb => Self::F14, - 0xffcc => Self::F15, - 0xffcd => Self::F16, - 0xffce => Self::F17, - 0xffcf => Self::F18, - 0xffd0 => Self::F19, - 0xffd1 => Self::F20, - 0xffd2 => Self::F21, - 0xffd3 => Self::F22, - 0xffd4 => Self::F23, - 0xffd5 => Self::F24, - 0xffd6 => Self::F25, - 0xffb0 => Self::Kp0, - 0xffb1 => Self::Kp1, - 0xffb2 => Self::Kp2, - 0xffb3 => Self::Kp3, - 0xffb4 => Self::Kp4, - 0xffb5 => Self::Kp5, - 0xffb6 => Self::Kp6, - 0xffb7 => Self::Kp7, - 0xffb8 => Self::Kp8, - 0xffb9 => Self::Kp9, - 0xffae => Self::KpDecimal, - 0xffaf => Self::KpDivide, - 0xffaa => Self::KpMultiply, - 0xffad => Self::KpSubtract, - 0xffab => Self::KpAdd, - 0xff8d => Self::KpEnter, - 0xffbd => Self::KpEqual, - 0xffe1 => Self::LeftShift, - 0xffe3 => Self::LeftControl, - 0xffe9 => Self::LeftAlt, - 0xffeb => Self::LeftSuper, - 0xffe2 => Self::RightShift, - 0xffe4 => Self::RightControl, - 0xffea => Self::RightAlt, - 0xffec => Self::RightSuper, - 0xff67 => Self::Menu, - _ => Self::Unknown, - } - } -} - -pub trait MouseButtonAsU8 { - fn to_u8(&self) -> u8; - fn from_u8(btn: u8) -> Self; -} - -impl MouseButtonAsU8 for MouseButton { - fn to_u8(&self) -> u8 { - match self { - Self::Left => 0, - Self::Middle => 1, - Self::Right => 2, - Self::Unknown => 3, - } - } - - fn from_u8(btn: u8) -> Self { - match btn { - 0 => Self::Left, - 1 => Self::Middle, - 2 => Self::Right, - _ => Self::Unknown, - } - } -} diff --git a/bin/darkwallet/src/main.rs b/bin/darkwallet/src/main.rs index 0ce94756d..146716a10 100644 --- a/bin/darkwallet/src/main.rs +++ b/bin/darkwallet/src/main.rs @@ -51,32 +51,25 @@ use log::LevelFilter; mod app; mod darkirc; -use darkirc::DarkIrcBackend; -//mod chatapp; -//mod chatview; -//mod editbox; mod error; mod expr; -//mod gfx; -mod gfx2; -mod keysym; +mod gfx; mod mesh; mod net; //mod plugin; mod prop; mod pubsub; //mod py; -//mod res; mod scene; -mod shader; -mod text2; +mod text; mod ui; mod util; use crate::{ + darkirc::DarkIrcBackend, net::ZeroMQAdapter, scene::{SceneGraph, SceneGraphPtr2}, - text2::TextShaper, + text::TextShaper, }; pub type ExecutorPtr = Arc>; @@ -134,8 +127,8 @@ fn main() { let (method_req, method_rep) = mpsc::channel(); // The UI actually needs to be running for this to reply back. // Otherwise calls will just hang. - let render_api = gfx2::RenderApi::new(method_req); - let event_pub = gfx2::GraphicsEventPublisher::new(); + let render_api = gfx::RenderApi::new(method_req); + let event_pub = gfx::GraphicsEventPublisher::new(); let text_shaper = TextShaper::new(); @@ -202,8 +195,8 @@ fn main() { }); async_runtime.push_task(ev_relay_task); - //let stage = gfx2::Stage::new(method_rep, event_pub); - gfx2::run_gui(app, async_runtime, method_rep, event_pub); + //let stage = gfx::Stage::new(method_rep, event_pub); + gfx::run_gui(app, async_runtime, method_rep, event_pub); debug!(target: "main", "Started GFX backend"); } diff --git a/bin/darkwallet/src/mesh.rs b/bin/darkwallet/src/mesh.rs index bd1e7b0bd..cabcc2438 100644 --- a/bin/darkwallet/src/mesh.rs +++ b/bin/darkwallet/src/mesh.rs @@ -18,7 +18,7 @@ use crate::{ error::Result, - gfx2::{DrawMesh, Point, Rectangle, RenderApi, Vertex}, + gfx::{DrawMesh, Point, Rectangle, RenderApi, Vertex}, }; use miniquad::{BufferId, TextureId}; diff --git a/bin/darkwallet/src/res.rs b/bin/darkwallet/src/res.rs deleted file mode 100644 index 8568220e0..000000000 --- a/bin/darkwallet/src/res.rs +++ /dev/null @@ -1,73 +0,0 @@ -/* This file is part of DarkFi (https://dark.fi) - * - * Copyright (C) 2020-2024 Dyne.org foundation - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -use crate::error::{Error, Result}; - -pub type ResourceId = u32; - -pub struct ResourceManager { - resources: Vec<(ResourceId, Option)>, - freed: Vec, - id_counter: ResourceId, -} - -impl ResourceManager { - pub fn new() -> Self { - Self { resources: vec![], freed: vec![], id_counter: 0 } - } - - pub fn alloc(&mut self, rsrc: T) -> ResourceId { - let id = self.id_counter; - self.id_counter += 1; - - if self.freed.is_empty() { - let idx = self.resources.len(); - self.resources.push((id, Some(rsrc))); - } else { - let idx = self.freed.pop().unwrap(); - let _ = std::mem::replace(&mut self.resources[idx], (id, Some(rsrc))); - } - id - } - - pub fn get(&self, id: ResourceId) -> Option<&T> { - for (idx, (rsrc_id, rsrc)) in self.resources.iter().enumerate() { - if self.freed.contains(&idx) { - continue - } - if *rsrc_id == id { - return rsrc.as_ref() - } - } - None - } - - pub fn free(&mut self, id: ResourceId) -> Result<()> { - for (idx, (rsrc_id, rsrc)) in self.resources.iter_mut().enumerate() { - if self.freed.contains(&idx) { - return Err(Error::ResourceNotFound) - } - if *rsrc_id == id { - *rsrc = None; - self.freed.push(idx); - return Ok(()) - } - } - Err(Error::ResourceNotFound) - } -} diff --git a/bin/darkwallet/src/text.rs b/bin/darkwallet/src/text.rs deleted file mode 100644 index f2cd09f56..000000000 --- a/bin/darkwallet/src/text.rs +++ /dev/null @@ -1,251 +0,0 @@ -/* This file is part of DarkFi (https://dark.fi) - * - * Copyright (C) 2020-2024 Dyne.org foundation - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -use freetype as ft; - -use crate::gfx::{FreetypeFace, Rectangle}; - -// From https://sourceforge.net/projects/freetype/files/freetype2/2.6/ -// -// * An `FT_Face' object can only be safely used from one thread at -// a time. -// -// * An `FT_Library' object can now be used without modification -// from multiple threads at the same time. -// -// * `FT_Face' creation and destruction with the same `FT_Library' -// object can only be done from one thread at a time. -// -// One can use a single `FT_Library' object across threads as long -// as a mutex lock is used around `FT_New_Face' and `FT_Done_Face'. -// Any calls to `FT_Load_Glyph' and similar API are safe and do not -// need the lock to be held as long as the same `FT_Face' is not -// used from multiple threads at the same time. - -// Harfbuzz is threadsafe. - -// Notes: -// * All ft init and face creation should happen at startup. -// * FT faces protected behind an async Mutex -// * Glyph cache. Key is (glyph_id, font_size) -// * Glyph texture cache: (glyph_id, font_size, color) - -#[derive(Clone)] -pub struct Glyph { - pub id: u32, - // Substring this glyph corresponds to - pub substr: String, - - // The texture - pub bmp: Vec, - pub bmp_width: u16, - pub bmp_height: u16, - - pub pos: Rectangle, -} - -pub struct TextShaper { - pub font_faces: Vec, -} - -unsafe impl Send for TextShaper {} -unsafe impl Sync for TextShaper {} - -impl TextShaper { - fn split_into_substrs(&self, text: String) -> Vec<(usize, String)> { - let mut current_idx = 0; - let mut current_str = String::new(); - let mut substrs = vec![]; - 'next_char: for chr in text.chars() { - let idx = 'get_idx: { - for i in 0..self.font_faces.len() { - let ft_face = &self.font_faces[i]; - if ft_face.get_char_index(chr as usize).is_some() { - break 'get_idx i - } - } - - warn!("no font fallback for char: '{}'", chr); - // Skip this char - continue 'next_char - }; - if current_idx != idx { - if !current_str.is_empty() { - // Push - substrs.push((current_idx, current_str.clone())); - } - - current_str.clear(); - current_idx = idx; - } - current_str.push(chr); - } - if !current_str.is_empty() { - // Push - substrs.push((current_idx, current_str)); - } - substrs - } - - pub fn shape(&self, text: String, font_size: f32, text_color: [f32; 4]) -> Vec { - let substrs = self.split_into_substrs(text.clone()); - - let mut glyphs: Vec = vec![]; - - let mut current_x = 0.; - let mut current_y = 0.; - - for (face_idx, text) in substrs { - //debug!("substr {}", text); - let face = &self.font_faces[face_idx]; - if face.has_fixed_sizes() { - // emojis required a fixed size - //face.set_char_size(109 * 64, 0, 72, 72).unwrap(); - face.select_size(0).unwrap(); - } else { - face.set_char_size(font_size as isize * 64, 0, 72, 72).unwrap(); - } - - let hb_font = harfbuzz_rs::Font::from_freetype_face(face.clone()); - let buffer = harfbuzz_rs::UnicodeBuffer::new() - .set_cluster_level(harfbuzz_rs::ClusterLevel::MonotoneCharacters) - .add_str(&text); - let output = harfbuzz_rs::shape(&hb_font, buffer, &[]); - - let positions = output.get_glyph_positions(); - let infos = output.get_glyph_infos(); - - let mut prev_cluster = 0; - - for (i, (position, info)) in positions.iter().zip(infos).enumerate() { - let gid = info.codepoint; - // Index within this substr - let curr_cluster = info.cluster as usize; - - // Skip first time - if i != 0 { - let substr = text[prev_cluster..curr_cluster].to_string(); - glyphs.last_mut().unwrap().substr = substr; - } - - prev_cluster = curr_cluster; - - let mut flags = ft::face::LoadFlag::DEFAULT; - if face.has_color() { - flags |= ft::face::LoadFlag::COLOR; - } - // FIXME: glyph 884 hangs on android - // For now just avoid using emojis on android - //debug!("load_glyph {}", gid); - face.load_glyph(gid, flags).unwrap(); - //debug!("load_glyph {} [done]", gid); - - let glyph = face.glyph(); - glyph.render_glyph(ft::RenderMode::Normal).unwrap(); - - let bmp = glyph.bitmap(); - let buffer = bmp.buffer(); - let bmp_width = bmp.width() as usize; - let bmp_height = bmp.rows() as usize; - let bearing_x = glyph.bitmap_left() as f32; - let bearing_y = glyph.bitmap_top() as f32; - - let pixel_mode = bmp.pixel_mode().unwrap(); - let bmp = match pixel_mode { - ft::bitmap::PixelMode::Bgra => { - let mut tdata = vec![]; - tdata.resize(4 * bmp_width * bmp_height, 0); - // Convert from BGRA to RGBA - for i in 0..bmp_width * bmp_height { - let idx = i * 4; - let b = buffer[idx]; - let g = buffer[idx + 1]; - let r = buffer[idx + 2]; - let a = buffer[idx + 3]; - tdata[idx] = r; - tdata[idx + 1] = g; - tdata[idx + 2] = b; - tdata[idx + 3] = a; - } - tdata - } - ft::bitmap::PixelMode::Gray => { - // Convert from greyscale to RGBA8 - let tdata: Vec<_> = buffer - .iter() - .flat_map(|coverage| { - let r = (255. * text_color[0]) as u8; - let g = (255. * text_color[1]) as u8; - let b = (255. * text_color[2]) as u8; - let α = ((*coverage as f32) * text_color[3]) as u8; - vec![r, g, b, α] - }) - .collect(); - tdata - } - _ => panic!("unsupport pixel mode: {:?}", pixel_mode), - }; - - let pos = if face.has_fixed_sizes() { - // Downscale by height - let w = (bmp_width as f32 * font_size) / bmp_height as f32; - let h = font_size; - - // Shouldn't this use the bearing? - let x = current_x; - let y = current_y - h; - - current_x += w; - - Rectangle { x, y, w, h } - } else { - let (w, h) = (bmp_width as f32, bmp_height as f32); - - let off_x = position.x_offset as f32 / 64.; - let off_y = position.y_offset as f32 / 64.; - - let x = current_x + off_x + bearing_x; - let y = current_y - off_y - bearing_y; - - let x_advance = position.x_advance as f32 / 64.; - let y_advance = position.y_advance as f32 / 64.; - current_x += x_advance; - current_y += y_advance; - - Rectangle { x, y, w, h } - }; - - let glyph = Glyph { - id: gid, - substr: String::new(), - bmp, - bmp_width: bmp_width as u16, - bmp_height: bmp_height as u16, - pos, - }; - - glyphs.push(glyph); - } - - let substr = text[prev_cluster..].to_string(); - glyphs.last_mut().unwrap().substr = substr; - } - - glyphs - } -} diff --git a/bin/darkwallet/src/text2/atlas.rs b/bin/darkwallet/src/text/atlas.rs similarity index 99% rename from bin/darkwallet/src/text2/atlas.rs rename to bin/darkwallet/src/text/atlas.rs index bf65c2f41..384eea6c2 100644 --- a/bin/darkwallet/src/text2/atlas.rs +++ b/bin/darkwallet/src/text/atlas.rs @@ -2,7 +2,7 @@ use miniquad::TextureId; use crate::{ error::{Error, Result}, - gfx2::{Rectangle, RenderApi, RenderApiPtr}, + gfx::{Rectangle, RenderApi, RenderApiPtr}, util::{ansi_texture, zip3}, }; diff --git a/bin/darkwallet/src/text2/mod.rs b/bin/darkwallet/src/text/mod.rs similarity index 99% rename from bin/darkwallet/src/text2/mod.rs rename to bin/darkwallet/src/text/mod.rs index 5c6c62919..26ddff4fa 100644 --- a/bin/darkwallet/src/text2/mod.rs +++ b/bin/darkwallet/src/text/mod.rs @@ -34,7 +34,7 @@ use std::{ use crate::{ error::Result, - gfx2::{Rectangle, RenderApi, RenderApiPtr}, + gfx::{Rectangle, RenderApi, RenderApiPtr}, util::ansi_texture, }; diff --git a/bin/darkwallet/src/text2/old_atlas.rs b/bin/darkwallet/src/text/old_atlas.rs similarity index 98% rename from bin/darkwallet/src/text2/old_atlas.rs rename to bin/darkwallet/src/text/old_atlas.rs index 8873b4802..91e9fcbe1 100644 --- a/bin/darkwallet/src/text2/old_atlas.rs +++ b/bin/darkwallet/src/text/old_atlas.rs @@ -2,7 +2,7 @@ use miniquad::TextureId; use crate::{ error::Result, - gfx2::{Rectangle, RenderApi, RenderApiPtr}, + gfx::{Rectangle, RenderApi, RenderApiPtr}, util::ansi_texture, }; diff --git a/bin/darkwallet/src/text2/wrap.rs b/bin/darkwallet/src/text/wrap.rs similarity index 100% rename from bin/darkwallet/src/text2/wrap.rs rename to bin/darkwallet/src/text/wrap.rs diff --git a/bin/darkwallet/src/ui/button.rs b/bin/darkwallet/src/ui/button.rs index 010279758..f4d854df1 100644 --- a/bin/darkwallet/src/ui/button.rs +++ b/bin/darkwallet/src/ui/button.rs @@ -24,7 +24,7 @@ use std::sync::{ }; use crate::{ - gfx2::{ + gfx::{ DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, Point, Rectangle, RenderApiPtr, Vertex, }, diff --git a/bin/darkwallet/src/ui/chatview.rs b/bin/darkwallet/src/ui/chatview.rs index 797548651..8a06527de 100644 --- a/bin/darkwallet/src/ui/chatview.rs +++ b/bin/darkwallet/src/ui/chatview.rs @@ -35,7 +35,7 @@ use std::{ use crate::{ error::Result, - gfx2::{ + gfx::{ DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, Point, Rectangle, RenderApi, RenderApiPtr, Vertex, }, @@ -46,7 +46,7 @@ use crate::{ }, pubsub::Subscription, scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId}, - text2::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr}, + text::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr}, util::zip3, ExecutorPtr, }; @@ -107,7 +107,7 @@ const PRELOAD_PAGES: usize = 10; #[derive(Clone)] struct Page { msgs: Vec, - atlas: text2::RenderedAtlas, + atlas: text::RenderedAtlas, } #[derive(Clone)] @@ -120,7 +120,7 @@ type Page2Ptr = Arc; struct Page2 { msgs: Vec, - atlas: SyncMutex, + atlas: SyncMutex, // One draw call per page. // Resizing the canvas means we recalc wrapping and the mesh changes mesh_inf: SyncMutex>, @@ -128,7 +128,7 @@ struct Page2 { impl Page2 { async fn new(msgs: Vec, render_api: &RenderApi) -> Arc { - let mut atlas = text2::Atlas::new(render_api); + let mut atlas = text::Atlas::new(render_api); for msg in &msgs { atlas.push(&msg.glyphs); } @@ -169,7 +169,7 @@ impl Page2 { // Finally is just the message itself let mut section = 2; - let mut lines = text2::wrap(clip.w, font_size, glyphs); + let mut lines = text::wrap(clip.w, font_size, glyphs); // We are drawing bottom up but line wrap gives us lines in normal order lines.reverse(); let last_idx = lines.len() - 1; diff --git a/bin/darkwallet/src/ui/editbox.rs b/bin/darkwallet/src/ui/editbox.rs index fccb83ec0..343a70ee7 100644 --- a/bin/darkwallet/src/ui/editbox.rs +++ b/bin/darkwallet/src/ui/editbox.rs @@ -29,7 +29,7 @@ use std::{ use crate::{ error::Result, - gfx2::{ + gfx::{ DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, Point, Rectangle, RenderApi, RenderApiPtr, Vertex, }, @@ -40,7 +40,7 @@ use crate::{ }, pubsub::Subscription, scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId}, - text2::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr}, + text::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr}, util::zip3, ExecutorPtr, }; @@ -361,7 +361,7 @@ impl EditBox { debug!(target: "ui::editbox", " cursor_pos={cursor_pos}, is_focused={is_focused}"); let glyphs = self.glyphs.lock().unwrap().clone(); - let atlas = text2::make_texture_atlas(&self.render_api, &glyphs).await.unwrap(); + let atlas = text::make_texture_atlas(&self.render_api, &glyphs).await.unwrap(); let mut mesh = MeshBuilder::with_clip(clip.clone()); self.draw_selected(&mut mesh, &glyphs, clip.h).unwrap(); diff --git a/bin/darkwallet/src/ui/image.rs b/bin/darkwallet/src/ui/image.rs index 06abedc70..000de1b15 100644 --- a/bin/darkwallet/src/ui/image.rs +++ b/bin/darkwallet/src/ui/image.rs @@ -26,14 +26,14 @@ use std::{ }; use crate::{ - gfx2::{DrawCall, DrawInstruction, DrawMesh, Rectangle, RenderApi, RenderApiPtr, Vertex}, + gfx::{DrawCall, DrawInstruction, DrawMesh, Rectangle, RenderApi, RenderApiPtr, Vertex}, mesh::{Color, MeshBuilder, MeshInfo, COLOR_BLUE, COLOR_WHITE}, prop::{ PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyStr, PropertyUint32, Role, }, scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId}, - text2::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr}, + text::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr}, util::zip3, ExecutorPtr, }; diff --git a/bin/darkwallet/src/ui/layer.rs b/bin/darkwallet/src/ui/layer.rs index 2bfd4b19b..1ef2d45a8 100644 --- a/bin/darkwallet/src/ui/layer.rs +++ b/bin/darkwallet/src/ui/layer.rs @@ -21,7 +21,7 @@ use rand::{rngs::OsRng, Rng}; use std::sync::{Arc, Weak}; use crate::{ - gfx2::{DrawCall, DrawInstruction, Rectangle, RenderApiPtr}, + gfx::{DrawCall, DrawInstruction, Rectangle, RenderApiPtr}, prop::{PropertyBool, PropertyPtr, Role}, scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId}, ExecutorPtr, diff --git a/bin/darkwallet/src/ui/mesh.rs b/bin/darkwallet/src/ui/mesh.rs index bc1344e97..e9d414b2b 100644 --- a/bin/darkwallet/src/ui/mesh.rs +++ b/bin/darkwallet/src/ui/mesh.rs @@ -20,7 +20,7 @@ use rand::{rngs::OsRng, Rng}; use std::sync::{Arc, Weak}; use crate::{ - gfx2::{DrawCall, DrawInstruction, DrawMesh, Rectangle, RenderApiPtr, Vertex}, + gfx::{DrawCall, DrawInstruction, DrawMesh, Rectangle, RenderApiPtr, Vertex}, prop::{PropertyPtr, PropertyUint32, Role}, scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId}, ExecutorPtr, diff --git a/bin/darkwallet/src/ui/mod.rs b/bin/darkwallet/src/ui/mod.rs index e52d79ca5..6786aa824 100644 --- a/bin/darkwallet/src/ui/mod.rs +++ b/bin/darkwallet/src/ui/mod.rs @@ -22,7 +22,7 @@ use std::sync::{Arc, Weak}; use crate::{ error::{Error, Result}, expr::{SExprMachine, SExprVal}, - gfx2::{DrawCall, Rectangle}, + gfx::{DrawCall, Rectangle}, prop::{PropertyPtr, Role}, scene::{SceneGraph, SceneNode, SceneNodeId, SceneNodeType}, ExecutorPtr, diff --git a/bin/darkwallet/src/ui/text.rs b/bin/darkwallet/src/ui/text.rs index 538f6137a..3775edb4d 100644 --- a/bin/darkwallet/src/ui/text.rs +++ b/bin/darkwallet/src/ui/text.rs @@ -22,14 +22,14 @@ use rand::{rngs::OsRng, Rng}; use std::sync::{Arc, Mutex as SyncMutex, Weak}; use crate::{ - gfx2::{DrawCall, DrawInstruction, DrawMesh, Rectangle, RenderApi, RenderApiPtr, Vertex}, + gfx::{DrawCall, DrawInstruction, DrawMesh, Rectangle, RenderApi, RenderApiPtr, Vertex}, mesh::{Color, MeshBuilder, MeshInfo, COLOR_BLUE, COLOR_WHITE}, prop::{ PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyStr, PropertyUint32, Role, }, scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId}, - text2::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr}, + text::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr}, util::zip3, ExecutorPtr, }; @@ -137,7 +137,7 @@ impl Text { ) -> TextRenderInfo { debug!(target: "ui::text", "Rendering label '{}'", text); let glyphs = text_shaper.shape(text, font_size).await; - let atlas = text2::make_texture_atlas(render_api, &glyphs).await.unwrap(); + let atlas = text::make_texture_atlas(render_api, &glyphs).await.unwrap(); let mut mesh = MeshBuilder::new(); let mut glyph_pos_iter = GlyphPositionIter::new(font_size, &glyphs, baseline); diff --git a/bin/darkwallet/src/ui/win.rs b/bin/darkwallet/src/ui/win.rs index eb29889a2..f395835f0 100644 --- a/bin/darkwallet/src/ui/win.rs +++ b/bin/darkwallet/src/ui/win.rs @@ -19,7 +19,7 @@ use std::sync::{Arc, Weak}; use crate::{ - gfx2::{DrawCall, GraphicsEventPublisherPtr, Rectangle, RenderApiPtr}, + gfx::{DrawCall, GraphicsEventPublisherPtr, Rectangle, RenderApiPtr}, prop::{PropertyPtr, Role}, scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId}, ExecutorPtr,