Hey there,
I was trying to understand the neon codebase and trying to read it. Currently many things regarding type conversion and maintenance of data types is unclear for me. The example basically only has a string echo, so it doesn't help me with that.
What I'm trying to do is I want to build something like node-sdl2 with a statically embedded sdl2 library, so that you can use it as an alternative runtime.
Before hearing about neon, I was trying to use node's (old!?) ffi library in order to embed a compiled .so file with it. Ignoring linking problems for now, it was possible to have some C-method there and to wrap the references and pointers and other data types on the JS object instances as hidden properties, so that the JS API could have very similar signatures and didn't need e.g. something like a createRenderer() method (like how WebGL vs. GL would solve it, for example).
If you are curious, the prototype is somewhat available here with its bindings and here with the final JS code. But bear with me, ffi is crap, node-gyp is total crap and everything is of anything but prototypical nature. I have also hundreds of linking problems left (node-gyp docs are very much not available for this use case), but it somehow works if you replace the libSDL-2.0.so with the one from your UNIX system. The currently shipped one in there should work with an Arch Linux system, but it requires the libwebp library for the SDL_image stuff.
So, I still want to do the bindings with neon while (hopefully) being able to use the sdl2 crate for it, but currently I have no effing clue how I can achieve that. Can you guide me with some more detailed examples, maybe? Like in the sense of how I can create e.g. a custom object that maintains a pointer and can be destroyed afterwards accordingly?
Currently, the examples I found in the test basically only have static objects, and additionally I have no clue how to maintain e.g. an *sdl_renderer or *sdl_window reference without breaking the idea of the neon VM bindings. I was trying to read and understand the context/mod.rs file but as an outside guy I have no clue what you did there.
So I guess I need help, and this issue can be interpreted as a feature request for further examples(!?!?) or so. Thanks much in advice.
Hi! Thanks for sharing your problem. I'll do my best to answer here, but I also recommend joining us in slack (https://rust-bindings-slackin.herokuapp.com/). The group there is very helpful.
Currently, the only way to wrap a native value in neon is with a class. The examples need improvement, but there are examples of most functionality in the dynamic tests: https://github.com/neon-bindings/neon/blob/master/test/dynamic/native/src/js/classes.rs.
Classes wrap a rust native type, apply a brand so that it can be dynamically type checked, and tie their lifetime to the JS object that wraps them. You can wrap your sdl objects in a class and expose methods that use them.
Here is a quick example based on the sdl2 crate example:
// lib.rs
#[macro_use]
extern crate neon;
extern crate sdl2;
use neon::context::Context;
use neon::types::{JsNumber, JsUndefined};
use sdl2::pixels::Color;
use sdl2::render::WindowCanvas;
pub struct Canvas(WindowCanvas);
declare_types! {
pub class JsCanvas for Canvas {
init(mut cx) {
let sdl_context = sdl2::init()
.or_else(|err| cx.throw_error(err.to_string()))?;
let video_subsystem = sdl_context.video()
.or_else(|err| cx.throw_error(err.to_string()))?;
let window = video_subsystem.window("rust-sdl2 demo", 800, 600)
.position_centered()
.build()
.or_else(|err| cx.throw_error(err.to_string()))?;
let mut canvas = window.into_canvas().build()
.or_else(|err| cx.throw_error(err.to_string()))?;
canvas.set_draw_color(Color::RGB(0, 255, 255));
canvas.clear();
canvas.present();
Ok(Canvas(canvas))
}
method set_draw_color(mut cx) {
let r = cx.argument::<JsNumber>(0)?.value() as u8;
let g = cx.argument::<JsNumber>(1)?.value() as u8;
let b = cx.argument::<JsNumber>(2)?.value() as u8;
let color = Color::RGB(r, g, b);
let mut this = cx.this();
cx.borrow_mut(&mut this, |mut canvas| canvas.0.set_draw_color(color));
Ok(JsUndefined::new().upcast())
}
method present(mut cx) {
let mut this = cx.this();
cx.borrow_mut(&mut this, |mut canvas| canvas.0.present());
Ok(JsUndefined::new().upcast())
}
method clear(mut cx) {
let mut this = cx.this();
cx.borrow_mut(&mut this, |mut canvas| canvas.0.clear());
Ok(JsUndefined::new().upcast())
}
}
}
register_module!(mut cx, {
cx.export_class::<JsCanvas>("Canvas")?;
Ok(())
});
// index.js
var { Canvas } = require('../native');
function sleep(n) {
return new Promise(resolve => setTimeout(resolve, n));
}
async function run() {
const canvas = new Canvas();
for (let i = 0; i < 1000; i++) {
const m = i % 255;
canvas.set_draw_color(m, 64, 255 - m);
canvas.clear();
canvas.present();
await sleep(10);
}
}
run();
@kjvalencik Uh, thank you very much, that should help me getting started! Awesome!
Most helpful comment
Here is a quick example based on the sdl2 crate example: