From 37e4f9d6f7adaa7b1632caaff16b8ee897b1ced7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 23 Apr 2013 19:43:57 +0200 Subject: [PATCH] Platform: ported Sdl2Application to Emscripten. Emscripten supports some hybrid between SDL1 and SDL2. I don't want to add Sdl1Application just for that and create more portability issues, so I just changed some things in Sdl2Application to be compatible with what Emscripten wants. Full SDL2 support is awaited in Emscripten, thus this is future-proof (rather than having SDL1 support, which would be deprecated later). Added documentation about Emscripten usage and template HTML markup to Sdl2Application docs, will move it somewhere else in the future when more than one Application will be supported (e.g. GLUT). --- src/Platform/CMakeLists.txt | 5 ++ src/Platform/EmscriptenApplication.js | 44 ++++++++++++++++ src/Platform/Sdl2Application.cpp | 76 ++++++++++++++++++++++++--- src/Platform/Sdl2Application.h | 55 ++++++++++++++++++- 4 files changed, 171 insertions(+), 9 deletions(-) create mode 100644 src/Platform/EmscriptenApplication.js diff --git a/src/Platform/CMakeLists.txt b/src/Platform/CMakeLists.txt index e0d0461a1..f9303e2ef 100644 --- a/src/Platform/CMakeLists.txt +++ b/src/Platform/CMakeLists.txt @@ -89,6 +89,11 @@ if(WITH_NACLAPPLICATION OR WITH_WINDOWLESSNACLAPPLICATION) install(FILES NaClApplication.js WebApplication.css DESTINATION ${MAGNUM_DATA_INSTALL_DIR}) endif() +# JavaScript and CSS stuff for Emscripten +if(WITH_SDL2APPLICATION AND CORRADE_TARGET_EMSCRIPTEN) + install(FILES EmscriptenApplication.js WebApplication.css DESTINATION ${MAGNUM_DATA_INSTALL_DIR}) +endif() + # GLX application if(WITH_GLXAPPLICATION) set(NEED_ABSTRACTXAPPLICATION 1) diff --git a/src/Platform/EmscriptenApplication.js b/src/Platform/EmscriptenApplication.js new file mode 100644 index 000000000..8a0706b0c --- /dev/null +++ b/src/Platform/EmscriptenApplication.js @@ -0,0 +1,44 @@ +var Module = { + preRun: [], + postRun: [], + + printErr: function(message) { + console.error(Array.prototype.slice.call(message).join(' ')); + }, + + print: function(message) { + console.log(Array.prototype.slice.call(message).join(' ')); + }, + + canvas: document.getElementById('module'), + + setStatus: function(message) { + var status = document.getElementById('status'); + if(status) status.innerHTML = message; + }, + + setStatusDescription: function(message) { + var statusDescription = document.getElementById('statusDescription'); + if(statusDescription) statusDescription.innerHTML = message; + }, + + totalDependencies: 0, + + monitorRunDependencies: function(left) { + this.totalDependencies = Math.max(this.totalDependencies, left); + + if(left) { + Module.setStatus('Downloading...'); + Module.setStatusDescription((this.totalDependencies - left) + '/' + this.totalDependencies); + } else { + Module.setStatus('Download complete'); + Module.setStatusDescription(''); + } + } +}; + +Module.setStatus('Downloading...'); + +Module.canvas.addEventListener('contextmenu', function(event) { + event.preventDefault(); +}, true); diff --git a/src/Platform/Sdl2Application.cpp b/src/Platform/Sdl2Application.cpp index 65e63a31d..f572522d8 100644 --- a/src/Platform/Sdl2Application.cpp +++ b/src/Platform/Sdl2Application.cpp @@ -24,6 +24,10 @@ #include "Sdl2Application.h" +#ifdef CORRADE_TARGET_EMSCRIPTEN +#include +#endif + #include "Context.h" namespace Magnum { namespace Platform { @@ -46,6 +50,13 @@ Sdl2Application::InputEvent::Modifiers fixedModifiers(Uint16 mod) { } +#ifdef CORRADE_TARGET_EMSCRIPTEN +Sdl2Application* Sdl2Application::instance = nullptr; +void Sdl2Application::staticMainLoop() { + instance->mainLoop(); +} +#endif + /** @todo Delegating constructor when support for GCC 4.6 is dropped */ Sdl2Application::Sdl2Application(const Arguments&, const Configuration& configuration): context(nullptr), flags(Flag::Redraw) { @@ -65,6 +76,11 @@ Sdl2Application::Sdl2Application(const Arguments&, std::nullptr_t): context(null } void Sdl2Application::initialize() { + #ifdef CORRADE_TARGET_EMSCRIPTEN + CORRADE_ASSERT(!instance, "Platform::Sdl2Application::Sdl2Application(): the instance is already created", ); + instance = this; + #endif + if(SDL_Init(SDL_INIT_VIDEO) < 0) { Error() << "Cannot initialize SDL."; std::exit(1); @@ -93,6 +109,8 @@ bool Sdl2Application::tryCreateContext(const Configuration& configuration) { Uint32 flags(configuration.flags()); if(!(configuration.flags() & Configuration::Flag::Hidden)) flags |= SDL_WINDOW_SHOWN; + /** @todo Remove when Emscripten has proper SDL2 support */ + #ifndef CORRADE_TARGET_EMSCRIPTEN if(!(window = SDL_CreateWindow(configuration.title().data(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, configuration.size().x(), configuration.size().y(), @@ -104,32 +122,61 @@ bool Sdl2Application::tryCreateContext(const Configuration& configuration) { window = nullptr; return false; } + #else + context = SDL_SetVideoMode(configuration.size().x(), configuration.size().y(), 24, SDL_OPENGL|SDL_HWSURFACE|SDL_DOUBLEBUF); + #endif /* Push resize event, so viewportEvent() is called at startup */ - SDL_Event* sizeEvent = new SDL_Event; - sizeEvent->type = SDL_WINDOWEVENT; - sizeEvent->window.event = SDL_WINDOWEVENT_RESIZED; - sizeEvent->window.data1 = configuration.size().x(); - sizeEvent->window.data2 = configuration.size().y(); - SDL_PushEvent(sizeEvent); +// SDL_Event* sizeEvent = new SDL_Event; +// sizeEvent->type = SDL_WINDOWEVENT; +// sizeEvent->window.event = SDL_WINDOWEVENT_RESIZED; +// sizeEvent->window.data1 = configuration.size().x(); +// sizeEvent->window.data2 = configuration.size().y(); +// SDL_PushEvent(sizeEvent); c = new Context; return true; } +void Sdl2Application::swapBuffers() { + #ifndef CORRADE_TARGET_EMSCRIPTEN + SDL_GL_SwapWindow(window); + #else + SDL_Flip(context); + #endif +} + Sdl2Application::~Sdl2Application() { delete c; + #ifndef CORRADE_TARGET_EMSCRIPTEN SDL_GL_DeleteContext(context); SDL_DestroyWindow(window); + #else + SDL_FreeSurface(context); + CORRADE_INTERNAL_ASSERT(instance == this); + instance = nullptr; + #endif SDL_Quit(); } int Sdl2Application::exec() { + #ifndef CORRADE_TARGET_EMSCRIPTEN while(!(flags & Flag::Exit)) mainLoop(); + #else + emscripten_set_main_loop(staticMainLoop, 0, true); + #endif return 0; } +void Sdl2Application::exit() { + #ifndef CORRADE_TARGET_EMSCRIPTEN + flags |= Flag::Exit; + #else + emscripten_cancel_main_loop(); + #endif +} + void Sdl2Application::mainLoop() { SDL_Event event; @@ -171,7 +218,11 @@ void Sdl2Application::mainLoop() { } case SDL_QUIT: + #ifndef CORRADE_TARGET_EMSCRIPTEN flags |= Flag::Exit; + #else + emscripten_cancel_main_loop(); + #endif return; } } @@ -179,12 +230,23 @@ void Sdl2Application::mainLoop() { if(flags & Flag::Redraw) { flags &= ~Flag::Redraw; drawEvent(); - } else SDL_WaitEvent(nullptr); + return; + } + + #ifndef CORRADE_TARGET_EMSCRIPTEN + SDL_WaitEvent(nullptr); + #endif } void Sdl2Application::setMouseLocked(bool enabled) { + /** @todo Implement this in Emscripten */ + #ifndef CORRADE_TARGET_EMSCRIPTEN SDL_SetWindowGrab(window, enabled ? SDL_TRUE : SDL_FALSE); SDL_SetRelativeMouseMode(enabled ? SDL_TRUE : SDL_FALSE); + #else + CORRADE_ASSERT(false, "Sdl2Application::setMouseLocked(): not implemented", ); + static_cast(enabled); + #endif } Sdl2Application::Configuration::Configuration(): _title("Magnum SDL2 Application"), _size(800, 600), _flags(Flag::Resizable), _sampleCount(0) {} diff --git a/src/Platform/Sdl2Application.h b/src/Platform/Sdl2Application.h index 480151564..e0a90fcac 100644 --- a/src/Platform/Sdl2Application.h +++ b/src/Platform/Sdl2Application.h @@ -38,6 +38,8 @@ #endif #include #include +#include +#include namespace Magnum { @@ -66,6 +68,44 @@ MAGNUM_SDL2APPLICATION_MAIN(MyApplication) If no other application header is included this class is also aliased to `Platform::Application` and the macro is aliased to `MAGNUM_APPLICATION_MAIN()` to simplify porting. + +@section NaClApplication-html Usage with Emscripten + +If you are targetting @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten", you need to +provide HTML markup for your application. Template one is below, you can modify +it to your liking. The markup references two files, `EmscriptenApplication.js` +and `WebApplication.css`, both are in `Platform/` directory in the source tree +and are also installed into `share/magnum/` inside your Emscripten toolchain. +@code + + + + Magnum Emscripten Application + + + + +

