From d15dc9f40f5a20bff452547a2dcb15231f9f969d Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 5 Dec 2020 21:57:47 +0100 Subject: Add: support for emscripten (play-OpenTTD-in-the-browser) Emscripten compiles to WASM, which can be loaded via HTML / JavaScript. This allows you to play OpenTTD inside a browser. Co-authored-by: milek7 --- src/video/sdl2_v.cpp | 45 +++++++++++++++++++++++++++++++++++++++++---- src/video/sdl2_v.h | 5 +++++ 2 files changed, 46 insertions(+), 4 deletions(-) (limited to 'src/video') diff --git a/src/video/sdl2_v.cpp b/src/video/sdl2_v.cpp index 09d0e8a0b..68b0aa983 100644 --- a/src/video/sdl2_v.cpp +++ b/src/video/sdl2_v.cpp @@ -27,6 +27,10 @@ #include #include #include +#ifdef __EMSCRIPTEN__ +# include +# include +#endif #include "../safeguards.h" @@ -673,7 +677,19 @@ void VideoDriver_SDL::LoopOnce() InteractiveRandom(); // randomness while (PollEvent() == -1) {} - if (_exit_game) return; + if (_exit_game) { +#ifdef __EMSCRIPTEN__ + /* Emscripten is event-driven, and as such the main loop is inside + * the browser. So if _exit_game goes true, the main loop ends (the + * cancel call), but we still have to call the cleanup that is + * normally done at the end of the main loop for non-Emscripten. + * After that, Emscripten just halts, and the HTML shows a nice + * "bye, see you next time" message. */ + emscripten_cancel_main_loop(); + MainLoopCleanup(); +#endif + return; + } mod = SDL_GetModState(); keys = SDL_GetKeyboardState(&numkeys); @@ -722,9 +738,17 @@ void VideoDriver_SDL::LoopOnce() _local_palette = _cur_palette; } else { /* Release the thread while sleeping */ - if (_draw_mutex != nullptr) draw_lock.unlock(); - CSleep(1); - if (_draw_mutex != nullptr) draw_lock.lock(); + if (_draw_mutex != nullptr) { + draw_lock.unlock(); + CSleep(1); + draw_lock.lock(); + } else { +/* Emscripten is running an event-based mainloop; there is already some + * downtime between each iteration, so no need to sleep. */ +#ifndef __EMSCRIPTEN__ + CSleep(1); +#endif + } NetworkDrawChatMessage(); DrawMouseCursor(); @@ -778,6 +802,10 @@ void VideoDriver_SDL::MainLoop() DEBUG(driver, 1, "SDL2: using %sthreads", _draw_threaded ? "" : "no "); +#ifdef __EMSCRIPTEN__ + /* Run the main loop event-driven, based on RequestAnimationFrame. */ + emscripten_set_main_loop_arg(&this->EmscriptenLoop, this, 0, 1); +#else while (!_exit_game) { LoopOnce(); } @@ -803,6 +831,15 @@ void VideoDriver_SDL::MainLoopCleanup() _draw_mutex = nullptr; _draw_signal = nullptr; } + +#ifdef __EMSCRIPTEN__ + emscripten_exit_pointerlock(); + /* In effect, the game ends here. As emscripten_set_main_loop() caused + * the stack to be unwound, the code after MainLoop() in + * openttd_main() is never executed. */ + EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs()); + EM_ASM(if (window["openttd_exit"]) openttd_exit()); +#endif } bool VideoDriver_SDL::ChangeResolution(int w, int h) diff --git a/src/video/sdl2_v.h b/src/video/sdl2_v.h index 6318c403f..c2ac87a06 100644 --- a/src/video/sdl2_v.h +++ b/src/video/sdl2_v.h @@ -46,6 +46,11 @@ private: void MainLoopCleanup(); bool CreateMainSurface(uint w, uint h, bool resize); +#ifdef __EMSCRIPTEN__ + /* Convert a constant pointer back to a non-constant pointer to a member function. */ + static void EmscriptenLoop(void *self) { ((VideoDriver_SDL *)self)->LoopOnce(); } +#endif + /** * This is true to indicate that keyboard input is in text input mode, and SDL_TEXTINPUT events are enabled. */ -- cgit v1.2.3-54-g00ecf