-
Notifications
You must be signed in to change notification settings - Fork 523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cleanup Emscripten canvas target selector string stuff. #1154
Comments
This path is still using strings (with I recommend passing canvas dom-object (with a selector on This way I can override the canvas object, even if there is no document-selector (like in the case of a web-component, which is in a shadow-dom.) |
You can't pass a JS object across the WASM and C boundary, and you also can't pass a JS object into the (AFAIK the special |
PS: e.g. see here: https://emscripten.org/docs/tools_reference/settings_reference.html#disable-deprecated-find-event-target-behavior
|
While I see that it's marked deprecated there, for my situation it really makes it less useful. I wonder what usecase that improves things for (seems like less control, with no payoff.) Still, it seems like a quick check once if canvas is a string (css selector) or DOM element is the most consistent way to handle it, looking at other libs, even if it is deprecated behavior. I could PR for both (so it handles new case and this deprecated thing that works everywhere else, and web components rely on.) |
I am not totally sure what you mean. You definitely can embed js in emscripten, which I'm sure you are aware of, since it's is in some of the sokol-code. The glfw code I pointed to sets things up in an emscritpen-level js library, but you can also do things like this in anything: EMJS(void, whatever, () {
// your js here
}); The current sokol_app setup uses this to actually resolve that Like maybe this is just that I don't know how to do it better, but there are situations where I have just been experimenting with sokol and seeing if it will work for my game-engine, but this has been in the back of my mind as something I need to eventually fix, or remove as a feature, if I use sokol. This means only 1 game-instance per page, which is a small bummer because I could not make a browse-page like this that actually worked, but to be fair, having a ton of instances actually running on a single page is tricky for performance, anyway (like tiny8bit use static screenshots that link to a single instance) so maybe that is ok. As a sidenote, tiny8bit is using sokol, and it may have been part of the decision to only run 1 instance on a page. I am not sure. I am willing to rethink that "need" for my engine, though, honestly. I still don't think using a
That may be true, but no alternative standard was put in place that handles things that you cannot get in Also, I think that link you pointed to may be saying something subtly different. To my eyes, it seems like it's saying "you can disable it automatically selecting the canvas using an id or just auto-grabbing I made raylib-wasm and so I just followed what I did there for my game-engine, for example this codepen uses a web-component to make it easy to tie the draw-loop, instance & user-code to a single canvas, so you can have multiple instances on a page to show ideas. Here is an instance of a game for my engine, and I can tie the lil mute-button (for click-to-play web-audio security stuff) and other things directly to the canvas. I use While working on games/engine locally, I have several demos loaded in a single page that auto-refreshes, so I can check that they all work right, in 1 place. This is possible because each web-component has it's own canvas & runtime instance. Screencast.from.11-16-2024.04.10.36.PM.webm |
Does your raylib glue code call any of the https://emscripten.org/docs/api_reference/html5.h.html#functions This target string is a 'css selector', and Emscripten calls
Yes, but you can only pass primitive types across the JS/WASM boundary (anything that can be converted to a JS 'number' value), but no Javascript object references. So you could only access the canvas object from JS code, but you can't tunnel it through C code (without additional boilerplate code at least - like what the WebGL and WebGPU Emscripten shims do). |
Yep, I get how it works. There are several ways to to do this, using I think maybe you are misunderstanding what I propose. I will make an example that modifies sokol_app, and works how I mean.
No, not mine, directly. I don't really add much glue code, the C entry-point pretty much just includes raylib, and adds a couple of utils. Most of what my thing does is wrap it up in wasm, and expose a bunch of js to make it more ergonomic to work with. Raylib games can already be compiled to emscripten, though, my thing just makes all that work nicer in browser with js/web-components/etc, so you can write your game in js instead of C. You can see here upstream raylib uses emscripten, directly, pretty lightly, to just setup main loop and things like that.
Yep, I get it. I do a lot of wasm things, with and without emscripten. My point is with emscripten, you can grab the canvas once (and use the options however you want to do that) and then use that grabbed canvas later, instead of selecting it from the DOM every time. This is exactly what the SDL and glfw shims do, and raylib uses glfw. |
I think the "full solution" is to setup I marked my changes with I also have some js-side debugging I will remove for a PR, but it shows which canvas is in use. To further illustrate the problem it solves, I get this on my demo-page, meaning only the legacy canvas is found (since the other 2 are hiding in the shadow-dom): Even though this all meant to solve my usecase, it helps others, even if they are not using web-components, because they can just pass |
There is also another trick: specialHTMLTargets["!canvas"] = Module.canvas; Combined with setting #if EMSCRIPTEN
EM_JS(bool, _dk_grab_canvas, (char* selector), {
if (Module.canvas) {
console.log('canvas set with param', Module.canvas);
specialHTMLTargets["!canvas"] = Module.canvas;
return true;
}
const e = document.querySelector(UTF8ToString(selector));
if (e) {
Module.canvas = e;
}
console.log('canvas set', Module.canvas);
return false;
});
#endif
// later in _sapp_init_state
#if EMSCRIPTEN
if (_dk_grab_canvas(_sapp.html5_canvas_selector)) {
char* s = "!canvas";
strcpy(_sapp.html5_canvas_selector, s);
}
#endif It could be merged into the id-copy stuff, so it's all 1 thing that sets up |
Thanks for taking the time to create an example, much appreciated, and I will set aside some time to properly understand it. I also asked around on the Emscripten Discord what the state of the special One thing I'm seeing is that you removed the OTH the calls to Also there's all those event handlers here: ...I wonder if those still work. Also a question: wouldn't the problem of multiple canvases on the same page also be solved by giving each canvas its unique id and then give each demo that id-string in the ...another thing we should check: assuming the special lookup behaviour still working (e.g. Emscripten looking at This would allow application to communicate the WebGL canvas by setting |
maybe. That was a quick hack to move away from a selector, but as I said
Not in my demo, I didn't touch them, but if the selector is set to
Maybe, if you are not using web-components, but in my example, as I said, the canvas in web-component is not even in |
(I added more stuff to my previous comment while you wrote your reply).
...if that means that the current Emscripten runtime functions which take a target-string parameter are not compatible with web-components, then I would rather prefer to not use web components instead of not being able to use those Emscripten functions tbh. |
I'll see if I can make some time to tinker around with the ideas in this thread (with the main target to easily enable multiple WebGL canvases on the same page). In any case, thanks for the input so far :) |
PS: the |
Yeh, that is what I am proposing, now. I forgot about
There are several parts to your question, I will elaborate:
Again, the deprecated part, as I understand it, is that emscripten used to automatically wire itself to |
...that's true, but the Emscripten shim functions also sometimes have convenient workarounds for web API problems I don't necessarily want to deal with myself. In general I prefer the Emscripten functions over my own EM_JS functions, unless there's a really good reason not to use them. |
I disagree, but it's a matter of taste I think, and I am fine with doing it either way. Again, with |
Well if it means kicking out all |
it doesn't. that is what I'm saying.
exactly, I think it's just a trick to support all those (in my opinion jenky) functions that wrap js apis in C, that require query-selectors, so very little needs to change. |
I am happy to make another demo that uses |
If you try that, you'd might at least want to replace this getElementById() line: Line 5020 in 2c6fc74
Ideally this would call Emscripten's You might also need to add Line 4783 in 2c6fc74
|
PS: also look for places which skip the first character in like Line 5934 in 2c6fc74
...to ...I think that should take care of most things (drag'n'drop won't work but that's not critical for the experiment). |
yep, I think there are sort of 2 parts to this, like:
I think the first is only in a few places, since everything mostly uses selectors anyway. |
I think if it all uses query-selector, it should work fine, right? Things in js-space can use |
Ok, I made more minimal changes, swapping out
I ended up just doing this: const canvas = Module.canvas; Since now we have a link to it, you don't need anything more in js-space. You actually already had I didn't test DnD, but it should work, too. I also made a PR for it, which helps to see how little changed (all together 6 lines changed, but most of that was other cleanup.) |
Thanks! Looks a bit different than I imagined. I might do a counterproposal, which we can compare then ;) |
There are basically 2 ways to target the "screen canvas", depending on if you are in JS or C, in my solution, and I am not sure how that could be improved upon:
As I said, I will suggest an improvement to my path, though: any sokol functions themselves do not need a string-selector arg, anymore. Like after init, id/selector should just not be touched/passed, ever. I tried to keep everything as it is, which means they all have unused (well, used, but it's set to I think my PR could be merged for an immediate improvement (supports |
Here's my 'counter-proposal': Canvases that can't be looked up via I also tested the above PR with samples that use the input event handlers and drag'n'drop stuff. |
sokol_app.h html5: cleanup canvas lookup handling (see #1154)
Closed via #1159 |
See: #407 (comment)
TL;DR:
sapp_desc.html5_canvas_name
tosapp_desc.html5_canvas_selector
canvas
to#canvas
#
prefixingfindCanvasEventTarget()
andfindEventTarget()
instead ofgetElementById()
orquerySelector()
so that the behaviour is identical with the Emscripten runtime functions.The text was updated successfully, but these errors were encountered: