--- a/src/App.cpp +++ b/src/App.cpp @@ -24,6 +24,9 @@ #include #include #include +#ifdef PLATFORM_WEB +#include +#endif #include #include @@ -34,6 +37,16 @@ void App::openWindow() { +#ifdef PLATFORM_WEB + // In the browser (fredygames.com build): the canvas fills the page and follows its size, nothing about monitors, + // no fullscreen at the start, and the browser paces the frames (WindowShouldClose waits for the next one). + SetConfigFlags( FLAG_WINDOW_RESIZABLE ); + InitWindow( EM_ASM_INT( { return window.innerWidth; } ), EM_ASM_INT( { return window.innerHeight; } ), "KIP KEPLER" ); + SetExitKey( 0 ); + HideCursor(); + m_screen.init(); + return; +#endif bool deck = getenv( "SteamDeck" ) && atoi( getenv( "SteamDeck" ) ) == 1; // Pictures and timed test runs stay hidden (and quiet: see run()). bool shot = !m_o.m_shot.empty() || !m_o.m_autotest.empty() || m_o.m_seconds > 0; @@ -511,7 +524,10 @@ items.push_back( tr( "menu.episodes" ) ); items.push_back( tr( "menu.extras" ) ); items.push_back( tr( "menu.options" ) ); +#ifndef PLATFORM_WEB + // A web page has no window to close. items.push_back( tr( "menu.quit" ) ); +#endif return items; } --- a/src/Checker.cpp +++ b/src/Checker.cpp @@ -498,7 +498,11 @@ } } }; +#ifdef __EMSCRIPTEN__ + unsigned threads = 1; // no threads in the web build +#else unsigned threads = std::clamp( std::thread::hardware_concurrency() / 2, 1u, 8u ); +#endif std::vector workers( threads, S ); const size_t MaxNodes = 150000; --- a/src/Editor.cpp +++ b/src/Editor.cpp @@ -1114,9 +1114,15 @@ m_reachOut = std::move( reach ); m_checked = true; }; +#ifdef __EMSCRIPTEN__ + // No threads in the web build: the check runs at once. + job(); + (void)wait; +#else if ( wait ) job(); else m_thread = std::thread( job ); +#endif pollCheck(); } --- a/src/Rumble.cpp +++ b/src/Rumble.cpp @@ -14,6 +14,8 @@ #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include +#elif defined( __EMSCRIPTEN__ ) + // The web build: no motors (the browser's pad API is left alone). #else #include #include @@ -92,6 +94,10 @@ } } +#elif defined( __EMSCRIPTEN__ ) + +void send( float, float ) {} + #else int g_fd = -1; ///< the device with force feedback --- a/src/Screen.cpp +++ b/src/Screen.cpp @@ -16,8 +16,15 @@ namespace { +// The web build runs on WebGL 2 (OpenGL ES 3.0): the same shaders under the GLSL ES header. +#ifdef PLATFORM_WEB +#define KEPLER_GLSL "#version 300 es\nprecision mediump float;" +#else +#define KEPLER_GLSL "#version 330" +#endif + // The light map holds half the light, so lamps can brighten up to twice (the lit shader doubles it again). -const char *LitFs = R"(#version 330 +const char *LitFs = KEPLER_GLSL R"( in vec2 fragTexCoord; in vec4 fragColor; uniform sampler2D texture0; @@ -31,7 +38,7 @@ } )"; -const char *BlurFs = R"(#version 330 +const char *BlurFs = KEPLER_GLSL R"( in vec2 fragTexCoord; in vec4 fragColor; uniform sampler2D texture0; --- a/third_party/raylib/src/platforms/rcore_web.c +++ b/third_party/raylib/src/platforms/rcore_web.c @@ -151,6 +151,11 @@ //---------------------------------------------------------------------------------- // Check if application should close +// fredygames.com: wait for the browser's next animation frame instead of a fixed 16 ms sleep (Asyncify) +EM_ASYNC_JS(void, WaitNextAnimationFrame, (void), { + await new Promise(resolve => requestAnimationFrame(() => resolve())); +}); + bool WindowShouldClose(void) { // Emterpreter-Async required to run sync code @@ -158,7 +163,7 @@ // By default, this function is never called on a web-ready raylib example because we encapsulate // frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously // but now emscripten allows sync code to be executed in an interpreted way, using emterpreter! - emscripten_sleep(16); + WaitNextAnimationFrame(); return false; } --- a/third_party/raylib/src/rcore.c +++ b/third_party/raylib/src/rcore.c @@ -1716,6 +1716,12 @@ { if (seconds < 0) return; // Security check +#if defined(PLATFORM_WEB) + // fredygames.com: the browser paces the frames (WindowShouldClose waits for requestAnimationFrame); nanosleep() + // would only spin the main thread here. + return; +#endif + #if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) double destinationTime = GetTime() + seconds; #endif