Magnum Emscripten Application

+
+ +
Initialization...
+
+ + +
+ + +@endcode + +You can modify all the files to your liking, but the HTML file must contain at +least the `<canvas>` enclosed in listener `<div>`. The JavaScript +file contains event listeners which print loading status on the page. The +status displayed in the remaining two `<div>`s, if they are available. +The CSS file contains rudimentary style to avoid eye bleeding. + +The application redirects @ref Corrade::Utility::Debug "Debug", +@ref Corrade::Utility::Warning "Warning" and @ref Corrade::Utility::Error "Error" +output to JavaScript console. */ class Sdl2Application { public: @@ -97,7 +137,7 @@ class Sdl2Application { int exec(); /** @brief Exit application main loop */ - void exit() { flags |= Flag::Exit; } + void exit(); protected: /* Nobody will need to have (and delete) Sdl2Application*, thus this is @@ -119,7 +159,7 @@ class Sdl2Application { virtual void drawEvent() = 0; /** @copydoc GlutApplication::swapBuffers() */ - void swapBuffers() { SDL_GL_SwapWindow(window); } + void swapBuffers(); /** @copydoc GlutApplication::redraw() */ void redraw() { flags |= Flag::Redraw; } @@ -174,17 +214,28 @@ class Sdl2Application { private: enum class Flag: UnsignedByte { Redraw = 1 << 0, + #ifndef CORRADE_TARGET_EMSCRIPTEN Exit = 1 << 1 + #endif }; typedef Containers::EnumSet Flags; CORRADE_ENUMSET_FRIEND_OPERATORS(Flags) + #ifdef CORRADE_TARGET_EMSCRIPTEN + static Sdl2Application* instance; + static void staticMainLoop(); + #endif + void initialize(); void mainLoop(); + #ifndef CORRADE_TARGET_EMSCRIPTEN SDL_Window* window; SDL_GLContext context; + #else + SDL_Surface* context; + #endif Context* c;