QuickJS-raylib
QuickJS-raylib copied to clipboard
I'd like to help
Hi, I am interested in raylib & javascript, and getting it to run well on a pi0. I tried out node-raylib, and it works pretty well, but not on mac, (which would be great for dev before deploying to pi) and I also have a feeling quickjs would run even better.
Is there anything you'd like help with, in particular? I'm not a strong C programmer, but I have some experience, and I am very experienced with js. I see your TODOs, but I'm not sure if it's up-to-date, or if you're still working on this.
When I see things like big APIs and wrappers, I start thinking of code-generation. I have written a few, in various languages. Maybe we could do something with swig? It has an XML output you can use to generate wrappers for other languages, and I have seen a couple swig wrappers for raylib (seem lua-focused, but the interface-definitions should work ok to generate XML, which we can use to generate C for quickjs.) I also think there aren't a lot of types (basically a handful of structs) and a lot of defines & functions, that all essentially look the same, so it may be feasible to just do it directly in some JS code. nm -D
seems useful for dumping the exports from a C lib, but the headers might be more useful for generating the calling-signatures.
I tried a first stab at regex and it seemed to do the trick for the function-definitions, complete with comments. I left the params as a string because they can just be split by ,
in another pass. We could maybe do similar for the defines & structs, too.
Based on your code, it seems like there are some kinda simple rules:
- structs get turned into classes, with members that are basic scalars & getters/setters
- scalar method params work by defining a compatible var, calling the quickjs converter to put it in memory, and calling the original, then returning a converted copy of original return, which is often void (
JS_UNDEFINED
.) Do you know if this has memory-leaks? I don't know how to check, but it seems like it might.
So this (picked at random):
RLAPI bool IsGamepadButtonUp(int gamepad, int button); // Check if a gamepad button is NOT being pressed
would pull this:
{
"return": "bool",
"name": "IsGamepadButtonUp",
"params": [
"int gamepad",
"int button"
],
"comment": "Check if a gamepad button is NOT being pressed"
}
which would translate to this in quickjs C:
static JSValue wrapped_IsGamepadButtonUp(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
int gamepad;
if (JS_ToInt32(ctx, &gamepad, argv[0])) return JS_EXCEPTION;
int button;
if (JS_ToInt32(ctx, &button, argv[1])) return JS_EXCEPTION;
return JS_NewBool(ctx, IsGamepadButtonUp(gamepad, button));
}
// later in JSCFunctionListEntry
JS_CFUNC_DEF("isGamepadButtonUp", 2, wrapped_IsGamepadButtonUp);
Does that sound right? Do you have interest in an auto-generator like this?
Hi David! First of all, thanks for taking the time to try and contribute to the project.
There could be memory leaks as you say, I have not looked for them. But I am not sure what you are talking about when you say that I'm "returning a converted copy of original return, which is often void (JS_UNDEFINED
)". Are you talking about the getter functions?
It would be pretty cool if we could use some code generation tool to get this to a more complete state, and I'm willing to answer any questions you may have.
Nice! I can't currently build this project on linux or mac with latest raylib/quickjs. Seems like maybe something changed in raylib (and possibly quickjs.) That might be a good first step for me, as I think this project would be an excellent reference for generating things, like "how do I expose this struct to js?", so having it working seems like a good start.
I actually thought maybe you had kind of abandoned the project, so I started working on this as a separate thing, but it's very incomplete. I actually need to check in a better generator (it's on my laptop, so I will put it up, in a little bit.) In the newer version, I made a tiny function for every input/output type, and just merge all the translation together. It's a bit easier to read and write type-converters for. I also added sub-modules for raylib & quickjs and started working on a cmake builder for it all, so it will be easier for people to get it working right away.
returning a converted copy of original return, which is often void (JS_UNDEFINED)". Are you talking about the getter functions?
I guess that is main part of it. I just mean you return void (JS_UNDEFINED
) or a wrapped (using exposed-to-js classes with getter/setters) for structs, or the quickjs way to return JS scalars (eg JS_NewBool
for a boolean retval). This might make more sense with the new generator I wrote, so I will go push that. I think I understand what you are doing, but haven't gotten to the struct part (or even completed all the quickjs scalars.)
I am trying to make the API match 1-for-1 with raylib, so you can basically just read the C docs (or the cheatsheet) and know what to do. Internally I have been just naming them all wrapped_Whatever
and exposing Whatever
, but again, it's not at all complete.
Ok, updated the generator. I basically found all the unique types for input/output and made little template functions. I still need to finish how structs work, and there are some missing outputTypes
scalars, too.
@konsumer You can take a look to raylib parser, it's a tool to generate json/xml/custom files from raylib.h structs/functions data. Actually, it's intended to automatize bindings generation.
Wow! I wish I had seen that before I made this. Seems like a much better way to do it than fragile regexes.
If they keep this up-to-date, it would probly work really well to just make the actual template part. Maybe I could get rid of the sub-modules, download that on the tool-run, then compile the generated C in github-action, for a really simple way to use it. Maybe just keep a good record of the versions I built against. So usage instructions would basically be "install raylib version X, install quickjs version X, grab the so/dll from releases, and import it like this"
I dunno, I need to think about git sub-modules a bit. I like how I can lock in a specific version, and I could build a "raylib game bundle" that is essentially all the so files + qjs runtime + wrapper so you can just drop in like a main.js
file or whatever and have a game (similar to distro of love game, for example.) Being able to treat deps just as a file in the same dir is very helpful for making sure it all works together. Even with pre-built C projects, my biggest headache with building something on top of a lib, and distributing it, is making sure all the the deps are the right version, etc.
I made some progress, I think, using the JSON. I have all the enums (so we don't need a separate js file) and a decent system in place for templating conversion in/out in functions.
I still need to work on some types, function in/out, and the actual classes exposed to js. You can see the progress here.
If you have interest in this kind of method, I could use any help you have to give with type-converters. If you have a minute, maybe you could look over the ones I have made, and see if they are sensible, and add any you like.
I could see a general process like this applying to all kinds of quickjs wrappers, so I might eventually turn this into a more general system for wrapping header files (maybe you give it a big JSON file, like they provide, and it does the rest.) It would be neat to just point a script at a header, and get a rough native wrapper for it (even if it needed other things to make it more useful, in js-space.)