فهرست منبع

wallet: atrocious hackjob chatapp working on android with APKs

darkfi 2 سال پیش
والد
کامیت
3b5ce35440

+ 8 - 4
bin/darkwallet/Cargo.toml

@@ -9,9 +9,12 @@ edition = "2021"
 
 [dependencies]
 #miniquad = "0.4.0-alpha"
-miniquad = { git = "https://github.com/not-fl3/miniquad" }
-harfbuzz_rs = { git = "https://github.com/narodnik/harfbuzz_rs.git", features = ["freetype"] }
+#miniquad = { git = "https://github.com/not-fl3/miniquad" }
+miniquad = { path = "/tmp/miniquad" }
+#harfbuzz_rs = { git = "https://github.com/narodnik/harfbuzz_rs.git", features = ["freetype"] }
+harfbuzz_rs = { path = "/tmp/harfbuzz_rs", features = ["freetype"] }
 freetype-rs = { git = "https://github.com/narodnik/freetype-rs.git" }
+#harfbuzz = { version = "0.6.0", features = ["bundled"] }
 #harfbuzz_rs = "2.0.1"
 #freetype-rs = "0.36.0"
 freetype-sys = "0.20.1"
@@ -19,13 +22,14 @@ image = "0.25.1"
 log = "0.4.21"
 glam = "0.27.0"
 zmq = "0.10.0"
-darkfi-serial = { path = "../../src/serial" }
+#darkfi-serial = { path = "../../src/serial" }
+darkfi-serial = { git = "https://codeberg.org/darkrenaissance/darkfi.git" }
 thiserror = "1.0.61"
 # Only for async Mutex
 smol = "2.0.0"
 atomic_float = "1.0.0"
 
-rustpython-vm = "0.3.1"
+#rustpython-vm = "0.3.1"
 
 #[patch."https://github.com/narodnik/harfbuzz_rs.git"]
 #freetype-rs = { path = "/tmp/freetype-rs/" }

+ 2 - 0
bin/darkwallet/README.md

@@ -1,2 +1,4 @@
 `cargo run` then `python client.py`
 
+docker run --rm -v $(pwd)":/root/src" -w /root/src cargo-quad-apk cargo quad-apk build
+

+ 1 - 1
bin/darkwallet/gui/api.py

@@ -2,7 +2,7 @@ from collections import namedtuple
 from pydrk import Api, HostApi, PropertyType, PropertySubType, Property, serial
 import zmq
 
-api = Api()
+api = Api(addr="[2a02:aa13:8342:1400:81b3:9662:6650:690d]")
 host = HostApi(api)
 print("Node status:", api.hello())
 

+ 478 - 0
bin/darkwallet/src/chatapp.rs

