Browse Source

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).
pull/23/head
Vladimír Vondruš 13 years ago
parent
commit
37e4f9d6f7
  1. 5
      src/Platform/CMakeLists.txt
  2. 44
      src/Platform/EmscriptenApplication.js
  3. 76
      src/Platform/Sdl2Application.cpp
  4. 55
      src/Platform/Sdl2Application.h

5
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}) install(FILES NaClApplication.js WebApplication.css DESTINATION ${MAGNUM_DATA_INSTALL_DIR})
endif() 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 # GLX application
if(WITH_GLXAPPLICATION) if(WITH_GLXAPPLICATION)
set(NEED_ABSTRACTXAPPLICATION 1) set(NEED_ABSTRACTXAPPLICATION 1)

44
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);

76
src/Platform/Sdl2Application.cpp

@ -24,6 +24,10 @@
#include "Sdl2Application.h" #include "Sdl2Application.h"
#ifdef CORRADE_TARGET_EMSCRIPTEN
#include <emscripten/emscripten.h>
#endif
#include "Context.h" #include "Context.h"
namespace Magnum { namespace Platform { 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 */ /** @todo Delegating constructor when support for GCC 4.6 is dropped */
Sdl2Application::Sdl2Application(const Arguments&, const Configuration& configuration): context(nullptr), flags(Flag::Redraw) { 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() { 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) { if(SDL_Init(SDL_INIT_VIDEO) < 0) {
Error() << "Cannot initialize SDL."; Error() << "Cannot initialize SDL.";
std::exit(1); std::exit(1);
@ -93,6 +109,8 @@ bool Sdl2Application::tryCreateContext(const Configuration& configuration) {
Uint32 flags(configuration.flags()); Uint32 flags(configuration.flags());
if(!(configuration.flags() & Configuration::Flag::Hidden)) flags |= SDL_WINDOW_SHOWN; 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(), if(!(window = SDL_CreateWindow(configuration.title().data(),
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
configuration.size().x(), configuration.size().y(), configuration.size().x(), configuration.size().y(),
@ -104,32 +122,61 @@ bool Sdl2Application::tryCreateContext(const Configuration& configuration) {
window = nullptr; window = nullptr;
return false; 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 */ /* Push resize event, so viewportEvent() is called at startup */
SDL_Event* sizeEvent = new SDL_Event; // SDL_Event* sizeEvent = new SDL_Event;
sizeEvent->type = SDL_WINDOWEVENT; // sizeEvent->type = SDL_WINDOWEVENT;
sizeEvent->window.event = SDL_WINDOWEVENT_RESIZED; // sizeEvent->window.event = SDL_WINDOWEVENT_RESIZED;
sizeEvent->window.data1 = configuration.size().x(); // sizeEvent->window.data1 = configuration.size().x();
sizeEvent->window.data2 = configuration.size().y(); // sizeEvent->window.data2 = configuration.size().y();
SDL_PushEvent(sizeEvent); // SDL_PushEvent(sizeEvent);
c = new Context; c = new Context;
return true; return true;
} }
void Sdl2Application::swapBuffers() {
#ifndef CORRADE_TARGET_EMSCRIPTEN
SDL_GL_SwapWindow(window);
#else
SDL_Flip(context);
#endif
}
Sdl2Application::~Sdl2Application() { Sdl2Application::~Sdl2Application() {
delete c; delete c;
#ifndef CORRADE_TARGET_EMSCRIPTEN
SDL_GL_DeleteContext(context); SDL_GL_DeleteContext(context);
SDL_DestroyWindow(window); SDL_DestroyWindow(window);
#else
SDL_FreeSurface(context);
CORRADE_INTERNAL_ASSERT(instance == this);
instance = nullptr;
#endif
SDL_Quit(); SDL_Quit();
} }
int Sdl2Application::exec() { int Sdl2Application::exec() {
#ifndef CORRADE_TARGET_EMSCRIPTEN
while(!(flags & Flag::Exit)) mainLoop(); while(!(flags & Flag::Exit)) mainLoop();
#else
emscripten_set_main_loop(staticMainLoop, 0, true);
#endif
return 0; return 0;
} }
void Sdl2Application::exit() {
#ifndef CORRADE_TARGET_EMSCRIPTEN
flags |= Flag::Exit;
#else
emscripten_cancel_main_loop();
#endif
}
void Sdl2Application::mainLoop() { void Sdl2Application::mainLoop() {
SDL_Event event; SDL_Event event;
@ -171,7 +218,11 @@ void Sdl2Application::mainLoop() {
} }
case SDL_QUIT: case SDL_QUIT:
#ifndef CORRADE_TARGET_EMSCRIPTEN
flags |= Flag::Exit; flags |= Flag::Exit;
#else
emscripten_cancel_main_loop();
#endif
return; return;
} }
} }
@ -179,12 +230,23 @@ void Sdl2Application::mainLoop() {
if(flags & Flag::Redraw) { if(flags & Flag::Redraw) {
flags &= ~Flag::Redraw; flags &= ~Flag::Redraw;
drawEvent(); drawEvent();
} else SDL_WaitEvent(nullptr); return;
}
#ifndef CORRADE_TARGET_EMSCRIPTEN
SDL_WaitEvent(nullptr);
#endif
} }
void Sdl2Application::setMouseLocked(bool enabled) { void Sdl2Application::setMouseLocked(bool enabled) {
/** @todo Implement this in Emscripten */
#ifndef CORRADE_TARGET_EMSCRIPTEN
SDL_SetWindowGrab(window, enabled ? SDL_TRUE : SDL_FALSE); SDL_SetWindowGrab(window, enabled ? SDL_TRUE : SDL_FALSE);
SDL_SetRelativeMouseMode(enabled ? SDL_TRUE : SDL_FALSE); SDL_SetRelativeMouseMode(enabled ? SDL_TRUE : SDL_FALSE);
#else
CORRADE_ASSERT(false, "Sdl2Application::setMouseLocked(): not implemented", );
static_cast<void>(enabled);
#endif
} }
Sdl2Application::Configuration::Configuration(): _title("Magnum SDL2 Application"), _size(800, 600), _flags(Flag::Resizable), _sampleCount(0) {} Sdl2Application::Configuration::Configuration(): _title("Magnum SDL2 Application"), _size(800, 600), _flags(Flag::Resizable), _sampleCount(0) {}

55
src/Platform/Sdl2Application.h

@ -38,6 +38,8 @@
#endif #endif
#include <SDL.h> #include <SDL.h>
#include <SDL_scancode.h> #include <SDL_scancode.h>
#include <Containers/EnumSet.h>
#include <Corrade.h>
namespace Magnum { namespace Magnum {
@ -66,6 +68,44 @@ MAGNUM_SDL2APPLICATION_MAIN(MyApplication)
If no other application header is included this class is also aliased to If no other application header is included this class is also aliased to
`Platform::Application` and the macro is aliased to `MAGNUM_APPLICATION_MAIN()` `Platform::Application` and the macro is aliased to `MAGNUM_APPLICATION_MAIN()`
to simplify porting. 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
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Magnum Emscripten Application</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="WebApplication.css" />
</head>
<body>
<h1>Magnum Emscripten Application</h1>
<div id="listener">
<canvas id="module"></canvas>
<div id="status">Initialization...</div>
<div id="statusDescription" />
<script src="EmscriptenApplication.js"></script>
<script async="async" src="application.js"></script>
</div>
</body>
</html>
@endcode
You can modify all the files to your liking, but the HTML file must contain at
least the `&lt;canvas&gt;` enclosed in listener `&lt;div&gt;`. The JavaScript
file contains event listeners which print loading status on the page. The
status displayed in the remaining two `&lt;div&gt;`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 { class Sdl2Application {
public: public:
@ -97,7 +137,7 @@ class Sdl2Application {
int exec(); int exec();
/** @brief Exit application main loop */ /** @brief Exit application main loop */
void exit() { flags |= Flag::Exit; } void exit();
protected: protected:
/* Nobody will need to have (and delete) Sdl2Application*, thus this is /* Nobody will need to have (and delete) Sdl2Application*, thus this is
@ -119,7 +159,7 @@ class Sdl2Application {
virtual void drawEvent() = 0; virtual void drawEvent() = 0;
/** @copydoc GlutApplication::swapBuffers() */ /** @copydoc GlutApplication::swapBuffers() */
void swapBuffers() { SDL_GL_SwapWindow(window); } void swapBuffers();
/** @copydoc GlutApplication::redraw() */ /** @copydoc GlutApplication::redraw() */
void redraw() { flags |= Flag::Redraw; } void redraw() { flags |= Flag::Redraw; }
@ -174,17 +214,28 @@ class Sdl2Application {
private: private:
enum class Flag: UnsignedByte { enum class Flag: UnsignedByte {
Redraw = 1 << 0, Redraw = 1 << 0,
#ifndef CORRADE_TARGET_EMSCRIPTEN
Exit = 1 << 1 Exit = 1 << 1
#endif
}; };
typedef Containers::EnumSet<Flag, UnsignedByte> Flags; typedef Containers::EnumSet<Flag, UnsignedByte> Flags;
CORRADE_ENUMSET_FRIEND_OPERATORS(Flags) CORRADE_ENUMSET_FRIEND_OPERATORS(Flags)
#ifdef CORRADE_TARGET_EMSCRIPTEN
static Sdl2Application* instance;
static void staticMainLoop();
#endif
void initialize(); void initialize();
void mainLoop(); void mainLoop();
#ifndef CORRADE_TARGET_EMSCRIPTEN
SDL_Window* window; SDL_Window* window;
SDL_GLContext context; SDL_GLContext context;
#else
SDL_Surface* context;
#endif
Context* c; Context* c;

Loading…
Cancel
Save