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;