Explorar o código

wallet: add a z_index property to control draw ordering of objs

darkfi %!s(int64=2) %!d(string=hai) anos
pai
achega
aa6cff4f2e

+ 63 - 0
bin/darkwallet/gui/__init__.py

@@ -286,6 +286,15 @@ def draw():
     api.add_property(layer_id, prop)
     api.set_property_bool(layer_id, "is_visible", 0, True)
 
+    #prop = Property(
+    #    "redraw", PropertyType.BOOL, PropertySubType.NULL,
+    #    None,
+    #    "redraw", "Redraw this layer",
+    #    False, False, 1, None, None, []
+    #)
+    #api.add_property(layer_id, prop)
+    #api.set_property_bool(layer_id, "redraw", 0, True)
+
     prop = Property(
         "rect", PropertyType.UINT32, PropertySubType.PIXEL,
         None,
@@ -354,6 +363,60 @@ def draw():
     code = [["-", ["load", "lh"], ["f32", 10]]]
     api.set_property_expr(mesh_id, "rect", 3, code)
 
+    prop = Property(
+        "z_index", PropertyType.UINT32, PropertySubType.NULL,
+        None,
+        "z-index", "Z-index: values greater than zero are deferred draws",
+        False, False, 1, None, None, []
+    )
+    api.add_property(mesh_id, prop)
+
+    api.link_node(mesh_id, layer_id)
+
+    # Add a second mesh to our layer
+
+    mesh_id = api.add_node("meshie2", SceneNodeType.RENDER_MESH)
+
+    prop = Property(
+        "data", PropertyType.BUFFER, PropertySubType.NULL,
+        None,
+        "mesh_data", "The face and vertex data for the mesh",
+        False, False, 2, None, None, []
+    )
+    api.add_property(mesh_id, prop)
+
+    x, y, w, h = 0, 0, 1, 1
+    vert1 = vertex(x,     y,     1, 0, 1, 1, 0, 0)
+    vert2 = vertex(x + w, y,     0.5, 0, 1, 1, 1, 0)
+    vert3 = vertex(x,     y + h, 1, 0, 0.5, 1, 0, 1)
+    vert4 = vertex(x + w, y + h, 0.5, 1, 0.5, 1, 1, 1)
+
+    verts = vert1 + vert2 + vert3 + vert4
+    faces = face(0, 2, 1) + face(1, 2, 3)
+
+    api.set_property_buf(mesh_id, "data", 0, verts)
+    api.set_property_buf(mesh_id, "data", 1, faces)
+
+    prop = Property(
+        "rect", PropertyType.FLOAT32, PropertySubType.PIXEL,
+        None,
+        "mesh_rect", "The position and size within the layer",
+        False, True, 4, None, None, []
+    )
+    api.add_property(mesh_id, prop)
+    api.set_property_f32(mesh_id, "rect", 0, 10)
+    api.set_property_f32(mesh_id, "rect", 1, 10)
+    api.set_property_f32(mesh_id, "rect", 2, 60)
+    api.set_property_f32(mesh_id, "rect", 3, 60)
+
+    prop = Property(
+        "z_index", PropertyType.UINT32, PropertySubType.NULL,
+        None,
+        "z-index", "Z-index: values greater than zero are deferred draws",
+        False, False, 1, None, None, []
+    )
+    api.add_property(mesh_id, prop)
+
     api.link_node(mesh_id, layer_id)
 
 def main():

+ 8 - 4
bin/darkwallet/gui/print_tree.py

@@ -50,11 +50,15 @@ def print_node_info(parent_id, indent):
         print_node_info(child_id, indent+1)
 
     for prop in api.get_properties(parent_id):
-        if prop.type == PropertyType.STR:
-            prop_val = api.get_property_value(parent_id, prop.name)
-            prop_val = f" = \"{prop_val}\""
-        elif prop.type != PropertyType.BUFFER:
+        if prop.type != PropertyType.BUFFER:
             prop_val = api.get_property_value(parent_id, prop.name)
+
+            if prop.type == PropertyType.STR:
+                prop_val = [f"\"{pv}\"" for pv in prop_val]
+
+            if len(prop_val) == 1:
+                prop_val = prop_val[0]
+
             prop_val = f" = {prop_val}"
         else:
             prop_val = ""

+ 9 - 4
bin/darkwallet/src/expr.rs

@@ -2,8 +2,13 @@ use crate::{
     error::{Error, Result},
     //prop::{Property, PropertySubType, PropertyType, PropertySExprValue},
 };
-use darkfi_serial::{Decodable, Encodable, ReadExt, SerialDecodable, SerialEncodable, WriteExt, serialize};
-use std::{io::{Read, Write}, sync::Arc};
+use darkfi_serial::{
+    serialize, Decodable, Encodable, ReadExt, SerialDecodable, SerialEncodable, WriteExt,
+};
+use std::{
+    io::{Read, Write},
+    sync::Arc,
+};
 
 #[derive(Clone, Debug, PartialEq, SerialEncodable, SerialDecodable)]
 pub enum SExprVal {
@@ -113,7 +118,7 @@ pub enum Op {
     Max((Box<Op>, Box<Op>)),
     IsEqual((Box<Op>, Box<Op>)),
     LessThan((Box<Op>, Box<Op>)),
-    Float32ToUint32(Box<Op>)
+    Float32ToUint32(Box<Op>),
 }
 
 impl<'a> SExprMachine<'a> {
@@ -384,7 +389,7 @@ impl Decodable for Op {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use darkfi_serial::{serialize, deserialize};
+    use darkfi_serial::{deserialize, serialize};
 
     #[test]
     fn seval() {

+ 67 - 70
bin/darkwallet/src/gfx.rs

@@ -14,8 +14,9 @@ use std::{
 
 use crate::{
     error::{Error, Result},
-    expr::{SExprVal, SExprMachine},
+    expr::{SExprMachine, SExprVal},
     prop::{Property, PropertySubType, PropertyType},
+    res::{ResourceId, ResourceManager},
     scene::{MethodResponseFn, SceneGraph, SceneGraphPtr, SceneNode, SceneNodeId, SceneNodeType},
     shader,
 };
@@ -82,60 +83,6 @@ impl<T> Rectangle<T> {
     }
 }
 
-type ResourceId = u32;
-
-struct ResourceManager<T> {
-    resources: Vec<(ResourceId, Option<T>)>,
-    freed: Vec<usize>,
-    id_counter: ResourceId,
-}
-
-impl<T> ResourceManager<T> {
-    fn new() -> Self {
-        Self { resources: vec![], freed: vec![], id_counter: 0 }
-    }
-
-    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
-    }
-
-    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
-    }
-
-    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)
-    }
-}
-
 #[derive(Debug)]
 enum GraphicsMethodEvent {
     CreateText,
@@ -262,7 +209,7 @@ impl Stage {
             font,
             method_recvr,
             method_sender,
-            last_draw_time: None
+            last_draw_time: None,
         };
         stage.setup_scene_graph_window();
         debug!("Finished loading GUI");
@@ -554,7 +501,7 @@ impl Stage {
         img_node.add_property(prop)?;
 
         let mut prop =
-            Property::new("texture_id", PropertyType::Uint32, PropertySubType::ResourceId);
+            Property::new("texture_rid", PropertyType::Uint32, PropertySubType::ResourceId);
         prop.set_u32(0, id).unwrap();
         img_node.add_property(prop)?;
 
@@ -630,12 +577,22 @@ impl Stage {
     }
 }
 
+struct DeferredDraw {
+    z_index: u32,
+    vertex_buffer: BufferId,
+    index_buffer: BufferId,
+    texture: TextureId,
+    uniforms_data: [u8; 128],
+    faces_len: usize,
+}
+
 struct RenderContext<'a> {
     scene_graph: MutexGuard<'a, SceneGraph>,
     ctx: &'a mut Box<dyn RenderingBackend>,
     pipeline: &'a Pipeline,
     proj: glam::Mat4,
     textures: &'a ResourceManager<TextureId>,
+    draw_calls: Vec<DeferredDraw>,
 }
 
 impl<'a> RenderContext<'a> {
@@ -646,10 +603,14 @@ impl<'a> RenderContext<'a> {
             .expect("no window attached!")
             .get_children(&[SceneNodeType::RenderLayer])
         {
+            self.draw_calls.clear();
+
             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<Rectangle<i32>> {
@@ -670,7 +631,7 @@ impl<'a> RenderContext<'a> {
                         ("sw".to_string(), SExprVal::Float32(screen_width)),
                         ("sh".to_string(), SExprVal::Float32(screen_height)),
                     ],
-                    stmts: &expr
+                    stmts: &expr,
                 };
 
                 rect[i] = machine.call()?.as_u32()? as i32;
@@ -711,7 +672,7 @@ impl<'a> RenderContext<'a> {
             // 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 python
+            // optionally evaluated using sexpr
 
             // mesh data is (0, 0) to (1, 1)
             // so scale by (w, h)
@@ -726,6 +687,28 @@ impl<'a> RenderContext<'a> {
             }
         }
 
+        // Order draw calls by z-index
+        self.draw_calls.sort_unstable_by_key(|dc| dc.z_index);
+        for draw_call in std::mem::take(&mut self.draw_calls) {
+            let bindings = Bindings {
+                vertex_buffers: vec![draw_call.vertex_buffer],
+                index_buffer: draw_call.index_buffer,
+                images: vec![draw_call.texture],
+            };
+
+            self.ctx.apply_bindings(&bindings);
+
+            self.ctx.apply_uniforms_from_bytes(
+                draw_call.uniforms_data.as_ptr(),
+                draw_call.uniforms_data.len(),
+            );
+
+            self.ctx.draw(0, 3 * draw_call.faces_len as i32, 1);
+
+            self.ctx.delete_buffer(draw_call.index_buffer);
+            self.ctx.delete_buffer(draw_call.vertex_buffer);
+        }
+
         self.ctx.end_render_pass();
 
         Ok(())
@@ -747,7 +730,7 @@ impl<'a> RenderContext<'a> {
                         ("lw".to_string(), SExprVal::Uint32(layer_rect.w as u32)),
                         ("lh".to_string(), SExprVal::Uint32(layer_rect.h as u32)),
                     ],
-                    stmts: &expr
+                    stmts: &expr,
                 };
 
                 rect[i] = machine.call()?.coerce_f32()?;
@@ -761,6 +744,8 @@ impl<'a> RenderContext<'a> {
     fn render_mesh(&mut self, mesh_id: SceneNodeId, layer_rect: &Rectangle<i32>) -> Result<()> {
         let mesh = self.scene_graph.get_node(mesh_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)?;
@@ -785,11 +770,6 @@ impl<'a> RenderContext<'a> {
         // temp
         let texture = self.textures.get(Stage::WHITE_TEXTURE_ID).unwrap();
 
-        let bindings =
-            Bindings { vertex_buffers: vec![vertex_buffer], index_buffer, images: vec![*texture] };
-
-        self.ctx.apply_bindings(&bindings);
-
         let rect = Self::get_dim(mesh, layer_rect)?;
         //debug!("mesh rect: {:?}", rect);
 
@@ -810,6 +790,24 @@ impl<'a> RenderContext<'a> {
         uniforms_data[64..].copy_from_slice(&data);
         assert_eq!(128, 2 * UniformType::Mat4.size());
 
+        if z_index > 0 {
+            let draw_call = DeferredDraw {
+                z_index,
+                vertex_buffer,
+                index_buffer,
+                texture: *texture,
+                uniforms_data,
+                faces_len: faces.len(),
+            };
+            self.draw_calls.push(draw_call);
+            return Ok(())
+        }
+
+        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);
@@ -881,9 +879,9 @@ impl EventHandler for Stage {
     fn draw(&mut self) {
         self.last_draw_time = Some(Instant::now());
 
-        let clear = PassAction::clear_color(0., 0., 0., 1.);
-        self.ctx.begin_default_pass(clear);
-        self.ctx.end_render_pass();
+        //let clear = PassAction::clear_color(0., 0., 0., 1.);
+        //self.ctx.begin_default_pass(clear);
+        //self.ctx.end_render_pass();
 
         let (screen_width, screen_height) = window::screen_size();
         // This will make the top left (0, 0) and the bottom right (1, 1)
@@ -896,7 +894,6 @@ impl EventHandler for Stage {
         //let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
 
         let scene_graph = self.scene_graph.lock().unwrap();
-        let window_id = scene_graph.lookup_node_id("/window").expect("no window attached");
 
         // We need this because scene_graph must remain locked for the duration of the rendering
         let mut render_context = RenderContext {
@@ -905,6 +902,7 @@ impl EventHandler for Stage {
             pipeline: &self.pipeline,
             proj,
             textures: &self.textures,
+            draw_calls: vec![],
         };
 
         render_context.render_window();
@@ -1074,7 +1072,6 @@ impl EventHandler for Stage {
             */
         }
         */
-        self.ctx.commit_frame();
     }
 
     fn key_down_event(&mut self, keycode: KeyCode, modifiers: KeyMods, repeat: bool) {

+ 4 - 1
bin/darkwallet/src/main.rs

@@ -22,6 +22,10 @@ mod plugin;
 
 mod prop;
 
+mod py;
+
+mod res;
+
 mod shader;
 
 #[macro_use]
@@ -95,4 +99,3 @@ def foo():
     println!("{:?}", res);
 }
 */
-

+ 170 - 22
bin/darkwallet/src/plugin.rs

@@ -1,25 +1,36 @@
+use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable};
+use std::{
+    io::Cursor,
+    sync::{mpsc, Arc, Mutex},
+    thread,
+    time::{Duration, Instant},
+};
+
 use crate::{
     error::{Error, Result},
-scene::{SceneGraph, SceneGraphPtr}
+    prop::{Property, PropertySubType, PropertyType},
+    py::PythonPlugin,
+    res::{ResourceId, ResourceManager},
+    scene::{MethodResponseFn, SceneGraph, SceneGraphPtr, SceneNodeId, SceneNodeType},
 };
 
-enum Category {
+pub enum Category {
     Null,
 }
 
-enum SubCategory {
+pub enum SubCategory {
     Null,
 }
 
-struct SemVer {
-    pub major: u64,
-    pub minor: u64,
-    pub patch: u64,
+pub struct SemVer {
+    pub major: u32,
+    pub minor: u32,
+    pub patch: u32,
     pub pre: String,
     pub build: String,
 }
 
-struct PluginMetadata {
+pub struct PluginMetadata {
     pub name: String,
     pub title: String,
     pub desc: String,
@@ -28,7 +39,6 @@ struct PluginMetadata {
 
     pub cat: Category,
     pub subcat: SubCategory,
-
     // icon
 
     // Permissions
@@ -36,21 +46,35 @@ struct PluginMetadata {
     // /window/input/*
 }
 
-enum PluginEvent {
+pub enum PluginEvent {
     // (signal_data, user_data)
     RecvSignal((Vec<u8>, Vec<u8>)),
 }
 
-trait Plugin {
+pub type PluginInstancePtr = Arc<Mutex<Box<dyn PluginInstance + Send>>>;
+
+pub trait Plugin {
     fn metadata(&self) -> PluginMetadata;
-    fn init(&mut self) -> Result<()>;
+    // Spawns a new context and begins running the plugin in that context
+    fn start(&self) -> Result<PluginInstancePtr>;
+}
+
+pub trait PluginInstance {
     fn update(&mut self, event: PluginEvent) -> Result<()>;
 }
 
-type InstanceId = u32;
+enum SentinelMethodEvent {
+    ImportPlugin,
+    StartPlugin(ResourceId),
+}
 
 pub struct Sentinel {
- scene_graph: SceneGraphPtr
+    scene_graph: SceneGraphPtr,
+    plugins: ResourceManager<Box<dyn Plugin>>,
+    insts: ResourceManager<PluginInstancePtr>,
+
+    method_recvr: mpsc::Receiver<(SentinelMethodEvent, SceneNodeId, Vec<u8>, MethodResponseFn)>,
+    method_sender: mpsc::SyncSender<(SentinelMethodEvent, SceneNodeId, Vec<u8>, MethodResponseFn)>,
 }
 
 impl Sentinel {
@@ -61,34 +85,158 @@ impl Sentinel {
         //
         // * import_plugin(pycode)
 
+        let mut sg = scene_graph.lock().unwrap();
+        let (method_sender, method_recvr) = mpsc::sync_channel(100);
+
+        let node = sg.add_node("plugin", SceneNodeType::Plugins);
+
+        let sender = method_sender.clone();
+        let node_id = node.id;
+        let method_fn = Box::new(move |arg_data, response_fn| {
+            sender.send((SentinelMethodEvent::ImportPlugin, node_id, arg_data, response_fn));
+        });
+        node.add_method("import", vec![("pycode", "", PropertyType::Str)], vec![], method_fn);
+
+        sg.link(node_id, SceneGraph::ROOT_ID).unwrap();
+        drop(sg);
+
         Self {
-            scene_graph
+            scene_graph,
+            plugins: ResourceManager::new(),
+            insts: ResourceManager::new(),
+            method_recvr,
+            method_sender,
         }
     }
 
     pub fn run(&mut self) {
-        // loop {
+        loop {
             // Monitor all running plugins
             // Check last update times
             // Kill any slowpokes
 
             // Check any SceneGraph method requests
-        // }
+            let deadline = Instant::now() + Duration::from_millis(4000);
+
+            let Ok((event, node_id, arg_data, response_fn)) =
+                self.method_recvr.recv_deadline(deadline)
+            else {
+                break
+            };
+            let res = match event {
+                SentinelMethodEvent::ImportPlugin => self.import_py_plugin(node_id, arg_data),
+                SentinelMethodEvent::StartPlugin(rid) => self.start_plugin(rid, node_id, arg_data),
+            };
+            response_fn(res);
+        }
+    }
+
+    fn import_py_plugin(&mut self, node_id: SceneNodeId, arg_data: Vec<u8>) -> Result<Vec<u8>> {
+        // Load the python code
+        let mut cur = Cursor::new(&arg_data);
+        let pycode = String::decode(&mut cur).unwrap();
+
+        let plugin = Box::new(PythonPlugin::new(self.scene_graph.clone(), pycode));
+        self.import_plugin(plugin)?;
+
+        // This function doesn't return anything
+        // Only success or an Err which is already handled elsewhere
+        Ok(vec![])
     }
 
     fn import_plugin(&mut self, plugin: Box<dyn Plugin>) -> Result<()> {
+        let metadata = plugin.metadata();
+        let plugin_rid = self.plugins.alloc(plugin);
+
+        let mut scene_graph = self.scene_graph.lock().unwrap();
+
         // Create /plugin/foo
-        // Add a method called start()
+
+        let node = scene_graph.add_node(metadata.name.clone(), SceneNodeType::Plugin);
+        let node_id = node.id;
+
+        // name
+        let mut prop = Property::new("name", PropertyType::Str, PropertySubType::Null);
+        prop.set_str(0, metadata.name);
+        node.add_property(prop).unwrap();
+        // title
+        let mut prop = Property::new("title", PropertyType::Str, PropertySubType::Null);
+        prop.set_str(0, metadata.title);
+        node.add_property(prop).unwrap();
+        // desc
+        let mut prop = Property::new("desc", PropertyType::Str, PropertySubType::Null);
+        prop.set_str(0, metadata.desc);
+        node.add_property(prop).unwrap();
+        // author
+        let mut prop = Property::new("author", PropertyType::Str, PropertySubType::Null);
+        prop.set_str(0, metadata.author);
+        node.add_property(prop).unwrap();
+        // version
+        let mut prop = Property::new("version", PropertyType::Uint32, PropertySubType::Null);
+        prop.set_array_len(3);
+        prop.set_u32(0, metadata.version.major);
+        prop.set_u32(1, metadata.version.minor);
+        prop.set_u32(2, metadata.version.patch);
+        node.add_property(prop).unwrap();
+        // TODO: add version.pre and patch, and cat/subcat enums
+
+        let mut prop = Property::new("insts", PropertyType::Uint32, PropertySubType::ResourceId);
+        prop.set_ui_text("instance resource IDs", "The currently running instances of this plugin");
+        prop.set_unbounded();
+        node.add_property(prop).unwrap();
+
+        // Add method start()
+
+        let sender = self.method_sender.clone();
+        let method_fn = Box::new(move |arg_data, response_fn| {
+            sender.send((
+                SentinelMethodEvent::StartPlugin(plugin_rid),
+                node_id,
+                arg_data,
+                response_fn,
+            ));
+        });
+        node.add_method("start", vec![], vec![("inst_rid", "", PropertyType::Uint32)], method_fn);
+
+        // Link node
+
+        let parent_id = scene_graph.lookup_node_id("/plugin").expect("no plugin node attached");
+        scene_graph.link(node_id, parent_id).unwrap();
+
         Ok(())
     }
 
-    fn start_plugin(&mut self, plugin_name: &str) -> Result<InstanceId> {
-        // Lookup plugin by name
+    fn start_plugin(
+        &mut self,
+        plugin_rid: ResourceId,
+        node_id: SceneNodeId,
+        arg_data: Vec<u8>,
+    ) -> Result<Vec<u8>> {
+        let plugin = self.plugins.get(plugin_rid).expect("plugin not found");
+
         // Call init()
         // Spawn a new thread, allocate it an ID
         // Thread waits for events from the scene_graph and calls update() when they occur.
         // See src/net.rs:81 for an example
-        Ok(0)
+        let inst = plugin.start()?;
+        let inst2 = inst.clone();
+        let inst_rid = self.insts.alloc(inst);
+
+        let _ = thread::spawn(move || {
+            inst2.lock().unwrap().update(PluginEvent::RecvSignal((vec![], vec![]))).unwrap();
+        });
+
+        let scene_graph = self.scene_graph.lock().unwrap();
+        let node = scene_graph.get_node(node_id).expect("node not found");
+        let prop = node.get_property("insts").unwrap();
+        prop.push_u32(inst_rid)?;
+
+        // TODO: when the plugin finishes, the instance ID should be cleared up somehow
+        // both from the resource manager and from the property
+        // https://www.chromium.org/developers/design-documents/inter-process-communication/
+
+        let mut reply = vec![];
+        inst_rid.encode(&mut reply).unwrap();
+        Ok(reply)
     }
 }
-

+ 1 - 1
bin/darkwallet/src/prop.rs

@@ -11,7 +11,7 @@ use std::{
     },
 };
 
-use crate::{scene::SceneNodeId, expr::SExprCode};
+use crate::{expr::SExprCode, scene::SceneNodeId};
 
 type Buffer = Arc<Vec<u8>>;
 

+ 58 - 0
bin/darkwallet/src/py.rs

@@ -0,0 +1,58 @@
+use std::{
+    sync::{Arc, Mutex},
+    thread,
+};
+
+use crate::{
+    error::{Error, Result},
+    plugin::{
+        Category, Plugin, PluginEvent, PluginInstance, PluginInstancePtr, PluginMetadata, SemVer,
+        SubCategory,
+    },
+    scene::{MethodResponseFn, SceneGraph, SceneGraphPtr, SceneNodeId, SceneNodeType},
+};
+
+pub struct PythonPlugin {
+    scene_graph: SceneGraphPtr,
+}
+
+impl PythonPlugin {
+    pub fn new(scene_graph: SceneGraphPtr, sourcecode: String) -> Self {
+        Self { scene_graph }
+    }
+}
+
+impl Plugin for PythonPlugin {
+    fn metadata(&self) -> PluginMetadata {
+        PluginMetadata {
+            name: "myplugin".to_string(),
+            title: "My Plugin - Very Good A++".to_string(),
+            desc: "This is the best plugin ever made. You should use it.".to_string(),
+            author: "Tyler Durden".to_string(),
+            version: SemVer {
+                major: 0,
+                minor: 0,
+                patch: 1,
+                pre: "alpha".to_string(),
+                build: "".to_string(),
+            },
+            cat: Category::Null,
+            subcat: SubCategory::Null,
+        }
+    }
+
+    fn start(&self) -> Result<PluginInstancePtr> {
+        let mut inst = PythonPluginInstance { scene_graph: self.scene_graph.clone() };
+        Ok(Arc::new(Mutex::new(Box::new(inst))))
+    }
+}
+
+struct PythonPluginInstance {
+    scene_graph: SceneGraphPtr,
+}
+
+impl PluginInstance for PythonPluginInstance {
+    fn update(&mut self, event: PluginEvent) -> Result<()> {
+        Ok(())
+    }
+}

+ 55 - 0
bin/darkwallet/src/res.rs

@@ -0,0 +1,55 @@
+use crate::error::{Error, Result};
+
+pub type ResourceId = u32;
+
+pub struct ResourceManager<T> {
+    resources: Vec<(ResourceId, Option<T>)>,
+    freed: Vec<usize>,
+    id_counter: ResourceId,
+}
+
+impl<T> ResourceManager<T> {
+    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)
+    }
+}

+ 6 - 4
bin/darkwallet/src/scene.rs

@@ -33,6 +33,8 @@ pub enum SceneNodeType {
     Fonts = 10,
     Font = 11,
     LinePosition = 12,
+    Plugins = 14,
+    Plugin = 15,
 }
 
 pub struct ScenePath(Vec<String>);
@@ -430,10 +432,10 @@ impl SceneNode {
     pub fn get_property_node_id(&self, name: &str) -> Result<SceneNodeId> {
         self.get_property(name).ok_or(Error::PropertyNotFound)?.get_node_id(0)
     }
-    //// Setters
-    //pub fn set_property_bool(&self, name: &str, val: bool) -> Result<()> {
-    //    self.get_property(name).ok_or(Error::PropertyNotFound)?.set_bool(val)
-    //}
+    // Setters
+    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)
     //}