Magnum::Platform::EmscriptenApplication class new in 2019.10

Emscripten application.

Application running on Emscripten. Available only on Emscripten, see respective sections in the Corrade and Magnum building documentation.

Bootstrap application

Fully contained base application using Sdl2Application for desktop build and EmscriptenApplication for Emscripten build along with full HTML markup and CMake setup is available in base-emscripten branch of the Magnum Bootstrap repository, download it as tar.gz or zip file. After extracting the downloaded archive, you can do the desktop build in the same way as with Sdl2Application. For the Emscripten build you also need to put the contents of toolchains repository from https://github.com/mosra/toolchains in a toolchains/ subdirectory. There are two toolchain files. The generic/Emscripten.cmake is for the classical (asm.js) build, the generic/Emscripten-wasm.cmake is for WebAssembly build. Don't forget to adapt EMSCRIPTEN_PREFIX variable in toolchains/generic/Emscripten*.cmake to path where Emscripten is installed; you can also pass it explicitly on command-line using -DEMSCRIPTEN_PREFIX. Default is /usr/emscripten.

Then create build directory and run cmake and build/install commands in it. Set CMAKE_PREFIX_PATH to where you have all the dependencies installed, set CMAKE_INSTALL_PREFIX to have the files installed in proper location (a webserver, e.g. /srv/http/emscripten).

mkdir build-emscripten && cd build-emscripten
cmake .. \
    -DCMAKE_TOOLCHAIN_FILE=path/to/toolchains/generic/Emscripten-wasm.cmake \
    -DCMAKE_PREFIX_PATH=/usr/lib/emscripten/system \
    -DCMAKE_INSTALL_PREFIX=/srv/http/emscripten
cmake --build .
cmake --build . --target install

