Closed roozbehid closed 5 years ago
It seems I am having a problem with SDL2. This is what I am getting with all my applications in Emscripten (WebAssembly) (Mouse is moving and working)ht
That is likely not the microwindows mouse, but the emscripten or browser mouse. All Microwindows SDL2 output goes through drivers/scr_sdl2.c::sdl_draw().
Uncomment the printf in the update routine to show when and where the drawing occurs.
Mouse is working but all Windows are black.
Yes, could be that the window background erase is working.
I am not 100% sure it is SDL2 problem but I am now also getting this error in my browser (which is ignored) caused by the call to SDL_CreateRenderer
emscripten_set_main_loop_timing: Cannot set timing mode for main loop since a main loop does not exist! Call emscripten_set_main_loop first to set one up.
Look very carefully at the demos/Makefile-emscripten link instructions to see whether your link is similar. You must be certain that you are also linking against the in-built SDL2 in emscripten.
Everything works fine in my SDL2-Windows but not SDL2-Emscripten.
Ok.
I also saw this : https://emscripten.org/docs/porting/emscripten-runtime-environment.html https://emscripten.org/docs/porting/emscripten-runtime-environment.html which recommends When using SDL you will probably need to set the main loop. So could it be the problem?
No, you don’t need to setup a main loop differently, I have got that all working, but it is very tricky and you need to look at the emscripten link in the makefile above.
Is is also very tricky which portions use the EMTERPRIFY=1 portions, read up on that.
Because SDL2 now uses the renderer?
Yes it does, but that’s not the problem here. It could be that your program lost control when it left the emterpreter after erasing the background in the first stack switch following a call to GsSelect in the microwindows message loop. This is because of the whitelist routines and restrictions required for the emterpreter. A fix would be to put the whole program into the emterpreter.
Regards,
Greg
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/ghaerr/microwindows/issues/25, or mute the thread https://github.com/notifications/unsubscribe-auth/ALbi5e71XWrGsAlfznHmB8NdqAL6CBkaks5vTh1_gaJpZM4bd9DJ.
Thanks, here are my results!
So I changed those #if 0 to #if 1 (in scr_sdl2.c), still same problem.
uncommented printf, it gets called with my mouse movement.
Also changed my link commands to resemble demos/Makefile-emscripten, I was missing some there, but still same problem.
Unfortunatly I can not put whole program into emterpreter, as it used to crash emcc, probably due to being huge!
I try to put some logging into my BitBlt and see if they are actually being passed to microwindows, but I do welcome if you have any more suggestions.
Thanks
So I changed those #if 0 to #if 1 (in scr_sdl2.c), still same problem.
Change those back, that’s for SDL1 library in case I had to go backwards and is untested.
uncommented printf, it gets called with my mouse movement.
That means that the lower level GsSelect() loop that checks the mouse and draws the cursor is running. I suspect your upper-level program (that is, portions of the program above the GsSelect in the call stack at any time) are breaking the rules for emscripten’s asyncify requirements.
Also changed my link commands to resemble demos/Makefile-emscripten, I was missing some there, but still same problem.
Are you running javascript output, not WebAssembly (WASM=0)? The system won’t currently work with WASM due to it not being compatible with emscripten_sleep(). More on that later.
Unfortunatly I can not put whole program into emterpreter, as it used to crash emcc, probably due to being huge!
Are you aware of how the emterpreter actually runs? You have to ensure that the call stack has been whitelisted at any time that emscripten_sleep() is called. I spent lots of time ensuring that the single, only call to emscripten_sleep() is proper. GetMessage and PeekMessage in mwin/winuser.c call GsSelect which calls GdDelay which calls it. You should run EMTERPRETIFY_ADVISE=1 to ensure that you have NO places in your program where another call to GetMessage or PeekMessage is made, as that will not work. Use ASSERTIONS=1 to test.
For instance, look carefully at the link lines for mwdemo2 versus, say, mwdvetest. Mwdvetest uses Dialog(), which runs another main loop above the one in winuser.c and thus must be FULLY linked EMTERPIFY, as there are many many routines that may be in the call stack when Dialog is called, which ends up calling emscripten_sleep(). I hope you understand what I’m talking about here.
The whitelist advise tools can help, and I found that printf debugging helped a lot when getting all of Microwindows ported.
If you want to run WASM rather than JS than we’ll have to come up with a whole new plan, but I don’t advise doing that until you get JS running.
I try to put some logging into my BitBlt and see if they are actually being passed to microwindows, but I do welcome if you have any more suggestions.
Put printf()s in various places of your code, including BitBlt, I bet that that draw code isn’t running. It seems that the lower level calls and the initial window background erase are working but then things go bad once an upper level routine calls PeekMessage or GetMessage would be my guess.
Run EMTERPRETIFY_ADVISE to get your whitelist and try ASSERTIONS=1 after that.
It is also possible SDL library calls sleep but I haven’t looked at that code. I don’t think this is a problem but could be if all the above doesn’t help. I don’t think your problem is an SDL or Microwindows bug, but a porting issue for your application.
Thanks
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/ghaerr/microwindows/issues/25#issuecomment-469584340, or mute the thread https://github.com/notifications/unsubscribe-auth/ALbi5dSifkx5xmmi2v3HoVnYvCcWoNGfks5vTijEgaJpZM4bd9DJ.
In thinking about your problem a bit more, it might be nice to have an option in Microwindows that would allow it to be built to run multiple win32 "apps" simultaneously. These "apps" could be compiled to WebAssembly or just linked together in C. The GetMessage/TranslateMessage/DispatchMessage loop would be removed from the normal applications, and instead, a rewritten internal main loop would collect events, and dispatch them to the window callback functions. This would allow these applications, and your application, to run without having to emterpretify anything, and WebAssembly could be used for speed.
If dialog boxes aren't used initially, this isn't that much work, as essentially just a basic main loop would be revised from the existing code which polled for events, then created messages and dispatched them to window procedures. I need a little time to do this, but need to know that your main loop would be compatible.
Can you point me to what your application main loop code looks like? What do you think of this idea?
Check it out, got the whole thing running! Multiple win32 apps in the browser and no need for emterpretify or any of the old restrictions! Almost have it working on WASM output but load error for some reason.
So I changed those #if 0 to #if 1 (in scr_sdl2.c), still same problem. Change those back, that’s for SDL1 library in case I had to go backwards and is untested. uncommented printf, it gets called with my mouse movement. That means that the lower level GsSelect() loop that checks the mouse and draws the cursor is running. I suspect your upper-level program (that is, portions of the program above the GsSelect in the call stack at any time) are breaking the rules for emscripten’s asyncify requirements. Also changed my link commands to resemble demos/Makefile-emscripten, I was missing some there, but still same problem.
Actually I found one of the problems, which was due to this line of code (winuser.c:373)
*pClass = *lpWndClass; pClass->lpfnWndProcBridge = NULL; /* always init bridge to NULL*/
I need that, because I set my WndProc when I call register window. Do you recall why you made that NULL? So it was causing none of my custom WndProc to be called. Although with this fix it progressed, I am getting nothing now :D Browser ends up in a very very busy state that I have to kill them. I suspect the EMTERPRETER problems here.....:(
Are you running javascript output, not WebAssembly (WASM=0)? The system won’t currently work with WASM due to it not being compatible with emscripten_sleep(). More on that later. Unfortunatly I can not put whole program into emterpreter, as it used to crash emcc, probably due to being huge! Are you aware of how the emterpreter actually runs? You have to ensure that the call stack has been whitelisted at any time that emscripten_sleep() is called. I spent lots of time ensuring that the single, only call to emscripten_sleep() is proper. GetMessage and PeekMessage in mwin/winuser.c call GsSelect which calls GdDelay which calls it. You should run EMTERPRETIFY_ADVISE=1 to ensure that you have NO places in your program where another call to GetMessage or PeekMessage is made, as that will not work. Use ASSERTIONS=1 to test. For instance, look carefully at the link lines for mwdemo2 versus, say, mwdvetest. Mwdvetest uses Dialog(), which runs another main loop above the one in winuser.c and thus must be FULLY linked EMTERPIFY, as there are many many routines that may be in the call stack when Dialog is called, which ends up calling emscripten_sleep(). I hope you understand what I’m talking about here. The whitelist advise tools can help, and I found that printf debugging helped a lot when getting all of Microwindows ported. If you want to run WASM rather than JS than we’ll have to come up with a whole new plan, but I don’t advise doing that until you get JS running.
Yeah I read alot about those problems and I think I know why we have those problems. But previously -S WASM=1 worked for me and all my examples where built with .wasm, so is it like a new requirement or new thing that -S WASM=1 does not work. I'll try with -s WASM=0 to see what happens.
In thinking about your problem a bit more, it might be nice to have an option in Microwindows that would allow it to be built to run multiple win32 "apps" simultaneously. These "apps" could be compiled to WebAssembly or just linked together in C. The GetMessage/TranslateMessage/DispatchMessage loop would be removed from the normal applications, and instead, a rewritten internal main loop would collect events, and dispatch them to the window callback functions. This would allow these applications, and your application, to run without having to emterpretify anything, and WebAssembly could be used for speed.
If dialog boxes aren't used initially, this isn't that much work, as essentially just a basic main loop would be revised from the existing code which polled for events, then created messages and dispatched them to window procedures. I need a little time to do this, but need to know that your main loop would be compatible.
Can you point me to what your application main loop code looks like? What do you think of this idea?
That would be great. Because it make my code huge and slow, if we can get rid of those emterpretifies!
My main loop is very similar to Win32 Loops
while (!quit && XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) {
Message m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
if (Application.FilterMessage (ref m))
continue;
switch((Msg)msg.message) {
case Msg.WM_KEYDOWN:
case Msg.WM_SYSKEYDOWN:
case Msg.WM_CHAR:
case Msg.WM_SYSCHAR:
case Msg.WM_KEYUP:
case Msg.WM_SYSKEYUP:
Control c;
c = Control.FromHandle(msg.hwnd);
// If we have a control with keyboard capture (usually a *Strip)
// give it the message, and then drop the message
if (keyboard_capture != null) {
// WM_SYSKEYUP does not make it into ProcessCmdKey, so do it here
if ((Msg)m.Msg == Msg.WM_SYSKEYDOWN)
if (m.WParam.ToInt32() == (int)Keys.Menu) {
keyboard_capture.GetTopLevelToolStrip ().Dismiss (ToolStripDropDownCloseReason.Keyboard);
continue;
}
m.HWnd = keyboard_capture.Handle;
switch (keyboard_capture.PreProcessControlMessageInternal (ref m)) {
case PreProcessControlState.MessageProcessed:
continue;
case PreProcessControlState.MessageNeeded:
case PreProcessControlState.MessageNotNeeded:
if (((m.Msg == (int)Msg.WM_KEYDOWN || m.Msg == (int)Msg.WM_CHAR) && !keyboard_capture.ProcessControlMnemonic ((char)m.WParam))) {
if (c == null || !ControlOnToolStrip (c))
continue;
else
m.HWnd = msg.hwnd;
} else
continue;
break;
}
}
if (((c != null) && c.PreProcessControlMessageInternal (ref m) != PreProcessControlState.MessageProcessed) ||
(c == null)) {
goto default;
}
break;
case Msg.WM_LBUTTONDOWN:
case Msg.WM_MBUTTONDOWN:
case Msg.WM_RBUTTONDOWN:
if (keyboard_capture != null) {
Control c2 = Control.FromHandle (msg.hwnd);
// the target is not a winforms control (an embedded control, perhaps), so
// release everything
if (c2 == null) {
ToolStripManager.FireAppClicked ();
goto default;
}
// If we clicked a ToolStrip, we have to make sure it isn't
// the one we are on, or any of its parents or children
// If we clicked off the dropped down menu, release everything
if (c2 is ToolStrip) {
if ((c2 as ToolStrip).GetTopLevelToolStrip () != keyboard_capture.GetTopLevelToolStrip ())
ToolStripManager.FireAppClicked ();
} else {
if (c2.Parent != null)
if (c2.Parent is ToolStripDropDownMenu)
if ((c2.Parent as ToolStripDropDownMenu).GetTopLevelToolStrip () == keyboard_capture.GetTopLevelToolStrip ())
goto default;
if (c2.TopLevelControl == null)
goto default;
ToolStripManager.FireAppClicked ();
}
}
goto default;
case Msg.WM_QUIT:
quit = true; // make sure we exit
break;
default:
XplatUI.TranslateMessage (ref msg);
XplatUI.DispatchMessage (ref msg);
break;
}
// If our Form doesn't have a handle anymore, it means it was destroyed and we need to *wait* for WM_QUIT.
if ((context.MainForm != null) && (!context.MainForm.IsHandleCreated))
continue;
// Handle exit, Form might have received WM_CLOSE and set 'closing' in response.
if ((context.MainForm != null) && (context.MainForm.closing || (Modal && !context.MainForm.Visible))) {
if (!Modal) {
XplatUI.PostQuitMessage (0);
} else {
break;
}
}
}
which basically is
GetMessage, TranslateMessage, DispatchMessage
All at the end comes down to Microwindows Win32 API.
Check it out, got the whole thing running! Multiple win32 apps in the browser and no need for emterpretify or any of the old restrictions! Almost have it working on WASM output but load error for some reason.
![]()
Wow! I want it :D
Actually I found one of the problems, which was due to this line of code (winuser.c:373)
pClass = lpWndClass; pClass->lpfnWndProcBridge = NULL; / always init bridge to NULL/
I need that, because I set my WndProc when I call register window. Do you recall why you made that NULL?
Yes, without it your bridge proc requires all old code to set the bridge proc to NULL prior to calling RegisterClass, or the pointer would be garbage. I didn’t want to require retrofit of old programs so I thought to NULL the pointer. To be Win32 compatible, your code should set the bridge pointer after RegisterClass.
But instead we can just #if !WINEXTRA this set to NULL since it isn’t used by any internal routines always. I’ll fix that in my next commit.
So it was causing none of my custom WndProc to be called. Although with this fix it progressed, I am getting nothing now :D
Not sure what to say about that!
Browser ends up in a very very busy state that I have to kill them. I suspect the EMTERPRETER problems here.....:(
Yes, you should likely have to go through the advice below in order to get things working.
Are you running javascript output, not WebAssembly (WASM=0)? The system won’t currently work with WASM due to it not being compatible with emscripten_sleep(). More on that later. Unfortunatly I can not put whole program into emterpreter, as it used to crash emcc, probably due to being huge! Are you aware of how the emterpreter actually runs? You have to ensure that the call stack has been whitelisted at any time that emscripten_sleep() is called. I spent lots of time ensuring that the single, only call to emscripten_sleep() is proper. GetMessage and PeekMessage in mwin/winuser.c call GsSelect which calls GdDelay which calls it. You should run EMTERPRETIFY_ADVISE=1 to ensure that you have NO places in your program where another call to GetMessage or PeekMessage is made, as that will not work. Use ASSERTIONS=1 to test. For instance, look carefully at the link lines for mwdemo2 versus, say, mwdvetest. Mwdvetest uses Dialog(), which runs another main loop above the one in winuser.c and thus must be FULLY linked EMTERPIFY, as there are many many routines that may be in the call stack when Dialog is called, which ends up calling emscripten_sleep(). I hope you understand what I’m talking about here. The whitelist advise tools can help, and I found that printf debugging helped a lot when getting all of Microwindows ported. If you want to run WASM rather than JS than we’ll have to come up with a whole new plan, but I don’t advise doing that until you get JS running.
Try the above and let me know what you find. Even though I am working on removing the need for emterpretify, it all depends on what your main loop looks like in order to know whether that will work.
Yeah I read alot about those problems and I think I know why we have those problems. But previously -S WASM=1 worked for me and all my examples where built with .wasm, so is it like a new requirement or new thing that -S WASM=1 does not work. I'll try with -s WASM=0 to see what happens.
I never had WASM=1 working, so I have no prior test results on that. It can’t be made to work when using EMTERPRETIFY, the docs say.
My demos/Makefile-emscripten link script shows WASM=0.
I fixed the EMTERPRETIFY using very few changes, basically I removed the main loop from all apps, and then wrote this very simple main loop, which is called by emscripten from the browser. See attached mwapp.c.
You can give this a try if you like. It does NOT currently support nested main loops, so for instance all modal dialog boxes will NOT work. I basically just call PeekMessage and never call GetMessage, which automatically handles almost everything else. I will check in the changes tomorrow sometime but this is basically the biggest change, then removing the -s EMTERPRETIFY options in the emscripten link.
On Mar 6, 2019, at 12:07 AM, roozbehid notifications@github.com wrote:
In thinking about your problem a bit more, it might be nice to have an option in Microwindows that would allow it to be built to run multiple win32 "apps" simultaneously. These "apps" could be compiled to WebAssembly or just linked together in C. The GetMessage/TranslateMessage/DispatchMessage loop would be removed from the normal applications, and instead, a rewritten internal main loop would collect events, and dispatch them to the window callback functions. This would allow these applications, and your application, to run without having to emterpretify anything, and WebAssembly could be used for speed.
If dialog boxes aren't used initially, this isn't that much work, as essentially just a basic main loop would be revised from the existing code which polled for events, then created messages and dispatched them to window procedures. I need a little time to do this, but need to know that your main loop would be compatible.
Can you point me to what your application main loop code looks like? What do you think of this idea?
That would be great. Because it make my code huge and slow, if we can get rid of those emterpretifies!
My main loop is very similar to Win32 Loops
while (!quit && XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) { Message m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam); if (Application.FilterMessage (ref m)) continue; switch((Msg)msg.message) { case Msg.WM_KEYDOWN: case Msg.WM_SYSKEYDOWN: case Msg.WM_CHAR: case Msg.WM_SYSCHAR: case Msg.WM_KEYUP: case Msg.WM_SYSKEYUP: Control c; c = Control.FromHandle(msg.hwnd); // If we have a control with keyboard capture (usually a *Strip) // give it the message, and then drop the message if (keyboard_capture != null) { // WM_SYSKEYUP does not make it into ProcessCmdKey, so do it here if ((Msg)m.Msg == Msg.WM_SYSKEYDOWN) if (m.WParam.ToInt32() == (int)Keys.Menu) { keyboard_capture.GetTopLevelToolStrip ().Dismiss (ToolStripDropDownCloseReason.Keyboard); continue; } m.HWnd = keyboard_capture.Handle; switch (keyboard_capture.PreProcessControlMessageInternal (ref m)) { case PreProcessControlState.MessageProcessed: continue; case PreProcessControlState.MessageNeeded: case PreProcessControlState.MessageNotNeeded: if (((m.Msg == (int)Msg.WM_KEYDOWN || m.Msg == (int)Msg.WM_CHAR) && !keyboard_capture.ProcessControlMnemonic ((char)m.WParam))) { if (c == null || !ControlOnToolStrip (c)) continue; else m.HWnd = msg.hwnd; } else continue; break; } } if (((c != null) && c.PreProcessControlMessageInternal (ref m) != PreProcessControlState.MessageProcessed) || (c == null)) { goto default; } break; case Msg.WM_LBUTTONDOWN: case Msg.WM_MBUTTONDOWN: case Msg.WM_RBUTTONDOWN: if (keyboard_capture != null) { Control c2 = Control.FromHandle (msg.hwnd); // the target is not a winforms control (an embedded control, perhaps), so // release everything if (c2 == null) { ToolStripManager.FireAppClicked (); goto default; } // If we clicked a ToolStrip, we have to make sure it isn't // the one we are on, or any of its parents or children // If we clicked off the dropped down menu, release everything if (c2 is ToolStrip) { if ((c2 as ToolStrip).GetTopLevelToolStrip () != keyboard_capture.GetTopLevelToolStrip ()) ToolStripManager.FireAppClicked (); } else { if (c2.Parent != null) if (c2.Parent is ToolStripDropDownMenu) if ((c2.Parent as ToolStripDropDownMenu).GetTopLevelToolStrip () == keyboard_capture.GetTopLevelToolStrip ()) goto default; if (c2.TopLevelControl == null) goto default; ToolStripManager.FireAppClicked (); } } goto default; case Msg.WM_QUIT: quit = true; // make sure we exit break; default: XplatUI.TranslateMessage (ref msg); XplatUI.DispatchMessage (ref msg); break; } // If our Form doesn't have a handle anymore, it means it was destroyed and we need to *wait* for WM_QUIT. if ((context.MainForm != null) && (!context.MainForm.IsHandleCreated)) continue; // Handle exit, Form might have received WM_CLOSE and set 'closing' in response. if ((context.MainForm != null) && (context.MainForm.closing || (Modal && !context.MainForm.Visible))) { if (!Modal) { XplatUI.PostQuitMessage (0); } else { break; } } }
which basically is
GetMessage, TranslateMessage, DispatchMessage All at the end comes down to Microwindows Win32 API.
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/ghaerr/microwindows/issues/25#issuecomment-469994340, or mute the thread https://github.com/notifications/unsubscribe-auth/ALbi5YpDryGhiBYfTpGc0lXMnozzWn7Oks5vT2k1gaJpZM4bd9DJ.
Nothing is attached!
/*
extern HWND rootwp; / root window pointer /
/ linked mwin apps/ int WINAPI mwmine_WinMain(HINSTANCE, HINSTANCE, LPSTR, int); int WINAPI mwdemo_WinMain(HINSTANCE, HINSTANCE, LPSTR, int); int WINAPI mwdemo2_WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
/ run a single round of main loop/ static void main_loop(void) { MSG msg;
/* process any messages in input queue*/
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) && msg.message != WM_QUIT)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
/* poll mouse and keyboard for input*/
MwSelect(FALSE);
/* handle expired timers*/
MwHandleTimers();
}
int main(int ac, char *av) { / startup win32 system*/ invoke_WinMain_Start(ac, av);
/* init each application by calling its main entrypoint*/
mwmine_WinMain(rootwp->hInstance, NULL, "", SW_SHOW);
mwdemo_WinMain(rootwp->hInstance, NULL, "", SW_SHOW);
mwdemo2_WinMain(rootwp->hInstance, NULL, "", SW_SHOW);
/* setup main loop to be called regularly by browser*/
emscripten_set_main_loop(main_loop, 0, 0);
/* main loop - collect events and send to mwin app*/
for (;;)
{
main_loop();
}
/* cleanup on real exit*/
//invoke_WinMain_End();
return 0;
}
Ok. My problem is solved. So mainly my issue was that setting of NULL to lpfnWndProcBridge and then I had some special charachters like ½ and ¼ which caused deadlock somewhere. I am not sure if deadlock is in microwindows or somewhere else....
I am gonna focus on seeing if I can make use of emscripten_set_main_loop so to not do async anymore.
Just updating you. I did switch to emscripten_set_main_loop which hugely improved performance. Reduced size of my .wasm file from 7mb to 5mb. But I notice from time to time everything just ends to a crash, especially moving windows.
Also somehow my timer messages are also not being delivered( I assume WM_TIMER) buit overall it is much faster than previous ASYNC everything.....
With regard to your timer messages not being delivered, make very sure that function signature (including the return type) exactly matches the TIMERCB definition in include/device.h. Due to the way that EMSCRIPTEN is implemented, if you have, say, an int pointer-to-function, and then pass it as a callback to a function expecting a void pointer-to-function, EMSCRIPTEN's implementation of function pointers will silently fail. No modern CPU's have this issue, but it is documented in EMSCRIPTEN: https://emscripten.org/docs/porting/guidelines/function_pointer_issues.html
If it works on Windows compile but not EMSCRIPTEN, this is likely the problem. I had the same problem with the edit box blinking caret when porting.
Did you get everything working by just removing the pClass->lpfnWndProcBridge = NULL in RegisterClass? I have that #defined !WINEXTRA but have not committed it yet.
This fix would leave the custom controls with an uninitialized lpfnWndProcBridge for WINEXTRA, but I don't think that's the cause of your crashes, and is probably ok.
Let me know if I should commit this change. Can you describe the deadlock issue with special characters? In which functions?
It seems I am having a problem with SDL2. This is what I am getting with all my applications in Emscripten (WebAssembly) (Mouse is moving and working)
Mouse is working but all Windows are black. I am not 100% sure it is SDL2 problem but I am now also getting this error in my browser (which is ignored) caused by the call to
SDL_CreateRenderer
emscripten_set_main_loop_timing: Cannot set timing mode for main loop since a main loop does not exist! Call emscripten_set_main_loop first to set one up.
Everything works fine in my SDL2-Windows but not SDL2-Emscripten.
I also saw this : https://emscripten.org/docs/porting/emscripten-runtime-environment.html which recommends
When using SDL you will probably need to set the main loop.
So could it be the problem? Because SDL2 now uses the renderer?