QuickJS-raylib icon indicating copy to clipboard operation
QuickJS-raylib copied to clipboard

I'd like to help

Open konsumer opened this issue 3 years ago • 7 comments

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?

konsumer avatar Oct 24 '21 09:10 konsumer

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.

sntg-p avatar Oct 27 '21 20:10 sntg-p

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.

konsumer avatar Oct 27 '21 21:10 konsumer

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 avatar Oct 27 '21 21:10 konsumer

@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.

raysan5 avatar Oct 27 '21 21:10 raysan5

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"

konsumer avatar Oct 27 '21 22:10 konsumer

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.

konsumer avatar Oct 27 '21 22:10 konsumer

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.)

konsumer avatar Oct 28 '21 23:10 konsumer