You can then open MyApplication.html in your browser (through a webserver, e.g. http://localhost/emscripten/MyApplication.html).

Detailed information about deployment for Emscripten and all needed boilerplate together with a troubleshooting guide is available in JavaScript, HTML5 and WebGL.

General usage

This application library is built if MAGNUM_WITH_EMSCRIPTENAPPLICATION is enabled when building Magnum. To use this library with CMake, put FindOpenGLES2.cmake (or FindOpenGLES3.cmake) into your modules/ directory, request the EmscriptenApplication component of the Magnum package and link to the Magnum::EmscriptenApplication target:

find_package(Magnum REQUIRED)
if(CORRADE_TARGET_EMSCRIPTEN)
    find_package(Magnum REQUIRED EmscriptenApplication)
endif()

# ...
if(CORRADE_TARGET_EMSCRIPTEN)
    target_link_libraries(your-app PRIVATE Magnum::EmscriptenApplication)
endif()

Additionally, if you're using Magnum as a CMake subproject, do the following before calling find_package() to ensure it's enabled, as the library is not built by default:

set(MAGNUM_WITH_EMSCRIPTENAPPLICATION ON CACHE BOOL "" FORCE)
add_subdirectory(magnum EXCLUDE_FROM_ALL)

If no other application is requested, you can also use the generic Magnum::Application alias to simplify porting. See Downloading and building and Usage with CMake for more information.

In C++ code you need to implement at least drawEvent() to be able to draw on the screen.

class MyApplication: public Platform::EmscriptenApplication {
    // implement required methods...
};
MAGNUM_EMSCRIPTENAPPLICATION_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.

Browser-specific behavior

Leaving a default (zero) size in Configuration will cause the app to use a size that corresponds to CSS pixel size of the <canvas> element. The size is then multiplied by DPI scaling value, see DPI awareness below for details.

If you enable Configuration::WindowFlag::Resizable, the canvas will be resized when size of the canvas changes and you get viewportEvent(). If the flag is not enabled, no canvas resizing is performed.

Unlike desktop platforms, the browser has no concept of application exit code, so the return value of exec() is always 0 and whatever is passed to exit(int) is ignored.

Main loop implementation

Magnum application implementations default to redrawing only when needed to save power and while this is simple to implement efficiently on desktop apps where the application has the full control over the main loop, it's harder in the callback-based browser environment.

Sdl2Application makes use of emscripten_set_main_loop(), which periodically calls window.requestAnimationFrame() in order to maintain a steady frame rate. For apps that need to redraw only when needed this means the callback will be called 60 times per second only to be a no-op. While that's still significantly more efficient than drawing everything each time, it still means the browser has to wake up 60 times per second to do nothing.

EmscriptenApplication instead makes use of requestAnimationFrame() directly — on initialization and on redraw(), an animation frame will be requested and the callback set up. The callback will immediately schedule another animation frame, but cancel that request after drawEvent() if redraw() was not requested. Note that due to the way Emscripten internals work, this also requires the class instance to be stored as a global variable instead of a local variable in main(). The MAGNUM_EMSCRIPTENAPPLICATION_MAIN() macro handles this in a portable way for you.

For testing purposes or for more predictable behavior for example when the application has to redraw all the time anyway this can be disabled using Configuration::WindowFlag::AlwaysRequestAnimationFrame. Setting the flag will make the main loop behave equivalently to Sdl2Application.

WebGL-specific behavior

While WebGL itself requires all extensions to be enabled explicitly, by default Emscripten enables all supported extensions that don't have a negative effect on performance to simplify porting. This is controlled by GLConfiguration::Flag::EnableExtensionsByDefault and the flag is enabled by default. When disabled, you are expected to enable desired extensions manually using emscripten_webgl_enable_extension().

DPI awareness

Since this application targets only web browsers, DPI handling isn't as general as in case of Sdl2Application or GlfwApplication. See Sdl2Application DPI awareness documentation for a guide covering all platform differences.

For this application in particular, windowSize() can be different than framebufferSize() on HiDPI displays — which is different from Sdl2Application behavior on Emscripten. By default, dpiScaling() is 1.0f in both dimensions but it can be overridden using custom DPI scaling — the --magnum-dpi-scaling command-line options are supported the same way as in Sdl2Application, only in the form of URL GET parameters, similarly to all other command-line options.

Having dpiScaling() set to 1.0f is done in order to have consistent behavior with other platforms — platforms have either windowSize() equivalent to framebufferSize() and then dpiScaling() specifies the UI scale (Windows/Linux/Android-like) or windowSize() different from framebufferSize() (which defines the UI scale) and then dpiScaling() is 1.0f (macOS/iOS-like), so this is the second case. The actual device pixel ratio is expressed in the ratio of windowSize() and framebufferSize() so crossplatform code shouldn't have a need to query it, however for completeness it's exposed in devicePixelRatio() and ViewportEvent::devicePixelRatio().

Setting custom DPI scaling will affect framebufferSize() (larger values making the canvas backing framebuffer larger and vice versa), windowSize() will stay unaffected as it's controlled by the CSS, and devicePixelRatio() will stay the same as well as it's defined by the browser.

To avoid confusion, documentation of all EmscriptenApplication APIs always mentions only the web case, consult equivalent APIs in Sdl2Application or GlfwApplication for behavior in those implementations.

Public types

struct Arguments
Application arguments.
class Configuration
Configuration.
class GLConfiguration
WebGL context configuration.
class InputEvent
Base for input events.
class KeyEvent
Key event.
class MouseEvent
Mouse event.
class MouseMoveEvent
Mouse move event.
class MouseScrollEvent
Mouse scroll event.
class TextInputEvent
Text input event.
class ViewportEvent
Viewport event.

Constructors, destructors, conversion operators

EmscriptenApplication(const Arguments& arguments, const Configuration& configuration, const GLConfiguration& glConfiguration) explicit
Construct with a WebGL context.
EmscriptenApplication(const Arguments& arguments, const Configuration& configuration = Configuration{}) explicit
Construct without explicit GPU context configuration.
EmscriptenApplication(const Arguments& arguments, NoCreateT) explicit
Construct without setting up a canvas.
EmscriptenApplication(const EmscriptenApplication&) deleted
Copying is not allowed.
EmscriptenApplication(EmscriptenApplication&&) deleted
Moving is not allowed.

Public functions

auto operator=(const EmscriptenApplication&) -> EmscriptenApplication& deleted
Copying is not allowed.
auto operator=(EmscriptenApplication&&) -> EmscriptenApplication& deleted
Moving is not allowed.
auto exec() -> int
Execute the application.
void exit(int exitCode = 0)
Exit application main loop.
auto glContext() -> EMSCRIPTEN_WEBGL_CONTEXT_HANDLE
Underlying WebGL context.

Protected functions

void create(const Configuration& configuration, const GLConfiguration& glConfiguration)
Set up a canvas with given configuration for WebGL context.
void create(const Configuration& configuration)
Set up a canvas with given configuration and WebGL context.
void create()
Set up a canvas with default configuration and WebGL context.
auto tryCreate(const Configuration& configuration, const GLConfiguration& glConfiguration) -> bool
Try to create context with given configuration for WebGL context.
auto tryCreate(const Configuration& configuration) -> bool
Try to create context with given configuration.

Screen handling

auto windowSize() const -> Vector2i
Canvas size.
auto framebufferSize() const -> Vector2i
Framebuffer size.
auto dpiScaling() const -> Vector2
DPI scaling.
auto dpiScaling(const Configuration& configuration) const -> Vector2
DPI scaling for given configuration.
auto devicePixelRatio() const -> Vector2
Device pixel ratio.
void setWindowTitle(Containers::StringView title)
Set window title.
void setContainerCssClass(Containers::StringView cssClass)
Set container CSS class.
void swapBuffers()
Swap buffers.
void redraw()
Redraw immediately.
void viewportEvent(ViewportEvent& event) private virtual
Viewport event.
void drawEvent() private pure virtual
Draw event.

Keyboard handling

void keyPressEvent(KeyEvent& event) private virtual
Key press event.
void keyReleaseEvent(KeyEvent& event) private virtual
Key release event.

Mouse handling

enum class Cursor: UnsignedInt { Auto, Arrow, Hidden, ContextMenu, Help, Hand, WaitArrow, Wait, Cell, Crosshair, TextInput, VerticalTextInput, Alias, Copy, ResizeAll, NoDrop, No, Grab, Grabbing, AllScroll, ColResize, RowResize, ResizeN, ResizeE, ResizeS, ResizeW, ResizeNE, ResizeNW, ResizeSE, ResizeSW, ResizeWE, ResizeNS, ResizeNESW, ResizeNWSE, ZoomIn, ZoomOut } new in 2020.06
Cursor type.
void setCursor(Cursor cursor) new in 2020.06
Set cursor type.
auto cursor() -> Cursor new in 2020.06
Get current cursor type.
void mousePressEvent(MouseEvent& event) private virtual
Mouse press event.
void mouseReleaseEvent(MouseEvent& event) private virtual
Mouse release event.
void mouseMoveEvent(MouseMoveEvent& event) private virtual
Mouse move event.
void mouseScrollEvent(MouseScrollEvent& event) private virtual
Mouse scroll event.

Text input handling

auto isTextInputActive() const -> bool
Whether text input is active.
void startTextInput()
Start text input.
void stopTextInput()
Stop text input.
void setTextInputRect(const Range2Di& rect)
Set text input rectangle.
void textInputEvent(TextInputEvent& event) private virtual
Text input event.

Enum documentation

enum class Magnum::Platform::EmscriptenApplication::Cursor: UnsignedInt new in 2020.06

Cursor type.

Value names in this enum don't necessarily match the CSS names in order to be compatible with Sdl2Application and GlfwApplication.

Enumerators
Auto

The browser determines the cursor depending on the context. Since this affects the cursor when hovering the <canvas> element, this is usually equivalent to Cursor::Arrow. Matches cursor: auto in CSS.

Arrow

Arrow. Matches cursor: default in CSS.

Hidden

Hidden. Matches cursor: none in CSS.

ContextMenu

Context menu. Matches cursor: context-menu in CSS.

Help

Help. Matches cursor: help in CSS.

Hand

Hand. Matches cursor: pointer in CSS.

WaitArrow

Small wait cursor. Matches cursor: progress in CSS.

Wait

Wait. Matches cursor: wait in CSS.

Cell

Cell. Matches cursor: cell in CSS.

Crosshair

Crosshair. Matches cursor: crosshair in CSS.

TextInput

Text input. Matches cursor: text in CSS.

VerticalTextInput

Vertical text input. Matches cursor: vertical-text in CSS.

Alias

Alias. Matches cursor: alias in CSS.

Copy

Copy. Matches cursor: copy in CSS.

ResizeAll

Four pointed arrow pointing north, south, east, and west. Matches cursor: move in CSS.

NoDrop

Drop not allowed. Matches cursor: no-drop in CSS.

No

Slashed circle or crossbones. Matches cursor: not-allowed in CSS.

Grab

Grab. Matches cursor: grab in CSS.

Grabbing

Grabbing. Matches cursor: grabbing in CSS.

AllScroll

Scroll in any direction. Matches cursor: all-scroll in CSS.

ColResize

Column resize. Matches cursor: col-resize in CSS.

RowResize

Row resize. Matches cursor: row-resize in CSS.

ResizeN

Resize arrow pointing north. Matches cursor: n-resize in CSS.

ResizeE

Resize arrow pointing east. Matches cursor: e-resize in CSS.

ResizeS

Resize arrow pointing south. Matches cursor: s-resize in CSS.

ResizeW

Resize arrow pointing west. Matches cursor: w-resize in CSS.

ResizeNE

Resize arrow pointing northeast. Matches cursor: ne-resize in CSS.

ResizeNW

Resize arrow pointing northwest. Matches cursor: nw-resize in CSS.

ResizeSE

Resize arrow pointing southeast. Matches cursor: se-resize in CSS.

ResizeSW

Resize arrow pointing southwest. Matches cursor: se-resize in CSS.

ResizeWE

Double resize arrow pointing west and east. Matches cursor: ew-resize in CSS.

ResizeNS

Double resize arrow pointing north and south. Matches cursor: ns-resize in CSS.

ResizeNESW

Double resize arrow pointing northeast and southwest. Matches cursor: nesw-resize in CSS.

ResizeNWSE

Double resize arrow pointing northwest and southeast. Matches cursor: nwse-resize in CSS.

ZoomIn

Zoom in. Matches cursor: zoom-in in CSS.

ZoomOut

Zoom out. Matches cursor: zoom-out in CSS.

Function documentation

Magnum::Platform::EmscriptenApplication::EmscriptenApplication(const Arguments& arguments, const Configuration& configuration, const GLConfiguration& glConfiguration) explicit

Construct with a WebGL context.

Parameters
arguments Application arguments
configuration Application configuration
glConfiguration WebGL context configuration

Creates application with default or user-specified configuration. See Configuration for more information. The program exits if the context cannot be created, see tryCreate() for an alternative.

Magnum::Platform::EmscriptenApplication::EmscriptenApplication(const Arguments& arguments, const Configuration& configuration = Configuration{}) explicit

Construct without explicit GPU context configuration.

If Configuration::WindowFlag::Contextless is present or Magnum was not built with MAGNUM_TARGET_GL, this creates a window without any GPU context attached, leaving that part on the user.

If none of the flags is present and Magnum was built with MAGNUM_TARGET_GL, this is equivalent to calling EmscriptenApplication(const Arguments&, const Configuration&, const GLConfiguration&) with default-constructed GLConfiguration.

See also Enabling or disabling features for more information.

Magnum::Platform::EmscriptenApplication::EmscriptenApplication(const Arguments& arguments, NoCreateT) explicit

Construct without setting up a canvas.

Parameters
arguments Application arguments

Unlike above, the canvas is not set up and must be created later with create() or tryCreate().

int Magnum::Platform::EmscriptenApplication::exec()

Execute the application.

Sets up Emscripten to execute event handlers until exit() is called. See MAGNUM_EMSCRIPTENAPPLICATION_MAIN() for usage information.

void Magnum::Platform::EmscriptenApplication::exit(int exitCode = 0)

Exit application main loop.

Parameters
exitCode Ignored, present only for API compatibility with other app implementations.

When called from application constructor, it will cause the application to exit immediately after constructor ends, without any events being processed. Calling this function is recommended over std::exit() or Fatal, which exit immediately and without calling destructors on local scope. Note that, however, you need to explicitly return after calling it, as it can't exit the constructor on its own:

MyApplication::MyApplication(const Arguments& arguments):
    Platform::Application{arguments, NoCreate}
{
    // …

    if(!everythingGoingAsExpected) {
        exit(1);
        return;
    }

    // …
}

When called from the main loop, the application exits cleanly before next main loop iteration is executed.

EMSCRIPTEN_WEBGL_CONTEXT_HANDLE Magnum::Platform::EmscriptenApplication::glContext()

Underlying WebGL context.

Use in case you need to call Emscripten functionality directly. Returns 0 in case the context was not created yet.

void Magnum::Platform::EmscriptenApplication::create(const Configuration& configuration, const GLConfiguration& glConfiguration) protected

Set up a canvas with given configuration for WebGL context.

Parameters
configuration Application configuration
glConfiguration WebGL context configuration

Must be called only if the context wasn't created by the constructor itself, i.e. when passing NoCreate to it. Error message is printed and the program exits if the context cannot be created, see tryCreate() for an alternative.

void Magnum::Platform::EmscriptenApplication::create(const Configuration& configuration) protected

Set up a canvas with given configuration and WebGL context.

Equivalent to calling create(const Configuration&, const GLConfiguration&) with default-constructed GLConfiguration.

void Magnum::Platform::EmscriptenApplication::create() protected

Set up a canvas with default configuration and WebGL context.

Equivalent to calling create(const Configuration&) with default-constructed Configuration.

bool Magnum::Platform::EmscriptenApplication::tryCreate(const Configuration& configuration, const GLConfiguration& glConfiguration) protected

Try to create context with given configuration for WebGL context.

Unlike create(const Configuration&, const GLConfiguration&) returns false if the context cannot be created, true otherwise.

bool Magnum::Platform::EmscriptenApplication::tryCreate(const Configuration& configuration) protected

Try to create context with given configuration.

Unlike create(const Configuration&) returns false if the context cannot be created, true otherwise.

Vector2i Magnum::Platform::EmscriptenApplication::windowSize() const

Canvas size.

Canvas size to which all input event coordinates can be related. On HiDPI displays, canvas size can be different from framebufferSize(). See DPI awareness for more information. Note that this method is named "window size" to be API-compatible with Application implementations on other platforms.

Vector2i Magnum::Platform::EmscriptenApplication::framebufferSize() const

Framebuffer size.

On HiDPI displays, framebuffer size can be different from windowSize(). See DPI awareness for more information.

Vector2 Magnum::Platform::EmscriptenApplication::dpiScaling() const

DPI scaling.

How the content should be scaled relative to system defaults for given windowSize(). If a window is not created yet, returns zero vector, use dpiScaling(const Configuration&) const for calculating a value depending on user configuration. By default set to 1.0, see DPI awareness for more information.

Vector2 Magnum::Platform::EmscriptenApplication::dpiScaling(const Configuration& configuration) const

DPI scaling for given configuration.

Calculates DPI scaling that would be used when creating a window with given configuration. Takes into account DPI scaling policy and custom scaling specified via URL GET parameters. See DPI awareness for more information. devicePixelRatio()

Vector2 Magnum::Platform::EmscriptenApplication::devicePixelRatio() const

Device pixel ratio.

Crossplatform code shouldn't need to query this value because the pixel ratio is already expressed in the ratio of windowSize() and framebufferSize() values.

void Magnum::Platform::EmscriptenApplication::setWindowTitle(Containers::StringView title)

Set window title.

The title is expected to be encoded in UTF-8.

void Magnum::Platform::EmscriptenApplication::setContainerCssClass(Containers::StringView cssClass)

Set container CSS class.

Assigns given CSS class to the <div class="mn-container"> enclosing the application <canvas>. Useful for example to change aspect ratio of the view or stretch it to cover the full page. See Modifying page style, canvas size and aspect ratio for more information about possible values. Note that this replaces any existing class (except for .mn-container, which is kept), to set multiple classes separate them with whitespace.

void Magnum::Platform::EmscriptenApplication::swapBuffers()

Swap buffers.

Paints currently rendered framebuffer on screen.

void Magnum::Platform::EmscriptenApplication::redraw()

Redraw immediately.

Marks the window for redrawing, resulting in call to drawEvent() in the next iteration. You can call it from drawEvent() itself to redraw immediately without waiting for user input.

void Magnum::Platform::EmscriptenApplication::viewportEvent(ViewportEvent& event) virtual private

Viewport event.

Called when window size changes. The default implementation does nothing. If you want to respond to size changes, you should pass the new size to GL::DefaultFramebuffer::setViewport() (if using OpenGL) and possibly elsewhere (to SceneGraph::Camera::setViewport(), other framebuffers...).

Note that this function might not get called at all if the window size doesn't change. You should configure the initial state of your cameras, framebuffers etc. in application constructor rather than relying on this function to be called. Size of the window can be retrieved using windowSize(), size of the backing framebuffer via framebufferSize() and DPI scaling using dpiScaling(). See DPI awareness for detailed info about these values.

void Magnum::Platform::EmscriptenApplication::drawEvent() pure virtual private

Draw event.

Called when the screen is redrawn. You should clean the framebuffer using GL::DefaultFramebuffer::clear() (if using OpenGL) and then add your own drawing functions. After drawing is finished, call swapBuffers(). If you want to draw immediately again, call also redraw().

void Magnum::Platform::EmscriptenApplication::keyPressEvent(KeyEvent& event) virtual private

Key press event.

Called when an key is pressed. Default implementation does nothing.

void Magnum::Platform::EmscriptenApplication::keyReleaseEvent(KeyEvent& event) virtual private

Key release event.

Called when an key is released. Default implementation does nothing.

void Magnum::Platform::EmscriptenApplication::setCursor(Cursor cursor) new in 2020.06

Set cursor type.

Default is Cursor::Arrow.

void Magnum::Platform::EmscriptenApplication::mousePressEvent(MouseEvent& event) virtual private

Mouse press event.

Called when mouse button is pressed. Default implementation does nothing.

void Magnum::Platform::EmscriptenApplication::mouseReleaseEvent(MouseEvent& event) virtual private

Mouse release event.

Called when mouse button is released. Default implementation does nothing.

void Magnum::Platform::EmscriptenApplication::mouseMoveEvent(MouseMoveEvent& event) virtual private

Mouse move event.

Called when mouse is moved. Default implementation does nothing.

void Magnum::Platform::EmscriptenApplication::mouseScrollEvent(MouseScrollEvent& event) virtual private

Mouse scroll event.

Called when a scrolling device is used (mouse wheel or scrolling area on a touchpad). Default implementation does nothing.

bool Magnum::Platform::EmscriptenApplication::isTextInputActive() const

Whether text input is active.

If text input is active, text input events go to textInputEvent().

void Magnum::Platform::EmscriptenApplication::startTextInput()

Start text input.

Starts text input that will go to textInputEvent().

void Magnum::Platform::EmscriptenApplication::stopTextInput()

Stop text input.

Stops text input that went to textInputEvent().

void Magnum::Platform::EmscriptenApplication::setTextInputRect(const Range2Di& rect)

Set text input rectangle.

The rect defines an area where the text is being displayed, for example to hint the system where to place on-screen keyboard.

void Magnum::Platform::EmscriptenApplication::textInputEvent(TextInputEvent& event) virtual private

Text input event.

Called when text input is active and the text is being input.