@@ -0,0 +1,478 @@
+use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable};
+use std::sync::mpsc;
+
+use crate::{expr::Op,
+error::Result,
+gfx::Rectangle,
+    prop::{Property, PropertySubType, PropertyType},
+    res::{ResourceId, ResourceManager},
+    scene::{
+        MethodResponseFn, SceneGraph, SceneGraphPtr, SceneNode, SceneNodeId, SceneNodeInfo,
+        SceneNodeType, Pimpl
+    }};
+
+struct Buffer {
+    verts: Vec<u8>,
+    faces: Vec<u8>,
+    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<f32>, 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<f32>, 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);
+    }
+}
+
+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::Uint32, PropertySubType::Pixel);
+    prop.set_array_len(4);
+    prop.allow_exprs();
+    node.add_property(prop).unwrap();
+
+    let node_id = node.id;
+    sg.link(node_id, win_id).unwrap();
+    node_id
+}
+
+fn create_mesh(sg: &mut SceneGraph, name: &str, layer_node_id: SceneNodeId) -> SceneNodeId {
+    let node = sg.add_node(name, SceneNodeType::RenderMesh);
+    let mut prop = Property::new("data", PropertyType::Buffer, PropertySubType::Null);
+    prop.set_array_len(2);
+    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("z_index", PropertyType::Uint32, PropertySubType::Null);
+    node.add_property(prop).unwrap();
+
+    let node_id = node.id;
+    sg.link(node_id, layer_node_id).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::<Result<Vec<u8>>>(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", 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::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", layer_node_id);
+    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", layer_node_id);
+    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::<Result<Vec<u8>>>(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();
+}
+

+ 7 - 0
bin/darkwallet/src/chatview.rs

@@ -1,5 +1,6 @@
 use atomic_float::AtomicF32;
 use miniquad::{KeyMods, UniformType, MouseButton, window};
+use log::debug;
 use std::{
     collections::HashMap,
     path::Path,
@@ -187,6 +188,8 @@ impl ChatView {
                 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);
                 render.render_clipped_box_with_texture2(&bound, &pos, COLOR_WHITE, texture);
                 render.ctx.delete_texture(texture);
@@ -214,6 +217,8 @@ impl ChatView {
                 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);
                 render.render_clipped_box_with_texture2(&bound, &pos, COLOR_WHITE, texture);
                 render.ctx.delete_texture(texture);
@@ -225,6 +230,8 @@ impl ChatView {
                 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);
                 render.render_clipped_box_with_texture2(&bound, &pos, COLOR_WHITE, texture);
                 render.ctx.delete_texture(texture);

+ 43 - 7
bin/darkwallet/src/editbox.rs

@@ -97,6 +97,7 @@ pub struct EditBox {
     key_repeat: Mutex<PressedKeysSmoothRepeat>,
     mouse_btn_held: AtomicBool,
     window_scale: f32,
+    screen_size: Arc<Property>,
 }
 
 impl EditBox {
@@ -121,11 +122,12 @@ impl EditBox {
 
         // TODO: catch window resize event and regen glyphs
         // Used for scaling the font size
-        let window = 
+        let window_node = 
             scene_graph
             .lookup_node("/window")
             .expect("no window attached!");
-        let window_scale = window.get_property_f32("scale")?;
+        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(),
@@ -146,6 +148,7 @@ impl EditBox {
             key_repeat: Mutex::new(PressedKeysSmoothRepeat::new(400, 50)),
             mouse_btn_held: AtomicBool::new(false),
             window_scale,
+            screen_size,
         });
         self_.regen_glyphs().unwrap();
 
@@ -193,8 +196,8 @@ impl EditBox {
             scene_graph
             .lookup_node_mut("/window/input/keyboard")
             .expect("no keyboard attached!");
-        keyb_node.register("key_down", slot_key_down);
-        keyb_node.register("key_up", slot_key_up);
+        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 {
@@ -247,9 +250,30 @@ impl EditBox {
             scene_graph
             .lookup_node_mut("/window/input/mouse")
             .expect("no mouse attached!");
-        mouse_node.register("button_down", slot_btn_down);
-        mouse_node.register("button_up", slot_btn_up);
-        mouse_node.register("move", slot_move);
+        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_))
@@ -264,6 +288,10 @@ impl EditBox {
         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;
@@ -459,8 +487,10 @@ impl EditBox {
     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);
         }
@@ -681,11 +711,14 @@ impl EditBox {
     fn mouse_button_down(self: Arc<Self>, 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!");
@@ -786,6 +819,9 @@ impl EditBox {
 
         MouseClickGlyph::Pos(cpos)
     }
+
+    fn window_resize(self: Arc<Self>, w: f32, h: f32) {
+    }
 }
 
 enum MouseClickGlyph {

+ 19 - 0
bin/darkwallet/src/gfx.rs

@@ -1,5 +1,6 @@
 use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable};
 use freetype as ft;
+use log::LevelFilter;
 use miniquad::{
     conf, window, Backend, Bindings, BlendFactor, BlendState, BlendValue, BufferId, BufferLayout,
     BufferSource, BufferType, BufferUsage, Equation, EventHandler, KeyCode, KeyMods, MouseButton,
@@ -218,6 +219,9 @@ impl Stage {
             last_draw_time: None,
         };
         stage.setup_scene_graph_window();
+
+        stage.setup_scene();
+
         debug!("Finished loading GUI");
         stage
     }
@@ -419,6 +423,7 @@ impl Stage {
         _: SceneNodeId,
         arg_data: Vec<u8>,
     ) -> Result<Vec<u8>> {
+        debug!("gfx::create_chatview()");
         let mut cur = Cursor::new(&arg_data);
         let node_id = SceneNodeId::decode(&mut cur).unwrap();
 
@@ -437,6 +442,7 @@ impl Stage {
         _: SceneNodeId,
         arg_data: Vec<u8>,
     ) -> Result<Vec<u8>> {
+        debug!("gfx::create_editbox()");
         let mut cur = Cursor::new(&arg_data);
         let node_id = SceneNodeId::decode(&mut cur).unwrap();
 
@@ -449,6 +455,11 @@ impl Stage {
         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> {
@@ -572,6 +583,10 @@ impl<'a> RenderContext<'a> {
                 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")
                     };
@@ -582,6 +597,10 @@ impl<'a> RenderContext<'a> {
                 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")
                     };

+ 3 - 0
bin/darkwallet/src/main.rs

@@ -6,6 +6,8 @@ use std::{
     thread,
 };
 
+mod chatapp;
+
 mod chatview;
 
 mod editbox;
@@ -61,6 +63,7 @@ fn start_sentinel(scene_graph: SceneGraphPtr) {
 
 fn main() {
     let scene_graph = Arc::new(Mutex::new(SceneGraph::new()));
+
     start_zmq(scene_graph.clone());
     start_sentinel(scene_graph.clone());
     run_gui(scene_graph);

+ 12 - 12
bin/darkwallet/src/scene.rs

@@ -445,18 +445,18 @@ impl SceneNode {
     pub fn set_property_bool(&self, name: &str, val: bool) -> Result<()> {
         self.get_property(name).ok_or(Error::PropertyNotFound)?.set_bool(0, val)
     }
-    //pub fn set_property_u32(&self, name: &str, val: u32) -> Result<()> {
-    //    self.get_property(name).ok_or(Error::PropertyNotFound)?.set_u32(val)
-    //}
-    //pub fn set_property_f32(&self, name: &str, val: f32) -> Result<()> {
-    //    self.get_property(name).ok_or(Error::PropertyNotFound)?.set_f32(val)
-    //}
-    //pub fn set_property_str<S: Into<String>>(&self, name: &str, val: S) -> Result<()> {
-    //    self.get_property(name).ok_or(Error::PropertyNotFound)?.set_str(val)
-    //}
-    //pub fn set_property_node_id(&self, name: &str, val: SceneNodeId) -> Result<()> {
-    //    self.get_property(name).ok_or(Error::PropertyNotFound)?.set_node_id(val)
-    //}
+    pub fn set_property_u32(&self, name: &str, val: u32) -> Result<()> {
+        self.get_property(name).ok_or(Error::PropertyNotFound)?.set_u32(0, val)
+    }
+    pub fn set_property_f32(&self, name: &str, val: f32) -> Result<()> {
+        self.get_property(name).ok_or(Error::PropertyNotFound)?.set_f32(0, val)
+    }
+    pub fn set_property_str<S: Into<String>>(&self, name: &str, val: S) -> Result<()> {
+        self.get_property(name).ok_or(Error::PropertyNotFound)?.set_str(0, val)
+    }
+    pub fn set_property_node_id(&self, name: &str, val: SceneNodeId) -> Result<()> {
+        self.get_property(name).ok_or(Error::PropertyNotFound)?.set_node_id(0, val)
+    }
 
     pub fn add_signal<S: Into<String>>(
         &mut self,

+ 6 - 0
bin/darkwallet/src/text.rs

@@ -1,4 +1,5 @@
 use freetype as ft;
+use log::debug;
 
 use crate::gfx::{Rectangle, FreetypeFace};
 
@@ -67,6 +68,7 @@ impl TextShaper {
         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
@@ -104,7 +106,11 @@ impl TextShaper {
                 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();